android中怎么样能获取正在播放的音乐名?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android中怎么样能获取正在播放的音乐名?相关的知识,希望对你有一定的参考价值。
是不是musicstore,media player之类的数据库中可以查询到啊?
如果我们在此基础上更深入一步,控制音乐播放的同时,我们还需要得到正在播放的音乐名称,这又该怎么实现呢?android Framework 提供了非常棒的 android.media,凡音乐播放都离不开 MediaPlayer,该类定义了七个内部类,其中 MediaPlayer.OnPreparedListener 是这么描述的:Interface definition for a callback to be invoked when the media source is ready for playback.媒体文件准备结束,准备播放时,Android 就会调用这个这个内部类,在 MediaPlaybackService 则是 notifyChange(String what) 方法进行处理:private void notifyChange(String what) Intent i = new Intent(what); i.putExtra(”id”, Integer.valueOf(getAudioId())); i.putExtra(”artist”, getArtistName()); i.putExtra(”album”,getAlbumName()); i.putExtra(”track”, getTrackName()); sendBroadcast(i); // …… 那么在我们的应用当中,则需要创建 一个BroadcastReceiver,过滤对应的 action name,也就是常量 META_CHANGED 对应的值即可达到目标。 查看原帖>> 参考技术A android获取正在播放的音乐文件信息的方法为:1、就是判断当前是否有音乐正在播放。
[java]
/** Checks whether any music is active. */
isMusicActive()
通过这个接口就可以判断当前系统是否有音乐在播放了。
2、还有一个问题,如果在音乐一开始就已经播放的时候,就知道这个事件,以便进行特殊的处理。
3、再进一步看一下 AudioManager 的源码,发现其中有如下方法:
[java]
/**
* Request audio focus.
* Send a request to obtain the audio focus
* @param l the listener to be notified of audio focus changes
* @param streamType the main audio stream type affected by the focus request
* @param durationHint use @link #AUDIOFOCUS_GAIN_TRANSIENT to indicate this focus request
* is temporary, and focus will be abandonned shortly. Examples of transient requests are
* for the playback of driving directions, or notifications sounds.
* Use @link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK to indicate also that it's ok for
* the previous focus owner to keep playing if it ducks its audio output.
* Use @link #AUDIOFOCUS_GAIN for a focus request of unknown duration such
* as the playback of a song or a video.
* @return @link #AUDIOFOCUS_REQUEST_FAILED or @link #AUDIOFOCUS_REQUEST_GRANTED
*/
public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)
从字面意思来看:请求音频焦点,再看这个函数的返回值:
[java]
/**
* A failed focus change request.
*/
public static final int AUDIOFOCUS_REQUEST_FAILED = 0;
/**
* A successful focus change request.
*/
public static final int AUDIOFOCUS_REQUEST_GRANTED = 1;
这个函数可能有帮助,进一步查一下Google官方的帮助:http://developer.android.com/training/managing-audio/audio-focus.html
Managing Audio Focus
With multiple apps potentially playing audio it's important to think about how they should interact. To avoid every music app playing at the same time, Android uses audio focus to moderate audio playback—only apps that hold the audio focus should play audio.
Before your app starts playing audio it should request—and receive—the audio focus. Likewise, it should know how to listen for a loss of audio focus and respond appropriately when that happens.
简单地翻译一下:
管理音频焦点
多个应用都在播放音频的可能性,所以考虑应用间如何交互非常重要。为避免每个音乐应用同时播放,Android使用音频焦点来协调音频的播放----只有获取到音频焦点的应用可以播放音频。
在应用开始播放音频之前,它应该先请求--并接收音频焦点。同样,它也应该知道当监听到失去音频焦点后如何合理地进行响应。
沿着这个路应该是对的,写了下面的测试代码进行验证。这个主要是Service的实现,还需要实现一个Activity去启动Service、结束Service:
[java]
package com.example.servicetest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class MainService extends Service
private static final String TAG = "MainService";
private MediaPlayer player;
private AudioManager mAm;
private MyOnAudioFocusChangeListener mListener;
@Override
public void onCreate()
Log.i(TAG, "onCreate");
player = MediaPlayer.create(this, R.raw.test); // 在res目录下新建raw目录,复制一个test.mp3文件到此目录下。
player.setLooping(false);
mAm = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
mListener = new MyOnAudioFocusChangeListener();
@Override
public IBinder onBind(Intent intent)
return null;
@Override
public void onStart(Intent intent, int startid)
Toast.makeText(this, "My Service Start", Toast.LENGTH_LONG).show();
Log.i(TAG, "onStart");
// Request audio focus for playback
int result = mAm.requestAudioFocus(mListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
Log.i(TAG, "requestAudioFocus successfully.");
// Start playback.
player.start();
5步告诉你QQ音乐的完美音质是怎么来的,播放器的秘密都在这里
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~
一、问题背景与分析
不久前,团队发现其Android平台App在播放MV视频《凤凰花开的路口》时,会带有如电流声一般的杂音,这影响了用户体验。 研发同学在初步定位时,发现有如下特征:
- Android平台杂音问题必现;
- iOS、PC平台能正常播放,没有噪音。
然而,各平台都是统一用HLS格式播放,即源头都是一样的。对于该问题,我们的定位思路如下:
- 梳理视频播放流程;
- 找到切入点排查。
二、播放流程概览
分析播放流程如上图(图中内容从左往右),概括其关键步骤如下:
- 播放器初始化:
- 创建读数据线程:
read_thread
; - 创建存放audio解码前数据的队列:
audioq
; - 创建存放audio解码后数据的队列:
sampq
。
- 创建读数据线程:
- 数据读取:
- ①创建context;
- ②探测协议类型:
avformat_open_input
; - ③探测媒体类型:
avformat_find_stream_info
; - ④获取音视频流:
av_find_best_stream
; - ⑤打开媒体解码器:
stream_component_open
; - ⑥读取媒体数据,获得AVPacket:
av_read_frame(ic, pkt)
; - ⑦音视频数据分别送入
audioq
中; - 重复⑥、⑦步骤到数据完毕。
- 音频解码:
- 在
audio_thread
中对audioq
中的数据进行decoder_decode_frame
解码; - 解码后的帧
AVFrame
存放到sampq
中;
- 在
- 音频播放:
aout_thread_n
中,通过调用回调接口sdl_audio_callback
,对sampq
中的音频帧数据进行解码成PCM数据;- 写入PCM数据到buffer数组,并由
AudioTrack
播放。
三、问题分解与切入
在梳理出播放流程后,标记出找到有可能出错的环节,方便进行“分层定位”(图中黄色标记)
- 播放下载文件是否有问题;
- 数据读取是否有问题;
- 音频解码逻辑是否有问题;
AudioTrack
的设置是否有问题;
接下来,根据难易程度,对上述环节逐个验证。
1、播放下载文件是否正常
把Android平台播放的ts文件与各平台的进行比对,发现两者一样,该环节正常。
2、AudioTrack设置是否正常
通过日志检查AudioTrack
以下配置参数:
- 采样率
- 位深
- 频道
以上参数设置的值与音频流的相符合,该环节正常。
3、音频解码逻辑是否有问题
验证解码逻辑是否有问题,可以通过对PCM数据进行分析来确认。 对aout_thread_n
进行修改,将PCM数据额外输出到本地,并与正常的PCM数据进行对比。
正常PCM数据频谱图:
异常PCM数据频谱图:
正常PCM数据波形图:
异常PCM数据波形图:
对比分析可得出:
- 从频谱图中看出,异常的PCM在人耳十分敏感的频响(1000~8000Hz )区域内的音频数据严重缺失,导致“杂音问题”
- 从波形图中看出,异常的与正常的无声区和有声区都吻合,若解封装、解码逻辑出现异常,极大几率是呈现无波动(一条直线的形式)情况。因此可以先大胆假设解码、解封装逻辑是符合预期的
若解码逻辑正常,再结合之前已经验证文件下载正常。可以推测是数据读取环节出现异常。
4、数据读取是否有问题
通过对数据读取的各步骤增加日志后,发现在av_find_best_stream
音频流选择时出现异常: ffmpeg -i
发现,该视频ts分片有2个音频流
通过强制分别读取两条音频流数据播放,发现:
- 第一条正常播放(PCM数据正常)
- 第二条播放杂音(PCM数据异常)
- Android平台选择了第二条进行播放
基于此,也就验证了在第3步中的假设是正确的。
由上分析,可以得出结论:Android平台选择了第二条数据有问题的流进行播放。
四、问题根源:音频流选择
1、选择方式
分析代码,大致如下所列,av_find_best_stream
函数选择音频流,该函数会根据2个主要参数进行选择:
- 各音频流的在探测媒体类型(
avformat_find_stream_info
)时,额外解码出来的帧数(选择多的) - 各音频流的比特率(选择高的)
int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type,
int wanted_stream_nb, int related_stream,
AVCodec **decoder_ret, int flags)
{
for (i = 0; i < nb_streams; i++) {
count = st->codec_info_nb_frames; //音频流探测中解码的帧数
bitrate = avctx->bit_rate;//音频流的比特率
multiframe = FFMIN(5, count);
//先比较解码帧数,再比较音频流比特率,谁大谁选
if ((best_multiframe > multiframe) ||
(best_multiframe == multiframe && best_bitrate > bitrate) ||
(best_multiframe == multiframe && best_bitrate == bitrate && best_count >= count))
continue;
best_count = count;
best_bitrate = bitrate;
best_multiframe = multiframe;
ret = real_stream_index;//最后选择的流index
best_decoder = decoder;
}
return ret;
}
在该视频中,我们可以看到:
codec_info_nb_frames | bit_rate | |
---|---|---|
audio_stream 1 | 38 | 122625 |
audio_stream 2 | 39 | 126375 |
第二条流的解码帧数和比特率要比第一条高,因此选择了第二条流播放
2、对比同类方案
分析了以上选择规则后,我们对各平台、框架进行了选择规则的对比:
备注:
- ExoPlayer对多音频流的ts分片支持不完善(issue),因此测试时需要调整相关接口。但选择规则依然以上述所示(DefaultTrackSelector)
- iOS和PC平台采用闭源组件,因此测试时使用了“互换两条音频流顺序”的方法进行测试。互换后,两平台都播放了杂音音频流
ffmpeg -i INPUT_FILE -map 0:0 -map 0:2 -map 0:1 -c copy -y OUTPUT_FILE
- QuickTime同样是闭源,互换音频流后无法明显差别,通过合成第三条音频流,来验证是它是对所有音频流全播放
ffmpeg -i INPUT_FILE_1 -i INPUT_FILE_2 -map 0:0 -map 0:1 -map 0:2 -map 1:0 -c copy OUTPUT_FILE
3、总结
从以上数据看到,iOS和PC平台会默认选择第一条流,而在Android平台的FFmpeg和ExoPlayer会根据音频流属性来选择数值更好的一条。
- “默认选择第一条”方案能更容易地把音源问题暴露。
- “比较音频流属性”方案能更大几率地选择质量更好的流来提升用户体验。
但以上2个选择方案都无法识别“内容异常”的音频流。
五、问题解决方案
因此,处理该问题,需要从音源上进行修复和规避,我们的建议是从源头杜绝,从终端规避:
- 编辑重新上架正常音源;
- 短期内增加双音频流的检测上报,帮助后台、编辑进行复查;
- 长远看由后台开发工具,分别对存量视频进行双音频流检测和对增量视频保证只转码单音频流;
参考资料
- https://ffmpeg.org/doxygen/2.8/
- https://github.com/google/ExoPlayer
- https://www.jianshu.com/p/daf0a61cc1e0
- https://www.jianshu.com/p/a6a4bf59cdae
- http://km.oa.com/articles/show/319627
- https://codeday.me/bug/20170711/39603.html
相关阅读
wamp2.0配置Zend Optimizer
藏匿在邮件里的“坏小子”
打造一个个人阅读追踪系统
【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识
此文已由作者授权腾讯云+社区发布,更多原文请点击
搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!
海量技术实践经验,尽在云加社区!
以上是关于android中怎么样能获取正在播放的音乐名?的主要内容,如果未能解决你的问题,请参考以下文章