EasyPlayerPro for Windows是基于ffmpeg進行開發的全功能播放器,開發過程中參考了很多開源的播放器,諸如vlc和ffplay等,其中最強大的莫過于vlc,但是鑒于vlc框架過于龐大而其中仍存在諸多問題而舍棄了,而其他的更傾向于演示demo,只能提供部分借鑒意義;故而,EasyPlayerPro 一貫秉承Easy系列小而精,接口簡單功能強大的宗旨從新設計了一套框架,該套框架能適應多線程調用以及多個播放實例同時運行,和EasyPlayer一樣Easy; 當然,在此也鄭重的感謝各大開源播放器以及ffmpeg的作者的無私奉獻。
EasyPlayerPro分為三大模塊:打開模塊,讀取流數據模塊,解碼模塊和渲染模塊,其中:
(1) 打開模塊 打開流模塊很簡單,教科書式的調用方法:
player->avformat_context = avformat_alloc_context();player->avformat_context->interrupt_callback.callback = interrupt_cb;player->avformat_context->interrupt_callback.opaque = player;// open input fileAVDictionary *options = NULL;//av_dict_set(&options, "rtsp_transport", "udp", 0);if (avformat_open_input(&player->avformat_context, url, fmt, &options) != 0) {goto error_handler;}// find stream infoif (avformat_find_stream_info(player->avformat_context, NULL) < 0) {goto error_handler;}// set current audio & video streamfor (i=0,idx=-1,cur=-1; i<(int)player->avformat_context->nb_streams; i++) {
switch (type) {caseAVMEDIA_TYPE_AUDIO:// getlastcodeccontextif (player->acodec_context) {lastctxt = player->acodec_context;}// get new acodec_context & astream_timebaseplayer->acodec_context = player->avformat_context->streams[idx]->codec;player->astream_timebase = player->avformat_context->streams[idx]->time_base;// reopen codecif (lastctxt) avcodec_close(lastctxt);decoder = avcodec_find_decoder(player->acodec_context->codec_id);if (decoder && avcodec_open2(player->acodec_context, decoder, NULL) == 0) {player->astream_index = idx;}else {av_log(NULL, AV_LOG_WARNING, "failed to find or open decoder for audio !\n");player->astream_index = -1;}break;caseAVMEDIA_TYPE_VIDEO:// get last codec contextif (player->vcodec_context) {lastctxt = player->vcodec_context;}// get new vcodec_context & vstream_timebaseplayer->vcodec_context = player->avformat_context->streams[idx]->codec;player->vstream_timebase = player->avformat_context->streams[idx]->time_base;// reopen codecif (lastctxt) avcodec_close(lastctxt);decoder = avcodec_find_decoder(player->vcodec_context->codec_id);if (decoder && avcodec_open2(player->vcodec_context, decoder, NULL) == 0) {player->vstream_index = idx;}else {av_log(NULL, AV_LOG_WARNING, "failed to find or open decoder for video !\n");player->vstream_index = -1;}break;caseAVMEDIA_TYPE_SUBTITLE:return -1; // todo...}}if (idx == -1) return -1;// for audioif (player->astream_index != -1){arate = player->acodec_context->sample_rate;aformat = player->acodec_context->sample_fmt;alayout = player->acodec_context->channel_layout;//++ fix audio channel layout issueif (alayout == 0) {alayout = av_get_default_channel_layout(player->acodec_context->channels);}//-- fix audio channel layout issue}// for videoif (player->vstream_index != -1) {vrate = player->avformat_context->streams[player->vstream_index]->r_frame_rate;if (vrate.num / vrate.den >= 100) {vrate.num = 25;vrate.den = 1;}player->vcodec_context->pix_fmt = vformat;width = player->vcodec_context->width;height = player->vcodec_context->height;}
(3) 解碼模塊 解碼模塊分為音頻和視頻解碼模塊,音視頻的解碼流程非常相似, 主要分為三步: a. 從隊列中讀取音視頻編碼數據; b. 音視頻分別采用avcodec_decode_audio4和avcodec_decode_video2進行解碼; c. 音視頻渲染; 這里著重講解視頻的解碼后的過程,其中涉及到解碼后的原始圖像數據進行處理,解碼出一幀圖像以后,我們需要對其進行字幕和圖像或者其他的視頻圖像的疊加,借助ffmpeg強大的圖像轉換和縮放能力,借助VFX庫我們很容易實現:
(4) 渲染模塊 渲染模塊分為音頻渲染和視頻渲染,音頻渲染即播放,使用waveOutOpen,waveOutWrite等waveout函數即可實現,下面重點說一下視頻渲染,視頻渲染通俗講也就是圖像繪制,Windows平臺可采用D3D,DDraw, GDI,OpenGL等多種方式進行呈現,本文主要采用3種渲染方式,D3D,GDI和OpenGL; 為了保證渲染的流暢性,我們創建線程執行渲染, a. 讀取解碼圖像隊列; b. 音視頻時間戳同步處理; c. D3D/gdi/openGL渲染: