Chinaunix首页 | 论坛 | 博客
  • 博客访问: 521725
  • 博文数量: 86
  • 博客积分: 1076
  • 博客等级: 准尉
  • 技术积分: 1018
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-02 19:15
文章分类

全部博文(86)

文章存档

2013年(15)

2012年(69)

2011年(2)

分类: 嵌入式

2013-05-15 09:50:22

    【特别提醒:本文写作时,贴上去的代码,"\n"回车符号的"\"没有了,不知道为啥,所以阅读代码时请注意区分,或者欢迎到我的CSDN网站阅读
        http://blog.csdn.net/jgf_ntu/article/details/8928977】   

    一般我们都是用ffmpeg来解码音视频,如果是JPG和PNG等图片呢,其实跟解码视频是一样的,因为视频也是一幅一幅的图片进行解码的,只不过视频的帧是会前后参考的,而JPG等图片来讲,就是独立的一帧而已。

那么,我们参考之前的一篇文章http://blog.chinaunix.net/uid-25272011-id-3633434.html一段ffmpeg视频解码为YUV420P的示例代码】 ,稍作修改即可来演示。
    同时为了能够保存解码后的图片,我们还需要了解一些YUV或者RGB等各种格式的数据的内存存储方式,这些知识可以参照我之前的另一篇文章《YUV420格式解析》 ,这里详细描述了各种格式的空间存储机制。
    一般解码视频时,我们在调用ffmpeg进行解码时,生成的格式一般都是YUV420P的,但解码图皮时可能会有各种形式,如YUVJ422P、YUVJ444P、RGB24等等,本文没有采用ffmpeg的sws_scale函数做统一的转
换,为的是记录如何来存储这些解码后的图片。

     先给出如何从ffmpeg的Frame结构体中保存上述的四种解码后的数据:

点击(此处)折叠或打开

  1. /**
  2.  * save yuv420p frame [YUV]
  3.  */
  4. void yuv420p_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
  5. {
  6.     int i = 0;

  7.     int width = pCodecCtx->width, height = pCodecCtx->height;
  8.     int height_half = height / 2, width_half = width / 2;
  9.     int y_wrap = pFrame->linesize[0];
  10.     int u_wrap = pFrame->linesize[1];
  11.     int v_wrap = pFrame->linesize[2];

  12.     unsigned char *y_buf = pFrame->data[0];
  13.     unsigned char *u_buf = pFrame->data[1];
  14.     unsigned char *v_buf = pFrame->data[2];

  15.     //save y
  16.     for (i = 0; i < height; i++)
  17.         fwrite(y_buf + i * y_wrap, 1, width, pfout);
  18.     fprintf(stderr, "===>save Y successn");
  19.     //save u
  20.     for (i = 0; i < height_half; i++)
  21.         fwrite(u_buf + i * u_wrap, 1, width_half, pfout);
  22.     fprintf(stderr, "===>save U successn");
  23.     //save v
  24.     for (i = 0; i < height_half; i++)
  25.         fwrite(v_buf + i * v_wrap, 1, width_half, pfout);
  26.     fprintf(stderr, "===>save V successn");

  27.     fflush(pfout);
  28. }

  29. /**
  30.  * save yuv422p frame [YUV]
  31.  */
  32. void yuv422p_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
  33. {
  34.     int i = 0;

  35.     int width = pCodecCtx->width, height = pCodecCtx->height;
  36.     int height_half = height / 2, width_half = width / 2;
  37.     int y_wrap = pFrame->linesize[0];
  38.     int u_wrap = pFrame->linesize[1];
  39.     int v_wrap = pFrame->linesize[2];

  40.     unsigned char *y_buf = pFrame->data[0];
  41.     unsigned char *u_buf = pFrame->data[1];
  42.     unsigned char *v_buf = pFrame->data[2];

  43.     //save y
  44.     for (i = 0; i < height; i++)
  45.         fwrite(y_buf + i * y_wrap, 1, width, pfout);
  46.     fprintf(stderr, "===>save Y successn");
  47.     //save u
  48.     for (i = 0; i < height; i++)
  49.         fwrite(u_buf + i * u_wrap, 1, width_half, pfout);
  50.     fprintf(stderr, "===>save U successn");
  51.     //save v
  52.     for (i = 0; i < height; i++)
  53.         fwrite(v_buf + i * v_wrap, 1, width_half, pfout);
  54.     fprintf(stderr, "===>save V successn");

  55.     fflush(pfout);
  56. }

  57. /**
  58.  * save rgb24 frame [PPM]
  59.  */
  60. void rgb24_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
  61. {
  62.     int i = 0;
  63.     int width = pCodecCtx->width, height = pCodecCtx->height;
  64.     
  65.     /* write PPM header */
  66.     fprintf(pfout, "P6n%d %dn255n", width, height);
  67.   
  68.     /* write pixel data */
  69.     for(i =0; i < height; i++)
  70.         fwrite(pFrame->data[0] + i * pFrame->linesize[0], 1, width * 3, pfout);
  71.     
  72.     fflush(pfout);
  73. }

  74. /**
  75.  * save yuv444p frame [YUV]
  76.  */
  77. void yuv444p_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
  78. {
  79.     int i = 0;

  80.     int width = pCodecCtx->width, height = pCodecCtx->height;
  81.     int y_wrap = pFrame->linesize[0];
  82.     int u_wrap = pFrame->linesize[1];
  83.     int v_wrap = pFrame->linesize[2];

  84.     unsigned char *y_buf = pFrame->data[0];
  85.     unsigned char *u_buf = pFrame->data[1];
  86.     unsigned char *v_buf = pFrame->data[2];

  87.     //save y
  88.     for (i = 0; i < height; i++)
  89.         fwrite(y_buf + i * y_wrap, 1, width, pfout);
  90.     fprintf(stderr, "===>save Y successn");
  91.     //save u
  92.     for (i = 0; i < height; i++)
  93.         fwrite(u_buf + i * u_wrap, 1, width, pfout);
  94.     fprintf(stderr, "===>save U successn");
  95.     //save v
  96.     for (i = 0; i < height; i++)
  97.         fwrite(v_buf + i * v_wrap, 1, width, pfout);
  98.     fprintf(stderr, "===>save V successn");

  99.     fflush(pfout);
  100. }

     可以对照各种格式看一下代码,应该是很好理解的,下面是其余的main代码,可以编译运行

