日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Mplayer 音频解码分析

發(fā)布時(shí)間:2023/12/10 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mplayer 音频解码分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一.序

? ? 還是按部就班的來,這次主要分析一下Mplayer中音頻解碼流程,特別說明一下,這里

的音頻解碼包括后面會(huì)說的視頻解碼統(tǒng)統(tǒng)不涉及到具體的格式和解碼算法,如果大伙對(duì)具

體文件格式和解碼感興趣可以在網(wǎng)上找相關(guān)資料看看~也可以留意popy的后續(xù)文章(廣告~

二.入口

? ? main函數(shù)中的入口如下~

/*========================== PLAY AUDIO ============================*/

if (mpctx->sh_audio)

? ? if (!fill_audio_out_buffers())

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

? ? 由mpctx->sh_audio引出,我想重點(diǎn)強(qiáng)調(diào)一下sh_audio_t結(jié)構(gòu),這是音頻流頭部結(jié)構(gòu),

要仔細(xì)看看(注釋也別放過)~

// 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()函數(shù)

static int fill_audio_out_buffers(void)

1.1查看音頻驅(qū)動(dòng)有多大的空間可以填充解碼后的音頻數(shù)據(jù)

bytes_to_write = mpctx->audio_out->get_space();

如果有空閑的空間,Mplayer會(huì)調(diào)用ao->play()函數(shù)來填充這個(gè)buffer,并播放音頻數(shù)據(jù)。

這個(gè)音頻驅(qū)動(dòng)的buffer的大小要設(shè)置合適,太小會(huì)導(dǎo)致一幀圖像還沒播放完,buffer就已

經(jīng)空了,會(huì)導(dǎo)致音視頻嚴(yán)重不同步;太大會(huì)導(dǎo)致Mplayer需要不停地解析媒體文件來填充這

個(gè)音頻buffer,而視頻可能還來不及播放。。。

1.2 對(duì)音頻流進(jìn)行解碼

if (decode_audio(sh_audio, playsize) < 0)

注:這里的decode_audio只是一個(gè)接口,并不是具體的解碼api。

這個(gè)函數(shù)從sh_audio->a_out_buffer獲得至少playsize bytes的解碼或過濾后的音頻數(shù)據(jù)

,成功返回0,失敗返回-1

1.3 將解碼后的音頻數(shù)據(jù)進(jìn)行播放

playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize, playflags)

;

注:從sh_audio->a_out_buffer中copy playsize bytes數(shù)據(jù),注意這里一定要copy出來,

因?yàn)楫?dāng)play調(diào)用后,這部分?jǐn)?shù)據(jù)就會(huì)被覆蓋了。這些數(shù)據(jù)不一定要用完,返回的值是用掉

的數(shù)據(jù)長(zhǎng)度。另外當(dāng)flags|AOPLAY_FINAL_CHUNK的值是真時(shí),說明音頻文件快結(jié)束了。

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ù)據(jù),同時(shí)減去

sh_audio->a_out_buffer_len的長(zhǎng)度

2.剛才說了,decode_audio()函數(shù)并不是真正的解碼函數(shù),它只是提供一個(gè)接口,下面我

們就來看看這個(gè)函數(shù)到底做了哪些事情

int decode_audio(sh_audio_t *sh_audio, int minlen)

2.1 解碼后的視頻數(shù)據(jù)都被cut成大小相同的一個(gè)個(gè)區(qū)間~

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

2.2 如果解碼器設(shè)置了audio_out_minsize,解碼可以等價(jià)于

? ? while (output_len < target_len) output_len += audio_out_minsize;

因此我們必需保證a_buffer_size大于我們需要的解碼數(shù)據(jù)長(zhǎng)度加上audio_out_minsize,

所以我們最大解碼長(zhǎng)度也就有了如下的限制:(是不是有點(diǎn)繞口?~~)

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

;

2.3 如果a_out_buffer中沒有我們需要的足夠多的解碼后數(shù)據(jù),我們當(dāng)然要繼續(xù)解碼撒~

只不過我們要注意,a_out_buffer中的數(shù)據(jù)是經(jīng)過filter過濾了的(長(zhǎng)度發(fā)生了變化),這

里會(huì)有一個(gè)過濾系數(shù),我們反方向求過濾前的數(shù)據(jù)長(zhǎng)度,所以要除以這個(gè)系數(shù)。

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.我們來看看這個(gè)filter_n_bytes函數(shù)

