Mplayer源码分析2

更新时间:2024-06-29 02:57:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

MPlayer流程

int c_mplay_main(int argc,char* argv[]) {

**调用 AddExcept()注册异常处理函数

** initmplayer(); //初始化,创建快进和暂停的信号量 **InitTimer();初始化计时器

**mp_msg_init();初始化消息系统 **set_path_env();设置路径、环境 **ipu_image_start();ipu初始化

**mplayer_showmode(1);设置显示模式

**parse_codec_cfg(NULL);解析codec配置寄存器 **打开数据流 **分析播放树 **添加播放树列表 **初始化预填充缓存 **打开播放的文件 **创建buffer **打开数据流

**检测数据流类型(音频格式和视频格式)

**分析音频流视频流的信息(原始视频尺寸、分辨率、帧频率、码流大小) **启动相应的分离器 **分析剪辑信息

**初始化codec(多媒体数字信号编解码器) **选择打开相应的视频解码器

**初始化视频解码器,分析视频流信息 **选择打开相应的音频解码器

**初始化音频解码器、PCM,分析音频信息 **同步音频视频输出 **开始播放 }

main函数中的入口如下~

/*========================== PLAY AUDIO ============================*/ if (mpctx->sh_audio)

if (!fill_audio_out_buffers())

// at eof, all audio at least written to ao

//由mpctx->sh_audio引出,我想重点强调一下sh_audio_t结构,这是音频流头部结构~

// Stream headers: typedef struct { int aid;

demux_stream_t *ds; struct codecs_st *codec; unsigned int format;

int initialized;

float stream_delay; // number of seconds stream should be delayed (according to dwStart or similar) // output format: int sample_format; int samplerate; int samplesize; int channels;

int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec) int i_bps; // == bitrate (compressed bytes/sec) // in buffers:

int audio_in_minsize; // max. compressed packet size (== min. in buffer size )

char* a_in_buffer; int a_in_buffer_len; int a_in_buffer_size; // decoder buffers:

int audio_out_minsize; // max. uncompressed packet size (==min. out buffsize )

char* a_buffer; int a_buffer_len; int a_buffer_size; // output buffers: char* a_out_buffer; int a_out_buffer_len; int a_out_buffer_size;

struct af_stream_s *afilter; // the audio filter stream struct ad_functions_s* ad_driver; #ifdef DYNAMIC_PLUGINS void *dec_handle; #endif

// win32-compatible codec parameters: AVIStreamHeader audio; WAVEFORMATEX* wf; // codec-specific:

void* context; // codec-specific stuff (usually HANDLE or struct pointer)

unsigned char* codecdata; // extra header data passed from demuxer to codec

int codecdata_len;

double pts; // last known pts value in output from decoder int pts_bytes; // bytes output by decoder after last known pts char* lang; // track language int default_track; } sh_audio_t;

三.step by step

1.下面我们来分析fill_audio_out_buffers()函数 static int fill_audio_out_buffers(void)

1.1查看音频驱动有多大的空间可以填充解码后的音频数据 bytes_to_write = mpctx->audio_out->get_space();

这里是查看音频驱动还有多少空闲空间(即pcm缓冲区队列的可用空间),如果有空闲的空间,就调用ao->play()

函数来填充这个buffer,并播放音频数据。(pcm缓冲区的数据会被dma自动送去播放,我们不能让pcm缓冲区为空,否则声音就没了!)这个音频驱动的buffer的大小要设置合适,太小会导致一帧图像还没播放完,buffer就已经空了,会导致音视频严重不同步;太大会导致Mplayer需要不停地解析媒体文件来填充这个音频buffer,而视频可能还来不及播放。。。

1.2 对音频流进行解码

if (decode_audio(sh_audio, playsize) < 0)

注:这里的decode_audio只是一个接口,并不是具体的解码api。这个函数从sh_audio->a_out_buffer获得至少playsize bytes的解码或过滤后的音频数据,成功返回0,失败返回-1

1.3 将解码后的音频数据进行播放

playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize, playflags); 注:从sh_audio->a_out_buffer中copy playsize bytes数据,注意这里一定要copy出来,因为当play调用后,这部分数据就会被覆盖了。这些数据不一定要用完,返回的值是用掉的数据长度。另外当flags|AOPLAY_FINAL_CHUNK的值是真时,说明音频文件快结束了。(play()函数把音频数据写到pcm缓冲区 。)

