ffmpeg学习---3.Outputting to the Screen

来源:互联网 发布:变频器模拟软件 编辑:程序博客网 时间:2024/05/17 07:36
1. 2output.c
  1. // tutorial01.c
  2. // Code based on a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)
  3. // Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1

  4. // A small sample program that shows how to use libavformat and libavcodec to
  5. // read video from a file.
  6. //
  7. // Use
  8. //
  9. // gcc -o tutorial01 tutorial01.-lavformat -lavcodec -lz
  10. //
  11. // to build (assuming libavformat and libavcodec are correctly installed
  12. // your system).
  13. //
  14. // Run using
  15. //
  16. // tutorial01 myvideofile.mpg
  17. //
  18. // to write the first five frames from "myvideofile.mpg" to disk in PPM
  19. // format.
  20. #include <stdio.h>
  21. #include <libavformat/avformat.h>
  22. #include <libswscale/swscale.h>
  23. #include <SDL.h>
  24. int main(int argc, char *argv[]) {
  25.     AVFormatContext *pFormatCtx;
  26.     int i, videoStream;
  27.     AVCodecContext *pCodecCtx;
  28.     AVCodec *pCodec;
  29.     AVFrame *pFrame; 
  30.     AVFrame *pFrameRGB;
  31.     AVPacket packet;
  32.     int frameFinished;
  33.     int numBytes;
  34.     uint8_t *buffer;

  35.     if(argc < 2) {
  36.         printf("Please provide a movie file\n");
  37.         return -1;
  38.     }
  39.     
  40.     if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER))
  41.     {
  42.         fprintf(stderr, "SD_init error \n");
  43.         exit(1);
  44.     }
  45.     // Register all formats and codecs
  46.     av_register_all();

  47.     pFormatCtx = avformat_alloc_context();

  48.     // Open video file
  49.     //if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
  50.     if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
  51.         return -1; // Couldn't open file

  52.     // Retrieve stream information
  53.     if(avformat_find_stream_info(pFormatCtx,NULL)<0)
  54.         return -1; // Couldn't find stream information

  55.     // Dump information about file onto standard error
  56.     av_dump_format(pFormatCtx, 0, argv[1], 0);

  57.     // Find the first video stream
  58.     videoStream=-1;
  59.     for(i=0; i<pFormatCtx->nb_streams; i++)
  60.         if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) 
  61.         {
  62.             videoStream=i;
  63.             break;
  64.         }
  65.     if(videoStream==-1)
  66.         return -1; // Didn't find a video stream

  67.     // Get a pointer to the codec context for the video stream
  68.     pCodecCtx=pFormatCtx->streams[videoStream]->codec;

  69.     // Find the decoder for the video stream
  70.     pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
  71.     if(pCodec==NULL) {
  72.         fprintf(stderr, "Unsupported codec!\n");
  73.         return -1; // Codec not found
  74.     }

  75.     // Open codec
  76.     if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
  77.         return -1; // Could not open codec

  78.     //Set up a screen
  79.     SDL_Surface * screen;
  80.     screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
  81.     if(!screen)
  82.     {
  83.         fprintf(stderr, "SetVideoMode error\n");
  84.         exit(1);
  85.     }

  86.     //create a YUV overlay
  87.     SDL_Overlay *bmp;
  88.     SDL_Rect rect;
  89.     SDL_Event event;
  90.     bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,
  91.             SDL_YV12_OVERLAY, screen);
  92.     // Allocate video frame
  93.     pFrame=avcodec_alloc_frame();

  94.     // Allocate an AVFrame structure
  95.     pFrameRGB=avcodec_alloc_frame();
  96.     if(pFrameRGB==NULL)
  97.         return -1;

  98.     // Determine required buffer size and allocate buffer
  99.     numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
  100.             pCodecCtx->height);
  101.     buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

  102.     // Assign appropriate parts of buffer to image planes in pFrameRGB
  103.     // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
  104.     // of AVPicture
  105.     avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
  106.             pCodecCtx->width, pCodecCtx->height);

  107.     // Read frames and display on screen 
  108.     while(av_read_frame(pFormatCtx, &packet)>=0) {
  109.         // Is this a packet from the video stream?
  110.         if(packet.stream_index==videoStream) {
  111.             // Decode video frame
  112.             avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

  113.             // Did we get a video frame?
  114.             if(frameFinished) {
  115.                 SDL_LockYUVOverlay(bmp);
  116.                 AVPicture pict;
  117.                 pict.data[0] = bmp->pixels[0];
  118.                 pict.data[1] = bmp->pixels[2];
  119.                 pict.data[2] = bmp->pixels[1];

  120.                 pict.linesize[0] = bmp->pitches[0];
  121.                 pict.linesize[1] = bmp->pitches[2];
  122.                 pict.linesize[2] = bmp->pitches[1];

  123.                // img_convert(&pict, PIX_FMT_YUV420P, (AVPicture*)pFrame, pCodecCtx->pix_fmt,
  124.                // pCodecCtx->width, pCodecCtx->height);
  125.                 struct SwsContext *img_convert_ctx;
  126.                 img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
  127.                 if(img_convert_ctx == NULL)
  128.                 {
  129.                     fprintf(stderr, "Cannot initialize the conversion context!\n");
  130.                             exit(1);
  131.                 }

  132.                 sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pict.data, pict.linesize);
  133.                 SDL_UnlockYUVOverlay(bmp);
  134.                 rect.= 0;
  135.                 rect.= 0;
  136.                 rect.= pCodecCtx->width;
  137.                 rect.= pCodecCtx->height;
  138.                 SDL_DisplayYUVOverlay(bmp, &rect);

  139.             }
  140.         }

  141.         // Free the packet that was allocated by av_read_frame
  142.         av_free_packet(&packet);
  143.         SDL_PollEvent(&event);
  144.         switch(event.type)
  145.         {
  146.             case SDL_QUIT:
  147.                 SDL_Quit();
  148.                 exit(0);
  149.                 break;
  150.             default:
  151.                 break;
  152.         }
  153.     }

  154.     // Free the RGB image
  155.     av_free(buffer);
  156.     av_free(pFrameRGB);

  157.     // Free the YUV frame
  158.     av_free(pFrame);

  159.     // Close the codec
  160.     avcodec_close(pCodecCtx);

  161.     // Close the video file
  162.     avformat_close_input(&pFormatCtx);

  163.     return 0;
  164. }
