FFMPEG+SDL簡單視訊播放器——視訊快進

2023-10-11 12:09:41

之前寫過一篇關於視訊播放器的文章。播放器只簡單實現了視訊播放的功能,在此功能的基礎上,給它加上一個視訊快進的功能。

實現

新增引數

// video play control
    bool do_seek = false; // 播放狀態
    int64_t seek_length = 5; // 快進秒數
    int64_t seek_pos; // 視訊跳轉到的位置
    int seek_dir; // 視訊跳轉方向(快進或者後退)
    int rem_seek;

鍵盤監聽

新增SDL鍵盤監聽事件,通過鍵盤控制視訊的快進後退。

...
else if (event.type == SDL_KEYDOWN)
{
    if (event.key.keysym.sym == SDLK_SPACE)
        thread_pause = !thread_pause;
    // <- 控制後退
    else if (event.key.keysym.sym == SDLK_LEFT)
    {
        do_seek = true;
        seek_dir = 0;
    }
    // -> 控制快進
    else if (event.key.keysym.sym == SDLK_RIGHT)
    {
        do_seek = true;
        seek_dir = 1;
    }
    // q 退出播放
    else if (event.key.keysym.sym == SDLK_q)
    {
        thread_exit = 1;
    }
}
...

跳轉引數獲取

獲取視訊的時間基。根據時間基和視訊實際跳轉的秒數,計算出視訊每次快進需要跳轉的長度。

...
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
    if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        videoindex = i;
        AVRational timebase = pFormatCtx->streams[i]->time_base; // 時間基
        printf("TimeBase: %d/%d\n", timebase.num, timebase.den);
        rem_seek = seek_length;
        seek_length *= timebase.den; // 實際跳轉的長度
        printf("seek length: %d\n", seek_length);
        break;
    }
}
...

快進

在迴圈中監聽視訊狀態,當視訊狀態為快進時,執行視訊快進或後退操作

...
if (do_seek)
{
    printf("current packet pts: %d\n", packet->pts);
    // 快進
    if (seek_dir)
    {
        printf("Fast Forward %d s \n", rem_seek);
        seek_pos = packet->pts + seek_length;
    }
    // 後退
    else
    {
        printf("Rewind %d s \n", rem_seek);
        seek_pos = packet->pts - seek_length;
    }
    // 視訊跳轉
    if (av_seek_frame(pFormatCtx, videoindex, seek_pos, AVSEEK_FLAG_BACKWARD) < 0)
    {
        printf("Error while seeking\n");
        return -1;
    }
    // 視訊跳轉狀態修改
    do_seek = false;
}
...

視訊快進用到的核心函數為av_seek_frame。
av_seek_frame用於在媒體檔案中尋找指定的幀(或者說時間位置)。這個函數通常用於跳轉到媒體檔案中的特定時間點或幀,以便從那裡開始播放或處理媒體資料。

函數的一般形式為:

int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);

引數說明:

  • s: AVFormatContext 結構體,表示媒體檔案的上下文,包括媒體檔案的資訊和流資訊。
  • stream_index: 要尋找的流的索引。
  • timestamp: 要尋找的時間位置,以微秒為單位。可以使用 AV_TIME_BASE 來進行時間單位的轉換。
  • flags: 控制尋找行為的標誌。

這個函數的返回值通常是零或正數,表示成功的跳轉,或者是一個負數,表示出現了錯誤。
av_seek_frame 可以用於不同的媒體檔案格式,包括音訊、視訊以及它們的組合。在視訊播放器、音訊編輯器等多媒體應用程式中,這個函數通常用於使用者拖動進度條、跳轉到指定時間點或進行其他使用者互動操作。

原始碼

https://github.com/canaconZion/streaming-practice/blob/main/ffmpeg/src/video_player.cpp

關於視訊播放部分的程式碼,可以參考文章《基於FFMPEG+SDL的簡單的視訊播放器分析 》