Mplayer 音频解码分析

一.序

    还是按部就班的来,这次主要分析一下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的代码,结构化模块化的特点很突出,通常是先定义一组

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

 

  mplayer音视频同步浅度学习

 mplayer播放时的大循环过程为:
while(!mpctx->eof){

  fill_audio_out_buffers();//音频stream的读取,解码,播放
  update_video(&blit_frame);//视频stream的读取,解码,过滤处理
  sleep_until_update(&time_frame, &aq_sleep_time);//计算延迟时间并睡眠等待
  mpctx->video_out->flip_page();//视频的播放
  adjust_sync_and_print_status(frame_time_remaining, time_frame);//根据音视频的PTS做同步矫正处理

}

音视频同步方法为
1)音频播放playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize,  playflags);  后,根据数据大小算出时间并累计mpctx->delay += playback_speed*playsize/(double)ao_data.bps;

2)视频解码前,用累计延迟时间剪掉本祯视频的时间mpctx->delay -= frame_time;

3)计算声音延迟时间*time_frame = delay - mpctx->delay / playback_speed;
其中float delay = mpctx->audio_out->get_delay();为距当前声音OUTPUT BUF里数据被全部播放完为止所需的时间。
4)播放视频同步完成,所以视频的播放是完全根据声卡最后的数据输出来同步的。
5)计算出当前音视频PTS差double AV_delay = a_pts - audio_delay - v_pts;再算出矫正值x =(AV_delay + timing_error * playback_speed) *0.1f;最后把矫正的时间加到延迟累计中mpctx->delay+=x;。

转载于:https://www.cnblogs.com/weinyzhou/archive/2012/12/13/2868682.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422040.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux格式化sd卡博客,linux设备驱动那点事儿之SD卡驱动理论篇

一.SD/MMC卡介绍1.1.什么是MMC卡MMC&#xff1a;MMC就是MultiMediaCard的缩写&#xff0c;即多媒体卡。它是一种非易失性存储器件&#xff0c;体积小巧(24mm*32mm*1.4mm)&#xff0c;容量大,耗电量低,传输速度快&#xff0c;广泛应用于消费类电子产品中。1.2.什么是SD卡SD&…

SQL Server 2008 数据库同步的两种方式 (发布、订阅)

通过SQL JOB的方式对数据库的同步&#xff0c;这一节作为上一节的延续介绍通过发布订阅的方式实现数据库之间的同步操作。发布订阅份为两个步骤&#xff1a;1、发布。2、订阅。首先在数据源数据库服务器上对需要同步的数据进行发布&#xff0c;然后在目标数据库服务器上对上述发…

前端学习(619):变量的小案例二

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><script>//请输入姓名…

python linux alias,linux命令:alias

alias命令简介&#xff1a;此个命令用于命令别名&#xff0c;在shell中定义的别名仅在当前shell生命周期中有效&#xff1b;别名的有效范围仅为当前shellj进程&#xff1b;要想别名永久有效&#xff0c;必须把别名定义在别名配置文档中(/root/.bashrc---/root表示当前用户的家目…

UITabBarController

/*UITabBarController//实例化三个controllerMyViewController1 *vc1 [[MyViewController1 alloc]init];MyViewController2 *vc2 [[MyViewController2 alloc]init];// MyViewController3 *vc3 [[MyViewController3 alloc]init];//实例化一个导航,导航管理vc2&#xff0c;vc3…

【eoe特刊】第二十七期 OpenGL ES学习及项目解析

经过一个月征稿、编辑&#xff0c;新的一版特刊终于出炉了。 本次特刊的制作&#xff0c;改变以往的制作方式&#xff0c;完全取自网友的独自的风格。 在只有一个主题的前提下&#xff0c;完全是通过社区的热心的网友&#xff0c;根据自己的想法&#xff0c;自行设计&#x…

Linux如何禁止集成显卡,Ubuntu中禁用独显只用集显的方法

新装了ubuntu 12.04 Beta。还不错&#xff0c;就是双显卡问题。显卡&#xff1a;intel i3集成显卡和ati 5650。以前装了ati 驱动的话&#xff0c;若BIOS里不禁用集显就不能进入ubuntu&#xff0c;禁用了集显进win7又不能用集显&#xff0c;很是麻烦。用了那个git acpi_call禁有…

linux ubuntu技术支持电话,Ubuntu 17.04 将在1月13日结束技术支持

Canonical今天宣布&#xff0c;将在下周1月13日终止对Ubuntu 17.04 “Zesty Zapus”操作系统提供的支持。去年4月13日推出的Ubuntu 17.04是一款功能强大的内部和外部版本&#xff0c;运行最新的(当时)稳定的Linux 4.10内核系列&#xff0c;并以最新的基于Mesa 17.0和X.Org Serv…

前端学习(623):交换两个变量的值

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>交换两个变量的值</title> </head> <…

realloc invalid pointer错误解析

realloc invalid pointer错误 char* temp(char*) realloc(src,sizeof(char)*100); 如上面这行代码&#xff0c;可能会出现标题中的错误。错误原因是因为src指向的不是NULL或堆中的地址。 具体的就是realloc函数要求src为下面两种情况 1.srcNULL 2.src指针必须是malloc(), callo…

Linux的使用和认识实验,通过一个小实验认识Linux vDSO

这里不再解释vDSO的概念&#xff0c;而直接谈其意义&#xff1a;vDSO类似一个信息公告板&#xff0c;用户可以直取所需&#xff0c;而无需为此办理任何手续。vDSO相当于内核直接暴露出来的一个C库&#xff0c;作为GLIBC的补充。…类似gettimeofday之类的调用&#xff0c;每次都…

linux 漏洞数量,Debian Linux被列为过去20年漏洞数量最多的操作系统

1999 至 2019 年间&#xff0c;研究人员共发现了 Debian Linux 中的 3067 个安全漏洞。至于 Windows 平台&#xff0c;Server 2008 以 1421 个安全漏洞位列第一。Android 和 Linux 内核分别以 2563 和 2357 个漏洞排名第二和第三&#xff0c;macOS 以 2212 个漏洞排名第四。然而…

请问这博客能有几种方便写法?

好像这博客园不如网易写博客方便&#xff01; 网易博客支持一键写博客&#xff0c;这点能方便我收集网络资源&#xff0c;以便后续查阅和梳理。 网易支持word写博客&#xff0c;图文并茂。 支持邮件写博客等等&#xff0c;都相当方便&#xff01;&#xff01; 转载于:https://w…

监测ASP.NET MVC 网站

使用MiniProfiler调试ASP.NET MVC网站性能&#xff0c;MiniProfiler可以很好的处理网站后端每个处理时间的事件&#xff0c;但是MiniProfiler是无法远程做监测的动作&#xff0c;MiniProfiler只能够监测本地端的动作&#xff0c;所以MiniProfier比较适合开发期间使用。 在开发A…

Win10 安装 MongoDB 3.6.5 失败的问题及解决方法

MongoDB 3.6.5 2008R2Plus SSL (64 bit) Setup Wizard ended prematurely 在安装 MongoDB 的时候&#xff0c;出现了MongoDB 3.6.5 2008R2Plus SSL (64 bit) Setup Wizard ended prematurely的错误&#xff0c;原因不明&#xff0c;但有解决办法&#xff1a; 解决办法 在安装…