2.Makefile
  1. CC=gcc
  2. CFLAGS = --I/home/sun/code/ffmpeg-1.0/install/include/ -I/home/sun/code/SDL-1.2.15/install/include/SDL/
  3. LDFLAGS = -L/home/sun/code/ffmpeg-1.0/install/lib/ -lavutil -lavformat -lavcodec -lavutil -lm -lswscale
  4. LDFLAGS += -L/home/sun/code/SDL-1.2.15/install/lib/ -lSDLmain -lSDL

  5. TARGETS=2output
  6. all: $(TARGETS)
  7. 2output.o:2output.c
  8.     $(CC) $(CFLAGS) -o $@ -c $^

  9. 2output:2output.o
  10.     $(CC) -o $@ $^ $(LDFLAGS)
  11. clean:
  12.     rm -rf *.o $(TARGETS)

二. 更新版
1. 代码
现在ffmpeg发布了ffmpeg-2.7.2,下面基于这个版本更新一下,同时加上注解,下次看的时候理解就更深了
  1. cong@msi:/work/ffmpeg/test/1view$ cat view.
  2. #include "utils.h"
  3. #include <libavformat/avformat.h>
  4. #include <libswscale/swscale.h>
  5. #include <SDL/SDL.h>

  6. SDL_mutex *affmutex;
  7. SDL_Event sdlevent;
  8. int signal_quit = 1;

  9. static int eventThread(void* data)
  10. {
  11.     while(signal_quit)
  12.     {
  13.         SDL_LockMutex(affmutex);
  14.         while(SDL_PollEvent(&sdlevent))
  15.         {
  16.             switch(sdlevent.type)
  17.             {
  18.                 case SDL_QUIT:
  19.                     {
  20.                         signal_quit = 0;
  21.                     }
  22.                     break;
  23.                 default:
  24.                     break;

  25.             }
  26.         }
  27.         SDL_UnlockMutex(affmutex);
  28.     } 
  29. }

  30. int main(int argc, char **argv)
  31. {
  32.     int i=0;
  33.     int ret;
  34.     int videoindex= -1;
  35.     int frameFinished;
  36.     AVFormatContext *pFormatCtx = NULL;
  37.     AVCodecContext * pCodecCtx;
  38.     AVCodec * pCodec;
  39.     AVFrame * pFrame;
  40.     AVFrame * pFrameYUV;
  41.     AVPacket * packet; 
  42.     struct SwsContext *img_convert_ctx;

  43.     SDL_Surface* psscreen;
  44.     SDL_Overlay* overlay;
  45.     SDL_Rect rect;
  46.     SDL_Thread* sdl_thread;
  47.     //a. ffmpeg的初始化(虽然名字是register就这么说吧)
  48.     avcodec_register_all();
  49.     avfilter_register_all();
  50.     av_register_all();
  51.     
  52.     //b.打开视频文件
  53.     pFormatCtx = avformat_alloc_context();
  54.     if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
  55.         return -1; 
  56.     //c.获取视频文件的流信息,(即查看有几个视频流几个音频流)    
  57.     if(avformat_find_stream_info(pFormatCtx, NULL)<0)
  58.         return -1; 
  59.     av_dump_format(pFormatCtx,0, 0, 0);
  60.     //d.获取了视频流的index,这样以后读取一帧之后,根据索引号才能判断这一帧是不是视频帧
  61.     for(i=0; i<pFormatCtx->nb_streams; i++)
  62.     {
  63.         if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  64.         {
  65.             videoindex= i;
  66.             break;
  67.         }
  68.     }
  69.     if(videoindex== -1)
  70.     {
  71.         dbmsg("no video stream found!");
  72.         return -1;
  73.     }
  74.     dbmsg("videoindex=%d", videoindex);
  75.     //e.为视频流寻找解码器:在c中不仅有视频流的index,还有视频流的编码codec_id
  76.     //通过这个codec_id就可以寻到视频流的解码器
  77.     pCodecCtx = pFormatCtx->streams[videoindex]->codec;
  78.     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
  79.     if(pCodec == NULL)
  80.     {
  81.         dbmsg("Codec not found");
  82.         return -1;
  83.     }
  84.     if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0)   //找到解码器之后打开解码器
  85.         return -1;

  86.     pFrame = av_frame_alloc();         //以前的avcodec_alloc_frame函数现在不用了
  87.     pFrameYUV = av_frame_alloc();

  88.     //显示的准备:SDL初始化,设置显示模式,创建画布
  89.     SDL_Init(SDL_INIT_EVERYTHING);
  90.     psscreen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, SDL_SWSURFACE);
  91.     SDL_WM_SetCaption( "FFMPEG Window", NULL);
  92.     //注意这儿的参数SDL_YU12_OVERLAY与SDL_YUY2_OVERLAY一定要与下面sws_scale中的参数配套
  93.     //overlay = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, psscreen);
  94.     overlay = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YUY2_OVERLAY, psscreen);

  95.     //与SDL的退出相关
  96.     affmutex = SDL_CreateMutex();
  97.     sdl_thread = SDL_CreateThread(eventThread, NULL);    //创建SDL线程监测退出信号

  98.     rect.= 0;
  99.     rect.= 0;
  100.     rect.= pCodecCtx->width;
  101.     rect.= pCodecCtx->height;

  102.     packet = (AVPacket*)av_malloc(sizeof(AVPacket));
  103.     //注意这儿的参数PIX_FMT_YUV420P或PIX_FMT_YUYU422一定要与上面SDL_CreateYUVOVerlay中的参数配套
  104.     //img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
  105.     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUYV422, SWS_BICUBIC, NULL, NULL, NULL);
  106.     if(img_convert_ctx == NULL)
  107.     {
  108.         dbmsg("img_convert error");
  109.         return -1;
  110.     }
  111.     //f.循环读取视频文件所有的帧,包括音频帧与视频帧:读取帧到packet
  112.     while( (av_read_frame(pFormatCtx, packet)>=0) && (signal_quit))
  113.     {
  114.         //g.判断是视频帧,则对视频帧解码
  115.         if(packet->stream_index == videoindex)   
  116.         {    //解码packet中的视频帧到pFrame中
  117.             if((ret=avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, packet)) < 0)
  118.             {
  119.                 dbmsg("decocode video error");
  120.                 return -1;
  121.             }
  122.             if(frameFinished)
  123.             {
  124.                 SDL_LockYUVOverlay(overlay);
  125.                 pFrameYUV->data[0] = overlay->pixels[0];      //Y
  126.                 pFrameYUV->data[1] = overlay->pixels[2];      //U   //对overlay中的数据组织不清楚,为什么这儿要把uv交换呢?有知道的告诉我一下,谢谢
  127.                 pFrameYUV->data[2] = overlay->pixels[1];      //V
  128.                 pFrameYUV->linesize[0] = overlay->pitches[0];
  129.                 pFrameYUV->linesize[1] = overlay->pitches[2];
  130.                 pFrameYUV->linesize[2] = overlay->pitches[1];
  131.                 sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, 
  132.                         pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);    //将解码后pFrame中的数据转为sws_getContext中设定的格式,这样就可以通过sdl进行显示了 
  133.                 SDL_UnlockYUVOverlay(overlay);
  134.                 SDL_DisplayYUVOverlay(overlay, &rect);
  135.                 SDL_Delay(40);
  136.             }
  137.         }
  138.     }
  139.     SDL_WaitThread(sdl_thread, &ret);
  140.     SDL_DestroyMutex(affmutex);
  141.     return 0;
  142. }