static int filter_n_bytes(sh_audio_t *sh, int len)

3.1 還記得我們剛才說的那個(gè)最大解碼長(zhǎng)度嗎? 這里是要確保不會(huì)發(fā)生解碼后數(shù)據(jù)溢出。

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

3.2 按需要解碼更多的數(shù)據(jù)

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

? ?? ???//這里才是調(diào)用之前確定的音頻解碼器進(jìn)行真正音頻解碼

? ? ? ? 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 在前面我們提到過過濾這個(gè)步驟,下面的圖描述了整個(gè)過程

filter_output = af_play(sh->afilter, &filter_input);

注:這里實(shí)際上做的是過濾這部分的工作

------------? ?? ?? ?----------? ?? ?? ? ------------

|? ?? ?? ?? ?|??解碼 |? ?? ?? ? |??過濾??|? ?? ?? ?? ?|??播放

| 未解碼數(shù)據(jù) | ----->|解碼后數(shù)據(jù)| ------>| 播放的數(shù)據(jù) | ------>

| a_in_buffer|? ?? ? | a_buffer |? ?? ???|a_out_buffer|

------------? ?? ?? ?----------? ?? ?? ? ------------

3.4 if (sh->a_out_buffer_size < sh->a_out_buffer_len + filter_output->len)

注:如果過濾后的數(shù)據(jù)長(zhǎng)度太長(zhǎng),需要擴(kuò)展a_out_buffer_size

3.5 將過濾后的數(shù)據(jù)從decoder buffer移除:

? ? sh->a_buffer_len -= len;

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

四.結(jié)語

? ? 回顧一下今天的內(nèi)容,我們從sh_audio_t結(jié)構(gòu)體出發(fā),依次分析了fill_audio_out_b

uffers()函數(shù),decode_audio()函數(shù),filter_n_bytes()函數(shù),但是并沒有分析具體的de

code和play函數(shù)。

? ? 順便說點(diǎn)題外話,看Mplayer的代碼,結(jié)構(gòu)化模塊化的特點(diǎn)很突出,通常是先定義一組

接口,然后具體的程序去實(shí)現(xiàn)這些接口,這樣子對(duì)代碼的維護(hù)和擴(kuò)展都很有好處~

?

??mplayer音視頻同步淺度學(xué)習(xí)

?mplayer播放時(shí)的大循環(huán)過程為:
while(!mpctx->eof){

??fill_audio_out_buffers();//音頻stream的讀取,解碼,播放
? update_video(&blit_frame);//視頻stream的讀取,解碼,過濾處理
??sleep_until_update(&time_frame, &aq_sleep_time);//計(jì)算延遲時(shí)間并睡眠等待
??mpctx->video_out->flip_page();//視頻的播放
??adjust_sync_and_print_status(frame_time_remaining, time_frame);//根據(jù)音視頻的PTS做同步矯正處理

}

音視頻同步方法為
1)音頻播放playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize,? playflags);? 后,根據(jù)數(shù)據(jù)大小算出時(shí)間并累計(jì)mpctx->delay?+=?playback_speed*playsize/(double)ao_data.bps;

2)視頻解碼前,用累計(jì)延遲時(shí)間剪掉本禎視頻的時(shí)間mpctx->delay -= frame_time;

3)計(jì)算聲音延遲時(shí)間*time_frame = delay - mpctx->delay / playback_speed;
其中float delay = mpctx->audio_out->get_delay();為距當(dāng)前聲音OUTPUT BUF里數(shù)據(jù)被全部播放完為止所需的時(shí)間。
4)播放視頻同步完成,所以視頻的播放是完全根據(jù)聲卡最后的數(shù)據(jù)輸出來同步的。
5)計(jì)算出當(dāng)前音視頻PTS差double AV_delay = a_pts - audio_delay - v_pts;再算出矯正值x =(AV_delay + timing_error * playback_speed) *0.1f;最后把矯正的時(shí)間加到延遲累計(jì)中mpctx->delay+=x;。

轉(zhuǎn)載于:https://www.cnblogs.com/weinyzhou/archive/2012/12/13/2868682.html

總結(jié)

以上是生活随笔為你收集整理的Mplayer 音频解码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。