ffplay播放器剖析(8)----逐帧/音量调节/快进快退/倍数分析

news2024/10/7 6:46:33

文章目录

  • 1.逐帧播放
  • 2. 音量调节
  • 3. seek 快进 快退
  • 4.倍速

1.逐帧播放

逐帧播放就是按s键触发的,调用step_to_next_frame触发

static void step_to_next_frame(VideoState *is)
{
    /* if the stream is paused unpause it, then step */
    if (is->paused)
        stream_toggle_pause(is);
    is->step = 1;
}

step就是启动逐帧播放模式,如果是暂停状态就会改为继续播放状态

看一下那里使用了step这个变量

  1. 逐帧状态下是不会丢帧的

                if (frame_queue_nb_remaining(&is->pictq) > 1) {//有nextvp才会检测是否该丢帧
                    Frame *nextvp = frame_queue_peek_next(&is->pictq);
                    duration = vp_duration(is, vp, nextvp);
                    if(!is->step        // 非逐帧模式才检测是否需要丢帧 is->step==1 为逐帧播放
                        && (framedrop>0 ||      // cpu解帧过慢
                            (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) // 非视频同步方式
                        && time > is->frame_timer + duration // 确实落后了一帧数据
                        ) {
                        printf("%s(%d) dif:%lfs, drop frame\n", __FUNCTION__, __LINE__,
                               (is->frame_timer + duration) - time);
                        is->frame_drops_late++;             // 统计丢帧情况
                        frame_queue_next(&is->pictq);       // 这里实现真正的丢帧
                        //(这里不能直接while丢帧,因为很可能audio clock重新对时了,这样delay值需要重新计算)
                        goto retry; //回到函数开始位置,继续重试
                    }
                }
                               frame_queue_next(&is->pictq);   // 当前vp帧出队列
                is->force_refresh = 1;          /* 说明需要刷新视频帧 */
                if (is->step && !is->paused)
                    stream_toggle_pause(is);    // 逐帧的时候那继续进入暂停状态
    

    这个代码逻辑是读取一帧数据,然后调用stream_toggle_pause暂停,如果播放一帧数据后不会继续往下播放,等待s键触发让其继续并且step

在这里插入图片描述

2. 音量调节

调节音量就是通过is->audio_volume这个变量进行设置的,因此有一个update_volume函数来修改is->audio_volume的值

static void update_volume(VideoState *is, int sign, double step)
{
    double volume_level = is->audio_volume ? (20 * log(is->audio_volume / (double)SDL_MIX_MAXVOLUME) / log(10)) : -1000.0;
    int new_volume = lrint(SDL_MIX_MAXVOLUME * pow(10.0, (volume_level + sign * step) / 20.0));
    is->audio_volume = av_clip(is->audio_volume == new_volume ? (is->audio_volume + sign) : new_volume, 0, SDL_MIX_MAXVOLUME);
}

这个计算方式不做具体讲解,具体增长曲线类似于慢增长

调节音量使用SDL_MixAudioFormat函数:

memset(stream, 0, len1);
// 3.调整音量
/* 如果处于mute状态则直接使用stream填0数据, 暂停时is->audio_buf = NULL */
if (!is->muted && is->audio_buf)
       SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index,
                          AUDIO_S16SYS, len1, is->audio_volume);

就是SDL_MixAudioFormat调用audio_volume进行修改音量,一开始是都初始化为0,如果不是静音的话那么就修改音量,否则就是0给SDL输出,因此可以得知静音只需要初始化为0即可!

3. seek 快进 快退

快进 快退就是让媒体文件跳转到一个时间点进行播放

