Android FFmpeg系列——6 Java 获取播放进度

FFmpeg安卓流媒体播放器开发实战视频课程 -基于NDK、C++和 FFmpeg Android 课程目标 https://edu.youkuaiyun.com/combo/detail/986---Android 音视频开发(杨万里付费版) 学会编译安卓平台的ffmpeg库(支持硬解码和neon),学会ffmpeg的解码解封装接口,学会opensles播放音频,学会opengles、egl、shader、glsl播放yuv视频,学会音视频同步策略,学会将设计模式应用到实际项目中。 适用人群 ... 阅读详情

Android FFmpeg系列——0 编译.so库
Android FFmpeg系列——1 播放视频
Android FFmpeg系列——2 播放音频
Android FFmpeg系列——3 C多线程使用
Android FFmpeg系列——4 子线程播放音视频
Android FFmpeg系列——5 音视频同步播放
Android FFmpeg系列——6 Java 获取播放进度
Android FFmpeg系列——7 实现快进/快退功能

一般播放器都能显示播放进度,这一节来实现这个功能。由于我是在 C 层播放视频的,至于播放到哪里,Java 层是不知道的,所以得利用 C 调用 Java 代码来实现。

Java 代码

/**
 * 同步播放音视频
 * @param path
 * @param surface
 * @param callback
 */
public native void play(String path, Surface surface, PlayerCallback callback);
/**
 * 播放器回调
 */
public interface PlayerCallback {
    /**
     * 播放开始
     */
    void onStart();
    /**
     * 进度
     * @param total
     * @param current
     */
    void onProgress(int total, int current);
    /**
     * 播放结束
     */
    void onEnd();
}

声明一个回调接口,通过 native 方法传入到 C 层。

public void play(View view) {
    player.play(videoPath, surfaceHolder.getSurface(), new Player.PlayerCallback() {
        @Override
        public void onStart() {
            System.err.println("播放开始了 -------------------");
        }
        @Override
        public void onProgress(final int total, final int current) {
            // C 层子线程调用 子线程不能直接更新 UI
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    currentTimeView.setText(formatTime(current));
                    totalTimeView.setText(formatTime(total));
                }
            });
        }
        @Override
        public void onEnd() {
            System.err.println("播放结束了 -------------------");
        }
    });
}

这是 Java 层播放代码,需要注意的是,回调 onProgress 函数是在 C 层子线程调用的,大家都知道,子线程不能直接更新 UI ,所以要特别注意一下!

OK,我们再来看 C 层代码!

C 代码

/**
 * 回调 Java Callback onStart方法
 * @param player
 */
void call_on_start(Player *player, JNIEnv *env) {
    jclass callback_class = env->GetObjectClass(player->callback);
    jmethodID on_start_method_id = env->GetMethodID(callback_class, "onStart", "()V");
    env->CallVoidMethod(player->callback, on_start_method_id);
    env->DeleteLocalRef(callback_class);
}
/**
 * 回调 Java Callback onStart方法
 * @param player
 */
void call_on_end(Player *player, JNIEnv *env) {
    jclass callback_class = env->GetObjectClass(player->callback);
    jmethodID on_end_method_id = env->GetMethodID(callback_class, "onEnd", "()V");
    env->CallVoidMethod(player->callback, on_end_method_id);
    env->DeleteLocalRef(callback_class);
}
/**
 * 回调 Java Callback onStart方法
 * @param player
 * @param env
 * @param total
 * @param current
 */
void call_on_progress(Player *player, JNIEnv *env, double total, double current) {
    jclass callback_class = env->GetObjectClass(player->callback);
    jmethodID on_progress_method_id = env->GetMethodID(callback_class, "onProgress", "(II)V");
    env->CallVoidMethod(player->callback, on_progress_method_id, (int) total, (int) current);
    env->DeleteLocalRef(callback_class);
}
/**
 * 消费函数
 * 从队列获取解码数据 同步播放
 * @param arg
 * @return
 */
