Android硬件编码

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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值