ffmpeg簡單視頻播放器


參考學習雷霄驊的最簡單的基於FFMPEG視頻播放器。

在移植代碼到我的linux環境中時總出現問題,所以干脆把雷神的代碼從頭到尾分析並移植過來調試。

開發環境:

  操作系統:ubuntu14

  ffmpeg版本:3.2.2

  sdl版本:2

代碼我是自己參照雷神的代碼敲的,在這過程中,我發現寫代碼還是要加打印,沒有什么能一拿過來就可以成功運行。尤其是在自己設計接口的時候,返回值最好是返回運行結果,需要傳出的內存通過參數來傳出。

對於錯誤的判斷打印還是需要詳細一些,以后在寫比較大的程序時要養成加log的習慣,這對調試程序還是有很大幫助的。

下面插入代碼:

說明:該程序運行一段時間后會變成黑白,具體原因再查

編譯與運行:

gcc -g main.c -o test -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -lavutil -lavformat -lavcodec -lz -lavutil -lswscale -L /usr/lib/x86_64-linux-gnu -lSDL2 -lSDL2main
(其中路徑按照自己的安裝路徑來 -g 是加GDB調試)

./test

  1 #ifdef _cplusplus
  2 extern "C"
  3 {
  4 #endif
  5 
  6 #include<stdio.h>
  7 #include<libavcodec/avcodec.h>
  8 #include<libavformat/avformat.h>
  9 #include<libavutil/avutil.h>
 10 #include<libswscale/swscale.h>
 11 #include<libavutil/avutil.h>
 12 #include<libavutil/imgutils.h>
 13 #include<SDL2/SDL.h>
 14 
 15 //是否將YUV420P內容輸出到文件
 16 #define OUTPUT_YUV420P 0
 17 //要播放的文件路徑
 18 #define filename "/home/sns/cuc_ieschool.flv"
 19 //要輸出YUV420P內容的文件路徑
 20 #define outfilename "/home/sns/output.yuv"
 21 
 22 int main(int argc, char **argv)
 23 {
 24     //變量定義*********************************************************************
 25     AVFormatContext *pFormatCtx;
 26     int i, videoStream;
 27     AVCodecContext *pCodecCtx;
 28     AVCodec *pCodec;
 29     AVFrame *pFrame;
 30     AVFrame *pFrameYUV;
 31     uint8_t *buffer;
 32     int numBytes;
 33 
 34     SDL_Window *screen;
 35     SDL_Renderer *sdlRender;
 36     SDL_Texture *sdlTexture;
 37     SDL_Rect sdlRect;
 38     int frameFinished;
 39     AVPacket packet;
 40     i = 0;
 41     struct SwsContext *img_convert_ctx;
 42     int err_code;
 43     char buf[1024];
 44     FILE *fp_yuv;
 45     int y_size;
 46     //*******************************************************************************
 47     av_register_all();
 48     //1、打開視頻文件*************************************************
 49     pFormatCtx = avformat_alloc_context();
 50     err_code = avformat_open_input(&pFormatCtx, filename, NULL, NULL);
 51     if (err_code != 0)
 52     {//打開文件失敗
 53         av_strerror(err_code, buf, 1024);
 54         printf("coundn't open the file!,error code = %d(%s)\n", err_code, buf);
 55         return -1;
 56     }
 57     if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
 58     {
 59         printf("Couldn't find stream information.\n");
 60         return -1;
 61     }
 62     //2、找到第一個視頻流****************************
 63     videoStream = -1;
 64     for (i = 0; i < pFormatCtx->nb_streams; i++)
 65     {
 66         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
 67         {
 68             videoStream = i;//得到視頻流的索引
 69             break;
 70         }
 71     }
 72     if (videoStream == -1)
 73     {
 74         printf("Didn't find a video stream.\n");
 75         return -1;
 76     }
 77     /* 3、從視頻流中得到一個編解碼上下文,里面包含了編解碼器的所有信息和一個
 78     指向真正的編解碼器     ,然后我們找到這個解碼器*/
 79     pCodecCtx = pFormatCtx->streams[videoStream]->codec;
 80     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
 81     if (pCodec == NULL)
 82     {
 83         fprintf(stderr, "Unsupported codec !\n");
 84         return -1;
 85     }
 86     //4、打開該編解碼器
 87     if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
 88     {
 89         printf("cann't open the codec!\n");
 90         return -1;
 91     }
 92     //5、分配兩個視頻幀,一個保存得到的原始視頻幀,一個保存為指定格式的視頻幀(該幀通過原始幀轉換得來)
 93     pFrame = av_frame_alloc();
 94     if (pFrame == NULL)
 95     {
 96         printf("pFrame alloc fail!\n");
 97         return -1;
 98     }
 99     pFrameYUV = av_frame_alloc();
100     if (pFrameYUV == NULL)
101     {
102         printf("pFrameYUV alloc fail!\n");
103         return -1;
104     }
105     //6、得到一幀視頻截圖的內存大小並分配內存,並將YUV數據填充進去
106     numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
107             pCodecCtx->height,1);
108     buffer = (uint8_t*) av_mallocz(numBytes * sizeof(uint8_t));
109     if (!buffer)
110     {
111         printf("numBytes :%d , buffer malloc 's mem \n", numBytes);
112         return -1;
113     }
114     //打印信息
115     printf("--------------- File Information ----------------\n");
116     av_dump_format(pFormatCtx, 0, filename, 0);
117     printf("-------------------------------------------------\n");
118     av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,buffer,
119             AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
120     //7、得到指定轉換格式的上下文**********************************
121     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
122             pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
123             AV_PIX_FMT_YUV420P,
124             SWS_BICUBIC,
125             NULL, NULL, NULL);
126     if (img_convert_ctx == NULL)
127     {
128         fprintf(stderr, "Cannot initialize the conversion context!\n");
129         return -1;
130     }
131     //***********************************************************
132 #if OUTPUT_YUV420P
133     fp_yuv = fopen(outfilename, "wb+");
134 #endif
135     //8、SDL初始化和創建多重windows等准備工作
136     if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_VIDEO))
137     {
138         fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
139         return -1;
140     }
141     //使用SDL_CreateWindow代替SDL_SetVideoMode
142     //創建一個給定高度和寬度、位置和標示的windows。
143     screen = SDL_CreateWindow("Simplest ffmpeg player's Window",
144     SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, pCodecCtx->width,
145             pCodecCtx->height, SDL_WINDOW_OPENGL);
146     if (!screen)
147     {
148         fprintf(stderr, "SDL: could not create window - exiting - %s\n",SDL_GetError());
149         return -1;
150     }
151     //對該window創建一個2D渲染上下文
152     sdlRender = SDL_CreateRenderer(screen, -1, 0);
153     if (!sdlRender)
154     {
155         fprintf(stderr, "SDL:cound not create render :    %s\n", SDL_GetError());
156         return -1;
157     }
158     //Create a texture for a rendering context.
159     //為一個渲染上下文創建一個紋理
160     //IYUV: Y + U + V  (3 planes)
161     //YV12: Y + V + U  (3 planes)
162     sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_IYUV,
163             SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
164     if (!sdlTexture)
165     {
166         fprintf(stderr, "SDL:cound not create Texture :    %s\n", SDL_GetError());
167         return -1;
168     }
169     //建立一個矩形變量,提供后面使用
170     sdlRect.x = 0;
171     sdlRect.y = 0;
172     sdlRect.w = pCodecCtx->width;
173     sdlRect.h = pCodecCtx->height;
174     //9、正式開始讀取數據*****************************************
175     while (av_read_frame(pFormatCtx, &packet) >= 0)
176     {
177         //如果讀取的包來自視頻流
178         if (packet.stream_index == videoStream)
179         {
180             //從包中得到解碼后的幀
181             if (avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet) < 0)
182             {
183                 printf("Decode Error!\n");
184                 return -1;
185             }
186             //如果確定完成得到該視頻幀
187             if (frameFinished)
188             {
189                 //轉換幀數據格式
190                 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
191                         pCodecCtx->height,
192                         pFrameYUV->data,
193                         pFrameYUV->linesize);
194 #if OUTPUT_YUV420P
195                 y_size = pCodecCtx->width * pCodecCtx->height;
196                 fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);    //Y
197                 fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);  //U
198                 fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);  //V
199 #endif
200                 //SDL顯示~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
201 #if 0
202                 SDL_UpdateTexture(sdlTexture,NULL,pFrameYUV->data[0],pFrameYUV->linesize[0]);
203 #else
204                 SDL_UpdateYUVTexture(sdlTexture, &sdlRect, pFrameYUV->data[0],
205                         pFrameYUV->linesize[0], pFrameYUV->data[1],
206                         pFrameYUV->linesize[1], pFrameYUV->data[2],
207                         pFrameYUV->linesize[2]);
208 #endif
209                 SDL_RenderClear(sdlRender);
210                 SDL_RenderCopy(sdlRender, sdlTexture, NULL, &sdlRect);
211                 SDL_RenderPresent(sdlRender);
212                 //結束SDL~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
213                 SDL_Delay(40);//延時
214             }
215         }
216         av_free_packet(&packet);//釋放讀出來的包
217     }
218     //**************************************************************************************
219     //10、釋放分配的內存或關閉文件等操作
220 #if OUTPUT_YUV420P
221     fclose(fp_yuv);
222 #endif
223     sws_freeContext(img_convert_ctx);
224     SDL_Quit();
225     av_free(buffer);
226     av_free(pFrame);
227     av_free(pFrameYUV);
228     avcodec_close(pCodecCtx);
229     avformat_close_input(&pFormatCtx);
230     return EXIT_SUCCESS;
231 }
232 
233 #ifdef _cplusplus
234 }
235 #endif

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM