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

一分钟学会MATLAB-数据清洗(含完整代码)

数据清洗是数据分析过程中的一个重要步骤,目的是提高数据的质量,以便进行更有效的分析。% 删除超出阈值的行。% 用线性插值填充缺失值。% 删除包含缺失值的行。% 找到缺失值的位置。% 可视化数据,识别异常值。缺失值是数据清洗中常见的问题。通过以上步骤,你可以有效地清洗数据,使其准备好进行后续的分析和建模。

2402_87132195的博客 3316

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

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

weixin_42651281的博客 754

VScode中配置 C/C++ 环境,超级详细,问题分析全面,绝对好用

VScode中配置 C/C++ 环境,超级详细,问题分析全面,绝对好用 VScode中配置 C/C++ 环境 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 1. 下载编辑器VScode 1.官网:https://code.visualstudio.com/ 2. 安装VScode(建议附加任务全部勾选; 2. 下载编译器MinGW并解压 官网页面:https://www.mi

m0_46147064的博客 8万+

lego-loam featureAssociation 源码注释(五)

上一次我们已经看完了特征提取,这次我们进入特征匹配的内容!!!systemInitedLM的初始值为false。

Joeybee的博客 889

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

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

JohanMan的博客 1914

【智能制造】- APS系列|05 APS 排产的五大底层算法逻辑

很多人误以为APS只是给生产工单排先后顺序,实则整套系统依托成熟算法体系,完成多维度约束下的最优生产决策在制造业数字化排产普及的当下,依旧有不少制造管理者对APS存在认知误区,将其等同于简易工单排序工具。事实上,专业级高级计划与排程系统,早已脱离单纯按时间排序的基础功能,而是依托一整套成熟完善的底层算法逻辑,整合现场各类生产限制条件,在海量变量中自动推演、筛选、输出最贴合企业经营目标的生产方案。

qq_40821260的博客 435

NDK30_FFmpeg显示播放进度与快进

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

baopengjian的专栏 354

matlab电容式传感器仿真,差动电容式位移传感器的仿真研究

设计了一种差动电容式位移传感器,主要对传感器的数学模型及电容检测电路进行了仿真研究。其中检测电路设计主要包括正弦激励电路、转换桥路、信号放大电路、精密整流电路和滤波电路。最后利用Matlab/SimurFnk软件对所设计的系统进行仿真,并分析了传感器性能。仿真研究表明,传感器及其检测电路具有合理性,测量范围内具有很好的线性度。0|| I≯ lat on ii{差动容尊翩’亘研发注:湖南省教育厅资...

weixin_36323601的博客 1390

【OpenClaw从入门到精通】零基础入门——OpenClaw核心概念快速理解(2026入门版)

OpenClaw是一个强大的AI代理框架,旨在构建智能化的自动化任务处理系统。随着人工智能技术的快速发展,OpenClaw为开发者和企业提供了一个完整的解决方案,用于创建能够理解、学习和执行复杂任务的智能体。本文将带领零基础的读者快速理解OpenClaw的核心概念,从基础架构到实际应用,为后续深入学习奠定坚实基础。通过本文的学习,你将掌握OpenClaw的基本原理、核心组件和快速上手方法。

一键难忘的博客 325

Android FFmpeg系列——1 播放视频

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

JohanMan的博客 6873

YOLOv12改进策略【Neck】| CGA Fusion:内容引导的注意力融合模块,空间权重引导的多模态特征自适应融合

本文记录的是利用CGA Fusion 模块改进 YOLOv12 的多模态融合部分。(Content-Guided Attention Fusion)通过内容引导注意力生成空间权重,引导高低层特征的自适应融合。本文利用模块,通过内容引导注意力生成空间权重,自适应地融合两个模态的特征,在特征融合阶段实现跨模态语义对齐与噪声抑制,增强对不同模态互补特征的利用能力,从而提升模型在多模态场景下的检测鲁棒性与准确性。DEA-Net: Single image dehazing based on detail-enhan

Limiiiing的博客 434

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

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

测试0901-1 870

File does not exist: /user/anonymous/.staging/job_1628851608378_6064/报错问题解决

user/admin/.staging/job_1571397171638_0001/job_1571397171638_0001_1.jhist 所以为什么没有下发,这里面jhist,只是被引发的错误,它还不是根本的错误。看到上面错误,我们就可以判断,这是网络问题,然而网络无非是ip地址,防火墙,端口,hosts等。我们正常的hosts是这样的。在回头复盘想想,我们找不到jhist,跟我们的网络问题二者是很难找到关联的,所以大家在遇到问题的时候,一定多排查和思考,而不是看到一个错误,就以为是它的问题。

石榴姐yyds 2170

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

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

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

weixin_34514218的博客 1039

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Android FFmpeg系列——2 播放音频

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

JohanMan的博客 1441

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

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、付费专栏及课程。

余额充值