FFMPEG+SDL简单视频播放器——视频快进

ffmpeg,sdl,简单,视频,播放器 · 浏览次数 : 22

小编点评

**视频播放器添加视频快进功能** **功能概述** 该功能允许用户通过键盘控制视频的快进或后退。通过添加参数**// video play control bool do_seek = false;**,用户可以控制播放状态,并在**// seek_pos**中设置视频跳转的目标位置。 **实现** 1. **获取视频时间基**:获取视频流的**AVRational**结构体中的**time_base**属性,它表示视频的总时间基。 2. **获取视频长度**:根据视频时间基和**seek_length**计算出每次快进需要跳转的长度。 3. **监听视频状态**:循环监听视频状态,当视频处于**快进**状态时,执行视频快进或后退操作。 4. **获取视频时间位置**:在视频状态为**快进**时,从**// seek_pos**中获取视频的当前播放位置。 5. **执行视频操作**:根据视频状态(**快进**或**退缩**)设置**seek_pos**,并调用**av_seek_frame**函数进行视频操作。 **代码示例** ```c++ #include #include // ... // 视频播放器结构体 struct AVFormatContext { // ... int time_base; }; // av_seek_frame 函数 int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); // ... // 主线程函数 int main() { // ... // 设置视频播放状态 thread_pause = false; // 监听键盘输入 while (1) { SDL_PollEvents(events, NULL, 0, 0); // 检查键盘输入 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; } // ... } // 检查视频状态 if (thread_pause) { // 如果播放暂停,停止视频快进或后退 } } // 释放资源 // ... return 0; } ```

正文

之前写过一篇关于视频播放器的文章。播放器只简单实现了视频播放的功能,在此功能的基础上,给它加上一个视频快进的功能。

实现

添加参数

// 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的简单的视频播放器分析 》

与FFMPEG+SDL简单视频播放器——视频快进相似的内容:

FFMPEG+SDL简单视频播放器——视频快进

之前写过一篇关于视频播放器的文章。播放器只简单实现了视频播放的功能,在此功能的基础上,给它加上一个视频快进的功能。

C#进程调用FFmpeg操作音视频

开发背景 因为公司需要对音视频做一些操作,比如说对系统用户的发音和背景视频进行合成,以及对多个音视频之间进行合成,还有就是在指定的源背景音频中按照对应的规则在视频的多少秒钟内插入一段客户发音等一些复杂的音视频操作。本篇文章主要讲解的是使用C#进程(Process)调用FFmpeg.exe进行视频合并

FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg

​《FFmpeg开发实战:从零基础到短视频上线》一书的“第11章 FFmpeg的桌面开发”介绍了如何在Windows环境对Qt结合FFmpeg实现桌面程序,那么Windows系统通过Visual Studio开发桌面程序也是很常见的,下面就介绍如何在Visual Studio的C++工程中集成FFm

FFmpeg开发笔记(三十八)APP如何访问SRS推流的RTMP直播地址

​《FFmpeg开发实战:从零基础到短视频上线》一书在第10章介绍了轻量级流媒体服务器MediaMTX,通过该工具可以测试RTSP/RTMP等流媒体协议的推拉流。不过MediaMTX的功能实在是太简单了,无法应用于真实直播的生产环境,真正能用于生产环境的流媒体服务器还要看SRS或者ZLMediaKi

FFmpeg开发笔记(三十七)分析SRS对HLS协议里TS包的插帧操作

​《FFmpeg开发实战:从零基础到短视频上线》一书的“2.1.2 音视频文件的封装格式”介绍了视频流的PS格式和TS格式。由于TS包的长度固定,从TS流的任一片段开始都能独立解码,因此可以把TS当成音视频文件的封装格式。 鉴于TS包的独立解码特性,HLS协议引入了TS格式作为传输单元。HLS协议的

FFmpeg开发笔记(三十六)Linux环境安装SRS实现视频直播推流

​《FFmpeg开发实战:从零基础到短视频上线》一书在第10章介绍了轻量级流媒体服务器MediaMTX,通过该工具可以测试RTSP/RTMP等流媒体协议的推拉流。不过MediaMTX的功能实在是太简单了,无法应用于真实直播的生产环境,真正能用于生产环境的流媒体服务器还要看SRS或者ZLMediaKi

FFmpeg开发笔记(三十五)Windows环境给FFmpeg集成libsrt

​《FFmpeg开发实战:从零基础到短视频上线》一书的“10.2 FFmpeg推流和拉流”提到直播行业存在RTSP和RTMP两种常见的流媒体协议。除此以外,还有比较两种比较新的流媒体协议,分别是SRT和RIST。 其中SRT全称为Secure Reliable Transport,中文叫做安全可靠传

FFmpeg开发笔记(三十四)Linux环境给FFmpeg集成libsrt和librist

​《FFmpeg开发实战:从零基础到短视频上线》一书的“10.2 FFmpeg推流和拉流”提到直播行业存在RTSP和RTMP两种常见的流媒体协议。除此以外,还有比较两种比较新的流媒体协议,分别是SRT和RIST。 其中SRT全称为Secure Reliable Transport,中文叫做安全可靠传

FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作

​《FFmpeg开发实战:从零基础到短视频上线》一书的“3.4.3 把原始的H264文件封装为MP4格式”介绍了如何把H.264裸流封装为MP4文件。那么在网络上传输的H.264裸流是怎样被接收端获取视频格式的呢?前文指出H.264流必定以“SPS帧→PPS帧→IDR帧”开头,接下来就来验证是否确实

FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo

不管是传统互联网还是移动互联网,实时数据传输都是刚需,比如以QQ、微信为代表的即时通信工具,能够实时传输文本和图片。其中一对一的图文通信叫做私聊,多对多的图文通信叫做群聊。 除了常见的图文即时通信,还有实时音视频通信,比如一对一的音频通话、一对一的视频通话等等,此时可采用WebRTC技术,有关Web