《android多媒体api》系列是整合梳理android开发中经常用到的媒体相关api;多媒体开发主要内容有音频、视频录制播放、摄像头操作、录制操作、流媒体、直播、推流、拉流等方面;最近几年移动直播和视频应用发展犹如雨后春笋一般直插云霄,呃。。好吧这段比喻可以不用看了!!,反正行业兴起肯定催生了很多多媒体相关应用开发程序员。那么怎样才能成为多媒体开发程序员,首先必须要熟练使用和了解android自带的多媒体api,并且还要掌握pcm、yuv、rgb、h264、aac、flv、mpegts、mp4、udp、rtp、rtmp等等众多文件格式和流媒体协议等等。所以这里整理android相关多媒体api,提供给想从事流媒体同学作为参照,同样还是要鸣谢网络上那些具有分享精神大神们!!
####基本概念:
- 视频播放:demuxer(解复用)->分离出音频流和视频流->decoder(解码)->播放原始数据(例如:pcm yuv)
- 视频录制:采集原始数据(例如:pcm yuv)->encoder(编码)->muxer(封装格式 例如:mp4 3gp)
- 流媒体协议:udp、rtp、rtmp、rtcp、rtsp等
- 音视频封装格式:mp4 、3gp、flv等
- 音视频编码格式:aac、amr、h264、h265等
- 原始音视频数据格式:pcm 、yuv、rgb等
流程图:
####文章目录:
- VideoView 视频播放控件
- camera配合surface预览相机画面和拍照
- MediaPlayer自定义视频播放器
- MediaRecorder音视频录制api
- AudioTrack原始音频pcm播放api
- AudioRecord原始音频pcm采集api
MediaPlayer是什么?
MediaPlayer类可用于控制音频/视频文件或流的播放。前面介绍了VideoView 跟MediaPlayer用法基本相同,不过MediaPlayer使用时候注意他有个缓存机制,如果没有缓存视频是不能够播放的;这里实现了一个基于MediaPlayer实现自定义视频播放器,具有功能播放、暂停、停止、进度条调整视频播放进度等。案例是通过surfaceview来显示画面,用seekbar来进度显示和控制,然后创建一个timer来一秒中更新一次进度条,同样进度条也可以通过手指抬起时来更新视频进度。
#####效果图:
#####MediaPlayer api方法:
方法:create(Context context, Uri uri)
解释:静态方法,通过Uri创建一个多媒体播放器。
方法:create(Context context, int resid)
解释:静态方法,通过资源ID创建一个多媒体播放器
方法:create(Context context, Uri uri, SurfaceHolder holder)
解释:静态方法,通过Uri和指定 SurfaceHolder 【抽象类】 创建一个多媒体播放器
方法: getCurrentPosition()
解释:返回 Int, 得到当前播放位置
方法: getDuration()
解释:返回 Int,得到文件的时间
方法:getVideoHeight()
解释:返回 Int ,得到视频的高度
方法:getVideoWidth()
解释:返回 Int,得到视频的宽度
方法:isLooping()
解释:返回 boolean ,是否循环播放
方法:isPlaying()
解释:返回 boolean,是否正在播放
方法:pause()
解释:无返回值 ,暂停
方法:prepare()
解释:无返回值,准备同步
方法:prepareAsync()
解释:无返回值,准备异步
方法:release()
解释:无返回值,释放 MediaPlayer 对象
方法:reset()
解释:无返回值,重置 MediaPlayer 对象
方法:seekTo(int msec)
解释:无返回值,指定播放的位置(以毫秒为单位的时间)
方法:setAudioStreamType(int streamtype)
解释:无返回值,指定流媒体的类型
方法:setDataSource(String path)
解释:无返回值,设置多媒体数据来源【根据 路径】
方法:setDataSource(FileDescriptor fd, long offset, long length)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】
方法:setDataSource(FileDescriptor fd)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】
方法:setDataSource(Context context, Uri uri)
解释:无返回值,设置多媒体数据来源【根据 Uri】
方法:setDisplay(SurfaceHolder sh)
解释:无返回值,设置用 SurfaceHolder 来显示多媒体
方法:setLooping(boolean looping)
解释:无返回值,设置是否循环播放
事件:setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener)
解释:监听事件,网络流媒体的缓冲监听
事件:setOnCompletionListener(MediaPlayer.OnCompletionListener listener)
解释:监听事件,网络流媒体播放结束监听
事件:setOnErrorListener(MediaPlayer.OnErrorListener listener)
解释:监听事件,设置错误信息监听
事件:setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener)
解释:监听事件,视频尺寸监听
方法:setScreenOnWhilePlaying(boolean screenOn)
解释:无返回值,设置是否使用 SurfaceHolder 显示
方法:setVolume(float leftVolume, float rightVolume)
解释:无返回值,设置音量
方法:start()
解释:无返回值,开始播放
方法:stop()
解释:无返回值,停止播放
####基于MediaPlayer实现自定义播放器:
xml布局文件:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<SurfaceView
android:id="@+id/surfaceView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/btnPlayUrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放网络视频"/>
<Button
android:id="@+id/btnPause"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="暂停"/>
<Button
android:id="@+id/btnStop"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="停止"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<SeekBar
android:id="@+id/skbProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1.0"
android:max="100"
android:progress="1"
android:paddingLeft="10dip"
android:paddingRight="10dip"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
java代码:
package com.jared.helloffmpeg;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
public class RecordMP4Push extends Activity implements SurfaceHolder.Callback, MediaPlayer.OnBufferingUpdateListener {
private SurfaceView surfaceView;
private Button btnPause, btnPlayUrl, btnStop;
private SeekBar skbProgress;
private MediaPlayer player;
private boolean isLoad=false;
private boolean isStop=false;
private Timer mTimer = new Timer();
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.record_aac_and_pcm);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView1);
btnPlayUrl = (Button) this.findViewById(R.id.btnPlayUrl);
btnPause = (Button) this.findViewById(R.id.btnPause);
btnStop = (Button) this.findViewById(R.id.btnStop);
skbProgress = (SeekBar) this.findViewById(R.id.skbProgress);
btnPlayUrl.setOnClickListener(new ClickEvent());
btnPause.setOnClickListener(new ClickEvent());
btnStop.setOnClickListener(new ClickEvent());
skbProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent());
surfaceView.getHolder().addCallback(this);
}
TimerTask mTimerTask = new TimerTask() {
@Override
public void run() {
if (player == null)
return;
if (player.isPlaying() && !skbProgress.isPressed()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
int position = player.getCurrentPosition();
int duration = player.getDuration();
if (duration > 0) {
long pos = skbProgress.getMax() * position / duration;
skbProgress.setProgress((int) pos);
}
}
});
}
}
};
private void initPlayer(SurfaceHolder holder){
if (player!=null)
return;
player=new MediaPlayer();
player.setDisplay(holder);
player.setOnBufferingUpdateListener(this);
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
if (mediaPlayer.getVideoWidth()!=0)
isLoad=true;
}
});
try {
player.setDataSource("/storage/emulated/0/other/input.mp4");
player.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
private void freePlayer(){
if (player==null)
return;
if (player.isPlaying()) {
player.stop();
}
//player.reset();//清空所有设置
player.release();
player=null;
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
initPlayer(surfaceHolder);
mTimer.schedule(mTimerTask, 1000, 1000);
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
freePlayer();
mTimer.cancel();
}
@Override
public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
Log.i(getClass().getSimpleName(), "buffer size=="+i);
skbProgress.setSecondaryProgress(i);
int currentProgress = skbProgress.getMax() * mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration();
Log.e(currentProgress + "% play", i + "% buffer");
}
class ClickEvent implements OnClickListener {
@Override
public void onClick(View arg0) {
if (!isLoad)
return;
if (arg0 == btnPause) {
if (player.isPlaying())
player.pause();
} else if (arg0 == btnPlayUrl) {
if (!player.isPlaying())
{
if (isStop)
{
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
if (mediaPlayer.getVideoWidth()==0)
return;
mediaPlayer.start();
isStop=false;
}
});
try {
player.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}else {
player.start();
}
}
} else if (arg0 == btnStop) {
if (player.isPlaying()) {
player.stop();
skbProgress.setProgress(0);
isStop=true;
}
}
}
}
class SeekBarChangeEvent implements SeekBar.OnSeekBarChangeListener {
int progress;
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// 原本是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()
this.progress = progress * player.getDuration() / seekBar.getMax();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字
player.seekTo(progress);
}
}
}