基于H.264的远程视频监控

来源:互联网 发布:java jdk版本查看 编辑:程序博客网 时间:2024/05/01 14:23
有兴趣的留言一起研究
    开始做自己的毕业设计了,希望这个暑假能把毕业设计搞完,下学期就去找工作,希望能早到一份好工作。回到正题,经过几天的摸索对H.264远程监控有了一定的认识,特别是图像采集这一块。jpg、yuv、bmp,M-jpeg等。
1、首先分析一下手上的资源:
   手上有两款开发板,一款是4.3寸天嵌的tq2440,和一款友善的tiny6410,一个中星微的zc301摄像头,一个coms的ov9650摄像头。mjpg-streamer源码,经过测试zc301的摄像头输出的是经过硬件压缩的jpg格式的图片。ov9650输出的是RGB565(未经测试,网上多处是这样说的)。老师的要求是用H.264编码并进行RTP封装然后发送,从网上得知H.264是目前的主流,所以有了学习的动力,后面希望能把opencv加入进去。客户端准备用android做。
2、目前面临的问题:
   jpg格式的图像解码为原始图像后进行H.264压缩是否可行?
   mjpg-streamer是否可以发送H.264文件?
   H.264和RTP封装了解基本为零。
3、接下来的工作:
   阅读mjpg-streamer源码。start......
4、ffmpeg的学习
用ffmpeg采集zc301摄像头的视频
输入如下命令
-f强制格式转化
-r 5设置每秒采集几帧
ffmpeg -f video4linux2 -s 320X240 -r 5 -i /dev/video0 t.avi
视频是采集到了,可是用linux下的播放器打开视频,视频一闪而过,就闪了一张采集开始的画面,后面就黑屏了,但是那到windows下有可以了。网上说要用servfox(不知道是个什么东西),现在至少可以确认ffmpeg可以将摄像头视频读取出来,现在要做的就是格式转换,H264需要yuv420的格式,现在有两个方案,一是买一个网眼V2000的摄像头,二是看能不能把jpeg转换为yuv420。现在必须学会如何使用ffmpeg的API获取摄像头的一帧图像。
4.1用ffmpeg采集一帧摄像头的图像
用的上一大牛的程序,不得不说中国人的创造力是多么的有限,网上一搜全是同一个代码,代码如下,程序中有一个小错误,可能是和我开发板有关吧,反正我这个开发板用源程序会出现Segmentation fault,原因是因为AVPacket *packet指针没初始化。
正确代码如下,红色部分为出错部分。
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
 
 #include <libavformat/avformat.h>
 #include <libavcodec/avcodec.h>
 #include <libavdevice/avdevice.h>
 
 
 void captureOneFrame()
 {
     AVFormatContext *fmtCtx = NULL;
     AVFormatParameters inputFmtParameter;
     AVPacket packet, *pkt=&packet;//原来的代码为 AVPacket *packet;或者AVPacket *pcaket;
         
     //输入格式(V4L2)
     AVInputFormat *inputFmt = av_find_input_format ("video4linux2"); 
     if (inputFmt == NULL) 
     {
         printf("can not find_input_format\n");
         return;
     }
 
     memset (&inputFmtParameter, 0, sizeof(inputFmtParameter));
     //采集图像的高度
     inputFmtParameter.height = 240;
     //采集图像的宽度
     inputFmtParameter.width  = 320;
 
     //打开摄像头设备
     if (av_open_input_file ( &fmtCtx, "/dev/video0", inputFmt,
                sizeof(inputFmtParameter),&inputFmtParameter) < 0)
     {
         printf("can not open_input_file\n");
          return;
     }
     //从摄像头获取一帧图像
     av_read_frame(fmtCtx, pkt);
     //输出图像的大小
     printf("data length = %d\n",pkt->size);
     
     FILE *fp;
     //打开(新建)文件
     fp = fopen("out.yuv", "wb");
     if (fp < 0)
     {
         printf("open frame data file failed\n");
         return ;
     }
     //将数据写入文件
     fwrite(pkt->data, 1, pkt->size, fp);
     //关闭文件
     fclose(fp);
 
     //关闭设备文件
     av_close_input_file(fmtCtx);
 }
 
 
 int main()
 {
     avcodec_init();    
     avcodec_register_all();
     av_register_all ();
     avdevice_register_all();
 
     captureOneFrame();
 
     return 0;
 }
