close

本篇博客使用 FFmpeg+SDL2 完成播放視頻流Demo(僅播放視頻),所有相關知識在之前的博客中都有提到,稍作整理完成。

FFmpeg解碼視頻流:

SDL2顯示YUV數據:

源碼
#include<stdio.h>#include<SDL.h>#include<libavcodec/avcodec.h>#include<libavformat/avformat.h>#include<libswscale/swscale.h>intWinMain(intargc,char*argv[]){intret=-1;char*file="C:\\Users\\lenovo\\Desktop\\fengjing.mp4";AVFormatContext*pFormatCtx=NULL;inti,videoStream;AVCodecParameters*pCodecParameters=NULL;AVCodecContext*pCodecCtx=NULL;AVCodec*pCodec=NULL;AVFrame*pFrame=NULL;AVPacketpacket;SDL_Rectrect;Uint32pixformat;SDL_Window*win=NULL;SDL_Renderer*renderer=NULL;SDL_Texture*texture=NULL;//默認窗口大小intw_width=640;intw_height=480;//SDL初始化if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER)){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"CouldnotinitializeSDL-%s\n",SDL_GetError());returnret;}//打開輸入文件if(avformat_open_input(&pFormatCtx,file,NULL,NULL)!=0){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Couldn'topenvideofile!");goto__FAIL;}//找到視頻流videoStream=av_find_best_stream(pFormatCtx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);if(videoStream==-1){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Din'tfindavideostream!");goto__FAIL;//Didn'tfindavideostream}//流參數pCodecParameters=pFormatCtx->streams[videoStream]->codecpar;//獲取解碼器pCodec=avcodec_find_decoder(pCodecParameters->codec_id);if(pCodec==NULL){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Unsupportedcodec!\n");goto__FAIL;//Codecnotfound}//初始化一個編解碼上下文pCodecCtx=avcodec_alloc_context3(pCodec);if(avcodec_parameters_to_context(pCodecCtx,pCodecParameters)!=0){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Couldn'tcopycodeccontext");goto__FAIL;//Errorcopyingcodeccontext}//打開解碼器if(avcodec_open2(pCodecCtx,pCodec,NULL)<0){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failedtoopendecoder!\n");goto__FAIL;//Couldnotopencodec}//AllocatevideoframepFrame=av_frame_alloc();w_width=pCodecCtx->width;w_height=pCodecCtx->height;//創建窗口win=SDL_CreateWindow("MediaPlayer",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,w_width,w_height,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);if(!win){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"FailedtocreatewindowbySDL");goto__FAIL;}//創建渲染器renderer=SDL_CreateRenderer(win,-1,0);if(!renderer){SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"FailedtocreateRendererbySDL");goto__FAIL;}pixformat=SDL_PIXELFORMAT_IYUV;//YUV格式//創建紋理texture=SDL_CreateTexture(renderer,pixformat,SDL_TEXTUREACCESS_STREAMING,w_width,w_height);//讀取數據while(av_read_frame(pFormatCtx,&packet)>=0){if(packet.stream_index==videoStream){//解碼avcodec_send_packet(pCodecCtx,&packet);while(avcodec_receive_frame(pCodecCtx,pFrame)==0){SDL_UpdateYUVTexture(texture,NULL,pFrame->data[0],pFrame->linesize[0],pFrame->data[1],pFrame->linesize[1],pFrame->data[2],pFrame->linesize[2]);//SetSizeofWindowrect.x=0;rect.y=0;rect.w=pCodecCtx->width;rect.h=pCodecCtx->height;//展示SDL_RenderClear(renderer);SDL_RenderCopy(renderer,texture,NULL,&rect);SDL_RenderPresent(renderer);}}av_packet_unref(&packet);//事件處理SDL_Eventevent;SDL_PollEvent(&event);switch(event.type){caseSDL_QUIT:goto__QUIT;default:break;}}__QUIT:ret=0;__FAIL://FreetheYUVframeif(pFrame){av_frame_free(&pFrame);}//Closethecodecif(pCodecCtx){avcodec_close(pCodecCtx);}if(pCodecParameters){avcodec_parameters_free(&pCodecParameters);}//Closethevideofileif(pFormatCtx){avformat_close_input(&pFormatCtx);}if(win){SDL_DestroyWindow(win);}if(renderer){SDL_DestroyRenderer(renderer);}if(texture){SDL_DestroyTexture(texture);}SDL_Quit();returnret;}

這個Demo目前只是通過一個while循環將視頻播放出來,所以可以播放視頻但是速度不正常,並且沒有聲音,這些問題會在後面一一解決,最後完成一個簡易的播放器。

源碼 GitHub-SimplePlayer:

https://github.com/David1840/SimplePlayer

#defineREFRESH_EVENT(SDL_USEREVENT+1)//刷新事件#defineBREAK_EVENT(SDL_USEREVENT+2)//退出事件intthread_exit=0;intthread_pause=0;//線程每40ms刷新一次intvideo_refresh_thread(void*data){thread_exit=0;thread_pause=0;while(!thread_exit){if(!thread_pause){SDL_Eventevent;event.type=REFRESH_EVENT;SDL_PushEvent(&event);//發送刷新事件}SDL_Delay(40);}thread_exit=0;thread_pause=0;//BreakSDL_Eventevent;event.type=BREAK_EVENT;SDL_PushEvent(&event);return0;}
//創建線程SDL_CreateThread(video_refresh_thread,"VideoThread",NULL);for(;;){SDL_WaitEvent(&event);//使用時間驅動,每40ms執行一次if(event.type==REFRESH_EVENT){while(1){if(av_read_frame(pFormatCtx,&packet)<0)thread_exit=1;if(packet.stream_index==videoStream)break;}if(packet.stream_index==videoStream){avcodec_send_packet(pCodecCtx,&packet);while(avcodec_receive_frame(pCodecCtx,pFrame)==0){SDL_UpdateYUVTexture(texture,NULL,pFrame->data[0],pFrame->linesize[0],pFrame->data[1],pFrame->linesize[1],pFrame->data[2],pFrame->linesize[2]);//SetSizeofWindowrect.x=0;rect.y=0;rect.w=pCodecCtx->width;rect.h=pCodecCtx->height;SDL_RenderClear(renderer);SDL_RenderCopy(renderer,texture,NULL,&rect);SDL_RenderPresent(renderer);}av_packet_unref(&packet);}}elseif(event.type==SDL_KEYDOWN){if(event.key.keysym.sym==SDLK_SPACE){//空格鍵暫停thread_pause=!thread_pause;}if(event.key.keysym.sym==SDLK_ESCAPE){//ESC鍵退出thread_exit=1;}}elseif(event.type==SDL_QUIT){thread_exit=1;}elseif(event.type==BREAK_EVENT){break;}}

來源:https://juejin.cn/post/6844903842711093255


-- END --

進技術交流群,掃碼添加我的微信:Byte-Flow

獲取視頻教程和源碼


推薦:

Android FFmpeg 實現帶濾鏡的微信小視頻錄製功能

全網最全的 Android 音視頻和 OpenGL ES 乾貨,都在這了

Android 利用 FFmpeg 實現視頻解碼播放

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()