今天被问到视频播放,录音的问题,又想起了曾经开发音视频播放,遇到的那些坑。其实多媒体的知识比较多,客户端开发难度也大,毕竟android应用层用java开发,对于音频开发,最好是有强大的C/C++功底才好研究底层,任重道远。
一起学习吧,先从音乐播放器开始。
不一定要这样写,仅供参考。
/**
* 可播放音视频的 MediaPlayerService duqian
* 既可以activity绑定该service,也可以直接start,实现后台播放音乐
* 2016/01/08
*/
public class AudioPlayerService extends Service {
private static final String TAG = AudioPlayerService.class.getSimpleName();
private MyBinder myBinder;
private String music_name;
public AudioPlayerService() {
super();
}
private static VideoView mediaPlayer;
/**
* 初始化播放器控件
* @param videoView
*/
private void initPlayer(VideoView videoView) {
mediaPlayer = videoView;
}
/**
* 播放音乐
*/
public void play(){
if(mediaPlayer != null){
mediaPlayer.start();
notifyChange(MediaStaticVar.ACTION_PREPARED, MediaStaticVar.DATA_PREPARED);
//showNotification();
}
}
/**
* 暂停音乐
*/
public void pause(){
if(mediaPlayer != null){
mediaPlayer.pause();
}
//stopNotification();
}
/**
* 跳到指定播放位置
* @param progress
*/
private void seekTo(int progress) {
MediaStaticVar.progress = progress;
if(mediaPlayer != null){
mediaPlayer.seekTo(progress);
}
}
//是否正在播放
public boolean isPlaying(){
boolean playing =false;
if(mediaPlayer != null){
playing= mediaPlayer.isPlaying();
}
return playing;
}
//总时长
private long getDuration(){
long duration = 1000*60*6;
if (mediaPlayer!=null){
duration = mediaPlayer.getDuration();
}
return duration;
}
//当前时长
private long getCurrentPosition(){
long currentPosition = 0;
if (mediaPlayer!=null){
currentPosition = mediaPlayer.getCurrentPosition();
}
return currentPosition;
}
/**
* 创建播放器
* @param path
*/
private void startPlay(String path,Context context) {
if (path == null||TextUtils.isEmpty(path)) {
return;
} else {
MediaStaticVar.path = path;
if (mediaPlayer==null){
LogUtils.debug(TAG, "mediaPlayer==null");
mediaPlayer = new VideoView(context);
}
mediaPlayer.setVideoPath(path);
mediaPlayer.seekTo(MediaStaticVar.progress);//上次播放的位置
mediaPlayer.requestFocus();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setPlaybackSpeed(1.0f);
play();
//notifyChange(MediaStaticVar.ACTION_PREPARED, MediaStaticVar.DATA_PREPARED);
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
notifyChange(MediaStaticVar.ACTION_PREPARED,MediaStaticVar.DATA_COMPLETED);
}
});
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
notifyChange(MediaStaticVar.ACTION_PREPARED,MediaStaticVar.DATA_ERROR);
LogUtils.debug(what + "" + extra + "error");
return true;
}
});
}
}
@Override
public IBinder onBind(Intent intent) {
LogUtils.debug(TAG,"onBind");
myBinder = new MyBinder();
return myBinder;//使用Binder对象,要返回
}
/**
* 定义一个类 继承 Binder,定义一个对象和调用者通信
*/
private class MyBinder extends Binder implements IMusicPlayerService
{
@Override
public void callInitPlayer(VideoView videoView) {
initPlayer(videoView);
}
@Override
public void callPlayer(String path, Context mContext) {
startPlay(path,mContext);
}
@Override
public void callPause() {
pause();
}
@Override
public void callPlay() {
play();
}
@Override
public boolean callIsPlaying() {
return isPlaying();
}
@Override
public void callSeekTo(int progress) {
seekTo(progress);
}
@Override
public void callPrevious() {
}
@Override
public void callNext() {
}
@Override
public long callGetDuration() {
return getDuration();
}
@Override
public long callGetCurrentPosition() {
return getCurrentPosition();
}
@Override
public void callNotifyChange(String nofity, int action) {
notifyChange(nofity,action);
}
@Override
public void stopNotification() {
stopNotification();
}
}
@Override
public boolean onUnbind(Intent intent) {
LogUtils.debug(TAG,"on unbind");
return super.onUnbind(intent);
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
try {
mediaPlayer.stopPlayback();
mediaPlayer = null;
LogUtils.debug(TAG,"mediaPlayer stop");
} catch (Exception e) {
e.printStackTrace();
}
}
stopNotification();
MediaStaticVar.progress = 0;
MediaStaticVar.path = "";
}
/**
* 发消息
* @param nofity
*/
private void notifyChange(String nofity,int action){
Intent intent = new Intent();
if (nofity==null) {
intent.setAction(MediaStaticVar.ACTION_PREPARED);
}else{
intent.setAction(nofity);
}
intent.putExtra("data",action);
sendBroadcast(intent);//发广播
}
}
如果activity绑定了该service,控制后台音乐的播放,暂停,下一曲,上一曲。那么可以写一个类似的接口, 定义一个类继承 Binder,实现IMusicPlayerService接口,以便service和调用者通信。
public interface IMusicPlayerService {
public void callInitPlayer(VideoView videoView);
public void callPlayer(String path,Context mContext);
public void callPause();
public void callPlay();
public boolean callIsPlaying();
public void callSeekTo(int progress);
public void callPrevious();
public void callNext();
public long callGetDuration();
public long callGetCurrentPosition();
public void callNotifyChange(String nofity,int action);
public void stopNotification();
}
以下代码不全,仅供参考。
//开启服务
private IMusicPlayerService iservice; //服务
private MyConn conn;
private void startService(Class clazz) {
//1.开启服务,与本activity不绑定
Intent service = new Intent(context, clazz);
service.setPackage(context.getPackageName());//这里你需要设置你应用的包名,5.0新要求
context.startService(service);//多次调用startService并不会启动多个service 而是会多次调用onStart
//2.开启服务与本activity绑定
Intent intent = new Intent(this,clazz);
conn = new MyConn();
bindService(intent,conn,Context.BIND_AUTO_CREATE);
}
private boolean isServiceConnected;
//定义绑定服务的接口
private class MyConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//当绑定服务成功后,会回调此方法
isServiceConnected = true;
iservice = (IMusicPlayerService)service;
iservice.callPlayer(MediaStaticVar.path,context);//高清音乐,开始播放
}
@Override
public void onServiceDisconnected(ComponentName name) {
//当取消绑定服务成功后,会回调这个方法
iservice = null;
}
}
对于背景音乐,将MediaPlayer封装在Service中即可实现。
MediaPlayer支持元数据、音频文件、音频流等形式的源数据的播放。
(1)播放元数据
所谓元数据即raw文件夹下的资源文件,调用方法如下:
mMediaPlayer=MediaPlayer.create(this, R.raw.test_cbr);
mMediaPlayer.start();
(2)播放音频文件
对于本地文件,其数据源可以通过Uri来表示,在开始播放前需要设置播放类型并加载缓冲,示例如下:
Uri myUri="/path/to/localfile";
MediaPlayer mediaPlayer=new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare(); //加载缓冲
mediaPlayer.start();
(3)播放流
MediaPlayer支持基于网络的流播放,其支持的网络协议包括RSTP(RTP、SDP)、HTTP渐进流(HTTP Progressive Streaming)、HTTP生活流(HTTP Live Streaming Draft Protocol,仅Honeycomb版本中用到)等。Android4.0开始支持HTTPS协议,在网络播放和本地播放上略有不同,示例如下:
String url="http://duqian.net.cn/demo.mp3;
MediaPlayer mediaPlayer=new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); //加载缓冲
mediaPlayer.start();
准备完成,播放完成,发生播放错误等,都可以监听。
不能在UI主线程中调用prepare方法。应通过prepareAsync方法将加载缓冲的工作放置在非UI主线程中进行,当准备工作完成时,MediaPlayer通过MediaPlayer.OnPreparedListener监听器可以监听到该事件。设置监听器的方法如下:
MediaPlayer.OnPreParedListener mPreparedListener=new MediaPlayer.OnPreparedListener(){
public void onPrepared(MediaPlayer mp){
mediaPlayer.start();
}
}
当播放结束时,通过MediaPlayer.OnCompletionListener可以监听到播放结束的消息,示例如下:
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
public void onCompletion(MediaPlayer mediaPlayer){
//stop or next
}
});
MediaPlayer还存在一个出错的状态,即MEDIA_PLAYER_STATE_ERROR。监听出错的监听器为MediaPlayer.OnErrorListener。
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
LogUtils.debug(what + "" + extra + "error");
return true;
}
});
其实android 系统API中的mediaPlayer使用起来还是挺方便的。当然也可以使用第三方多媒体框架,实现音视频的播放。
杜乾,Dusan,Q291902259;欢迎交流。