從零開始學習音視頻編程技術(八) FFMPEG Qt視頻播放器之音視頻同步

原文地址:http://blog.yundiantech.com/?log=blog&id=11

前面分別講解了:

  1. 用FFMPEG和Qt解碼視頻並顯示到界面上。

  2. 用FFMPEG+SDL解碼播放音頻


現在我們就將視頻和音頻合併,並讓聲音和畫面同步。


加入音頻的部分就不做講解了,這裏主要講下聲音和視頻同步的步驟。


首先剛開始播放的時候通過av_gettime()獲取系統主時鐘,記錄下來。

以後便不斷調用av_gettime()獲取系統時鐘 減去之前記錄下的差值,便得到了一個視頻播放了多久的實際時間。


對於視頻的同步我們這樣做:

從視頻讀取出的數據中包含一個pts的信息(每一幀圖像都會帶有pts的信息,pts就是播放視頻的時候此圖像應該顯示的時間)。 這樣只需要使用pts和前面獲取的時間進行對比,pts比實際時間大,就調用sleep函數等一等,否則就直接播放出來。這樣就達到了某種意義上的同步了。


而對於音頻:

從前面使用SDL的例子,其實就能夠發現一個現象:我們讀取音頻的線程差不多就是瞬間讀完放入隊列的,但是音頻播放速度卻是正常的,並不是一下子播放完畢。因此可以看出,在音頻播放上,SDL已經幫我們做了處理了,只需要將數據直接交給SDL就行了。


視頻部分同步代碼大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    int64_t start_time = av_gettime();    
    int64_t pts = 0; //當前視頻的pts
 
    while (1)
    {
        if (av_read_frame(pFormatCtx, packet) < 0)
        {
            break//這裏認爲視頻讀取完了
        }
 
        int64_t realTime = av_gettime() - start_time; //主時鐘時間
 
        while(pts > realTime)
        {
            SDL_Delay(10);
            realTime = av_gettime() - start_time; //主時鐘時間
        }
 
        if (packet->stream_index == videoStream)
        {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
            if (packet->dts == AV_NOPTS_VALUE && pFrame->opaque&& *(uint64_t*) pFrame->opaque != AV_NOPTS_VALUE)
            {
                pts = *(uint64_t *) pFrame->opaque;
            }
            else if (packet->dts != AV_NOPTS_VALUE)
            {
                pts = packet->dts;
            }
            else
            {
                pts = 0;
            }
 
            pts *= 1000000 * av_q2d(mVideoState.video_st->time_base);
            pts = synchronize_video(&mVideoState, pFrame, pts);
 
            if (got_picture) {
                sws_scale(img_convert_ctx,
                        (uint8_t const const *) pFrame->data,
                        pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                        pFrameRGB->linesize);
 
                //把這個RGB數據 用QImage加載
                QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
                QImage image = tmpImg.copy(); //把圖像複製一份 傳遞給界面顯示
                emit sig_GetOneFrame(image);  //發送信號
            }
 
            av_free_packet(packet);
        }


synchronize_video函數是根據解碼後的視頻數據 計算出視頻的pts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) {
    double frame_delay;
 
    if (pts != 0) {
        /* if we have pts, set video clock to it */
        is->video_clock = pts;
    else {
        /* if we aren't given a pts, set it to the clock */
        pts = is->video_clock;
    }
    /* update the video clock */
    frame_delay = av_q2d(is->video_st->codec->time_base);
    /* if we are repeating a frame, adjust clock accordingly */
    frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
    is->video_clock += frame_delay;
    return pts;
}



到此,播放器已經有點樣子了,已經可以播放大部分的視頻了,是的,我說的是大部分,還有些特別的視頻,播放的時候音視頻同步有問題,後面再來完善它。


完整工程下載地址:

http://download.csdn.net/detail/qq214517703/9628842


音視頻技術交流討論歡迎加 QQ羣 121376426  

原文地址:http://blog.yundiantech.com/?log=blog&id=11


發佈了43 篇原創文章 · 獲贊 46 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章