void* consume(void* arg) {
    ...
    if (index == player->audio_stream_index) {
        // 播放开始
        call_on_start(player, env);
    }
    double total = stream->duration * av_q2d(stream->time_base);
    AVFrame *frame = av_frame_alloc();
    for (;;) {
        ...
        if (index == player->video_stream_index) {
            ...
        } else if (index == player->audio_stream_index) {
            ...
            //  更新进度
            call_on_progress(player, env, total, player->audio_clock);
        }
        av_packet_free(&packet);
    }
    if (index == player->audio_stream_index) {
    	// 播放结束
        call_on_end(player, env);
    }
    ...
    return NULL;
}

在测试过程中,遇到 ReferenceTable overflow (max=512) 这个Bug,是由于 JNI 局部引用表溢出导致的!然后我查了一下,发现是因为我多次调用 call_on_progress 方法导致的,以上代码是我修复之后的代码,之前的代码是这样的:

void call_on_progress(Player *player, JNIEnv *env, double total, double current) {
    jclass callback_class = env->GetObjectClass(player->callback);
    jmethodID on_progress_method_id = env->GetMethodID(callback_class, "onProgress", "(II)V");
    env->CallVoidMethod(player->callback, on_progress_method_id, (int) total, (int) current);
    // 调用 GetObjectClass 方法后,没有及时回收局部引用,会持续存在局部引用表
    // 由于多次调用 最终会导致 ReferenceTable overflow
    // env->DeleteLocalRef(callback_class);
}

代码注释得很清楚了!

其实还有一些局部引用需要我们 Delete 的,对于 GetObjectClass 返回的一定需要调用DeleteLocalRef,还有 jbyteArray 类型的变量需要 DeleteLocalRef,还有 NewString/ NewStringUTF/NewObject/ GetObjectField 这些都需要 DeleteLocalRef!如果再遇到 ReferenceTable overflow 局部引用表溢出,就需要检查一下局部变量问题。

代码地址:https://github.com/JohanMan/Player

参考

NDK ReferenceTable overflow (max=512)的解决方法

Android音乐播放器-使用FFmpeg及OpenSLES 在之前的文章《解码音频代码》中,已经实现了使用FFmpeg解码音频为PCM。这次我们利用FFmpeg以及OpenSLES来实现一个简单的音乐播放器。 阅读详情

相关推荐

视频教程-基于NDK、C++、FFmpeg的android视频播放器开发实战-Android

基于NDK、C++、FFmpeg的android视频播放器开发实战 夏曹俊:...

weixin_30545017的博客 919

ffplay实现自定义输入流播放

使用ffplay播放视频,有时我们只能获取到byte数据,比如Windows的嵌入资源只能拿到在内存中的视频文件数据,或者是自定义协议网络传输的视频,这个时候我们就需要实现一个流数据输入接口来进行播放了,ffmpeg的AVIOContext就支持这一功能,我们只需要对ffplay进行简单的拓展即可。.........

codeofcc的博客 792

FFmpeg入门详解之71:获取ffmpeg转码的实时进度

/ 问题1:总时长。// 问题1:总时长,微妙转毫秒,只能第一次时转。// 问题2:获取当前进度的时机。/// 获取实时的转码进度。/// 获取实时的转码进度。

福优学苑@音视频+流媒体 3034

ffmpeg 获取播放的时间来实现进度条

一般来说,进度条的实现都是用dts或者pts来做参考实现 但是我现在用一种更加简单的方法去获取总时长和当前播放时间来做进度条。 获取总时长的函数: if(avformat_open_input(&pFormatCtx, is->filename, NULL, NULL)!=0) //获取总时长 times = pFormatCtx->duration/1000000...

猫瑾的博客 8908

FFmpeg与OpenSL ES:Android万能音频播放器实现指南

FFmpeg是一个非常强大的多媒体框架,提供了录制、转换和流化音频和视频的广泛功能。它是开源项目,广泛应用于音视频编解码、格式转换、流处理等领域。在移动设备上,FFmpeg的性能和功能同样强大,因此被广泛应用于Android平台。至此,我们已经完成了FFmpeg源码的获取以及Android NDK环境的配置。这为后续编译FFmpeg和将其集成到Android应用中奠定了基础。

weixin_30951515的博客 955

给ffplay加了音量控制并采用osd方式显示音量大小和播放进度

给ffplay加了音量控制并采用osd方式显示音量大小和播放进度

扫描音乐

package com.example.day_10_music; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import android.app.Activi

仓仓 597