4.2 undefined reference to `img_convert

解决办法是将原有的img_convert函数改为sws_scale函数,原来的函数调用为:

// Convert the image from its native format to RGB img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,                 (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width,                 pCodecCtx->height);

修改为

#include <ffmpeg/swscale.h> // other codes static struct SwsContext *img_convert_ctx; // other codes img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,  PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); // other codes // Convert the image from its native format to RGB sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize,  0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

编译正常,测试后程序可用,搞定。

4.3 undefined reference to `avcodec_decode_video'

修改为avcodec_decode_video2(pCodecCtx, &pFrame, &frameFinished, &packet); 可是程序还是有错误,Segmentation fault。

4.4 X11/extensions/XShm.h: 没有那个文件或目录

在这里找到了解决方法http://hi.baidu.com/lmy0525/blog/item/5b7af500169f5391e950cda2.html

X11/extensions/XShm.h: No such file or directory

安装x11proto-xext-dev和libxext-dev  通过命令sudo apt-get install libxext-dev解决

4.5

/opt/webcam/vshow.cpp:111: undefined reference to `sws_getContext(int, int, PixelFormat, int, int, PixelFormat, int, SwsFilter*, SwsFilter*, double const*)'

/opt/webcam/vshow.cpp:111: undefined reference to `Ctx::sws'

/opt/webcam/vshow.cpp:128: undefined reference to `avpicture_alloc(AVPicture*, PixelFormat, int, int)'

vshow.o: In function `vs_show(void*, unsigned char**, int*)':

/opt/webcam/vshow.cpp:154: undefined reference to `avpicture_free(AVPicture*)'

/opt/webcam/vshow.cpp:155: undefined reference to `Ctx::sws'

/opt/webcam/vshow.cpp:155: undefined reference to `sws_freeContext(SwsContext*)'

/opt/webcam/vshow.cpp:159: undefined reference to `sws_getContext(int, int, PixelFormat, int, int, PixelFormat, int, SwsFilter*, SwsFilter*, double const*)'

/opt/webcam/vshow.cpp:159: undefined reference to `Ctx::sws'

/opt/webcam/vshow.cpp:160: undefined reference to `avpicture_alloc(AVPicture*, PixelFormat, int, int)'

/opt/webcam/vshow.cpp:184: undefined reference to `Ctx::sws'

/opt/webcam/vshow.cpp:184: undefined reference to `sws_scale(SwsContext*, unsigned char const* const*, int const*, int, int, unsigned char* const*, int const*)'

collect2: ld returned 1 exit status

make: *** [webcam_shower] 错误 1

解决办法
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
改为
extern "C" {
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
};
4.6./webcam_shower: error while loading shared libraries: libavcodec.so.53: cannot open shared object file: No such file or directory

1.#vi /etc/ld.so.conf

添加一行:/opt/webcam/ffmpeg/lib
4.7现在问题又出来了
ERR: bind 3020
我觉得已经没有办法了,在找找吧。被吓到了,自己粗心了!
4.8摄像头输出格式的设置
今天发现了一个有趣的事情,摄像头在windows插过后,那到linux开发板上就不能用了。纠结了老半天,后面经过摸索,发现是和摄像头输出格式设置有关,当摄像头插到window平台后,系统对其进行初始化,可能设置为jpeg格式或者其它格式,或者根本就没有。然后然后那到开发板上用就出错了,也不能说出错,是由于我的程序问题,应为我刚才是没有对摄像头进行格式设置,就进行了后面的工作,结果就报错了,经过摸索,专门写了一个函数,功能是列摄像头支持到格式,并且设置需要的格式,至于列出摄像头支持的格式,这个没有进行测试,可能有误,还望指正。代码如下:

