Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30058
  • 博文数量: 10
  • 博客积分: 165
  • 博客等级: 入伍新兵
  • 技术积分: 115
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-27 11:01
文章分类
文章存档

2015年(5)

2012年(5)

我的朋友

分类: LINUX

2012-03-01 11:24:11

一、配置文件的解析代码
 
 
 
二、音频数据流的获取过程
 
2. open_stream,                               打开文件流
        open_stream_full                       找到合适的打开文件的方式,对于本地文件选择
            open_stream_plugin                 stream_info_file,使用它的open_f函数读取文件
                open_f                                  成功,返回文件句柄,文件访问函数
   对于流媒体文件,第8行改成如下的第10行,表明流媒体文件选择stream_info_rtsp_sip
                open_live_rtsp_sip
三、音频数据流的解析
 mpctx->stream                                 2854行,记录文件流的信息,文件句柄,访问函数等
4. demux_open                                           打开分离器(音视频)
        demux_open_stream           从demuxer_list中找到合适的demuxer描述符,此处为demuxer_desc_audio
            new_demuxer
                stream_seek
                    cache_stream_seek_long
                        stream_seek_long                读取部分数据至内存
                            stream_fill_buffer       while(stream_fill_buffer(s) > 0 && pos >= 0) {
                                
            demux_audio_open         使用demuxer_desc_audio的打开函数,读取媒体数据的信息,头信息
                                                        对于m4v视频demuxer_desc_lavf_preferred,打开demux_open_lavf
   对于流媒体文件,上面的第15行换成如下的第18行,使用demuxer_desc_rtp,使用sdp文件建立流媒体会话,
            demux_open_rtp        创建媒体会话,创建音视频数据接收会话,初始化音视频的codec 
                          
四、音频解码器的初始化
6. reinit_audio_chain
        init_best_audio_codec                           mpcodecs_ad_drivers(见LIBAD_EXTERN(mp3lib)),ad_internal.h
            init_audio                                  选择mpcodecs_ad_mp3lib解码mp3的音频文件
        init_best_audio_out                             从audio_out_drivers选择音频输出设备audio_out_win32
            init                                        初始化
五、音频数据的播放
开始播放
音频,mp3
7. fill_audio_out_buffers
        decode_audio                                    dec_audio.c
            mpadec->decode_audio
            MP3_DecodeFrame                             获取数据并解码
                read_frame
                    stream_read_frame_body
                        mp3_read
                            mplayer_audio_read
                                demux_read_data
                                    ds_fill_buffer
                                        demux_fill_buffer
                                            demux_audio_fill_buffer(demux_audio.c)
                                                stream_read
                                                    cache_stream_fill_buffer
                                                        stream_fill_buffer
                                                            fill_buffer(stream_file.c)
        如果是流媒体,上面的47行将变为:     demux_rtp_fill_buffer(demux_rtp.cpp)                                                     
        play
            write_waveOutBuffer
 
 
 
 
Mplayer 音频解码分析
2010年08月25日 星期三 20:03

一.序

还是按部就班的来,这次主要分析一下Mplayer中音频解码流程,特别说明一下,这里

的音频解码包括后面会说的视频解码统统不涉及到具体的格式和解码算法,如果大伙对具

体文件格式和解码感兴趣可以在网上找相关资料看看~也可以留意popy的后续文章(广告~

二.入口

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

如果有空闲的空间,Mplayer会调用ao->play()函数来填充这个buffer,并播放音频数据。

这个音频驱动的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的值是真时,说明音频文件快结束了。

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

四.结语

回顾一下今天的内容,我们从sh_audio_t结构体出发,依次分析了fill_audio_out_b

uffers()函数,decode_audio()函数,filter_n_bytes()函数,但是并没有分析具体的de

code和play函数。

顺便说点题外话,看Mplayer的代码,结构化模块化的特点很突出,通常是先定义一组

接口,然后具体的程序去实现这些接口,这样子对代码的维护和扩展都很有好处~



阅读(2233) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~