STM8库函数开发手册(3) //UART1、内部EEPROM、FLASH操作库、IIC(硬、软)

STM8S 微控制器家族的通用同步异步收发器(UART1,UART2 或者UART3)提供了一种灵活的方法与使用工业标准NRZ 异步串行数据格式的外部设备之间进行全双工数据交换。STM8 的UART 提供范围的波特率选择,并且支持多处理器通讯。UART 也支持LIN(局部互联网)协议版本1.3,2.0,和2.1 以及在主模式下的J2602。 UART1 和UAR...

finedayforu的博客 2520

java音乐播放器进度条_教你用MediaPlayer做个带进度条的音乐播放器

效果图布局文件好像还有点小清新???布局文件MediaPlayer的使用/***初始化播放器MediaPlayer*/privatevoidinitMediaPlayer(){//1.加载选中歌曲try{//1.1设置音频文件路径,或者是资源uriif(mType==1){mediaPlayer.setDataSource(mFilePath);}...

weixin_42651281的博客 754

Python爬虫实战(基础篇)—23—爬取微信小程序<随机目的地旅行>生成随机地址(附GUI版)

爬取微信小程序<随机目的地旅行>生成随机地址

weixin_42636075的博客 1228

别再手动敲代码了!用STM32CubeMX+FreeRTOS图形化配置,5分钟搞定多任务通信框架

本文详细介绍了如何利用STM32CubeMX和FreeRTOS快速搭建多任务通信框架,大幅提升嵌入式开发效率。通过图形化配置工具,开发者可在5分钟内完成任务调度、时钟配置和内核对象生成,特别适合物联网设备开发。文章还提供了代码优化和调试技巧,帮助开发者轻松应对复杂应用场景。

weixin_30363285的博客 291

Android FFmpeg系列——4 子线程播放音视频

利用工作闲余时间,终于实现在子线程播放音视频! 上一接学习了在 C 使用多线程,接着就是利用 C 多线程同时播放音视频(暂时还不同步)。 不多说,直接上码。 代码 // C 层播放器结构体 typedef struct _Player { // Env JavaVM *java_vm; // Java 实例 jobject instance; jobject...

JohanMan的博客 1914

【AI概念】迁移学习 vs. 域自适应 vs. 域泛化|定义、核心原理、数学表达、易混淆点与工程选择建议|实际案例、工程实现细节、未来研究趋势、实际应用场景

大家好,我是爱酱。本篇将会详细梳理迁移学习(Transfer Learning)、域自适应(Domain Adaptation)、域泛化(Domain Generalization)三大常被混淆的核心概念,结合数学公式、典型算法与实际应用场景,帮助你厘清它们的本质区别、联系与工程意义。 注:本文章含大量数学算式、详细例子说明及大量代码演示,大量干货,建议先收藏再慢慢观看理解。新频道发展不易,你们的每个赞、收藏跟转发都是我继续分享的动力!

AI人工智能爱酱~你的AI学习好帮手~ 2470

NDK30_FFmpeg显示播放进度与快进

NDK开发汇总 文章目录一 原理二 播放进度显示1 java层回调2 native层去回调java3 监听播放进度三 快进功能1 native-lib启动2 FFmpeg实现3 读取视频数据包时设置锁四 Demo 直播没有进度显示和快进,只有确定长度的视频如本地文件可以有进度和快进功能 上一篇文章介绍了内存释放: NDK29_FFmpeg内存释放 本文接着之前的功能继续实现本地文件的播放进度显示与快进功能 一 原理 AVFormatContext->duration能够获取视频时长 AVFram

baopengjian的专栏 354

LightTools宏 vs Python API:光学仿真自动化方案选型指南

本文深入对比了LightTools光学仿真自动化中VBA宏与Python API两种方案。VBA宏内嵌易用,适合固定流程批处理;Python API生态强大,尤其在参数优化算法集成方面优势显著,便于构建复杂工作流。文章结合案例,为不同场景下的技术选型提供清晰指南。

garlic的博客 940

一步步教你离线安装vsftpd并配置FTP服务

在安装vsftpd之前,需确保系统更新至最新版本,并检查防火墙配置以允许FTP流量。安装过程涉及使用包管理器(如apt-get或yum)下载vsftpd及其依赖包,特别是在离线环境中,需先下载所有相关包并传输到目标机器。安装完成后,通过命令验证安装并启动服务。配置vsftpd包括编辑配置文件以设置访问权限和安全性参数,如匿名访问和本地用户访问。此外,需配置防火墙开放FTP端口,并通过FTP客户端测试服务。最后,建议进行安全配置,如禁用匿名访问和配置SSL/TLS加密,并设置日志记录以监控服务状态。

a_join的博客 2486

Android FFmpeg系列——1 播放视频

由于公司项目原因,现在才得空来学习关于FFmpeg库的使用。 前言 在使用FFmpeg库的过程中,哎呦,各种心酸!!项目重新创建了N次,调试了N次,终于把视频流播放出来,心里甚是激动呀! 环境搭建 Android Studio 创建Demo项目,记得把 “Include c++ support” 勾上。 这里主要说2点: 项目目录结构; 文件配置,主要是app模块build.gradle 和 C...

JohanMan的博客 6873

海康网口相机不能被flexscan3d识别到MVS版本为4.6.1,flex为3.3.24在MVS中能够检测到,但是在flex中只能看到电脑自带相机,如何解决?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1044

Android使用FFmpeg(七)--ffmpeg实现暂停、快退快进播放

Android使用FFmpeg(一)--编译ffmpeg Android使用FFmpeg(二)--Android Studio配置ffmpeg Android使用FFmpeg(三)--ffmpeg实现视频播放 Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTr...

测试0901-1 870

YOLOv11改进策略【损失函数篇】| Shape-IoU:考虑边界框形状和尺度的更精确度量

本文记录的是改进YOLOv11的损失函数,将其替换成Shape-IoU。现有边界框回归方法通常考虑与之间的几何关系,通过边界框的相对位置和形状计算损失,但忽略了边界框本身的形状和尺度等固有属性对边界框回归的影响。为了弥补现有研究的不足,Shape-IoU提出了一种关注边界框本身形状和尺度的边界框回归方法。

Limiiiing的博客 4596

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ffmpeg拉流设置暂停_Android使用FFmpeg(七)--ffmpeg实现暂停、快退快进播放

前言这篇文章关于ffmpeg的解决方案仅提供参考,如有错误,欢迎指正。播放、暂停在音视频播放的时候,我们均是将读取到的Packet放入到队列中,然后再从队列中取出packet进行解码播放。所以在播放、暂停时可以在队列的取出上面做文章。int FFmpegMusic::get(AVPacket *avPacket) {LOGE("取出队列")pthread_mutex_lock(&mutex...

weixin_34514218的博客 1039

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Android FFmpeg系列——2 播放音频

音频简介 虽然听了很多音频,但其实对音频知之甚少,所以很有必要了解一下音频。 Audio,指人耳可以听到的声音频率在20Hz~20kHz之间的声波,称为音频。 音频录制 播放音频之前,我们得先了解音频是怎么保存的。保存音频,其实也就是录音和制作。 模拟时代 模拟时代是把原始信号以物理方式录制到磁带上(当然在录音棚里完成了),然后加工、剪接、修改,最后录制到磁带、LP等广大听众可以欣赏的载体上。这一...

JohanMan的博客 1441

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

NDK FFmpeg音视频播放器六

一二三四五节已经实现了音视频的播放器功能,本节主要是对音视频播放器增加拖动条功能,以及项目的一些释放工作。Native层native-lib.cpp调用NdkPlayer.cpp获取播放总时长。将拖动条% 转化成视频播放的时间戳,传给native层,并设置为视频的播放时间。2)获取视频播放时间戳,实时同步到UI更新当前播放时间和拖动条进度;通过NdkPlayer.java调用Native层获取播放总时长。3)手指拖动拖动条,视频播放到拖动条对应的时间的画面。1)获取视频播放总时长,并显示总时长和拖动条;

sziitjin的博客 572

Android 音视频播放器 Demo(二)—— 音频解码与音视频同步

本篇会介绍音频的解码与渲染、音视频同步以及进度条的实现。

tmacfrank 3215
上一篇: Android FFmpeg系列——4 子线程播放音视频
下一篇: Android FFmpeg系列——7 实现快进/快退功能
JohanMan
博客等级 码龄10年 72粉丝 131原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值