(原)关于OpenSL ES播放音频数据的一个奇怪的问题

关于OpenSL ES播放音频数据的一个奇怪的问题

Author:[email protected]

最近用业余时间做了一个android平台的播放器sdk,其中视频用的opengl es,音频用的opensl es 做渲染,其中整个播放器在音视频同步的过程中,使用的视频同步到音频的方式,以音频作为主时钟。

今天在测试的过程中发现一个奇怪的问题,我音频数据的填充,使用了单独的音频线程,这其中的实现,主要参考ijk的实现代码和方式。

整个流程方式:我在音频播放的过程中设置的200ms的播放缓冲区给底层队列,当我把音频数据缓冲区填满以后,就wait_timeout(1s),如果在wait的过程中,底层有回调,就会signal,从而唤醒wait函数,然后再查询缓冲区大小,是否可以填充数据,如果可以填充数据的话,就填充数据进去,同时在填充数据的函数中,通过计算音频的pts和缓冲区的剩余数据大小,来更新当前的音频播放时钟位置。其中问题就是出在更新音频时钟位置的这个地方,我们计算剩余缓冲区的数据大小的时候,通过调用这个函数来获取底层数据的缓冲区延时:

通过计算state.count的计数来获取这个延时时间。

那么现在的问题现象是这样的,在视频正常播放的过程中,我填充了200ms满的缓冲区音频数据去播放,在填充数据线程中wait,然后在大概过了120ms之后,底层回调:

发送信号唤醒wait函数,然后我去拷贝音频数据,拷贝完数据之后,计算时钟,这个时候查询底层缓冲区数据还剩多少,发现state.count=17,也就是说填满的时候20,现在获取还剩17,意思是中间难道只消耗了30ms的音频数据?不太可能啊,正常播放的情况下,120ms应该消耗120ms的音频数据才对啊,为啥这个地方只消耗了30ms的数据呢?

经过进一步的排查和分析,发现了问题,如果经过了120ms的播放时间,底层实际消耗的音频数据是为120ms,而他开始回调,并连续回调12次。但第一次回调的时候,我们就唤醒了wait函数,这个时候我们去拷贝数据,而从唤醒到拷贝数据完成,并重新获取底层缓冲区大小的的这个过程,因为时间很短,短到他12次的连续回调,实际上只完成了3次回调,这个时候我们就去获取了state.count,发现它比之前只少了3次,所以误认为这个时间他只消耗了30ms的数据。因为state.count这个底层计数,他回调一次-1,并不会真正的实时反映实际的数据消耗过程。

那么我的代码和ijk的貌似看上去一致啊,为啥他的不会出现这个问题?后面思考发现问题所在,ijk在播放填充的数据线程中,通过回调给上层,他在上层的实现中实现了音频数据的解码,也就是正是这个解码函数产生的影响,他的这个耗时,完全足以让底层的音频连续回调,回调完毕,所以再去拿数据以后,就是正确的结果。

本人主要是个人对调试的理解和记录,仅供参考。

转载请注明出处:https://www.cnblogs.com/lihaiping/p/11739836.html

原文地址:https://www.cnblogs.com/lihaiping/p/11739836.html

时间: 2024-10-09 09:54:25

(原)关于OpenSL ES播放音频数据的一个奇怪的问题的相关文章

Android 音视频深入 十四 FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码

项目地址https://github.com/979451341/FFmpegOpenslES 这次说的是FFmpeg解码mp3,数据给OpenSL ES播放,并且能够暂停.1.创建引擎 slCreateEngine(&engineObject,0,NULL,0,NULL,NULL);//创建引擎 (*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE);//实现engineObject接口对象 (*engineObject)->G

iOS 9音频应用播放音频之第一个ios9音频实例2

iOS 9音频应用播放音频之第一个ios9音频实例2 ios9音频应用关联 iOS9音频应用中对于在主视图上添加的视图或控件,在使用它们时必须要与插座变量进行关联.ios9插座变量其实就是为主视图中的视图或者控件起的别名,类似于实例化的对象.将主ios9视图中的Play Button按钮控件与插座变量playButton进行关联.具体的操作步骤如下: (1)使用设置编辑器的三个视图方式的图标,如图2.14所示,将Xcode的界面调整为如图2.15所示的效果. 图2.14  编辑器的三个视图方式的

Android音视频学习第7章:使用OpenSL ES进行音频解码

/* * *这里使用了transcode-1.1.7对wav文件进行解码,然后使用opensl es进行播放 * */ //用到的变量和结构体 WAV wav; //wav文件指针 SLObjectItf engineObject; //引擎对象 SLEngineItf engineInterface; //引擎接口 SLObjectItf outputMixObject; //混音器 SLObjectItf audioPlayerObject; //播放器对象 SLAndroidSimpleB

记录一个多线程问题中,访问共享数据的一个奇怪的问题

首先是mythread类 public class MyThread extends Thread { private int count =5; public void run(){ super.run(); count--; System.out.println("由"+this.currentThread().getName()+"计算,count="+count); }} 主方法类 public class Main { public static void

Android音频开发(6):使用 OpenSL ES API(上)

前面几篇文章介绍了如何在 Java 层,利用 Android 提供的 AudioRecord 采集音频,利用 AudioTrack 播放音频,利用 MediaCodec 来编解码,这些 API 均是 Android 提供的 Java 层 API,无论是采集.播放还是编解码,这些 API 接口都需要将音频数据从 Java 拷贝到 native 层,或者从 native 层拷贝到 Java,如果希望减少拷贝,开发更加高效的 Android 音频应用,则建议使用 Android NDK 提供的 Ope

DirectSound播放PCM(可播放实时采集的音频数据)

前言 该篇整理的原始来源为http://blog.csdn.net/leixiaohua1020/article/details/40540147.非常感谢该博主的无私奉献,写了不少关于不同多媒体库的博文.让我这个小白学习到不少.现在将其整理是为了收录,以备自己查看. 一.DirectSound简介 DirectSound是微软所开发DirectX的组件之一,可以在Windows 操作系统上录音,并且记录波形音效(waveform sound).目前DirectSound 是一个成熟的API ,

使用AudioTrack播放PCM音频数据(android)

众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制.但是该类只能对完整的音频文件进行操作,而不能直接对纯PCM音频数据操作.假如我们通过解码得到

FFmpeg学习3:播放音频

参考dranger tutorial,本文将介绍如何使用FFmpeg解码音频数据,并使用SDL将解码后的数据输出.本文主要包含以下几方面的内容: 关于播放音频的需要的一些基础知识介绍 使用SDL2播放音频 数据队列 音频格式的转换 dranger tutorial确实入门FFmpeg比较好的教程,虽然作者在2015年的时候根据新版本的FFmpeg更新了,但是其中还是有不少API过时了.特别是,教程中使用的是SDL1.0,和现在的SDL2的API也有很大的不同,并且不能兼容. 1. 关于音频的一些

安卓播放pcm数据

Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制. 但是该类只能对完整的音频文件进行操作,而不能直接对纯PCM音频数据操作.假如我们通过解码得到PCM数