ffplay有两种方案一种是按字节,一种是按时间

                if (seek_by_bytes) {
                    pos = -1;
                    if (pos < 0 && cur_stream->video_stream >= 0)
                        pos = frame_queue_last_pos(&cur_stream->pictq);
                    if (pos < 0 && cur_stream->audio_stream >= 0)
                        pos = frame_queue_last_pos(&cur_stream->sampq);
                    if (pos < 0)
                        pos = avio_tell(cur_stream->ic->pb);
                    if (cur_stream->ic->bit_rate)
                        incr *= cur_stream->ic->bit_rate / 8.0;
                    else
                        incr *= 180000.0;
                    pos += incr;
                    stream_seek(cur_stream, pos, incr, 1);
                } else {
                    pos = get_master_clock(cur_stream);
                    if (isnan(pos))
                        pos = (double)cur_stream->seek_pos / AV_TIME_BASE;
                    pos += incr;    // 现在是秒的单位
                    if (cur_stream->ic->start_time != AV_NOPTS_VALUE && pos < cur_stream->ic->start_time / (double)AV_TIME_BASE)
                        pos = cur_stream->ic->start_time / (double)AV_TIME_BASE;
                    stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);
                }

快进之前先获取上一帧pts然后赋值给pos,incr是快进时间,如果通过码流算出对应的是位置,然后pos加上incr后通过stream_seek进行seek

不同的方案是由flags来确定的,当我们使用avformat_seek_file时会传入flags,然后avformat_seek_file会自己判断

static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes)
{
    if (!is->seek_req) {
        is->seek_pos = pos; // 按时间微秒,按字节 byte
        is->seek_rel = rel;
        is->seek_flags &= ~AVSEEK_FLAG_BYTE;        // 不按字节的方式去seek
        if (seek_by_bytes)
            is->seek_flags |= AVSEEK_FLAG_BYTE;     // 强制按字节的方式去seek
        is->seek_req = 1;       // 请求seek, 在read_thread线程seek成功才将其置为0
        SDL_CondSignal(is->continue_read_thread);
    }
}

stream_seek最主要的设置了seek_pos和seek_flags,然后通知读线程,

        if (is->seek_req) { // 是否有seek请求
            int64_t seek_target = is->seek_pos; // 目标位置
            int64_t seek_min    = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;
            int64_t seek_max    = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;
            // 前进seek seek_rel>0
            //seek_min    = seek_target - is->seek_rel + 2;
            //seek_max    = INT64_MAX;
            // 后退seek seek_rel<0
            //seek_min = INT64_MIN;
            //seek_max = seek_target + |seek_rel| -2;
            //seek_rel =0  鼠标直接seek
            //seek_min = INT64_MIN;
            //seek_max = INT64_MAX;

            // FIXME the +-2 is due to rounding being not done in the correct direction in generation
            //      of the seek_pos/seek_rel variables
            // 修复由于四舍五入,没有再seek_pos/seek_rel变量的正确方向上进行
            ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
            ......

成功之后就会清空队列,然后packet队列中插入一个flush_pkt,frame队列读取到flush_pkt时也会清空队列!

            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR,
                       "%s: error while seeking\n", is->ic->url);
            } else {
                /* seek的时候,要把原先的数据情况,并重启解码器,put flush_pkt的目的是告知解码线程需要
                 * reset decoder
                 */
                if (is->audio_stream >= 0) { // 如果有音频流
                    packet_queue_flush(&is->audioq);    // 清空packet队列数据
                    // 放入flush pkt, 用来开起新的一个播放序列, 解码器读取到flush_pkt也清空解码器
                    packet_queue_put(&is->audioq, &flush_pkt);
                }
                if (is->subtitle_stream >= 0) { // 如果有字幕流
                    packet_queue_flush(&is->subtitleq); // 和上同理
                    packet_queue_put(&is->subtitleq, &flush_pkt);
                }
                if (is->video_stream >= 0) {    // 如果有视频流
                    packet_queue_flush(&is->videoq);    // 和上同理
                    packet_queue_put(&is->videoq, &flush_pkt);
                }
                if (is->seek_flags & AVSEEK_FLAG_BYTE) {
                    set_clock(&is->extclk, NAN, 0);
                } else {
                    set_clock(&is->extclk, seek_target / (double)AV_TIME_BASE, 0);
                }
            }

