100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
=====================================================
最簡單的基于FFmpeg的視頻播放器系列文章列表:
100行代碼實現最簡單的基于FFMPEG+SDL的視頻播放器(SDL1.x)
最簡單的基于FFMPEG+SDL的視頻播放器 ver2 (采用SDL2.0)
最簡單的基于FFmpeg的解碼器-純凈版(不包含libavformat)
最簡單的基于FFMPEG+SDL的視頻播放器:拆分-解碼器和播放器
最簡單的基于FFMPEG的Helloworld程序
=====================================================
簡介
FFMPEG工程浩大,可以參考的書籍又不是很多,因此很多剛學習FFMPEG的人常常感覺到無從下手。我剛接觸FFMPEG的時候也感覺不知從何學起。
因此我把自己做項目過程中實現的一個非常簡單的視頻播放器(大約100行代碼)源代碼傳上來,以作備忘,同時方便新手學習FFMPEG。
該播放器雖然簡單,但是幾乎包含了使用FFMPEG播放一個視頻所有必備的API,并且使用SDL顯示解碼出來的視頻。
并且支持流媒體等多種視頻輸入,處于簡單考慮,沒有音頻部分,同時視頻播放采用直接延時40ms的方式
平臺使用VC2010,使用了新版的FFMPEG類庫。
SourceForge項目主頁:
https://sourceforge.net/projects/simplestffmpegplayer/
注:本文SDL采用1.x版本。另一版本采用SDL2.0,可參考:
基于FFMPEG+SDL的視頻播放器 ver2 (采用SDL2.0):http://blog.csdn.net/leixiaohua1020/article/details/38868499
流程圖
沒想到這篇文章中介紹的播放器挺受FFMPEG初學者的歡迎,因此再次更新兩張流程圖,方便大家學習。此外在源代碼上添加了注釋,方便理解。
該播放器解碼的流程用圖的方式可以表示稱如下形式:
SDL顯示YUV圖像的流程圖:
簡單解釋幾句:
SDL_Surface就是使用SDL的時候彈出的那個窗口。在SDL1.x版本中,只可以創建一個SDL_Surface。
SDL_Overlay用于顯示YUV數據。一個SDL_Overlay對應一幀YUV數據。
SDL_Rect用于確定SDL_Overlay顯示的位置。注意:一個SDL_Overlay可以指定多個不同的SDL_Rect,這樣就可以在SDL_Surface不同位置顯示相同的內容。
它們的關系如下圖所示:
下圖舉了個例子,指定了4個SDL_Rect,可以實現4分屏的顯示。
simplest_ffmpeg_player(標準版)代碼
/*** 最簡單的基于FFmpeg的視頻播放器* Simplest FFmpeg Player** 雷霄驊 Lei Xiaohua* leixiaohua1020@126.com* 中國傳媒大學/數字電視技術* Communication University of China / Digital TV Technology* http://blog.csdn.net/leixiaohua1020** 本程序實現了視頻文件的解碼和顯示(支持HEVC,H.264,MPEG2等)。* 是最簡單的FFmpeg視頻解碼方面的教程。* 通過學習本例子可以了解FFmpeg的解碼流程。* This software is a simplest video player based on FFmpeg.* Suitable for beginner of FFmpeg.*/#include <stdio.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32 //Windows extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "SDL/SDL.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <SDL/SDL.h> #ifdef __cplusplus }; #endif #endif//Full Screen #define SHOW_FULLSCREEN 0 //Output YUV420P #define OUTPUT_YUV420P 0int main(int argc, char* argv[]) {//FFmpegAVFormatContext *pFormatCtx;int i, videoindex;AVCodecContext *pCodecCtx;AVCodec *pCodec;AVFrame *pFrame,*pFrameYUV;AVPacket *packet;struct SwsContext *img_convert_ctx;//SDLint screen_w,screen_h;SDL_Surface *screen; SDL_VideoInfo *vi;SDL_Overlay *bmp; SDL_Rect rect;FILE *fp_yuv;int ret, got_picture;char filepath[]="bigbuckbunny_480x272.h265";av_register_all();avformat_network_init();pFormatCtx = avformat_alloc_context();if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){printf("Couldn't open input stream.\n");return -1;}if(avformat_find_stream_info(pFormatCtx,NULL)<0){printf("Couldn't find stream information.\n");return -1;}videoindex=-1;for(i=0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoindex=i;break;}if(videoindex==-1){printf("Didn't find a video stream.\n");return -1;}pCodecCtx=pFormatCtx->streams[videoindex]->codec;pCodec=avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){printf("Codec not found.\n");return -1;}if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){printf("Could not open codec.\n");return -1;}pFrame=av_frame_alloc();pFrameYUV=av_frame_alloc();//uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));//avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);//SDL----------------------------if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1;} #if SHOW_FULLSCREENvi = SDL_GetVideoInfo();screen_w = vi->current_w;screen_h = vi->current_h;screen = SDL_SetVideoMode(screen_w, screen_h, 0,SDL_FULLSCREEN); #elsescreen_w = pCodecCtx->width;screen_h = pCodecCtx->height;screen = SDL_SetVideoMode(screen_w, screen_h, 0,0); #endifif(!screen) { printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError()); return -1;}bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,SDL_YV12_OVERLAY, screen); rect.x = 0; rect.y = 0; rect.w = screen_w; rect.h = screen_h; //SDL End------------------------packet=(AVPacket *)av_malloc(sizeof(AVPacket));//Output Information-----------------------------printf("------------- File Information ------------------\n");av_dump_format(pFormatCtx,0,filepath,0);printf("-------------------------------------------------\n");#if OUTPUT_YUV420P fp_yuv=fopen("output.yuv","wb+"); #endif SDL_WM_SetCaption("Simplest FFmpeg Player",NULL);img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //------------------------------while(av_read_frame(pFormatCtx, packet)>=0){if(packet->stream_index==videoindex){//Decoderet = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if(ret < 0){printf("Decode Error.\n");return -1;}if(got_picture){SDL_LockYUVOverlay(bmp);pFrameYUV->data[0]=bmp->pixels[0];pFrameYUV->data[1]=bmp->pixels[2];pFrameYUV->data[2]=bmp->pixels[1]; pFrameYUV->linesize[0]=bmp->pitches[0];pFrameYUV->linesize[1]=bmp->pitches[2]; pFrameYUV->linesize[2]=bmp->pitches[1];sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); #if OUTPUT_YUV420Pint 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); //Ufwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V #endifSDL_UnlockYUVOverlay(bmp); SDL_DisplayYUVOverlay(bmp, &rect); //Delay 40msSDL_Delay(40);}}av_free_packet(packet);}//FIX: Flush Frames remained in Codecwhile (1) {ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret < 0)break;if (!got_picture)break;sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);SDL_LockYUVOverlay(bmp);pFrameYUV->data[0]=bmp->pixels[0];pFrameYUV->data[1]=bmp->pixels[2];pFrameYUV->data[2]=bmp->pixels[1]; pFrameYUV->linesize[0]=bmp->pitches[0];pFrameYUV->linesize[1]=bmp->pitches[2]; pFrameYUV->linesize[2]=bmp->pitches[1]; #if OUTPUT_YUV420Pint 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); //Ufwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V #endifSDL_UnlockYUVOverlay(bmp); SDL_DisplayYUVOverlay(bmp, &rect); //Delay 40msSDL_Delay(40);}sws_freeContext(img_convert_ctx);#if OUTPUT_YUV420P fclose(fp_yuv); #endif SDL_Quit();//av_free(out_buffer);av_free(pFrameYUV);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0; }1.1版之后,新添加了一個工程:simplest_ffmpeg_player_su(SU版)。
標準版在播放視頻的時候,畫面顯示使用延時40ms的方式。這么做有兩個后果:
(1)SDL彈出的窗口無法移動,一直顯示是忙碌狀態
(2)畫面顯示并不是嚴格的40ms一幀,因為還沒有考慮解碼的時間。SU(SDL Update)版在視頻解碼的過程中,不再使用延時40ms的方式,而是創建了一個線程,每隔40ms發送一個自定義的消息,告知主函數進行解碼顯示。這樣做之后:
(1)SDL彈出的窗口可以移動了
(2)畫面顯示是嚴格的40ms一幀
simplest_ffmpeg_player_su(SU版)代碼
/*** 最簡單的基于FFmpeg的視頻播放器SU(SDL升級版)* Simplest FFmpeg Player (SDL Update)** 雷霄驊 Lei Xiaohua* leixiaohua1020@126.com* 中國傳媒大學/數字電視技術* Communication University of China / Digital TV Technology* http://blog.csdn.net/leixiaohua1020** 本程序實現了視頻文件的解碼和顯示(支持HEVC,H.264,MPEG2等)。* 是最簡單的FFmpeg視頻解碼方面的教程。* 通過學習本例子可以了解FFmpeg的解碼流程。* 本版本中使用SDL消息機制刷新視頻畫面。* This software is a simplest video player based on FFmpeg.* Suitable for beginner of FFmpeg.* * Version:1.2* * 備注:* 標準版在播放視頻的時候,畫面顯示使用延時40ms的方式。這么做有兩個后果:* (1)SDL彈出的窗口無法移動,一直顯示是忙碌狀態* (2)畫面顯示并不是嚴格的40ms一幀,因為還沒有考慮解碼的時間。* SU(SDL Update)版在視頻解碼的過程中,不再使用延時40ms的方式,而是創建了* 一個線程,每隔40ms發送一個自定義的消息,告知主函數進行解碼顯示。這樣做之后:* (1)SDL彈出的窗口可以移動了* (2)畫面顯示是嚴格的40ms一幀* Remark:* Standard Version use's SDL_Delay() to control video's frame rate, it has 2* disadvantages:* (1)SDL's Screen can't be moved and always "Busy".* (2)Frame rate can't be accurate because it doesn't consider the time consumed * by avcodec_decode_video2()* SU(SDL Update)Version solved 2 problems above. It create a thread to send SDL * Event every 40ms to tell the main loop to decode and show video frames.*/#include <stdio.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32 //Windows extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "SDL/SDL.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <SDL/SDL.h> #ifdef __cplusplus }; #endif #endif//Refresh #define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)int thread_exit=0; //Thread int sfp_refresh_thread(void *opaque) {SDL_Event event;while (thread_exit==0) {event.type = SFM_REFRESH_EVENT;SDL_PushEvent(&event);//Wait 40 msSDL_Delay(40);}return 0; }int main(int argc, char* argv[]) {AVFormatContext *pFormatCtx;int i, videoindex;AVCodecContext *pCodecCtx;AVCodec *pCodec;AVFrame *pFrame,*pFrameYUV;AVPacket *packet;struct SwsContext *img_convert_ctx;//SDLint ret, got_picture;int screen_w=0,screen_h=0;SDL_Surface *screen; SDL_Overlay *bmp; SDL_Rect rect;SDL_Thread *video_tid;SDL_Event event;char filepath[]="bigbuckbunny_480x272.h265";av_register_all();avformat_network_init();pFormatCtx = avformat_alloc_context();if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){printf("Couldn't open input stream.\n");return -1;}if(avformat_find_stream_info(pFormatCtx,NULL)<0){printf("Couldn't find stream information.\n");return -1;}videoindex=-1;for(i=0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoindex=i;break;}if(videoindex==-1){printf("Didn't find a video stream.\n");return -1;}pCodecCtx=pFormatCtx->streams[videoindex]->codec;pCodec=avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){printf("Codec not found.\n");return -1;}if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){printf("Could not open codec.\n");return -1;}pFrame=av_frame_alloc();pFrameYUV=av_frame_alloc();//uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));//avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); //------------SDL----------------if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1;} screen_w = pCodecCtx->width;screen_h = pCodecCtx->height;screen = SDL_SetVideoMode(screen_w, screen_h, 0,0);if(!screen) { printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError()); return -1;}bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,SDL_YV12_OVERLAY, screen); rect.x = 0; rect.y = 0; rect.w = screen_w; rect.h = screen_h; packet=(AVPacket *)av_malloc(sizeof(AVPacket));printf("---------------File Information------------------\n");av_dump_format(pFormatCtx,0,filepath,0);printf("-------------------------------------------------\n");img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //--------------video_tid = SDL_CreateThread(sfp_refresh_thread,NULL);//SDL_WM_SetCaption("Simple FFmpeg Player (SDL Update)",NULL);//Event Loopfor (;;) {//WaitSDL_WaitEvent(&event);if(event.type==SFM_REFRESH_EVENT){//------------------------------if(av_read_frame(pFormatCtx, packet)>=0){if(packet->stream_index==videoindex){ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if(ret < 0){printf("Decode Error.\n");return -1;}if(got_picture){SDL_LockYUVOverlay(bmp);pFrameYUV->data[0]=bmp->pixels[0];pFrameYUV->data[1]=bmp->pixels[2];pFrameYUV->data[2]=bmp->pixels[1]; pFrameYUV->linesize[0]=bmp->pitches[0];pFrameYUV->linesize[1]=bmp->pitches[2]; pFrameYUV->linesize[2]=bmp->pitches[1];sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);SDL_UnlockYUVOverlay(bmp); SDL_DisplayYUVOverlay(bmp, &rect); }}av_free_packet(packet);}else{//Exit Threadthread_exit=1;break;}}}SDL_Quit();sws_freeContext(img_convert_ctx);//--------------//av_free(out_buffer);av_free(pFrameYUV);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0; }simplest_ffmpeg_player_su(SU版)中將simplest_ffmpeg_player(標準版)中的循環做了更改。標準版中為播放視頻的循環如下代碼所示。
main(){//...while(av_read_frame(pFormatCtx, packet)>=0){//Decode...SDL_Delay(40);}//... }可以看出標準版中使用SDL_Delay(40)控制視頻的播放速度。這樣有一些問題在前文中已經敘述。SU版定義了一個函數專門用于發送“解碼和顯示”的Event。 //自定義事件 //刷新畫面 #define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)int thread_exit=0; //Thread int sfp_refresh_thread(void *opaque) {while (thread_exit==0) {SDL_Event event;event.type = SFM_REFRESH_EVENT;SDL_PushEvent(&event);//Wait 40 msSDL_Delay(40);}return 0; }
主函數形式如下。使用SDL_WaitEvent()等待Event進行解碼和顯示。 main(){//...SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread,NULL);//Event LoopSDL_Event event;for (;;) {//WaitSDL_WaitEvent(&event);if(event.type==SFM_REFRESH_EVENT){//Decode...}}//... }
結果
軟件運行截圖:
完整工程下載地址:
http://download.csdn.net/detail/leixiaohua1020/5122959
更新(2014.5.10)==========================
完整工程(更新版)下載地址:
http://download.csdn.net/detail/leixiaohua1020/7319153
注1:類庫版本2014.5.6,已經支持HEVC以及VP9的解碼,附帶了這兩種視頻編碼的碼流文件。此外修改了個別變更的API函數,并且提高了一些程序的效率。
注2:新版FFmpeg類庫Release下出現錯誤的解決方法如下:
(注:此方法適用于所有近期發布的FFmpeg類庫)
VC工程屬性里,linker->Optimization->References 選項,改成No(/OPT:NOREF)即可。
更新(2014.8.25)==========================
simplest ffmpeg player 1.1
版本升級至1.1,變為2個項目:
simplest_ffmpeg_player:標準版,FFmpeg學習的開始。
simplest_ffmpeg_player_su:SU(SDL Update)版,加入了簡單的SDL的Event。
simplest_ffmpeg_player(標準版)增加了以下兩個選項(當然,代碼量超過了100行)
1.輸出解碼后的YUV420P像素數據文件
2.全屏播放
以上兩項可以通過文件前面的宏進行控制:
#define SHOW_FULLSCREEN 0 #define OUTPUT_YUV420P 0另外修補了幾個的函數,例如增加了SDL_Quit()等。
simplest_ffmpeg_player_su(SU版)具體情況在上文中已經說明。
1.1版下載地址:http://download.csdn.net/detail/leixiaohua1020/7814403
SourceForge上已經更新。
更新(2014.10.4)==========================
simplest ffmpeg player 1.2
版本升級至1.2。
1.新版本在原版本的基礎上增加了“flush_decoder”功能。當av_read_frame()循環退出的時候,實際上解碼器中可能還包含剩余的幾幀數據。因此需要通過“flush_decoder”將這幾幀數據輸出。“flush_decoder”功能簡而言之即直接調用avcodec_decode_video2()獲得AVFrame,而不再向解碼器傳遞AVPacket。參考代碼如下:
//FIX: Flush Frames remained in Codecwhile (1) {ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret < 0)break;if (!got_picture)break;sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);//處理...}具體信息參見文章:avcodec_decode_video2()解碼視頻后丟幀的問題解決
2.為了更好地適應Linux等其他操作系統,做到可以跨平臺,去除掉了VC特有的一些函數。比如“#include "stdafx.h"”,“_tmain()”等等。
1.2版下載地址:http://download.csdn.net/detail/leixiaohua1020/8001575
SourceForge上已經更新。
Linux版本=================================
Linux下代碼下載地址:
http://download.csdn.net/detail/leixiaohua1020/7696879
這個是Linux下的代碼,在Ubuntu下測試可以運行,前提是安裝了FFmpeg和SDL(版本1.2)。
編譯命令:
下列命令即可播放同一目錄下的test.flv文件。
./smp.out test.flv更新-最終版(2015.2.12)==========================
simplest ffmpeg player 1 final
這是該播放器源代碼的最后一次更新,以后會把更新的重點集中在基于FFmpeg和SDL2.0的視頻播放器。這次考慮到了跨平臺的要求,源代碼的調整幅度比較大。經過這次調整之后,源代碼可以在以下平臺編譯通過:
VC++:打開sln文件即可編譯,無需配置。
cl.exe:打開compile_cl.bat即可命令行下使用cl.exe進行編譯,注意可能需要按照VC的安裝路徑調整腳本里面的參數。編譯命令如下。
::VS2010 Environment call "D:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" ::include @set INCLUDE=include;%INCLUDE% ::lib @set LIB=lib;%LIB% ::compile and link cl simplest_ffmpeg_player.cpp /MD /link SDL.lib SDLmain.lib avcodec.lib ^ avformat.lib avutil.lib avdevice.lib avfilter.lib postproc.lib swresample.lib swscale.lib ^ /SUBSYSTEM:WINDOWS /OPT:NOREF exitMinGW:MinGW命令行下運行compile_mingw.sh即可使用MinGW的g++進行編譯。編譯命令如下。
g++ simplest_ffmpeg_player.cpp -g -o simplest_ffmpeg_player.exe \ -I /usr/local/include -L /usr/local/lib \ -lmingw32 -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lswscaleGCC(Linux):Linux命令行下運行compile_gcc.sh即可使用GCC進行編譯。編譯命令如下。
gcc simplest_ffmpeg_player.cpp -g -o simplest_ffmpeg_player.out \ -I /usr/local/include -L /usr/local/lib -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lswscaleGCC(MacOS):Mac終端下運行compile_gcc_mac.sh即可使用Mac 的GCC進行編譯,Mac的GCC和Linux的GCC差別不大,但是使用SDL1.2的時候,必須加上“-framework Cocoa”參數,否則編譯無法通過。編譯命令如下。
gcc simplest_ffmpeg_player.cpp -g -o simplest_ffmpeg_player.out \ -framework Cocoa -I /usr/local/include -L /usr/local/lib \ -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lswscalePS:相關的編譯命令已經保存到了工程文件夾中
此外,該版本修正了在某些系統下(例如部分Ubuntu)SDL綠屏顯示的問題,經過測試已經不再有綠屏現象。
CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/8443253
SourceForge上已經更新。
FFMPEG相關學習資料
SDL GUIDE 中文譯本
http://download.csdn.net/detail/leixiaohua1020/6389841ffdoc (FFMPEG的最完整教程)
http://download.csdn.net/detail/leixiaohua1020/6377803
如何用FFmpeg編寫一個簡單播放器
http://download.csdn.net/detail/leixiaohua1020/6373783
補充問題
補充1:舊版程序有一個小BUG,就是sws_getContext()之后,需要調用sws_freeContext()。否則長時間運行的話,會出現內存泄露的狀況。更新版已經修復。
補充2:有人會疑惑,為什么解碼后的pFrame不直接用于顯示,而是調用swscale()轉換之后進行顯示?
如果不進行轉換,而是直接調用SDL進行顯示的話,會發現顯示出來的圖像是混亂的。關鍵問題在于解碼后的pFrame的linesize里存儲的不是圖像的寬度,而是比寬度大一些的一個值。其原因目前還沒有仔細調查(大概是出于性能的考慮)。例如分辨率為480x272的圖像,解碼后的視頻的linesize[0]為512,而不是480。以第1行亮度像素(pFrame->data[0])為例,從0-480存儲的是亮度數據,而從480-512則存儲的是無效的數據。因此需要使用swscale()進行轉換。轉換后去除了無效數據,linesize[0]變為480。就可以正常顯示了。
總結
以上是生活随笔為你收集整理的100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jquery.validate.min.
- 下一篇: ffmpeg各种操作处理