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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

音视频学习之ffmpeg时间戳相关整理(时间基tbr,tbn,tbc)

發(fā)布時間:2023/12/14 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 音视频学习之ffmpeg时间戳相关整理(时间基tbr,tbn,tbc) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1: I幀/P幀/B幀

I幀:I幀(Intra-coded picture, 幀內(nèi)編碼幀,常稱為關(guān)鍵幀)包含?幅完整的圖像信息,屬于幀內(nèi)編碼圖像,不含運(yùn)動?量,在解碼時不需要參考其他幀圖像。因此在I幀圖像處可以切換頻道,?不會導(dǎo)致圖像丟失或?法解碼。I幀圖像?于阻?誤差的累積和擴(kuò)散。在閉合式GOP中,每個GOP的第?個幀?定是I幀,且當(dāng)前GOP的數(shù)據(jù)不會參考前后GOP的數(shù)據(jù)。

P幀:P幀(Predictive-coded picture, 預(yù)測編碼圖像幀)是幀間編碼幀,利?之前的I幀或P幀進(jìn)?預(yù)測編碼。

B幀:B幀(Bi-directionally predicted picture, 雙向預(yù)測編碼圖像幀)是幀間編碼幀,利?之前和(或)之后的I幀或P幀進(jìn)?雙向預(yù)測編碼。B幀不可以作為參考幀。B幀具有更?的壓縮率,但需要更多的緩沖時間以及更?的CPU占?率,B幀適合本地存儲以及視頻點播,?不適?對實時性要求較?的直播系統(tǒng)。

2: DTS和PTS

DTS(Decoding Time Stamp, 解碼時間戳),表示壓縮幀的解碼時間。
PTS(Presentation Time Stamp, 顯示時間戳),表示將壓縮幀解碼后得到的原始幀的顯示時間。

?頻中DTS和PTS是相同的。
視頻中由于B幀需要雙向預(yù)測,B幀依賴于其前和其后的幀,因此含B幀的視頻解碼順序與顯示順序不同,即DTS與PTS不同。當(dāng)然,不含B幀的視頻,其DTS和PTS是相同的。

下圖以?個開放式GOP示意圖為例,說明視頻流的解碼順序和顯示順序:

采集順序:指圖像傳感器采集原始信號得到圖像幀的順序。
編碼順序:指編碼器編碼后圖像幀的順序。存儲到磁盤的本地視頻?件中圖像幀的順序與編碼順序相同。
傳輸順序:指編碼后的流在?絡(luò)中傳輸過程中圖像幀的順序。
解碼順序:指解碼器解碼圖像幀的順序。
顯示順序:指圖像幀在顯示器上顯示的順序。

采集順序與顯示順序相同編碼順序、傳輸順序和解碼順序相同。
以圖中“B[1]”幀為例進(jìn)?說明,“B[1]”幀解碼時需要參考“I[0]”幀和“P[3]”幀,因此“P[3]”幀必須?“B[1]”幀先解碼。這就導(dǎo)致了解碼順序和顯示順序的不?致,后顯示的幀需要先解碼。

3: FFmpeg中的時間基與時間戳

3.1 時間基與時間戳的概念:

? ? ? ? 在FFmpeg中,時間基(time_base)是時間戳(timestamp)的單位,時間戳值乘以時間基,可以得到實際的時刻值(以秒等為單位)。
?? ??? ?例如,如果?個視頻幀的dts是40,pts是160,其time_base是1/1000秒,那么可以計算出此視頻幀的解碼時刻是40毫秒(40/1000),顯示時刻是160毫秒(160/1000)。
?? ??? ?FFmpeg中時間戳(pts/dts)的類型是int64_t類型,把?個time_base看作?個時鐘脈沖,則可把dts/pts看作時鐘脈沖的計數(shù)。

3.2 三種時間基tbr、tbn和tbc

不同的封裝格式具有不同的時間基。在FFmpeg處理?視頻過程中的不同階段,也會采?不同的時間基。FFmepg中有三種時間基,命令?中tbr、tbn和tbc的打印值就是這三種時間基的倒數(shù):
tbn:?? ?對應(yīng)容器中的時間基。?? ??? ? 值是AVStream.time_base的倒數(shù)
tbc:?? ?對應(yīng)編解碼器中的時間基。?? ?值是AVCodecContext.time_base的倒數(shù)
tbr:?? ?從視頻流中猜算得到,可能是幀率或場率(幀率的2倍)

測試?件下載(右鍵另存為):tnmil3.flv
使?ffprobe探測媒體?件格式,可以了解文件中使用的相關(guān)編碼,如下:

