《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
android开发自定义相机或者一些扫描功能的时候经常会遇到相机预览的问题了,这个必须获取相机然后将画面预览到手机界面上,那么输出界面用的就是surface控件。首先看看surfaceview是什么?
SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。surfaceview提供了一个可见区域,只有在这个可见区域内 的surface部分内容才可见,可见区域外的部分不可见。surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果surface上面 有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。
你可以通过SurfaceHolder接口访问这个surface,getHolder()方法可以得到这个接口。
surfaceview变得可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。如果你要查看 surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
surfaceview的核心在于提供了两个线程:UI线程和渲染线程。这里应注意:
1> 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。
2> 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。
相机camera api也提供了方法配合使用surfaceview控件。这里实现一个自定义相机预览和拍照功能。
首先上代码:
xml布局文件:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<SurfaceView
android:id="@+id/surfaceView_2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" />
<ImageView
android:id="@+id/pic_pre_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<Button
android:id="@+id/take_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:background="@mipmap/camera_canon"
android:text="拍照" />
</FrameLayout>
java代码文件:
public class TakePictureActivity extends Activity {
/**
* 图片预览及展示
*/
private SurfaceView surfaceView;
/**
* 拍照
*/
private Button takePic;
/**
* 图片展示
*/
private ImageView preViewPic;
/**
* 调用系统相机
*/
private Camera camera;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_take_pic);
/**
* 窗口布满全局
*/
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
initView();
initListener();
}
/**
* 初始化View
*/
private void initView() {
surfaceView = (SurfaceView) findViewById(R.id.surfaceView_2);
takePic = (Button) findViewById(R.id.take_pic);
preViewPic = (ImageView) findViewById(R.id.pic_pre_view);
}
/**
* 初始化监听器
*/
private void initListener() {
SurfaceHolder holder = surfaceView.getHolder();
holder.setFixedSize(176, 155);
holder.setKeepScreenOn(true);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new TakePictureSurfaceCallback());
takePic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (camera != null) {
camera.takePicture(null, null, new TakePictureCallback());
}
}
});
}
private final class TakePictureSurfaceCallback implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera = Camera.open();
if (camera == null) {
int cametacount = Camera.getNumberOfCameras();
camera = Camera.open(cametacount - 1);
}
Camera.Parameters params = camera.getParameters();
params.setJpegQuality(80);//照片质量
params.setPictureSize(1024, 768);//图片分辨率
params.setPreviewFrameRate(5);//预览帧率
camera.setDisplayOrientation(90);
/**
* 设置预显示
*/
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}
});
/**
* 开启预览
*/
camera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
camera.release();
camera = null;
}
}
}
private final class TakePictureCallback implements Camera.PictureCallback {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
byte[] tempData = os.toByteArray();
if (tempData != null && tempData.length > 0) {
Bitmap bitmap = BitmapFactory.decodeByteArray(tempData, 0, tempData.length);
preViewPic.setVisibility(View.VISIBLE);
surfaceView.setVisibility(View.GONE);
preViewPic.setImageBitmap(bitmap);
}
}
}
}
######上面流程是:
1,依据布局,获取Surfaceview对象;
2,获取Holder对象并设置属性;
3,绑定SurfaceHolder.Callback回调接口;
4,回调接口的surfaceCreated方法中设置Camera并设置对应属性;
5,设置拍照点击事件,在监听事件中绑定Camera.PictureCallback回调监听;
6,PictureCallback监听中依据回调数据,进行结果保存。