mplayer嚴格來說是不支持插件的,這與他的定位有關。mplayer有很多定製版,比如GMplayer,SMPlayer,MPlayerX,更多定製版是用在嵌入式平臺,沒有名字。
mplayer是比較輕量級的播放器,結構小巧,但是編解碼支持並不比其他播放器少。
在mplayer上增加插件,需要直接修改源碼,發佈自己的定製版。
與vlc不一樣,mplayer中協議輸入和解封裝是必須分開的兩個功能,不能合併在一起,因此,需要增加兩個模塊:stream,demux,對於源碼文件分別是stream/stream_mylib.c、libmpdemux/demux_mylib.c
我們採用的是mplayer-1.0rc4版本的源代碼,mplayer-1.1版本有問題,具體問題可以看我的另一篇文章《MPlayerWin32版本H.264解碼問題》
1、註冊stream模塊
首先需要增加一個stream類型定義:
在stream/stream.h中增加STREAMTYPE_MYLIB宏定義:
#define STREAMTYPE_MF 18 #define STREAMTYPE_RADIO 19 #define STREAMTYPE_BLURAY 20 #define STREAMTYPE_MYLIB 21 // 自定義流類型 #define STREAM_BUFFER_SIZE 2048
在stream/stream.c,auto_open_streams數組中增加stream_info_mylib
extern const stream_info_t stream_info_mylib; static const stream_info_t* const auto_open_streams[] = { #ifdef CONFIG_VCD &stream_info_vcd, #endif .... &stream_info_mylib, &stream_info_null, &stream_info_mf, &stream_info_file, NULL };
這裏的stream_info_mylib實際定義在插件源碼stream/stream_mylib.c中:
const stream_info_t stream_info_ppbox = { "mylib input", "mylib", "luansxx", "", mylib_stream_open, { "vod", NULL }, NULL, 1 };
其中最關鍵的是定義了一個流打開回調接口:mylib_stream_open,以及協議標識:"vod"
2、實現mylib_stream_open
static int mylib_stream_open (stream_t *stream, int mode, void *opts, int *file_format) { my_int ret = Mylib_Open(stream->url); if (ret != mylib_success) { return STREAM_ERROR; } stream->type = STREAMTYPE_MYLIB; stream->close = mylib_stream_close; *file_format = DEMUXER_TYPE_MYLIB; return STREAM_OK; }
這裏主要工作是設置流類型(stream->type),用來關閉流的回調函數(stream->close)文件格式(file_format)和文件格式(file_format),通過文件類型可以與後面我們的demux模塊對應。
3、實現mylib_stream_close
close就比較直接了
static void mylib_stream_close (stream_t *stream) { Mylib_Close(); }
stream這邊的代碼就這些,下面是demux相關的代碼
4、註冊stream模塊
和stream模塊註冊過程一樣,我們需要在libmpdemux/demuxer.h中增加DEMUXER_TYPE_MYLIB,在libmpdemux/demuxer.c中的demuxer_list數組中增加我們的demux記錄demuxer_desc_mylib
在libmpdemux/demux_mylib.c中定義demux相關信息:
const demuxer_desc_t demuxer_desc_mylib = { "mylib demuxer", "mylib", "mylib", "?", "", DEMUXER_TYPE_MYLIB, 0, // no autodetect NULL, demux_mylib_fill_buffer, demux_mylib_open, NULL, demux_mylib_seek, NULL };
這裏面關鍵信息有類型DEMUXER_TYPE_MYLIB,以及6個回調函數,分別是:檢查、解封裝、打開、關閉、拖動、控制,大都是可選的,如果覺得沒有用設置爲NULL。
5、實現demux_mylib_open
typedef struct mylib_demux_priv { uint64_t duration; uint32_t stream_count; demux_stream_t * streams[8]; } mylib_demux_priv_t; static demuxer_t* demux_mylib_open(demuxer_t* demuxer) { mylib_demux_priv_t * priv = (mylib_demux_priv_t *)calloc(sizeof(mylib_demux_priv_t), 1); priv->duration = Mylib_GetDuration()(); priv->stream_count = Mylib_GetStreamCount()(); mp_msg(MSGT_DEMUX, MSGL_ERR, "demux_mylib_open duration %u\n", priv->duration); mp_msg(MSGT_DEMUX, MSGL_ERR, "demux_mylib_open stream count %u\n", priv->stream_count); for (uint32_t idx = 0; idx < priv->stream_count; ++idx) { Mylib_StreamInfoEx info; Mylib_GetStreamInfoEx()(idx, &info); if (info.type == mylib_video) { mp_msg(MSGT_DEMUX, MSGL_ERR, "video stream\n"); sh_video_t* sh_video = new_sh_video(demuxer, idx);; BITMAPINFOHEADER *bih = calloc(sizeof(*bih) + info.format_size, 1); bih->biSize = sizeof(*bih) + info.format_size; bih->biCompression = mmioFOURCC('H', '2', '6', '4'); bih->biWidth = info.video_format.width; bih->biHeight = info.video_format.height; memcpy(bih + 1, info.format_buffer, info.format_size); sh_video->format = bih->biCompression; sh_video->disp_w = info.video_format.width; sh_video->disp_h = info.video_format.height; sh_video->fps = info.video_format.frame_rate; sh_video->frametime = 1.0 / info.video_format.frame_rate; sh_video->bih = bih; demuxer->video->sh = sh_video; priv->streams[idx] = demuxer->video; } else if (info.type == mylib_audio) { mp_msg(MSGT_DEMUX, MSGL_ERR, "audio stream\n"); #if STREAMTYPE_PPBOX == 21 sh_audio_t* sh_audio = new_sh_audio(demuxer, idx); #else sh_audio_t* sh_audio = new_sh_audio(demuxer, idx, NULL); #endif WAVEFORMATEX *wf = calloc(sizeof(*wf) + info.format_size, 1);; wf->wFormatTag = 255; wf->nChannels = info.audio_format.channel_count; wf->nSamplesPerSec = info.audio_format.sample_rate; wf->wBitsPerSample = info.audio_format.sample_size; wf->cbSize = info.format_size; memcpy(wf + 1, info.format_buffer, info.format_size); sh_audio->wf = wf; sh_audio->format = mmioFOURCC('M', 'P', '4', 'A'); sh_audio->samplerate = info.audio_format.sample_rate; sh_audio->samplesize = info.audio_format.sample_size; sh_audio->channels = info.audio_format.channel_count; demuxer->audio->sh = sh_audio; demuxer->audio->id = idx; priv->streams[idx] = demuxer->audio; } } demuxer->seekable = priv->duration > 0; demuxer->priv = priv; return demuxer; }
這裏設置了私有數據demuxer->priv=priv,增加了音視頻流,主要工作在設置sh_video_t、sh_audio_t,比較詭異的是用了windows結構體:BITMAPINFOHEADER、WAVEFORMATEX。另外,運行的時候一直說沒有audio,後來沒辦法強制設置demuxer->audio->id=idx。
6、實現demux_mylib_fill_buffer
static int demux_mylib_fill_buffer(demuxer_t* demuxer, demux_stream_t *ds) { mylib_demux_priv_t *priv = (mylib_demux_priv_t *) demuxer->priv; Mylib_SampleEx2 sample; my_int32 ret; while (1) { ret = Mylib_ReadSampleEx2()(&sample); if (ret == mylib_success) { demux_packet_t *dp = new_demux_packet(sample.buffer_length); memcpy(dp->buffer, sample.buffer, sample.buffer_length); dp->pts = (double)(sample.start_time + sample.composite_time_delta) * 0.000001; ds_add_packet(priv->streams[sample.stream_index], dp); if (priv->streams[sample.stream_index] == ds) return 1; } else if (ret == mylib_would_block) { if (mp_input_check_interrupt(100)) return 0; } else if (ret == mylib_stream_end) { demuxer->stream->eof = 1; return 0; } else { return 0; } }; return 0; }
這個函數的最終目標是通過ds_add_packet向後面的模塊輸出一個音視頻幀,第二個參數好像是指定哪個流(音頻或者視頻)需要數據,但是也可以向另一個流輸出數據。
mp_input_check_interrupt可以讓你在輸入流阻塞時也能夠響應用戶退出請求,退出循環。
6、實現demux_mylib_seek
拖動處理很簡單,代碼如下
static void demux_mylib_seek(demuxer_t *demuxer,float rel_seek_secs,float audio_delay,int flags) { Mylib_Seek((uint32_t)rel_seek_secs * 1000); }