点击(此处)折叠或打开

  1. void CheckAndSetFmt(int fd,char* Sformat)
  2. {
  3.     int rc;
  4.     struct v4l2_fmtdesc fmt_desc; //获取摄像头
  5.     memset(&fmt_desc, 0, sizeof(fmt_desc));
  6.     struct v4l2_format fmt; //设置获取视频的格式
  7.     memset(&fmt, 0, sizeof(fmt));
  8.     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE既然是永远了为什么还要弄出个选项了
  9.     fmt.fmt.pix.width = 320; //设置视频宽度
  10.     fmt.fmt.pix.height = 240; //设置视频高度
  11.     uint32_t i = 0;
  12.     printf("Check the Camera format and Set the format\n");
  13.     for(i=0;;i++)
  14.     {
  15.         fmt_desc.index = i;
  16.         fmt_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  17.         rc = ioctl(fd, VIDIOC_ENUM_FMT, &fmt_desc);
  18.         if (rc < 0)
  19.          break;
  20.          printf("support {pixelformat= %c%c%c%c, description= %s}\n",
  21.         fmt_desc.pixelformat & 0xFF, 
  22.         (fmt_desc.pixelformat >> 8) & 0xFF, 
  23.         (fmt_desc.pixelformat>> 16) & 0xFF, 
  24.         (fmt_desc.pixelformat >> 24) & 0xFF, 
  25.         fmt_desc.description);
  26.         if (strcmp((const char*) fmt_desc.description, Sformat) == 0)//"YUV 4:2:2 (YUYV)"
  27.             {
  28.                     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //视频源的格式为YUYV
  29.             if (ioctl(fd, VIDIOC_S_FMT, &fmt) >= 0) //使配置生效
  30.                         printf("Set format %s succeed\n",Sformat);
  31.             else
  32.                 printf("Set format %s failed\n",Sformat);
  33.             }    
  34.     } 
  35. }

  36. //函数功能列出摄像头支持到格式,并且设置需要到格式
  37. int main(int argc, char** argv)
  38. {
  39.     int fd = open("/dev/video0", O_RDWR);            //打开摄像头设备,使用阻塞方式打开
  40.     if (fd<0)
  41.     {
  42.         printf("open error\n");
  43.         return -1;
  44.     }
  45.     CheckAndSetFmt(fd,"YUV 4:2:2 (YUYV)");
  46.     return 0;
  47. }


 4.9摄像头缓冲区映射的问题

点击(此处)折叠或打开

  1. // mmap
  2.             v4l2_buffer buf;
  3.             for (int i = 0; i < BUFSISE; i++) {
  4.                 
  5.                 memset(&buf, 0, sizeof(buf));
  6.                 buf.type = Rebufs.type;
  7.                 buf.memory = Rebufs.memory;
  8.                 buf.index = i;//如何不加这个会出现四个映射在同一个摄像头缓冲区上
  9.                 if (ioctl(id, VIDIOC_QUERYBUF, &buf) < 0) {
  10.                     fprintf(stderr, "%s: VIDIOC_QUERYBUF ERR\n", __func__);
  11.                     close(id);
  12.                     delete ctx;
  13.                     return 0;
  14.                 }
  15.                 ctx->bufs[i].length = buf.length;
  16.                 ctx->bufs[i].start= mmap(NULL, buf.length, PROT_READ|PROT_WRITE,MAP_SHARED, id, buf.m.offset);
  17.                 fprintf(stderr, "buf.length=%d buf.m.offset=%d\t ctx->bufs[i].start=%d\n",buf.length,buf.m.offset,ctx->bufs[i].start);
  18.             }
 
5.H264相关
5.1 AVPicture解析
typedef struct AVPicture {
    uint8_t *data[AV_NUM_DATA_POINTERS];
    int linesize[AV_NUM_DATA_POINTERS];     ///< number of bytes per line
} AVPicture;复习一下C语言支持,指针为4个字节,让我纠结了1个多小时,uint8_t* data[AV_NUM_DATA_POINTERS]是指针数组存储的是4个字节的指针!

//================================

对应AVPicture里面有data[4]和linesize[4]其中data是一个指向指针的指针(二级、二维指针),也就是指向视频数据缓冲区的首地址,而data[0]~data[3]是一级指针,可以用如下的图来表示:

data -->xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        ^                ^              ^
        |                |              |
         data[0]      data[1]         data[2]

比如说,当pix_fmt=PIX_FMT_YUV420P时,data中的数据是按照YUV的格式存储的,也就是:

