Android设备用AudioRecord采集的实时音频可以不需要编码在网络中直接传输,而对于摄像头采集的实时视频数据最好通过编码后再进行网络传输,下面的代码主要采用MediaCodec进行硬件编码视频数据。编码时,针对具体设备需要设置相对应的参数
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world"
/>
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="352dp"
android:layout_gravity="center"
android:layout_height="288dp" />
<SurfaceView
android:id="@+id/surfaceView1"
android:layout_width="352dp"
android:layout_gravity="center"
android:layout_height="288dp" />
</LinearLayout>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
package com.xtxk.camerademon;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
public class MainActivity extends Activity {
private SurfaceView surfaceView;
private Camera camera;
private SurfaceHolder surfaceHolder;
private Camera.Parameters mParameters = null;
@SuppressLint("InlinedApi")
private int mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
//不同手机有不同的匹配颜色
private int width = 352;
private int height = 288;
private int mFrameRate = 20;
private int bitrate = 2500000;
byte[] h264 = new byte[width*height*3/2];
private AvcEncode avcEncode;
private Button button;
int a = 1;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.text_name);
FileOperator.createFile(FileOperator.fileName);
avcEncode = new AvcEncode(width, height, mFrameRate, bitrate);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.setFixedSize(width, height);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolder.addCallback(new SurfaceCallback());
button.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO 自动生成的方法存根
camera.startPreview();
}
});
}
private final class SurfaceCallback implements Callback, PreviewCallback{
@SuppressLint("NewApi") @Override
public void surfaceCreated(SurfaceHolder holder)
{
// TODO 自动生成的方法存根
try {
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
camera = Camera.open();
} else {
camera = Camera.open(mCameraId);
}
camera.setPreviewDisplay(surfaceHolder);
//camera.setDisplayOrientation(90);
mParameters = camera.getParameters();
mParameters.setPreviewSize(width, height);
mParameters.setPictureSize(width, height);
//此处颜色设置与后面要匹配 否则颜色会出现变化
mParameters.setPreviewFormat(ImageFormat.YV12);
camera.setParameters(mParameters);
camera.setPreviewCallback(this);
//camera.startPreview();
} catch (Exception e)
{
Log.e("TEST", "setPreviewDisplay fail " + e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
{
// TODO 自动生成的方法存根
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
// TODO 自动生成的方法存根
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
avcEncode.close();
}
@Override
public void onPreviewFrame(byte[] data, Camera camera)
{
// TODO 自动生成的方法存根
//System.out.println("--------------onPreviewFrame-----------------------");
//h264 = avcEncode.offerEncode(data, h264);
h264 = avcEncode.offerEncode(data, h264);
//保存成.h264文件,将每一帧数据追加写在一个文件里面
FileOperator.addToFile(h264, FileOperator.fileName);
}
}
}
package com.xtxk.camerademon;
import java.nio.ByteBuffer;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.media.CamcorderProfile;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
@SuppressLint("NewApi")
public class AvcEncode
{
private MediaCodec mediaCodec;
private int width;
private int height;
byte[] m_info = null;
private byte[] yuv420 = null;
protected byte[] pps;
protected byte[] sps;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) @SuppressLint("NewApi")
public AvcEncode(int width, int height, int framerate, int bitrate){
this.width = width;
this.height = height;
yuv420 = new byte[width*height*3/2];
mediaCodec = MediaCodec.createEncoderByType("video/avc");
MediaFormat mediaFormat = null;
if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){
mediaFormat = MediaFormat.createVideoFormat("video/avc", 352, 288);
}else{
mediaFormat = MediaFormat.createVideoFormat("video/avc", 352, 288);
}
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 700000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10);
//根据手机设置不同的颜色参数
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
//mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();
}
public void close(){
try
{
mediaCodec.stop();
mediaCodec.release();
}
catch (Exception e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
long mCount = 0;
public byte[] offerEncode(byte[] input, byte[] output)
{
int pos = 0;
swapYV12toI420(input, yuv420, width, height);
try {
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(yuv420);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, yuv420.length, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,10000);
while (outputBufferIndex >= 0)
{
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
if(m_info != null)
{
System.arraycopy(outData, 0, output, pos, outData.length);
pos += outData.length;
/*System.out.println("里面");
for(int i = 0; i < output.length; i++){
System.out.println(output[i]+" ");
}*/
}
else
{
ByteBuffer spsPpsBuffer = ByteBuffer.wrap(outData);
if (spsPpsBuffer.getInt() == 0x00000001)
{
m_info = new byte[outData.length];
System.arraycopy(outData, 0, m_info, 0, outData.length);
}
}
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
if(output[4] == 0x65){
System.arraycopy(output,0,yuv420,0,pos);
System.arraycopy(m_info,0,output,0,m_info.length);
System.arraycopy(yuv420,0,output,m_info.length,pos);
pos += m_info.length;
}
}catch (Throwable t) {
t.printStackTrace();
}
return output;
}
private void swapYV12toI420(byte[] yv12bytes, byte[] i420bytes, int width, int height)
{
System.arraycopy(yv12bytes,0,i420bytes,0,width*height);
System.arraycopy(yv12bytes, width*height+width*height/4, i420bytes, width*height,width*height/4);
System.arraycopy(yv12bytes, width*height, i420bytes, width*height+width*height/4,width*height/4);
}
public byte[] CompressBuffer(byte[] yuvBuff, byte[] h264Buff)
{
int pos = 0;
//swapYV12toI420(yuvBuff, yuv420);
try {
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(yuvBuff);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, yuv420.length, mCount *1000/10,0);
mCount++;
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,10000);
while (outputBufferIndex >= 0)
{
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
if(m_info != null)
{
System.arraycopy(outData, 0, h264Buff, pos, outData.length);
pos += outData.length;
}
else
{
ByteBuffer spsPpsBuffer = ByteBuffer.wrap(outData);
if (spsPpsBuffer.getInt() == 0x00000001)
{
m_info = new byte[outData.length];
System.arraycopy(outData, 0, m_info, 0, outData.length);
}
}
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
if(h264Buff[4] == 0x65){
System.arraycopy(h264Buff,0,yuv420,0,pos);
System.arraycopy(m_info,0,h264Buff,0,m_info.length);
System.arraycopy(yuv420,0,h264Buff,m_info.length,pos);
pos += m_info.length;
}
}catch (Throwable t) {
t.printStackTrace();
}
return h264Buff;
}
}
package com.xtxk.camerademon;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import android.os.Environment;
public class FileOperator
{
//public static String fileName = getSDPath()+ "/" + "chenhan.h264";
public static String fileName = "/data/data/com.xtxk.camerademon/chenhan.h264";
public static void addToFile(byte[] content, String file){
BufferedOutputStream out = null;
try
{
out = new BufferedOutputStream(new FileOutputStream(file, true));
out.write(content);
}
catch (Exception e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally{
try
{
out.close();
}
catch (Exception e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public static String getSDPath(){
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
if(sdCardExist){
sdDir = Environment.getExternalStorageDirectory();
}
return sdDir.toString();
}
public static void createFile(String fileName){
File file = new File(fileName);
System.out.println("文件路径1:" + fileName + file.exists());
if(file.exists()){
file.delete();
}
try
{
file.createNewFile();
}
catch (IOException e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println("文件路径2:" + fileName);
}
}
需要相对应的解码播放器才能打开该文件,本人用的是
ffmpeg