Android SeekBar控制视频播放进度(二)——seekTo()不准确

news/2024/4/29 23:02:32/文章来源:https://blog.csdn.net/tracydragonlxy/article/details/129956718

Android SeekBar控制视频播放进度二——seekTo不准确

    • 简介
    • seekTo()
    • 视频帧 和 视频关键帧
    • 解决办法
      • 方法一
      • 方法二

简介

上一篇文章中,我们介绍了使用SeekBar控制视频播放,使用过程中发现,对于一些视频,我们拖动SeekBar进度条调节播放进度时,调节到指定位置后,进度条会往回跳,并不会在我们拖动位置继续播放。

网上搜索了解到,VideoView.seekTo()方法的策略决定的。具体看一下seekTo()方法:

seekTo()

  1. 如下是VideoView.seekTo(int msec)的代码实现,我们就是通过调用该方法实现进度调节。通过查看代码,我们知道该方法实际调用的是 MediaPlayer.seekTo(msec);
@Override
public void seekTo(int msec) {if (isInPlaybackState()) {mMediaPlayer.seekTo(msec);mSeekWhenPrepared = 0;} else {mSeekWhenPrepared = msec;}
}
  1. 继续查看 MediaPlayer.seekTo(msec);方法的实现,该方法调用seekTo(long msec, @SeekMode int mode)方法,默认的modeSEEK_PREVIOUS_SYNC
/*** Seeks to specified time position.* Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.** @param msec the offset in milliseconds from the start to seek to* @throws IllegalStateException if the internal player engine has not been* initialized*/
public void seekTo(int msec) throws IllegalStateException {seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
}/*** Moves the media to specified time position by considering the given mode.* <p>* When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.* There is at most one active seekTo processed at any time. If there is a to-be-completed* seekTo, new seekTo requests will be queued in such a way that only the last request* is kept. When current seekTo is completed, the queued request will be processed if* that request is different from just-finished seekTo operation, i.e., the requested* position or mode is different.** @param msec the offset in milliseconds from the start to seek to.* When seeking to the given time position, there is no guarantee that the data source* has a frame located at the position. When this happens, a frame nearby will be rendered.* If msec is negative, time position zero will be used.* If msec is larger than duration, duration will be used.* @param mode the mode indicating where exactly to seek to.* Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame* that has a timestamp earlier than or the same as msec. Use* {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame* that has a timestamp later than or the same as msec. Use* {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame* that has a timestamp closest to or the same as msec. Use* {@link #SEEK_CLOSEST} if one wants to seek to a frame that may* or may not be a sync frame but is closest to or the same as msec.* {@link #SEEK_CLOSEST} often has larger performance overhead compared* to the other options if there is no sync frame located at msec.* @throws IllegalStateException if the internal player engine has not been* initialized* @throws IllegalArgumentException if the mode is invalid.*/
public void seekTo(long msec, @SeekMode int mode) {if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {final String msg = "Illegal seek mode: " + mode;throw new IllegalArgumentException(msg);}// TODO: pass long to native, instead of truncating here.if (msec > Integer.MAX_VALUE) {Log.w(TAG, "seekTo offset " + msec + " is too large, cap to " + Integer.MAX_VALUE);msec = Integer.MAX_VALUE;} else if (msec < Integer.MIN_VALUE) {Log.w(TAG, "seekTo offset " + msec + " is too small, cap to " + Integer.MIN_VALUE);msec = Integer.MIN_VALUE;}_seekTo(msec, mode);
}
  1. SeekMode 有如下几种模式,

SEEK_PREVIOUS_SYNC: seek到上一个关键帧
SEEK_NEXT_SYNC: seek到下一个关键帧
SEEK_CLOSEST_SYNC: seek到最近的关键帧
SEEK_CLOSEST: seek到最近的帧(不需要是关键帧)

    /*** Seek modes used in method seekTo(long, int) to move media position* to a specified location.** Do not change these mode values without updating their counterparts* in include/media/IMediaSource.h!*//*** This mode is used with {@link #seekTo(long, int)} to move media position to* a sync (or key) frame associated with a data source that is located* right before or at the given time.** @see #seekTo(long, int)*/public static final int SEEK_PREVIOUS_SYNC    = 0x00;/*** This mode is used with {@link #seekTo(long, int)} to move media position to* a sync (or key) frame associated with a data source that is located* right after or at the given time.** @see #seekTo(long, int)*/public static final int SEEK_NEXT_SYNC        = 0x01;/*** This mode is used with {@link #seekTo(long, int)} to move media position to* a sync (or key) frame associated with a data source that is located* closest to (in time) or at the given time.** @see #seekTo(long, int)*/public static final int SEEK_CLOSEST_SYNC     = 0x02;/*** This mode is used with {@link #seekTo(long, int)} to move media position to* a frame (not necessarily a key frame) associated with a data source that* is located closest to or at the given time.** @see #seekTo(long, int)*/public static final int SEEK_CLOSEST          = 0x03;
  1. 所以当视频在跳转到相应的 position 位置缺少关键帧的情况下,调用 seekTo 方法是无法在当前位置开始播放。这时会寻找离指定 position 最近的关键帧位置开始播放。
    我们通过seekTo函数调用的实际是默认的mode——SEEK_PREVIOUS_SYNC ,这时会寻找position的上一个关键帧。所以调节视频进度后,视频会往回跳一段,并没有在我们拖动位置继续播放。

