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解码问题》
首先需要增加一个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"
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模块对应。
close就比较直接了
static void mylib_stream_close (stream_t *stream) { Mylib_Close(); }
stream这边的代码就这些,下面是demux相关的代码
和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。
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。
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可以让你在输入流阻塞时也能够响应用户退出请求,退出循环。
拖动处理很简单,代码如下
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); }