data -->YYYYYYYYYYYYYYUUUUUUUUUUUUUVVVVVVVVVVVV ^ ^ ^ | | | data[0] data[1] data[2]

linesize是指对应于每一行的大小,为什么需要这个变量,是因为在YUV格式和RGB格式时,每行的大小不一定等于图像的宽度,对于RGB格式输出时,只有一个通道(bgrbgrbgr......)可用,即linesize[0],和data[0],so RGB24 : data[0] = packet rgb//bgrbgrbgr......

linesize[0] = width*3

其他的如data[1][2][3]与linesize[1][2][3]无任何意义.

而对于YUV格式输出时,有三个通道可用,即data[0][1][2],与linesize[0][1][2],而yuv格式对于运动估计时,需要填充padding(right, bottom),故:linesize=width+padding size(16+16).



int rs = sws_scale(ctx->sws, ctx->pic_src.data, ctx->pic_src.linesize,
0, ctx->rows, ctx->pic_target.data, ctx->pic_target.linesize);
经过sws_scale函数后ctx->pic_target.data[0],ctx->pic_target.data[1],ctx->pic_target.data[2]分别存储了Y,U,V平面的首地址。这是通过代码进行测试得出的结论,关键代码代码:

点击(此处)折叠或打开

  1. ctx->pic_src.data[0] = (unsigned char*)ctx->bufs[buf.index].start;
  2.     ctx->pic_src.data[1] = ctx->pic_src.data[2] = ctx->pic_src.data[3] = 0;
  3.     ctx->pic_src.linesize[0] = ctx->bytesperrow;
  4.     ctx->pic_src.linesize[1] = ctx->pic_src.linesize[2] = ctx->pic_src.linesize[3] = 0;
  5.     fprintf(stderr, "ctx->pic_src.data[1] =%d\n", ctx->pic_src.data[1]);
  6.     // sws_scale
  7.     int rs = sws_scale(ctx->sws, ctx->pic_src.data, ctx->pic_src.linesize,
  8.             0, ctx->rows, ctx->pic_target.data, ctx->pic_target.linesize);
  9.     fprintf(stderr, "ctx->pic_target.data[1] =%d\n", ctx->pic_target.data[1]);
  10.     // out
  11.     for (int i = 0; i < 4; i++) {
  12.         pic->data[i] = ctx->pic_target.data[i];
  13.         pic->stride[i] = ctx->pic_target.linesize[i];
  14.         fprintf(stderr, "pic_target.data[%d] =%d\n",i, ctx->pic_target.data[i]);
  15. fprintf(stderr, "ctx->pic_target.linesize[%d] =%d\n",i, ctx->pic_target.linesize[i]);
  16.     }
串口答应的结果:
ctx->pic_src.data[1] =0
ctx->pic_target.data[1] =182048
pic_target.data[0]          =105248
ctx->pic_target.linesize[0] =320
pic_target.data[1]          =182048
ctx->pic_target.linesize[1] =160
pic_target.data[2]          =201248
ctx->pic_target.linesize[2] =160
pic_target.data[3]          =0
ctx->pic_target.linesize[3] =0
182048-105248=76800=320x240。因为是单个平面,所以一个像素点一个字节。Y、U、V的宽度分别是320,160,160这应该是4:2:0产生的效果,为什么是4:2:0一直没弄懂。

5.2 ffmpeg的H264软件编码和ARM11提供的H264硬件编码的比较
分别用两种方法进行采集图片并压缩为264格式,得出的结果是硬件编码大约是软件编码的10倍,如图:
ARM11硬件编码平均0.25s抓取和压缩一张图片(像素为320x240)

ffmpeg软件编码平均2.2s抓取和压缩一张图片
看了我还是低估了ARM11的硬件压缩速度,上图的数据为ARM11压缩一帧264图片的时间,单位为秒,captuere是打印的时候没改!

5.3 ARM硬件编码的h264视频播放很快
那是应为一般播放器每秒播放30fps,而我们的h264格式的视频并没有这些信息,而且我的摄像头每秒只能获取4到5帧的图像,也就是说我用暴风影音播放1秒钟其实是播放了6到7秒的采集图像,所以说视频播放的很快。
解决办法:1.在解码一张图片后的时候稍微延时一下。
  2.用rtp协议(里面有个时间戳,现在正在看)
