ffmpeg 同步

音視頻同步有三種方式

  1. 同步音頻到視頻
  2. 同步到外部時鐘
  3. 同步視頻到音頻

同步視頻到音頻

  • 以audio為基準(zhǔn)同步video,只要設(shè)置好了 ao 的參數(shù),如sample rate, channels, sample size等, audio驅(qū)動就能以正確的速度播放,所以只要程序里write不出大問題的話,這種同步是非常有效的。
  • 設(shè)置以聲音為基準(zhǔn)進(jìn)行同步,聲頻播放是只管自己播放,視頻每渲染一張圖片取到的avframe中對應(yīng)的時間戳和音頻適中的時間戳進(jìn)行對比,調(diào)整視頻的渲染速度。
  • 設(shè)置聲音為2倍速度進(jìn)行播放,讓視頻同步音頻,測試可以完美支持。
  • 音頻去同步視頻設(shè)置多倍速度播放,無法有簡單的方式指定視頻嚴(yán)格的按照兩倍的速度進(jìn)行播放。
  • 三個clock,一個音頻的,一個視頻,一個中立的。
  • 以視頻為準(zhǔn),AudioQueue每讀取一音頻幀,讀取當(dāng)前幀中的時間戳,并通過到中立的clock中。
  • 播放視頻的時候,每渲染一個視頻幀都需要拿videoclock和中立的clock進(jìn)行對比,來決定是否要進(jìn)行渲染。

二倍速度播放音頻

  • AVPlayer 修改視頻的播放速度

屬性:

/* indicates the current rate of playback; 0.0 means "stopped", 1.0 means "play at the natural rate of the current item" */
@property (nonatomic) float rate;

實(shí)例代碼:

-(void)setPlaybackRate:(float)playbackRate
{
   _playbackRate = playbackRate;
   if (_player != nil && !isFloatZero(_player.rate)) {
       _player.rate = _playbackRate;
   }
}
  • AudioQueue修改視頻的播放速度
    實(shí)例代碼:
-  (void)setPlaybackRate:(float)playbackRate
{
    if (fabsf(playbackRate - 1.0f) <= 0.000001) {
        UInt32 propValue = 1;
        AudioQueueSetProperty(_audioQueueRef, kAudioQueueProperty_TimePitchBypass, &propValue, sizeof(propValue));
        AudioQueueSetParameter(_audioQueueRef, kAudioQueueParam_PlayRate, 1.0f);
    } else {
        UInt32 propValue = 0;
        AudioQueueSetProperty(_audioQueueRef, kAudioQueueProperty_TimePitchBypass, &propValue, sizeof(propValue));
        AudioQueueSetParameter(_audioQueueRef, kAudioQueueParam_PlayRate, playbackRate);
    }
}

pts

視頻讀取pts

  • 函數(shù)實(shí)例:
 ret = avcodec_decode_video2(d->avctx, frame, &got_frame, &d->pkt_temp);
                if (got_frame) {
                    ffp->stat.vdps = SDL_SpeedSamplerAdd(&ffp->vdps_sampler, FFP_SHOW_VDPS_AVCODEC, "vdps[avcodec]");
                    if (ffp->decoder_reorder_pts == -1) {
                        frame->pts = av_frame_get_best_effort_timestamp(frame);
                    } else if (ffp->decoder_reorder_pts) {
                        frame->pts = frame->pkt_pts;
                    } else {
                        frame->pts = frame->pkt_dts;
                    }
                }
  • 視頻獲取時間戳

int64_t av_frame_get_best_effort_timestamp(const AVFrame *frame);

音頻讀取pts

函數(shù)實(shí)例:

 ret = avcodec_decode_audio4(d->avctx, frame, &got_frame, &d->pkt_temp);
                if (got_frame) {
                    AVRational tb = (AVRational){1, frame->sample_rate};
                    if (frame->pts != AV_NOPTS_VALUE)
                        frame->pts = av_rescale_q(frame->pts, d->avctx->time_base, tb);
                    else if (frame->pkt_pts != AV_NOPTS_VALUE)
                        frame->pts = av_rescale_q(frame->pkt_pts, av_codec_get_pkt_timebase(d->avctx), tb);
                    else if (d->next_pts != AV_NOPTS_VALUE)
                        frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
                    
                    if (frame->pts != AV_NOPTS_VALUE) {
                        d->next_pts = frame->pts + frame->nb_samples;
                        d->next_pts_tb = tb;
                    }
                }

音頻一個AVPacket中只解析一個AVFrame, pkt_pts 可以當(dāng)做真實(shí)的pts

流的時間基準(zhǔn)

從某個音頻或者視頻流的時間基準(zhǔn)中得到時間戳的 AVRational

AVRational av_codec_get_pkt_timebase (const AVCodecContext *avctx);

FrameQueue

typedef struct FrameQueue {
    Frame queue[FRAME_QUEUE_SIZE];
    int rindex;         // read index
    int windex;     // write index 
    int size;           // 大小
    int max_size;
    int keep_last;
    int rindex_shown; // read shown 已經(jīng)讀取的個數(shù) 
    SDL_mutex *mutex;
    SDL_cond *cond;
    PacketQueue *pktq;
} FrameQueue;

幾個重要的函數(shù)

  1. synchronize_audio 音頻clock校準(zhǔn)函數(shù)
  2. video_refresh 視頻的渲染調(diào)用
  3. compute_target_delay 計算音頻和視頻的delay
  4. update_video_pts 同步視頻的時間戳
  5. audio_decode_frame 讀取音頻數(shù)據(jù)后同步音頻時鐘
  6. video_refresh_thread remaining_time決定每次休眠時間
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容