播放器插件實現系列 —— mplayer

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);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章