视频帧 和 视频关键帧

上面的方法提到了帧和关键帧,下面我们简单的介绍一下两者的关联和区别。我们知道视频是由一帧一帧的图像组成的,而关键帧则是其中的某些帧。理想情况下,我们将所有的普通帧都变为关键帧,那么调节视频播放进度时将不会发生回跳情况。
在这里插入图片描述

解决办法

方法一

根据SeekMode 几种模式的描述,调用时指定modeSEEK_CLOSEST

方法二

对视频源文件进行处理,增加其关键帧数量。使用FFmpeg对视频处理,增加视频的关键帧。

  1. 首先我们通过如下命令查看当前视频中关键帧的数量:
ffprobe -show_frames /Users/Admin/Desktop/test.mp4 >video_log.txt

将视频信息输出到文本文件中,打开video_log.txt文件,搜索关键字pict_type=I查看关键帧。可以看到我们当前视频只有11个关键帧。
在这里插入图片描述
2. 对视频增加关键帧,keyint=30每隔 30 帧设置一个关键帧。命令如下:

ffmpeg.exe -i "/Users/Admin/Desktop/test.mp4" -c:v libx264 -preset superfast -x264opts keyint=30 -acodec copy -f mp4 "/Users/Admin/Desktop/test_out.mp4"

使用步骤1的命令,查看处理后的视频信息。可以看到处理后,我们视频的关键帧数量有99个。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谈谈如何用开源网关进行 API 管理

需求痛点 1.企业不清楚到底有多少个API&#xff0c;无法形成API资产管理等问题。 2.API在不同集群的生命周期问题。 3.API运行状态监控和告警问题。 4.API请求限流、流量控制以及安全等问题。 功能介绍 Apinto的API管理提供API生命周期控制&#xff1a;可管理所有API&…

【DRF配置管理】如何在视图类使用get_objects()

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 DRF应用和管理 【DRF配置管理】Django使用DRF框架 【DRF配置管理】如何在视图类配置参数(一) 【DRF配置管理】如何在视图类配置参数(二) 【DRF配置管理…

跨数据中心下的 Kafka 高可用架构分析

导语 本文介绍了 Kafka 跨数据中心的两种部署方式&#xff0c;简要分析两种方式下的不同架构以及优缺点&#xff0c;对这些架构可能碰到的问题也提供了一些解决思路&#xff1b;同时也说明了 Kafka 跨数据中心部署的社区解决方案和商业化解决方案。 背景 Kafka 作为世界上最…

Excel技能之实用技巧,高手私藏

今天来讲一下Excel技巧&#xff0c;工作常用&#xff0c;高手私藏。能帮到你是我最大的荣幸。 与其加班熬夜赶进度&#xff0c;不如下班学习提效率。能力有成长&#xff0c;效率提上去&#xff0c;自然不用加班。 消化吸收&#xff0c;工作中立马使用&#xff0c;感觉真不错。…

宠物领养系统【GUI/Swing+MySQL】(Java课设)

系统类型 Swing窗口类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87708775 更多系统资源库…

「OceanBase 4.1 体验」|大厂开始接入的国产分布式数据库,不来了解了解?

OceanBase 4.1 体验 前言OCP Express在线升级功能租户级物理备库TP&#xff08;事务处理&#xff09;和AP&#xff08;分析处理&#xff09;优化TP 性能优化AP 性能优化 结尾 前言 上次我们讲了本人自己亲自上手OceanBase 4.1的初体验&#xff0c;国产的分布式数据库也太太太太…

【STM32】基础知识 第八课 MDK 工程

【STM32】基础知识 第八课 MDK 工程 准备工作新建寄存器版本 MDK 工程步骤新建工程文件夹添加文件魔术棒设置绝对路径和相对路径对比测试程序 新建 HAL 库版本 MDK 工程CMSISHAL 库简介DriversMiddlewaresDevice 和 Include HAL 库文件介绍HAL 库 API 函数和比那辆命名规则HAL …

【ArcGIS】常见问题总结

1 arcgis如何打开*.adf文件 在处理数据时发现&#xff0c;获取到的土地利用类型数据有两个文件夹&#xff0c;一个叫info&#xff0c;另一个叫lucc2010&#xff08;年份&#xff09;&#xff0c;打开lucc2010里面是一系列的*.adf文件&#xff0c;数据应该如何打开呢&#xff1…

