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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ffplay.c学习-6-⾳视频同步基础

發布時間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ffplay.c学习-6-⾳视频同步基础 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

ffplay.c學習-6-?視頻同步基礎


目錄

  • ?視頻同步策略
  • ?視頻同步概念
  • FFmpeg中的時間單位
  • ?視頻時間換算的問題
  • 不同結構體的time_base/duration分析
  • 不同結構體的PTS/DTS分析
  • ffplay中PTS的轉換流程分析
  • Video Frame PTS的獲取
  • Audio Frame PTS的獲取

  • 由于?頻和視頻的輸出不在同?個線程,?且,也不?定會同時解出同?個pts的?頻幀和視頻幀。更有甚者,編碼或封裝的時候可能pts還是不連續的,或有個別錯誤的。因此,在進??頻和視頻的播放時,需要對?頻和視頻的播放速度、播放時刻進?控制,以實現?頻和視頻保持同步,即所謂的?視頻同步。
  • 在ffplay中,?頻(audio)和視頻(video)有各?的輸出線程,其中?頻的輸出線程是sdl的?頻輸出回調線程,video的輸出線程是程序的主線程。
  • 1. ?視頻同步策略

  • ?視頻的同步策略,?般有如下?種:

  • 以?頻為基準,同步視頻到?頻(AV_SYNC_AUDIO_MASTER)
  • 視頻慢了則丟掉部分視頻幀(視覺->畫?跳幀)
  • 視頻快了則繼續渲染上?幀
  • 以視頻為基準,同步?頻到視頻(AV_SYNC_VIDEO_MASTER)
  • ?頻慢了則加快播放速度(或丟掉部分?頻幀,丟幀極容易聽出來斷?)
  • ?頻快了則放慢播放速度(或重復上?幀 )
  • ?頻改變播放速度時涉及到重采樣
  • 以外部時鐘為基準,同步?頻和視頻到外部時鐘(AV_SYNC_EXTERNAL_CLOCK)
  • 前兩者的綜合,根據外部時鐘改變播放速度
  • 視頻和?頻各?輸出,即不作同步處理(FREE RUN)
  • 由于??對于聲?變化的敏感度?視覺?,因此,?般采樣的策略是將視頻同步到?頻,即對畫?進?適當的丟幀或重復以追趕或等待?頻。

  • 特殊地,有時候會碰到?些特殊封裝(或者有問題的封裝),此時就不作同步處理,各?為主時鐘,進?播放。

  • 在ffplay中實現了上述前3種的同步策略。由 sync 參數控制:

  • { "sync", HAS_ARG | OPT_EXPERT, { .func_arg = opt_sync }, "set audiovideo sync. type (type=audio/video/ext)", "type" },
  • ?如ffplay source.200kbps.768x320.flv -sync video設置以video master
  • 2. ?視頻同步概念

  • 在深?代碼了解其實現前,需要先簡單了解下?些結構體和概念。
  • DTS(Decoding Time Stamp):即解碼時間戳,這個時間戳的意義在于告訴播放器該在什么時候解碼這?幀的數據。
  • PTS(Presentation Time Stamp):即顯示時間戳,這個時間戳?來告訴播放器該在什么時候顯示這?幀的數據。
  • timebase 時基:pts的值的真正單位
  • ffplay中的pts,ffplay在做?視頻同步時使?秒為單位,使?double類型去標識pts,在ffmpeg內部不會?浮點數去標記pts。
  • Clock 時鐘
  • 當視頻流中沒有 B 幀時,通常 DTS 和 PTS 的順序是?致的。但存在B幀的時候兩者的順序就不?致了。
  • pts是presentation timestamp的縮寫,即顯示時間戳,?于標記?個幀的呈現時刻,它的單位由timebase決定。timebase的類型是結構體AVRational(?于表示分數):
  • /*** Rational number (pair of numerator and denominator).*/ typedef struct AVRational{int num; ///< Numeratorint den; ///< Denominator } AVRational;
  • 如 timebase={1, 1000} 表示千分之?秒(毫秒),那么pts=1000,即為pts*1/1000 = 1秒,那么這?幀就需要在第?秒的時候呈現
  • 將AVRatioal結構轉換成double
  • static inline double av_q2d(AVRational a)return a.num / (double) a.den; }
  • 計算時間戳
  • timestamp() = pts * av_q2d(st->time_base)
  • 計算幀時?
  • time() = st->duration * av_q2d(st->time_base)
  • 不同時間基之間的轉換
  • int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
  • 在ffplay中,將pts轉化為秒,?般做法是: pts * av_q2d(timebase)
  • 在做同步的時候,我們需要?個"時鐘"的概念,?頻、視頻、外部時鐘都有??獨?的時鐘,各?set各?的時鐘,以誰為基準(master), 其他的則只能get該時鐘進?同步,ffplay定義的結構體是Clock:
  • // 這里講的系統時鐘 是通過av_gettime_relative()獲取到的時鐘,單位為微妙 typedef struct Clock {double pts; // 時鐘基礎, 當前幀(待播放)顯示時間戳,播放后,當前幀變成上一幀// 當前pts與當前系統時鐘的差值, audio、video對于該值是獨立的double pts_drift; // clock base minus time at which we updated the clock// 當前時鐘(如視頻時鐘)最后一次更新時間,也可稱當前時鐘時間double last_updated; // 最后一次更新的系統時鐘double speed; // 時鐘速度控制,用于控制播放速度// 播放序列,所謂播放序列就是一段連續的播放動作,一個seek操作會啟動一段新的播放序列int serial; // clock is based on a packet with this serialint paused; // = 1 說明是暫停狀態// 指向packet_serialint *queue_serial; /* pointer to the current packet queue serial, used for obsolete clock detection */ } Clock;
  • 這個時鐘的?作原理是這樣的:
  • 需要不斷“對時”。對時的?法 set_clock_at(Clock *c, double pts, int serial,double time) ,需要?pts、serial、time(系統時間)進?對時。
  • 獲取的時間是?個估算值。估算是通過對時時記錄的pts_drift估算的。pts_drift是最精華的設計,?定要理解。
  • 可以看這個圖來幫助理解:
  • 圖中央是?個時間軸(time是?直在按時間遞增),從左往右看。?先我們調? set_clock 進??次對時,假設這時的 pts 是落后時間 time 的,那么計算 pts_drift = pts - time ,計算出pts和time的相對差值。
  • 接著,過了?會?,且在下次對時前,通過 get_clock 來查詢時間,因為set_clock時的 pts 已經過時,不能直接拿set_clock時的pts當做這個時鐘的時間。不過我們前?計算過 pts_drift ,也就是 pts和 time 的差值,所以我們可以通過當前時刻的時間來估算當前時刻的pts: pts = time +pts_drift 。
  • ?般time會取CLOCK_MONOTONIC(單調遞增的時鐘),即系統開機到現在的時間.
  • ffplay使?ffmpeg提供的av_gettime_relative()函數
  • 3. FFmpeg中的時間單位

  • AV_TIME_BASE
  • 定義#define AV_TIME_BASE 1 000 000
  • ffmpeg中的內部計時單位(時間基)
  • AV_TIME_BASE_Q
  • 定義#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}
  • ffmpeg內部時間基的分數表示,實際上它是AV_TIME_BASE的倒數
  • 時間基轉換公式
  • timestamp(ffmpeg內部時間戳) = AV_TIME_BASE * time(秒)
  • time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg內部時間戳)
  • 4. ?視頻時間換算的問題

  • 標準時間 秒(seconds)
  • 標準時間 微秒(microsecond)
  • ?定義時間單位 (a/b 秒)
  • 以?頻AAC?頻幀舉例,如果pts以1/采樣率為單位,?如44.1khz, 則時間單位是1/44100,因此PTS表示:
  • 第?幀 PTS1 = 0
  • 第?幀 PTS2 = 1024
  • 第三幀 PTS2 = 2048
  • 播放的時候要將PTS換算成秒的單位,則PTS1 = 0 1/44100, PTS2 = 10241/44100, PTS3 =2048*1/44100= 0.046439
  • 5. 不同結構體的time_base/duration分析

  • ffmpeg存在多個時間基準(time_base),對應不同的階段(結構體),每個time_base具體的值不?樣,ffmpeg提供函數在各個time_base中進?切換。
  • AVFormatContext
  • duration:整個碼流的時?,獲取正常時?的時候要除以AV_TIME_BASE,得到的結果單位是秒
  • AVStream
  • time_base:單位為秒,?如AAC?頻流,他可能是{1,44100}TS流,按{1, 90khz}
  • duration:表示該數據流的時?,以AVStream->time_base 為單位
  • AVStream的time_base是在demuxer或者muxer內設置的,以TS,FLV,MP4為例?:
  • TS
    avpriv_set_pts_info(st, 33, 1, 90000) (mpegts.c和mpegtsenc.c)
  • FLV
    avpriv_set_pts_info(st, 32, 1, 1000) (flvdec.c)
    avpriv_set_pts_info(s->streams[i], 32, 1, 1000) (flvenc.c)
  • MP4
    avpriv_set_pts_info(st, 64, 1, sc->time_scale); (mov.c)
    avpriv_set_pts_info(st, 64, 1, track->timescale); (movenc.c)
  • 6. 不同結構體的PTS/DTS分析

  • 不同結構體下,pts和dts使?哪個time_base來表示?
  • AVPacket
    pts:以AVStream->time_base為單位
    dts:以AVStream->time_base為單位
    duration:以AVStream->time_base為單位
  • AVFrame
    pts:以AVStream->time_base為單位
    pkt_pts和pkt_dts:拷??AVPacket,同樣以AVStream->time_base為單位
    duration:以AVStream->time_base為單位
  • 7. ffplay中PTS的轉換流程分析

    1. Video Frame PTS的獲取

  • PTS校正
  • frame->pts = frame->best_effort_timestamp;
  • 這?為什么不?AVFrame中的pts來直接計算呢?其實?多數情況下AVFrame的pts和best_effort_timestamp值是?樣的
  • /*** frame timestamp estimated using various heuristics, in stream time base* - encoding: unused* - decoding: set by libavcodec, read by user.*/int64_t best_effort_timestamp;

    2. Audio Frame PTS的獲取

  • ffplay有3次對于Audio的pts進?轉換
  • 第?次 將其由AVStrean->time_base轉換為(1/采樣率)
    frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);
  • 第?次 將其由(1/采樣率)轉換為秒
  • af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
  • 第三次 根據實際拷?給sdl的數據?度做調整
  • audio_pts = is->audio_clock -(double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec;
  • 總結

    以上是生活随笔為你收集整理的ffplay.c学习-6-⾳视频同步基础的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。