1 think@opensuse> ffprobe tnmil3.flv 2 ffprobe version 4.1 Copyright (c) 2007-2018 the FFmpeg developers 3 Input #0, flv, from 'tnmil3.flv': 4 Metadata: 5 encoder : Lavf58.20.100 6 Duration: 00:00:03.60, start: 0.017000, bitrate: 513 kb/s 7 Stream #0:0: Video: h264 (High), yuv420p(progressive), 784x480, 2 5 fps, 25 tbr, 1k tbn, 50 tbc 8 Stream #0:1: Audio: aac (LC), 44100 Hz, stereo, fltp, 128 kb/s

3.3 內(nèi)部時間基AV_TIME_BASE

除以上三種時間基外,FFmpeg還有?個內(nèi)部時間基AV_TIME_BASE(以及分?jǐn)?shù)形式的?AV_TIME_BASE_Q

// Internal time base represented as integer #define AV_TIME_BASE 1000000 //微妙 // Internal time base represented as fractional value #define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}

AV_TIME_BASE及AV_TIME_BASE_Q?于FFmpeg內(nèi)部函數(shù)處理,使?此時間基計算得到時間值表示的是微秒。

3.4 時間值形式轉(zhuǎn)換

===》**av_q2d()**將時間從AVRational形式轉(zhuǎn)換為double形式(分?jǐn)?shù)轉(zhuǎn)double)。

===》AVRational是分?jǐn)?shù)類型,double是雙精度浮點數(shù) 類型,轉(zhuǎn)換的結(jié)果單位是秒

===》轉(zhuǎn)換前后的值基于同?時間基,僅僅是數(shù)值的表現(xiàn)形式不同?已。

av_q2d()實現(xiàn)如下:

static inline double av_q2d(AVRational a){return a.num / (double) a.den; }//av_q2d()使??法如下: AVStream stream; AVPacket packet; packet播放時刻值: timestamp(單位秒) = packet.pts * av_q2d(stream.time_base); packet播放時?值: duration(單位秒) = packet.duration * av_q2d(stream.time_base);

3.5 時間基轉(zhuǎn)換函數(shù)

===》**av_rescale_q()**?于不同時間基的轉(zhuǎn)換,?于將時間值從?種時間基轉(zhuǎn)換為另?種時間基。

===》 將a數(shù)值由 bq時間基轉(zhuǎn)成 cq的時間基,

===》 通過返回結(jié)果獲取以cq時間基表示的新數(shù)值。

int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const;

??av_rescale_rnd()?是計算 “a * b / c” 的值并分五種?式來取整

int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) //它的作?是計算 "a * b / c" 的值并分五種?式來取整:AV_ROUND_ZERO = 0, // Round toward zero. 趨近于0, round(2.5) 為 2, ?round(-2.5) 為 -2 AV_ROUND_INF = 1, // Round away from zero. 趨遠(yuǎn)于0 round(3.5)=4, round(-3.5)=-4 AV_ROUND_DOWN = 2, // Round toward -infinity.向負(fù)?窮??向 [-2.9, -1.2, 2.4, 5.6,7.0, 2.4] -> [-3, -2, 2, 5, 7, 2] AV_ROUND_UP = 3, // Round toward +infinity. 向正?窮??向[-2.9, -1.2, 2.4, 5.6,7.0, 2.4] -> [-2, -1, 3, 6, 7, 3] AV_ROUND_NEAR_INF = 5, // Round to nearest and halfway cases away from zero. // 四舍五?,?于0.5取值趨向0,?于0.5取值趨遠(yuǎn)于0

??av_packet_rescale_ts()?于AVPacket中各種時間值從?種時間基轉(zhuǎn)換為另?種時間基。

void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst); //將AVPacket中各種時間值從?種時間基轉(zhuǎn)換為另?種時間基。

3.6 轉(zhuǎn)封裝過程中的時間基轉(zhuǎn)換

容器中的時間基(AVStream.time_base,3.2節(jié)中的tbn)定義如下:

typedef struct AVStream {......AVRational time_base;......轉(zhuǎn)封裝過程中的時間基轉(zhuǎn)換 }AVStream.time_base是AVPacket中pts和dts的時間單位,輸?流與輸出流中time_base按如下?式確定:對于輸?流:打開輸??件后,調(diào)?avformat_find_stream_info()可獲取到每個流中的time_base對于輸出流:打開輸出?件后,調(diào)?avformat_write_header()可根據(jù)輸出?件封裝格式確定每個流的time_base并寫?輸出?件中