1.4 if (playsize > 0) {

sh_audio->a_out_buffer_len -= playsize;

memmove(sh_audio->a_out_buffer, &sh_audio->a_out_buffer[playsize], sh_audio->a_out_buffer_len);

mpctx->delay += playback_speed*playsize/(double)ao_data.bps; }

播放成功后就从sh_audio->a_out_buffer中移出这段数据,同时减去sh_audio->a_out_buffer_len的长度 .

2.刚才说了,decode_audio()函数并不是真正的解码函数,它只是提供一个接口,下面我们就来看看这个函数到底做了哪些事情.

int decode_audio(sh_audio_t *sh_audio, int minlen)

2.1 解码后的视频数据都被cut成大小相同的一个个区间~

int unitsize = sh_audio->channels * sh_audio->samplesize * 16;

2.2 如果解码器设置了audio_out_minsize,解码可以等价于 while (output_len < target_len) output_len += audio_out_minsize;

因此我们必需保证a_buffer_size大于我们需要的解码数据长度加上audio_out_minsize,所以我们最大解码长度也就有了如下的限制:(是不是有点绕口?~~)

int max_decode_len = sh_audio->a_buffer_size - sh_audio->audio_out_minsize;

2.3 如果a_out_buffer中没有我们需要的足够多的解码后数据,我们当然要继续解码撒~ 只不过我们要注意,

a_out_buffer中的数据是经过filter过滤了的(长度发生了变化),这里会有一个过滤系数,我们反方向求过滤前的数据长度,所以要除以这个系数。 while (sh_audio->a_out_buffer_len < minlen) {

int declen = (minlen - sh_audio->a_out_buffer_len) / filter_multiplier + (unitsize << 5); // some extra for possible filter buffering declen -= declen % unitsize;

if (filter_n_bytes(sh_audio, declen) < 0) return -1; }

3.我们来看看这个filter_n_bytes函数

static int filter_n_bytes(sh_audio_t *sh, int len)

3.1 还记得我们刚才说的那个最大解码长度吗? 这里是要确保不会发生解码后数据溢出。

assert(len-1 + sh->audio_out_minsize <= sh->a_buffer_size);

3.2 按需要解码更多的数据 while (sh->a_buffer_len < len) {

unsigned char *buf = sh->a_buffer + sh->a_buffer_len; int minlen = len - sh->a_buffer_len;

int maxlen = sh->a_buffer_size - sh->a_buffer_len;

//这里才是调用之前确定的音频解码器进行真正音频解码 int ret = sh->ad_driver->decode_audio(sh, buf, minlen, maxlen); if (ret <= 0) { error = -1;

len = sh->a_buffer_len; break; }

//解码之后a_buffer_len增加 sh->a_buffer_len += ret; }

3.3 在前面我们提到过过滤这个步骤,下面的图描述了整个过程 filter_output = af_play(sh->afilter, &filter_input); 注:这里实际上做的是过滤这部分的工作

------------ ---------- ------------

| | 解码 | | 过滤 | | 播放 | 未解码数据 | ----->|解码后数据| ------>| 播放的数据 | ------> | a_in_buffer| | a_buffer | |a_out_buffer| ------------ ---------- ------------

3.4 if (sh->a_out_buffer_size < sh->a_out_buffer_len + filter_output->len) 注:如果过滤后的数据长度太长,需要扩展a_out_buffer_size

3.5 将过滤后的数据从decoder buffer移除: sh->a_buffer_len -= len;

memmove(sh->a_buffer, sh->a_buffer + len, sh->a_buffer_len);

libavcodec libavformat libavutil三个文件夹来自ffmpeg的库 ;

libfaad2 libao2 liba52 libmpg2 mp3lib vidix几个文件夹是其它的三方库 ; libmpcodecs libmpdemux 文件夹中为mplayer 的 demux 和codecs。 ; 其中demux_XXX.c为处理各种不同的container.

vd_XXX.c为mplayer的内置视频解码器,ad_xxx.c是音频解码器。 vf_XXX.c 和af_XXX.c 为视,音频的各种filter。 vo_XXX.c 为解码后视频输出。

libvo 视频输出模块的 Make 文件配置部分头 libao2 音频输出模块的 Make 文件配置部分头

drivers 使用 VIDIX 技术用到的直接硬件访问驱动程序

vo_XXX.c是在libvo下

本文来源:https://www.bwwdw.com/article/7k73.html

Top