6.live555的学习
live555的安装:
解压修改config.armlinux 中第一行:CROSS_COMPILE?=        arm-linux- 其他的没有变了。
然后./genMakefile armlinux。
再make,就OK了,它没有make install。
7.vlc流媒体搭建
我使用的是vlc-0.9.9,刚开始用的其它版本,要嘛就是telnet连不上,要嘛就是播放不了,
1. 打开命令行服务,监听5554媒体端口
vlc.exe -I telnet --control telnet --telnet-password videolan --rtsp-host 172.19.72.110:5554  //密码如果自己设置需要一定的复杂度,不然设置不了
2. 登录RTSP点播服务器

在这里我是运用SecureCRT软件进行telnet登录。界面如下所示:

1

当点击连接后,要求输入密码:videolan,回车后如果显示:Welcome, Master,则表明登录成功。

3. 在vlc命令行接口中新增一个条目
new fzhman vod enabled input D:\TDDOWNLOAD\1.avi
4. 在客户中的vlc中- 打开网络媒体- 输入下面的东东就可以观看大片啦
rtsp://172.19.72.110:5554/fzhman

7.2vlc通过网络上的sdp文件打开视频
vlc -vvv (视频文件地址) --sout "rtp{dst=172.19.72.255(采用广播方式),port=1234,sdp=rtsp://172.19.72.110:8080/test.sdp}"
刚开始这条命令一直不行,结果把下面的流程做了一边又行了,目前不知道原因

首先进入到VLC的安装目录下,执行类似如下的命令:

vlc –ttl 12 -vvv –color -I telnet –telnet-password videolan –rtsp-host 219.219.218.239:5554

对上述参数的解释:–ttl:是对hop的限制;-vvv选项用来输出错误信息,可以省略;219.219.218.239是RSTP点播服务器的主机地址; videolan 是telnet登录RTSP点播服务器时输入的口令,这两个部分用户可以根据自己的情况进行修改。

登录RTSP点播服务器

在这里我是运用SecureCRT软件进行telnet登录。界面如下所示:

当点击连接后,要求输入密码:videolan,回车后如果显示:Welcome, Master,则表明登录成功。

在主机名栏输入:219.219.218.239,该内容必须与前面建立的RTSP点播服务器的主机地址一致;在端口栏输入:4212,该端口号在使用VLC默认设置时不能改为其他的数值。

接下来就可以设置点播文件了

在登录成功的界面上,输入以下的命令:

new Test vod enabled 
setup Test input myVideo.mpg

最后,就可以在客户端观看视频,命令如下:

vlc rtsp://219.219.218.239:5554/Test

还可以通过VLC播放器的VLM进行可视化配制,并生成.vlm配置文件,然后利用SecureCRT工具登录到RTSP点播服务器,利用load命令将配置文件导入,使用show命令可以查看导入的文件信息。

测试结果:

CDXA/MPEG-PS   未经过编码转换即可播放,但是仅可以用VLC播放器进行播放。原因是其他播放器无法解析rtsp://219.219.218.147:5554/Test 中的Test文件名。


7.3知己做的rtsp服务器和网上的rtsp服务器的区别

1)先说网上的rtsp服务器,我是通过千里眼(http://218.204.223.237:8081/wap/)获取的视频了进行抓包分析

rtsp://218.204.223.237:554/mobile/1/66251FC11353191F/syiubra2azl52x0g.sdp

如下图


产生的问题:1、RTP到底是用TCP发生还是用UDP发送,根据抓包的情况应该是通过TCP发发送的,(推测可能是应为远距离传输所以采用的是RTP打包为TCP)。


2)下面是对我自己的RTSP服务器抓包分析

可以看到我还没点击播放rstp服务器就向客户端发送UDP包了!

也就是说rtsp://172.19.72.110:8080/test.sdp只是让vlc知道如何去解析这些udp包


点击播放,就可以看到视频了