不同封裝格式具有不同的時間基,在轉(zhuǎn)封裝(將?種封裝格式轉(zhuǎn)換為另?種封裝格式)過程中,時間基轉(zhuǎn)換相 關(guān)代碼如下:

//在轉(zhuǎn)封裝過程中,時間基轉(zhuǎn)換相關(guān)代碼如下: av_read_frame(ifmt_ctx, &pkt); pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);//下?的代碼具有和上?代碼相同的效果: // 從輸??件中讀取packet av_read_frame(ifmt_ctx, &pkt); // 將packet中的各時間值從輸?流封裝格式時間基轉(zhuǎn)換到輸出流封裝格式時間基 av_packet_rescale_ts(&pkt, in_stream->time_base, out_stream->time_base); // in_stream->time_base 和 out_stream->time_base ,是容器中的時間基

例如:flv封裝格式的time_base為{1,1000},ts封裝格式的time_base為{1,90000}

我們編寫程序?qū)lv封裝格式轉(zhuǎn)換為ts封裝格式,抓取原?件(flv)的前四幀顯示時間戳:

#抓取原?件(flv)的前四幀顯示時間戳: think@opensuse> ffprobe -show_frames -select_streams v tnmil3.flv | grep pkt_pts ffprobe version 4.1 Copyright (c) 2007-2018 the FFmpeg developers Input #0, flv, from 'tnmil3.flv': Metadata: encoder : Lavf58.20.100 Duration: 00:00:03.60, start: 0.017000, bitrate: 513 kb/s Stream #0:0: Video: h264 (High), yuv420p(progressive), 784x480,25 fps, 25 tbr, 1k tbn, 50 tbc Stream #0:1: Audio: aac (LC), 44100 Hz, stereo, fltp, 128 kb/s pkt_pts=80 pkt_pts_time=0.080000 pkt_pts=120 pkt_pts_time=0.120000 pkt_pts=160 pkt_pts_time=0.160000 pkt_pts=200 pkt_pts_time=0.200000#再抓取轉(zhuǎn)換的?件(ts)的前四幀顯示時間戳: think@opensuse> ffprobe -show_frames -select_streams v tnmil3.ts | grep pkt_pts ffprobe version 4.1 Copyright (c) 2007-2018 the FFmpeg developers Input #0, mpegts, from 'tnmil3.ts': Duration: 00:00:03.58, start: 0.017000, bitrate: 619 kb/s Program 1 Metadata: service_name : Service01 service_provider: FFmpeg Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B),yuv420p(progressive), 784x480, 25 fps, 25 tbr, 90k tbn, 50 tbc Stream #0:1[0x101]: Audio: aac (LC) ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltp, 127 kb/s pkt_pts=7200 pkt_pts_time=0.080000 pkt_pts=10800 pkt_pts_time=0.120000 pkt_pts=14400 pkt_pts_time=0.160000 pkt_pts=18000 pkt_pts_time=0.200000#可以發(fā)現(xiàn),對于同?個視頻幀,它們時間基(tbn)不同因此時間戳(pkt_pts)也不同,但是計算出來的時刻值(pkt_pts_time)是相同的。 #看第?幀的時間戳,計算關(guān)系:80×{1,1000} == 7200×{1,90000} == 0.080000

3.7 轉(zhuǎn)碼過程中的時間基轉(zhuǎn)換

編解碼器中的時間基(AVCodecContext.time_base,3.2節(jié)中的tbc)定義如下:

typedef struct AVCodecContext {......AVRational time_base;...... }//AVCodecContext.time_base是幀率(視頻幀)的倒數(shù),每幀時間戳遞增1,那么tbc就等于幀率。 // 編碼過程中,應(yīng)由?戶設(shè)置好此參數(shù)。 // 解碼過程中,此參數(shù)已過時,建議直接使?幀率倒數(shù)?作時間基。// 這?有?個問題:按照此處注釋說明,幀率為25的視頻流,tbc理應(yīng)為25,但實際值卻為50,不知作何解釋?是否tbc已經(jīng)過時,不具參考意義// 根據(jù)注釋中的建議, //實際使?時,在視頻解碼過程中,我們不使?AVCodecContext.time_base,??幀率倒數(shù)作時間基 // 在視頻編碼過程中,我們將AVCodecContext.time_base設(shè)置為幀率的倒數(shù)。

3.7.1 視頻流

? 視頻按幀播放,所以解碼后的原始視頻幀時間基為 1/framerate。