点击(此处)折叠或打开

  1. /**
  2.  * decode picture by ffmpeg-1.0 for jpg and png ...
  3.  *
  4.  * 2013-05-14
  5.  *    juguofeng<jgfntu@gmail.com>
  6.  */

  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <string.h>

  10. #include <libavcodec/avcodec.h>
  11. #include <libavformat/avformat.h>


  12. FILE *pfout = NULL;
  13. char ffrvout[128] = { 0 };

  14. /* how many yuv pic you want to save */
  15. #define FRAME_NUM 1
  16. /* enable video demux data save to file */
  17. //#define ENABLE_DEMUX_SAVE
  18. /* enable yuv pic save to file */
  19. #define ENABLE_YUV_SAVE
  20. /* enable print each video bytes */
  21. #define ENABLE_PRINT_FRAME_BYTES
  22. /* how many bytes you want to print */
  23. #define PRINT_BYTES 30


  24. /**
  25.  * main thread
  26.  */
  27. int main(int argc, char *argv[])
  28. {
  29.     int i;
  30.     char szFileName[128] = {0};
  31.     int decLen = 0;
  32.     int frame = 0;

  33.     AVCodecContext *pCodecCtx = NULL;
  34.     AVFrame *pFrame = NULL;
  35.     AVCodec *pCodec = NULL;
  36.     AVFormatContext *pFormatCtx = NULL;

  37.     if(argc != 3)
  38.     {
  39.         fprintf(stderr, "ERROR:need 3 argument!n");
  40.         exit(-1);
  41.     }
  42.     
  43.     sprintf(szFileName, "%s", argv[1]);

  44. #ifdef ENABLE_DEMUX_SAVE
  45.     FILE* frvdemux = fopen("rvdemuxout.rm","wb+");
  46.     if (NULL == frvdemux)
  47.     {
  48.         fprintf(stderr, "create rvdemuxout file failedn");
  49.         exit(1);
  50.     }
  51. #endif

  52.     /* output yuv file name */
  53.     sprintf(ffrvout, "%s", argv[2]);

  54.     pfout = fopen(ffrvout, "wb+");
  55.     if (NULL == pfout)
  56.     {
  57.         printf("create output file failedn");
  58.         exit(1);
  59.     }
  60.     printf("==========> Begin test ffmpeg call ffmpeg rv decodern");
  61.     av_register_all();

  62.     /* Open input video file */
  63.     //printf("before avformat_open_input [%s]n", szFileName);
  64.     if(avformat_open_input(&pFormatCtx, szFileName, NULL, NULL)!= 0)
  65.     {
  66.         fprintf(stderr, "Couldn't open input filen");
  67.         return -1;
  68.     }
  69.     //printf("after avformat_open_inputn");

  70.     /* Retrieve stream information */
  71.     if(av_find_stream_info(pFormatCtx) < 0)
  72.     {
  73.         printf("av_find_stream_info ERRORn");
  74.         return -1;
  75.     }
  76.     //printf("after av_find_stream_info, n");


  77.     /* Find the first video stream */
  78.     int videoStream = -1;
  79.     printf("==========> pFormatCtx->nb_streams = %dn", pFormatCtx->nb_streams);

  80.     for(i = 0; i < pFormatCtx->nb_streams; i++) {
  81.         if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
  82.             videoStream = i;
  83.             printf("the first video stream index: videoStream = %dn",videoStream);
  84.             break;
  85.         }
  86.     }

  87.     if(videoStream == -1)
  88.         return -1;        // Didn't find a video stream

  89.     /* Get a pointer to the codec context for the video stream */
  90.     pCodecCtx = pFormatCtx->streams[videoStream]->codec;
  91.     printf("pCodecCtx->codec_id = %dn", pCodecCtx->codec_id);

  92.     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
  93.     if(pCodec == NULL) {
  94.         fprintf(stderr, "can not find decoder!n");
  95.         return -1;
  96.     }

  97.     /* Open codec */
  98.     if(avcodec_open(pCodecCtx, pCodec)<0)
  99.     {
  100.         printf("cannot open software codecn");
  101.         return -1; // Could not open codec
  102.     }
  103.     printf("==========> Open software codec successn");

  104.     pFrame = avcodec_alloc_frame();
  105.     if(pFrame == NULL)
  106.     {
  107.         fprintf(stderr, "avcodec_alloc_frame() ERRORn");
  108.         return -1;
  109.     }
  110.     
  111.     /* flag whether we get a decoded yuv frame */
  112.     int frameFinished;
  113.     int packetno = 0;

  114.     AVPacket packet;
  115.     av_init_packet(&packet);

  116.     while(av_read_frame(pFormatCtx, &packet) >= 0) {
  117.         //printf("[main]avpkt->slice_count=%dn", packet.sliceNum);

  118.         /* Is this a packet from the video stream? */
  119.         if(packet.stream_index == videoStream) {
  120.             packetno++;
  121. #ifdef ENABLE_PRINT_FRAME_BYTES
  122.         if ( 1 ) {
  123.             int i;
  124.             int size = packet.size < PRINT_BYTES ? packet.size : PRINT_BYTES;
  125.             unsigned char *data = packet.data;
  126.             printf("===>[%5d] [", packet.size);
  127.             for (i = 0; i < size; i++)
  128.                 printf("%02x ", data[i]);
  129.             printf("]n");
  130.         }
  131. #endif
  132. #ifdef ENABLE_DEMUX_SAVE
  133.             fwrite(packet.data, 1, packet.size, frvdemux);
  134. #endif
  135.             //printf("[the %d packet]packet.size = %dn", packetno++, packet.size);

  136.             while (packet.size > 0) {
  137.                 decLen = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
  138.                 //printf("[video_decode_example]after avcodec_decode_video2,decoded=%dn",decLen);

  139.                 if (decLen < 0)    {
  140.                     fprintf(stderr, "[video_decode_example]Error while decoding frame %dn", frame);
  141.                     //exit(1);
  142.                     /* FIXME if decode one frame err, ignore this frame */
  143.                     decLen = packet.size;
  144.                 }

  145.                 if (frameFinished) {
  146.                     printf("got a yuv framen");
  147.                     //printf(stderr, "[video_decode_example]saving frame %3dn", frame);

  148.                     /* the picture is allocated by the decoder. no need to free it */
  149.                     if (frame == 0) {
  150.                         printf("[video_decode_example]picture->linesize[0]=%d, c->width=%d,c->height=%dn",
  151.                                 pFrame->linesize[0], pCodecCtx->width, pCodecCtx->height);
  152.                         printf("===>YUV format = %dn", pFrame->format);
  153.                     }
  154. #ifdef ENABLE_YUV_SAVE
  155.                     /* save yuv pic */
  156.                     if (frame < FRAME_NUM) {
  157.                         switch (pFrame->format) {
  158.                             case 0 :    /* YUV420P */
  159.                                 yuv420p_save(pFrame, pCodecCtx);
  160.                                 break;
  161.                             case 2 :    /* RGB24 */
  162.                                 rgb24_save(pFrame, pCodecCtx);
  163.                                 break;
  164.                             case 13 :    /* YUVJ422P */
  165.                                 yuv422p_save(pFrame, pCodecCtx);
  166.                                 break;
  167.                             case 14 :    /* YUVJ444P */
  168.                                 yuv444p_save(pFrame, pCodecCtx);
  169.                                 break;
  170.                             default :
  171.                                 fprintf(stderr, "unsupport YUV format for savingn");
  172.                                 break;
  173.                         }
  174.                         fprintf(stderr, "===>save pic successn");
  175.                     }
  176. #endif
  177.                     /* frame index grow */
  178.                     frame++;
  179.                 }
  180.                 //printf("===========> %dn", decLen);
  181.                 /* left data in pkt , go on decoding */
  182.                 packet.data += decLen;
  183.                 packet.size -= decLen;
  184.             }
  185.             if (frame == FRAME_NUM) {
  186.                 printf("==========> decoded [%d pkt frames] ---> save [%d YUV frames], enough to stop!n", packetno, FRAME_NUM);
  187.                 break;
  188.             }
  189.         }

  190.         /* FIXME no need free in this file */
  191.         //printf("free packet that was allocated by av_read_framen");
  192.         // Free the packet that was allocated by av_read_frame
  193.         //av_free_packet(&packet);
  194.     }

  195.     printf("decoding job down! begin to freen");
  196.     /* Free the YUV frame */
  197.     av_free(pFrame);

  198.     /* Close the codec */
  199.     avcodec_close(pCodecCtx);

  200.     /* Close the video file */
  201.     av_close_input_file(pFormatCtx);
  202.     fclose(pfout);

  203.     printf("==========> END-OKn");

  204.     return 0;
  205. }

     最后是Makefile文件