4.倍速

后续补充

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/783939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jmeter随记2:压测

jmeter随记1:压测 简述一、压测步骤二、观察cpu和内存占用情况三、查看磁盘占用情况 简述 关于压测&#xff0c;jmeter更直观的作用是用来编写压测脚本【请求和压测策略】&#xff0c;然后在linux服务器上执行&#xff0c;也可以在本地执行&#xff0c;压测执行脚本在启动jmet…

PHP注册/登录/发邮件--【强撸项目】

强撸项目系列总目录在000集 PHP要怎么学–【思维导图知识范围】 文章目录 本系列校训本项目使用技术 上效果图phpStudy 设置导数据库程序基本流程项目目录如图&#xff1a;注册zhuce.html配套资源作业&#xff1a; 本系列校训 用免费公开视频&#xff0c;卷飞培训班哈人&…

Oracle物化视图刷新和物化视图日志

Oracle物化视图刷新和物化视图日志 Oracle的物化视图是包括一个查询结果的数据库对像&#xff0c;它是远程数据的的本地副本&#xff0c;或者用来生成基于数据表求和的汇总表。 测试物化视图的刷新&#xff0c;参考物化视图日志&#xff0c;一个源表对应多个物化视图刷新。 物…

【Linux工具篇】项目自动构建化工具-make/Makefile

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【Linux专栏】&#x1f388; 本专栏旨在分享学习Linux的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 &#x1f4ab…

matlab cross()函数叉乘 计算过程详解

向量叉乘 在数学上&#xff0c;两向量的叉乘是一个过两相交向量的交点且垂直于两向量所在平面的向量。在Matlab中&#xff0c;用函数cross实现。 函数 cross() 格式 C cross(A,B) %若A、B为向量&#xff0c;则返回A与B的叉乘&#xff0c;即CAB&#xff0c;A、B必须是3个…

c#封装bool到cpp

c#那边传一个结构体&#xff0c;结构体里包含两个bool&#xff0c;封送到cpp&#xff0c;结果发现cpp那边读取有问题。一看cpp接收变量的内存&#xff0c;两个bool占的内存都不是一个字节了&#xff0c;再次记录原因。 封送的时候&#xff0c;默认是占4个字节&#xff0c;如果…

Pycharm----导入库文件夹不在py文件的目录下

问题描述&#xff1a; 想在不同目录下导入根目录的包&#xff0c;直接写会报错。如下边object_detect.py在function文件夹下&#xff0c;导入包默认在这个文件下&#xff0c;但我想导入根目录models和utils下的包 解决方法&#xff1a; 将根目录设置为源代码根目录&#xff0…

linux静态库,动态库总结

1.介绍 使用GNU的工具我们如何在Linux下创建自己的程序函数库?一个“程序函数库”简单的说就是一个文件包含了一些编译好的代码和数据&#xff0c;这些编译好的代码和数据可以在事后供其他的程序使用。程序函数库可以使整个程序更加模块化&#xff0c;更容易重新编译&#xff…

【Spring Cloud】Hystrix熔断机制

文章目录 前言什么是hystrix的熔断&#xff1f;使用hystrix熔断功能的配置Hystrix 工作原理Hystrix工作流 前言 什么是hystrix的熔断&#xff1f; hystrix熔断主要是指在一定的时间窗口内&#xff0c;当请求的次数达到一定的失败比率后&#xff0c;hystrix就会主动拒绝服务&a…

【图像分割】基于浣熊优化算法COA的Otsu(大津法)多阈值电表数字图像分割 电表数字识别【Matlab代码#52】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】1. 原始COA算法1.1 开发阶段1.2 探索阶段 2. 多阈值Otsu原理3. 部分代码展示4. 仿真结果展示5. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】 1. 原始…

长尾式差分放大电路

3.3.2差分放大电路 电路 条件 静态分析 对共模信号的抑制作用 当产生温度变化时&#xff0c;也类似加入了共模信号&#xff0c;由于负反馈电阻 的存在&#xff0c;会产生如下变化。 对差模信号的放大作用 电路 交流等效电路 具体分析 具有恒流源的差分放大电路 电路 详细分析…