??視頻解碼過程中的時間基轉(zhuǎn)換處理(該段沒有參考意義,packet的pts到底什么,要看實際的 情況,從av_read_frame讀取的packet,是以AVSteam->time_base,送給解碼器之前沒有必要轉(zhuǎn)成 AVCodecContext->time_base, 需要注意的是avcodec_receive_frame后以AVSteam->time_base為 單位即可。):

//視頻解碼時間基轉(zhuǎn)換處理: AVFormatContext *ifmt_ctx; AVStream *in_stream; AVCodecContext *dec_ctx; AVPacket packet; AVFrame *frame; // 從輸??件中讀取編碼幀 av_read_frame(ifmt_ctx, &packet); // 時間基轉(zhuǎn)換(先轉(zhuǎn)換,再解碼) int raw_video_time_base = av_inv_q(dec_ctx->framerate); av_packet_rescale_ts(packet, in_stream->time_base, raw_video_time_base);// 解碼 avcodec_send_packet(dec_ctx, packet) avcodec_receive_frame(dec_ctx, frame);

??視頻編碼過程中的時間基轉(zhuǎn)換處理(編碼的時候frame如果以AVstream為time_base送編碼器, 則avcodec_receive_packet讀取的時候也是以轉(zhuǎn)成AVSteam->time_base,本質(zhì)來講就是具體情況具體 分析,沒必要硬套流程):

//視頻編碼時間基轉(zhuǎn)換處理: AVFormatContext *ofmt_ctx; AVStream *out_stream; AVCodecContext *dec_ctx; AVCodecContext *enc_ctx; AVPacket packet; AVFrame *frame; // 編碼 avcodec_send_frame(enc_ctx, frame); avcodec_receive_packet(enc_ctx, packet); // 時間基轉(zhuǎn)換 packet.stream_index = out_stream_idx; enc_ctx->time_base = av_inv_q(dec_ctx->framerate); av_packet_rescale_ts(&opacket, enc_ctx->time_base, out_stream->time_base); // 將編碼幀寫?輸出媒體?件 av_interleaved_write_frame(o_fmt_ctx, &packet);

3.7.2 ?頻流

??對于?頻流也是類似的

??===> ?如ffplay 解碼播放時就是AVSteam的time_base為基準(zhǔn)的packet進(jìn)?到編碼器,然后出來的frame再?AVSteam的 time_base講對應(yīng)的pts轉(zhuǎn)成秒,

??===>但是要注意的是ffplay做了?個?較隱秘的設(shè)置:avctx- >pkt_timebase = ic->streams[stream_index]->time_base;

??===>即是對應(yīng)的codeccontext??對 pkt_timebase設(shè)置和AVStream?樣的time_base。

? ?頻按采樣點播放,所以解碼后的原始?頻幀時間基為 1/sample_rate:

? ?頻解碼過程中的時間基轉(zhuǎn)換處理:

//?頻解碼過程中的時間基轉(zhuǎn)換處理: AVFormatContext *ifmt_ctx; AVStream *in_stream; AVCodecContext *dec_ctx; AVPacket packet; AVFrame *frame; // 從輸??件中讀取編碼幀 av_read_frame(ifmt_ctx, &packet); // 時間基轉(zhuǎn)換 int raw_audio_time_base = av_inv_q(dec_ctx->sample_rate); av_packet_rescale_ts(packet, in_stream->time_base, raw_audio_time_base); // 解碼 avcodec_send_packet(dec_ctx, packet) avcodec_receive_frame(dec_ctx, frame);

?頻編碼過程中的時間基轉(zhuǎn)換處理:

//?頻編碼過程中的時間基轉(zhuǎn)換處理: AVFormatContext *ofmt_ctx; AVStream *out_stream; AVCodecContext *dec_ctx; AVCodecContext *enc_ctx; AVPacket packet; AVFrame *frame; // 編碼 avcodec_send_frame(enc_ctx, frame); avcodec_receive_packet(enc_ctx, packet); // 時間基轉(zhuǎn)換 packet.stream_index = out_stream_idx; enc_ctx->time_base = av_inv_q(dec_ctx->sample_rate); av_packet_rescale_ts(&opacket, enc_ctx->time_base, out_stream->time_base); // 將編碼幀寫?輸出媒體?件 av_interleaved_write_frame(o_fmt_ctx, &packet);

轉(zhuǎn)自:音視頻學(xué)習(xí)之時間戳相關(guān)整理(時間基tbr,tbn,tbc)_yun6853992的博客-CSDN博客?

總結(jié)

以上是生活随笔為你收集整理的音视频学习之ffmpeg时间戳相关整理(时间基tbr,tbn,tbc)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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