总结:我自己通过vlc做的简单的rstp服务器只是将视频同通过打包为RTP然后通过UDP发送到固定IP或者广播到局域网中,然后客户端通过vlc通过读取指定rtsp://172.19.72.110:8080/test.sdp的sdp文件对udp进行解析并播放,我都不清楚我的到底是不是rtsp流媒体服务器,服务器直接就发送了,好像都没看到过RTSP协议的影子,要是用3G网卡哪的多耗流量呀,还有就是估计远程传输得用TCP模式的RTP。希望我的这些猜测能在后面得到证实


7.4 RTSP传输方式由什么决定呢? TCP or UDP

请教一个问题:RTSP 中 SETUP 命令中传输方式是有什么指定呢? 是由服务器决定的吗?

SETUP rtsp://www.loacl.com/sample.3gp

Transport: RTP/AVP/TCP 【TCP传输方式】 或
Transport: RTP/AVP 【UDP传输方式】

-------------------------

我用的是openRTSP,如果没有 -t 参数的话,就接受不到数据,加上-t 才能收到数据。

-t 参数为 TCP方式传输.  

我用抓包工具观察  
有-t 的话 Transport: RTP/AVP/TCP;
无-t 的话 Transport: RTP/AVP;

-----------------------

对于一个未知的服务器,这么判断服务器是什么方式传输呢? udp 还是 tcp  


------解决方案--------------------------------------------------------
openRTSP有-t参数说明使用tcp接收数据;

无-t默认是UDP接收数据,而无-t参数接收不到数据,大概是因为你机器狮子内网,而rtsp server是在外网吧?

如果rtsp server在外网,外网udp数据自然无法到达你所在的内网的;

rtsp协议本身不支持似网穿透、UDP打洞等; 
------解决方案--------------------------------------------------------
貌似发describ的时候,服务器会返回一些信息的吧,貌似就能知道是tcp还是udp了。

------解决方案--------------------------------------------------------
openRTSP有-t是TCP方式。


用vlc2.0.1做rtsp服务器,vlc-0.9.9没有rtsp这个选项,

早知道vlc很强大, 但是流媒体功能一直没有成功过, 今天终于成功了, 分享经验给大家
举例, 本地有一视频文件
打开vlc, [media]->streaming-> load your media file-> press[streaming] 
output类型选mmsh,  地址选你本机的ip地址, 端口可以不用变 1234
[方案 或 档案]选 video- wma wmv  或 windows(wmv or asf)
然后点击[streaming]

接下来在ie的地址栏里写入mms://your ip address:1234就可以看到你现在正在播放的视频流媒体了


7.5到底是选择live555还是vlc做rtsp服务器了?
做如下分析
在androd的官方文档中提到

官方文档:Android Supported Media Formats提到

The following network protocols are supported for audio and video playback:

RTSP (RTP, SDP)
HTTP/HTTPS progressive streaming
HTTP/HTTPS live streaming draft protocol:
MPEG-2 TS media files only
Protocol version 3 (Android 4.0 and above)
Protocol version 2 (Android 3.x)
Not supported before Android 3.0

VideoView类支持以上视频流的播放,经测试似乎只有mpeg4编码的视频才能通过rtsp发送到android上显示

(1)创建H264 编码结构。调用SsbSipH264EncodeInit (width, height, frame_rate, bitrate, gop_num)函数实现的,其中width 表示图像的宽度,height 表示图像的高度,frame_rate 表示帧频,bitrate 表示比特率或码率,gop_num 表示两个相离关键帧之间最多包含多少个帧(B 或P 帧)。
获取linux系统时间的函数
char *timeString(char *str)
{
struct   tm     *ptm; 
    long       ts; 
    int      year,mon,day,hour,min,sec;
    ts    =   time(NULL); 
    ptm   =   localtime(&ts); 
    year  =   ptm-> tm_year+1900;     //年 
    mon   =   ptm-> tm_mon+1;         //月 
    day   =   ptm-> tm_mday;          //日 
    hour  =   ptm-> tm_hour;        //h为与现在相比的时间差,h为0时表示当前时间
   min   =   ptm-> tm_min;           //分 
    sec   =   ptm-> tm_sec;           //秒
sprintf(str, "%d-%d-%d-%d-%d-%d", year, mon, day, hour, min, sec);
    return str;
}
原文:http://blog.chinaunix.net/uid-26851094-id-3276088.html
0 0