播放器插件实现系列 —— 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版本有问题,具体问题可以看我的另一篇文章《MPlayer Win32版本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);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting Horse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值