文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>让mplayer支持cue的可行性

让mplayer支持cue的可行性

时间:2010-05-03  来源:kaperx

之前发现Amarok支持cue文件的时候,感动得泪流满面。后来慢慢因为其各种大小缺陷,最终决定回归mplayer。但是回归之后用不了cue又是个心病,于是开始这篇蛋疼的分析。

假如把mplayer当作后端,那么支持cue是很容易的。前端解析cue之后,播放每个音轨可以用以下命令:
mplayer -ss start_time -endpos duration_time file
(两个time的格式都是[[hh:]mm:]ss[.ms])
现在我主要分析mplayer自身支持cue的可行性。

首先发现mplayer有一个选项:mplayer cue://file[:track] [options]
我以为它可以支持cue,使用之后发现不行。但这个选项的存在激发了我尝试在mplayer中完整支持cue的冲动。
查看mplayer代码stream/stream_cue.c才发现其对cue的支持相当有限。首先,它完全是为了支持VCD的bin/cue或img/cue文件而写的,流类型也定义为STREAMTYPE_VCDBINCUE了。其次,它要求cue文件里FILE必须写在第一行。另外,像TITLE、PERFORMER这类信息会被忽略。

FILE的问题好解决,主要的问题是如何让它播放wav、flac等文件中的track。
先看看stream_cue.c是如何实现的。调试后发现,mplayer是从 open_s函数进入该模块的,于是分析如下:

int open_s(stream_t *stream,int mode, void* opts, int* file_format)
{
检查参数正确性:
    mode必须是STREAM_READ。
    opts实际上是个内部结构,仅包含filename这个域,在这里判断filename非空。

检查filename中是否用  ”file[:track]”格式指定了track号。

cue_read_cue()解析cue文件,取得track信息。FILE的问题就需要在这函数里改。
cue_vcd_read_toc() 打印出每个track的格式和时间。
cue_vcd_seek_to_track()跳到filename中指定的track。

设置流属性:
    stream->fd = f; bin文件的描述符。
    stream->type = STREAMTYPE_VCDBINCUE; 流类型。
    stream->sector_size = VCD_SECTOR_DATA; 段大小。
    stream->flags = STREAM_READ | MP_STREAM_SEEK_FW;
    stream->fill_buffer = cue_vcd_read; 读内容的回调。
    stream->seek = seek; 流字节定位的回调。
    stream->control = control; 流控制回调(track定位等功能)。

成功返回。
}

三个回调的原型(stream/stream.h):
// Read
int (*fill_buffer)(struct stream_st *s, char* buffer, int max_len);
// Seek
int (*seek)(struct stream_st *s,off_t pos);
// Control
// Will be later used to let streams like dvd and cdda report
// their structure (ie tracks, chapters, etc)
int (*control)(struct stream_st *s,int cmd,void* arg);


其中control需要处理STREAM_CTRL_GET_NUM_CHAPTERS、STREAM_CTRL_SEEK_TO_CHAPTER和STREAM_CTRL_GET_CURRENT_CHAPTER这些命令,这就是实现track切换的地方。需要考虑track号与字节位置之间的换算。

对于VCD来说,这是可行的。而对于我现在要支持的wav、flac、ape的cue,问题就复杂了。
每种音频文件的格式是在libavformat内部定义的,并未对外提供接口。
我应该把这些格式暴露出来,在stream里面使用,还是应该要求每种格式提供按时间定位到字节位置的功能?这都是很破坏封装增加耦合的办法,很不合适。
鉴于之前对mplayer/ffmpeg毫无了解,我还是得先理解mplayer各模块如何交互之后才好下手。

根据DOCS/tech/general.txt和其他资料来理解,mplayer播放过程中涉及三层:
stream
demuxer in libmpdemux
codec

stream把输入的媒介(file,stdin,vcd,etc)抽象为字节流,这是最低的一层。
demuxer把字节流分解为视频流、音频流等。(libavformat好像是demuxer的一个实现,见libmpdemux/demux_lavf.c)
codec把视频流、音频流解码。

现在继续考虑如何切换音轨。换个角度想,看看哪个函数发出STREAM_CTRL_SEEK_TO_CHAPTER命令。grep了一下,除了mplaye.c以外(该处仅针对DVD),就是libmpdemuxer/demuxer.c中的demuxer_seek_chapter了:

/**
 * demuxer_seek_chapter() seeks to a chapter in two possible ways:
 * either using the demuxer->chapters structure set by the demuxer
 * or asking help to the stream layer (e.g. dvd)
 */
int demuxer_seek_chapter(demuxer_t *demuxer, int chapter, int mode,
                         float *seek_pts, int *num_chapters,
                         char **chapter_name)
{
如果demuxer中没有chapter信息
    把STREAM_CTRL_SEEK_TO_CHAPTER发给demuxer->stream
否则
    使用demuxer->chapters来进行chapter定位。
}

现在明白了,VCD的track切换可以用stream来实现是因为它不需要demuxer。而ape之类的track则需要实现在demuxer里。尴尬的是,stream接受cue文件,但它主要是用来读音频文件的。尽管它能解析出cue中的track信息却处理不了。Demuxer需要track信息却收不到。

即便也许可以把libavformat hack一下,让每个format支持cue文件流,结果想来也是极为丑陋的。

到目前为止,没找到恰当的途径来支持cue。因为我这是脚痛医脚,没有对mplayer有个整体的了解,也许遗漏了别的方面,也许已误入歧途,但无论如何不打算继续分析下去了(蛋疼也是有限度的)。总之,让mplayer原生支持cue看来是吃力不讨好的事,相比之下,还是写个支持cue的前端比较省心。


说到前端,有个CMMusic: http://forum.ubuntu.org.cn/viewtopic.php?f=74&t=178910&start=0
不过试用时播不出声音。
仅就界面和操作而言,MOC也不错,特别是它的server-client结构的设计允许后台播放。

===============================================================================

mplayer那混乱的代码看来颇受诟病。其maillist上看到的玩笑:

The M in Mplayer stands for "mess"

to draw such graphs for mplayer needs 6 dimensional paper in 9 dimensional spacetime.
相关阅读 更多 +
排行榜 更多 +
空中跑酷汉化版

空中跑酷汉化版

赛车竞速 下载
修仙传说

修仙传说

角色扮演 下载
魔界零之迷宫

魔界零之迷宫

冒险解谜 下载