STM32 OLED显示汉字及屏幕滚动(I2C协议)

文章目录 一、任务目标二、材料准备硬件&#xff1a;软件&#xff1a; 三、AHT20温湿度传感器的使用四、OLED的使用1.硬件2.字模汉字编码原理取字模 3.工程4.电路连接5.实验效果 五、总结六、参考资料 一、任务目标 理解OLED屏显和汉字点阵编码原理&#xff0c;使用STM32F103的…

Linux下基本指令 -> ls指令

​ Linux - ls 1 简介2 简介3 语法4 常用选项4.1 -a 列出目录下的所有文件&#xff0c;包括以 . 开头的隐含文件4.2 -l 列出文件的详细信息4.3 -d 将目录象文件一样显示&#xff0c;而不是显示其下的文件4.4 -i 输出文件的 i 节点的索引信息4.5 -n 用数字的 UID,GID 代替名称4.…

Qt学习14:Designer设计师

文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 Qt项目地址及源码&#xff1a;点击这里 什么是Designer设计师&#xff1f; 简单来说就是通过拖拖拽拽就可以实现窗口上控件的布局。 1. 创建带UI文件的项目 我们在之前的学习中都是不用这个按钮的&#xff0c;现在我们需…

富文本CKEditor5简易入门,包括自定义上传图片(html版+vue.js)

一、安装及引入 官网&#xff1a;https://ckeditor.com/ckeditor-5/download/ 我这边使用的是自定义构建&#xff0c;然后下载下来。 二、简单使用 引入js <script src"../../../assets/plugins/ckeditor5/ckeditor.js"></script>html&#xff1a;…

【PWN · ret2libc】[BJDCTF 2020]babyrop

这题是经典的ret2libc&#xff0c;而且保护开的也不多&#xff0c;实际上&#xff0c;这篇博客的意义更大&#xff1a; 【PWN ret2libc】[2021 鹤城杯]babyof_Mr_Fmnwon的博客-CSDN博客 目录 前言 一、题目 二、思路 三、exp具体编写 总结 前言 简单而纯粹的ret2libc&am…

《DocRED: A Large-Scale Document-Level Relation Extraction Dataset》阅读笔记

引言 近期关注篇章级关系抽取&#xff0c;两个原因&#xff0c;一是之前做大规模知识抽取&#xff0c;遗留的一块内容就是篇章级关系抽取和事件抽取&#xff1b;另一个是大模型目前在抽取任务&#xff0c;特别是复杂抽取任务上表现不如其他NLP任务&#xff0c;这也引起我的研究…

5.2.6.字符设备驱动工作原理1

什么是模块&#xff1f;什么是驱动&#xff1f; 模块 驱动的雏形&#xff0c; 你要能操控硬件才叫驱动 在空的模块的基础上&#xff0c;安装驱动 5.2.6.1、系统整体工作原理 (1)应用层->API->设备驱动->硬件 (2)API&#xff1a;open、read、write、close等 (3)驱动…

20230720今天youtube上的中文字幕自动翻译成为英文的功能失效!

20230720今天youtube上的中文字幕自动翻译成为英文的功能失效&#xff01; 2023/7/20 12:42 &#xff1f;做YouTube挣钱吗&#xff1f; 115网盘 满了。最新 张家界 旅游的视频 放到 youtube就是 60岁/老了的时候的回忆&#xff01; 放到 大陆不保险&#xff01; 如需使用自动翻…

智能合约安全审计

智能合约安全审计的意义 智能合约审计用于整个 DeFi 生态系统&#xff0c;通过对协议代码的深入审查&#xff0c;可以帮助解决识别错误、低效代码以及这些问题。智能合约具有不可篡改的特点&#xff0c;这使得审计成为任何区块链项目安全流程的关键部分。 代码审计对任何应用…