Skip to content
This repository has been archived by the owner on Jun 5, 2024. It is now read-only.
Jackarain edited this page Apr 20, 2012 · 10 revisions

#AVPlayer 之libav模块 ##libav模块简介 Libav是在基于ffmpeg之上的一个封装模块(不是ffmpeg分裂出来的那个libav), 采用c编写, 以提供少量API, 就可以组织成一个播放器. 整个模块以avplay结构为核心, media_source为媒体数据输入, audio_render为音频播放, video_render为视频渲染, 其中除avplay之外都需要在libav模块外实现, 这样方便制定各种需求.
libav模块的基本流程是, 以read_pkt_thrd为视频数据读取线程函数, 将读取的音视频packet(未解码), 投递到未解码的队列中(m_audio_q, m_video_q), 并通知视频和音频解码线程(video_dec_thrd, audio_dec_thrd)去从队列中获取packet并解码, 音视频解码线程在收到通知获得packet, 便开始解码, 解码后将解码frame投递到音视频解码队列(m_audio_dq, m_video_dq)中, 并通知音视频渲染线程(audio_render_thrd, video_render_thrd)去队列中获得解码frame并渲染.

整个如下图所示:

                         +----------------+     +------------------+
+-------------+   +--->  | video_dec_thrd | --> | video_render_thrd|-->口 视频输出
|read_pkt_thrd|---|      +----------------+     +------------------+
+-------------+   +--->  | audio_dec_thrd | --> | audio_render_thrd|-->(  音频输出
                         +----------------+     +------------------+
下面介绍上图中各线程的实现.

##read_pkt_thrd线程 在外部调用initialize之后(initialize将创建各线程, 并打开解码器, 初始化各队列, 创建m_format_ctx), read_pkt_thrd线程将循环调用av_read_frame, 将根据读取的packet的stream_index投递到对应的队列中, 在put_quque函数中将会通知解码线程获得packet去解码.

##audio_dec_thrd线程 音频解码线程循环调用get_queue来获得read_pkt_thrd线程所读取的packet, 并将packet解码, 解码完成后copy为一份新的frame, 保证不与解码buf冲突, 在audio_copy函数中, 判断如果是多于2声道的音频, 将重采样成2声道的音频, 如果不是16位的采样也将会重采样统一成16位. 接下来计算它的pts, 计算音频的pts方法非常简单, 就是根据channels和sample_rate以及数据大小来计算的, 比如一个packet解码后有1024byte, 2channels, 16bit, 44100sample rate, 那么这个frame的播放时长=1024/441002(16/2)=0.0058秒, 那么pts就是m_audio_clock, 那么下一个音频的pts就是m_audio_clock += 0.0058;
需要注意的是, 在avplay播放时和视频同步使用的时间戳是通过audio_clock函数来计算的, 在这个函数中, 首先得到m_audio_clock值(也就是所有已解码数据播放完成时的pts, 但此时实际上音频播放器还只播放了一小部分数据), 根据m_audio_clock和已播放完成的字节数计算出当前的准确播放位置, 如下图所示:

如下图所示:

若下面方括号中这个块是已解码的音频帧, 那么对应关系是:
         已播        未播   m_audio_clock
         0.2s        0.8s      |
         [----+----------------] 共1s
      begin  假设'+'这个位置是当前播放的位置.
那么, 你要给出和视频同步时间戳位置, 肯定是+所在的位置, 而不是m_audio_clock位置, 也不是begin位置, 可以看出是m_audio_clock-0.8; 而audio_clock函数就是完成这个工作的.

未完待续...

Clone this wiki locally