说明1: SDL_CreateYUVOverlay与sws_getContext中的参数是相对应的
    SDL_CreateYUVOverlay   SDL_YV12_OVERLAY
                                       SDL_YUY2_OVERLAY     
    sws_getContext              PIX_FMT_YUV420P
                                       PIX_FMT_YUYV422    
     SDL_YV12_OVERLAY + PIX_FMT_YUYV422: 则显示不正常 
    SDL_YUY2_OVERLAY + PIX_FMT_YUV420P
sws_scale会报错bad dst image pointers
说明2:sws_scale之后就可以把yuv420p数据写到文件中
FILE *fp_yuv;
fp_yuv=fopen("output.yuv","wb+");

int y_size=pCodecCtx->width*pCodecCtx->height;    
fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y   
fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U  
fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V 

1.2 Makefile
  1. cong@msi:/work/ffmpeg/test/1view$ cat Makefile 
  2. EXE=view
  3. CC=gcc
  4. FFMPEG=/work/ffmpeg/out
  5. CFLAGS=--O0 -I$(FFMPEG)/include
  6. LDFLAGS = -L$(FFMPEG)/lib/ -lswscale -lswresample -lavformat -lavdevice -lavcodec -lavutil -lavfilter -lm -lSDL
  7. SRC=$(wildcard *.c)
  8. OBJ=$(patsubst %.c,%.o,$(SRC))
  9. DEP=$(patsubst %.c,.%.d,$(SRC))
  10. $(EXE):$(OBJ)
  11.     $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)

  12. $(DEP):.%.d:%.c
  13.     @set -e; rm -f $@; \
  14.     $(CC) -MM $< > $@.$$$$; \
  15.     sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; \
  16.     rm -f $@.$$$$

  17. -include $(DEP)
  18. clean:
  19.     @rm $(EXE) $(OBJ) $(DEP) -f
  20. run:
  21.     export LD_LIBRARY_PATH=$(FFMPEG)/lib/ \
  22.     && ./$(EXE) ../resource/bing.rmvb

2.运行
  1. cong@msi:/work/ffmpeg/test/1view$ make run
  2. export LD_LIBRARY_PATH=/work/ffmpeg/out/lib/ \     -->己经在Makefile中写好了视频文件的路径
  3.     && ./view ../resource/bing.rmvb
3. 代码打包
1view.rar (下载后改名为1view.tar.gz)
0 0
原创粉丝点击