点击(此处)折叠或打开

  1. # use pkg-config for getting CFLAGS abd LDFLAGS
  2. FFMPEG_LIBS=libavdevice libavformat libavfilter libavcodec libswscale libavutil
  3. CFLAGS+=$(shell pkg-config --cflags $(FFMPEG_LIBS))
  4. LDFLAGS+=$(shell pkg-config --libs $(FFMPEG_LIBS))

  5. EXAMPLES=pic_dec

  6. OBJS=$(addsuffix .o,$(EXAMPLES))

  7. %: %.o
  8.     $(CC) $< $(LDFLAGS) -o $@

  9. %.o: %.c
  10.     $(CC) $< $(CFLAGS) -c -o $@

  11. .phony: all clean

  12. all: $(OBJS) $(EXAMPLES)

  13. clean:
  14.     rm -rf $(EXAMPLES) $(OBJS)

     注意如果是自己编译的ffmpeg-1.0等版本,安装到例如/usr/local/目录的话,需要在环境变量中设置
     PKG_CONFIG_PATH和LD_LIBRARY_PATH,指定到/usr/local/lib/pkgconfig和/usr/local/lib/目录(如果以后要利用你的PC来交叉编译如VLC等开源代码,最好将这两个变量注释掉,因为交叉编译时的configure脚本会根据
这个配置错误的检查到PC也就是X86结构的lib,这个显然是不对的,会让VLC模块错误的认为你的机子上有了一些第三方的库,但VLC并不知道这是X86结构的)
阅读(17834) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

jgfntu2013-05-15 13:32:57

/**
 * save rgb24 frame [PGM]
 */
void pgm_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
{
    int i;
 int width = pCodecCtx->width, height = pCodecCtx->height;
 
    fprintf(pfout,"P5\n%d %d\n%d\n", width, height, 255);
    
    for(i =0;i < height; i++)
        fwri

jgfntu2013-05-15 12:28:15

不知到为啥,今天贴的代码,最后的“\n”的"\"没了,如果要复制代码的话,请到我的CSDN的网站吧
http://blog.csdn.net/jgf_ntu/article/details/8928977