【Vue】Vue 前端设计模式梳理

文章目录 一、什么是设计模式&#xff1f;二、设计几个原则三、常见的设计模式及实际案例【1】单例模式1. 什么是单例模式&#xff1f;2.Vue中的单例模式 【2】工厂模式1. 什么是工厂模式&#xff1f;2.Vue中的工厂模式 【3】策略模式1. 什么是策略模式&#xff1f;2.策略模式的…

QT笔记——QtPropertyBrowser的使用

上一节&#xff0c;我们将了如何去配置QtPropertyBrowser 本节&#xff0c;我们将说明 如何 去 使用QtPropertyBrowser 这个属性类的一些基本知识 简单的几种用法&#xff1a; 首先&#xff1a; 我们需要创建一个Widget 提升一个类 为 QtTreePropertyBrowser .h文件 QtVariant…

详解客户关系管理系统

一、客户关系管理系统的重要性 客户关系管理系统&#xff0c;是指利用软件、硬件和网络技术&#xff0c;为企业建立一个客户信息收集、管理、分析和利用的信息系统。以客户数据的管理为核心&#xff0c;记录企业在市场营销和销售过程中和客户发生的各种交互行为&#xff0c;以…

华为C++研发工程师编程题 ACM模式输入输出|| 1.汽水瓶,2.明明的随机数,3.进制转换

C ACM输入输出 1.汽水瓶题目描述思路代码如下 2.明明的随机数题目描述思路&#xff1a;代码如下&#xff1a; 3.进制转换题目描述思路&#xff1a;代码如下 题目链接&#xff1a; 华为研发工程师编程题 1.汽水瓶 题目描述 某商店规定&#xff1a;三个空汽水瓶可以换一瓶汽水…

服务器空间不足处理与解决思路—实战docker占用空间太大

前言 服务器Centos操作系统&#xff0c;空间不足的问题处理了三次了&#xff0c;决定把它的解决思路和处理过程记录下来。服务器空间不足是一个经常会遇到的问题&#xff0c;尤其是在大型应用程序和网站上。当服务器空间不足时&#xff0c;应该采取一些步骤来处理和解决这个问…

LeetCode:206. 反转链表

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;206. 反转链表 题目描述&#xff1a;给你单链表的头节点 head &#x…

html学习(布局方式(layout)、浮动(float)、定位(position)、弹性盒(flex))

布局方式(layout) 文档流 文档流&#xff08;normal flow&#xff09; 文档流通俗的讲&#xff0c;就是一个web页面中&#xff0c;每一个模块只能从上到下从左往右的方式排列在页面上。 将窗口自下而上分成一行一行&#xff0c;应在每行中按从左至右的依次排放元素&#xff0…

光纤网卡传输速率和它的应用领域有哪些呢?通常会用到哪些型号网络变压器呢?

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;常有客户问起光纤网卡该如何选用到合适的产品&#xff0c;选用时要注意到哪些事项&#xff0c;这节将结合配合到的网络变压器和大家一起探讨&#xff0c;希望对大家有些帮助。 1&#xff0e;光纤网卡传输速率与网络…

【iOS-分类,拓展和关联对象底层探究】

前言 寒假分享会问题解决二 早在大一的OC的学习过程就知道了分类和拓展的区别和联系&#xff0c;分类不能添加成员变量&#xff0c;而拓展可以添加成员变量。分类是在运行时期实现的&#xff0c;而拓展只是编译器的时候就实现了。对于分类我们可以通过关联对象来为我们需要的分…

线程池四种拒绝机制 实现 及执行日志

目录 目录 目录 创建线程池 测试代码 运行线程 全量代码 日志 AbortPolicy 报出异常模式 DiscardPolicy 放弃机制啥也不处理 DiscardOldestPolicy 放弃机制&#xff0c;放弃列队最早进入的 CallerRunsPolicy 交给主线程执行 创建线程池 public static ExecutorServi…

项目范围控制:如何控制项目范围的变化?

一个成功的项目需要在进度、成本和质量之间取得平衡。控制项目交付范围是实现这个平衡的关键。然而&#xff0c;项目范围是会变化的&#xff0c;因此控制项目范围变化是必要的。 如何控制项目范围的变化&#xff1f; 1、了解项目的交付范围 项目经理、团队成员、利益相关者和…

我用什么写Python?

入门教程、案例源码、学习资料、读者群 请访问&#xff1a; python666.cn 大家好&#xff0c;欢迎来到 Crossin的编程教室 &#xff01; 通常来说&#xff0c;每个程序员都有自己趁手的兵器&#xff1a;代码编辑器。你要是让他换个开发环境&#xff0c;恐怕开发效率至少下降三成…