原文地址:http://www.apkbus.com/blog-86476-43829.html
废话不说直接贴代码,看不懂代码的同志可以参考:
http://blog.csdn.net/zblue78/article/details/6078040
http://blog.csdn.net/zblue78/article/details/6058147
这两篇讲的比较详细,不过他的代码我没有跑通。下面贴出我跑通的代码:
CSDN代码地址,能正常运行:http://download.csdn.net/detail/cooler126/5777745
上一篇文章进行了思路和16进制文件的分析。这篇该代码实现了。目前没有在真实手机上测试, android4.0之后的模拟器可以用模拟摄像头或者叫做webcam的【其实就是笔记本摄像头】。之后会在程序安装包data/data/edu.ustb.videoencoder/下面会有h264.3gp,sps【存放sps数据】、pps【存放pps数据】、media.xml【存放找到mdat的位置】,/sdcard/encoder.h264【这个文件就是编码后的h264文件】。
不多说,上源码。
[代码]:AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="edu.ustb.videoencoder" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.RECORD_VIDEO"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
[主程序代码]MainActivity.java:
package edu.ustb.videoencoder; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import android.media.MediaRecorder; import android.net.LocalServerSocket; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.util.Log; import android.view.Menu; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.view.View; public class MainActivity extends Activity implements Callback, Runnable{ private static final String TAG = "VideoCamera"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); InitSurfaceView(); InitMediaSharePreference(); } //初始化SurfaceView private SurfaceView mSurfaceView; private void InitSurfaceView() { mSurfaceView = (SurfaceView) this.findViewById(R.id.surface_camera); SurfaceHolder holder = mSurfaceView.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); mSurfaceView.setVisibility(View.VISIBLE); } //初始化,记录mdat开始位置的参数 SharedPreferences sharedPreferences; private final String mediaShare = "media"; private void InitMediaSharePreference() { sharedPreferences = this.getSharedPreferences(mediaShare, MODE_PRIVATE); } private SurfaceHolder mSurfaceHolder; private boolean mMediaRecorderRecording = false; public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mSurfaceHolder = holder; if(!mMediaRecorderRecording) { InitLocalSocket(); getSPSAndPPS(); initializeVideo(); startVideoRecording(); } } public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub } //初始化LocalServerSocket LocalSocket LocalServerSocket lss; LocalSocket receiver, sender; private void InitLocalSocket(){ try { lss = new LocalServerSocket("H264"); receiver = new LocalSocket(); receiver.connect(new LocalSocketAddress("H264")); receiver.setReceiveBufferSize(500000); receiver.setSendBufferSize(50000); sender = lss.accept(); sender.setReceiveBufferSize(500000); sender.setSendBufferSize(50000); } catch (IOException e) { Log.e(TAG, e.toString()); this.finish(); return; } } //得到序列参数集SPS和图像参数集PPS,如果已经存储在本地 private void getSPSAndPPS(){ StartMdatPlace = sharedPreferences.getInt( String.format("mdata_%d%d.mdat", videoWidth, videoHeight), -1); if(StartMdatPlace != -1) { byte[] temp = new byte[100]; try { FileInputStream file_in = MainActivity.this.openFileInput( String.format("%d%d.sps", videoWidth,videoHeight)); int index = 0; int read=0; while(true) { read = file_in.read(temp,index,10); if(read==-1) break; else index += read; } Log.e(TAG, "sps length:"+index); SPS = new byte[index]; System.arraycopy(temp, 0, SPS, 0, index); file_in.close(); index =0; //read PPS file_in = MainActivity.this.openFileInput( String.format("%d%d.pps", videoWidth,videoHeight)); while(true) { read = file_in.read(temp,index,10); if(read==-1) break; else index+=read; } Log.e(TAG, "pps length:"+index); PPS = new byte[index]; System.arraycopy(temp, 0, PPS, 0, index); } catch (FileNotFoundException e) { //e.printStackTrace(); Log.e(TAG, e.toString()); } catch (IOException e) { //e.printStackTrace(); Log.e(TAG, e.toString()); } } else { SPS = null; PPS = null; } } //初始化MediaRecorder private MediaRecorder mMediaRecorder = null; private int videoWidth = 320; private int videoHeight = 240; private int videoRate = 10; private String fd = "data/data/edu.ustb.videoencoder/h264.3gp"; private boolean initializeVideo(){ if(mSurfaceHolder == null) { return false; } mMediaRecorderRecording = true; if(mMediaRecorder == null) { mMediaRecorder = new MediaRecorder(); } else { mMediaRecorder.reset(); } mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mMediaRecorder.setVideoFrameRate(videoRate); mMediaRecorder.setVideoSize(videoWidth, videoHeight); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); mMediaRecorder.setMaxDuration(0); mMediaRecorder.setMaxFileSize(0); if(SPS==null) { mMediaRecorder.setOutputFile(fd); } else { mMediaRecorder.setOutputFile(sender.getFileDescriptor()); } try { mMediaRecorder.prepare(); mMediaRecorder.start(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); releaseMediaRecorder(); } return true; } //释放MediaRecorder资源 private void releaseMediaRecorder(){ if(mMediaRecorder != null) { if(mMediaRecorderRecording) { mMediaRecorder.stop(); mMediaRecorderRecording = false; } mMediaRecorder.reset(); mMediaRecorder.release(); mMediaRecorder = null; } } //开始录像,启动线程 private void startVideoRecording() { new Thread(this).start(); } private final int MAXFRAMEBUFFER = 20480;//20K private byte[] h264frame = new byte[MAXFRAMEBUFFER]; private final byte[] head = new byte[]{0x00,0x00,0x00,0x01}; private RandomAccessFile file_test; public void run() { try { if(SPS == null) { Log.e(TAG, "Rlease MediaRecorder and get SPS and PPS"); Thread.sleep(1000); //释放MediaRecorder资源 releaseMediaRecorder(); //从已采集的视频数据中获取SPS和PPS findSPSAndPPS(); //找到后重新初始化MediaRecorder initializeVideo(); } DataInputStream dataInput = new DataInputStream(receiver.getInputStream()); //先读取ftpy box and mdat box, 目的是skip ftpy and mdat data,(decisbe by phone) dataInput.read(h264frame, 0, StartMdatPlace); try { File file = new File("/sdcard/encoder.h264"); if (file.exists()) file.delete(); file_test = new RandomAccessFile(file, "rw"); } catch (Exception ex) { Log.v("System.out", ex.toString()); } file_test.write(head); file_test.write(SPS);//write sps file_test.write(head); file_test.write(PPS);//write pps int h264length =0; while(mMediaRecorderRecording) { h264length = dataInput.readInt(); Log.e(TAG, "h264 length :" + h264length); // int number=0 , num=0; // int frame_size = 1024; // file_test.write(head); // while(number<h264length) // { // int lost=h264length-number; // num = dataInput.read(h264frame,0,frame_size<lost?frame_size:lost); // Log.d(TAG,String.format("H264 %d,%d,%d", h264length,number,num)); // number+=num; // file_test.write(h264frame, 0, num); // } ReadSize(h264length, dataInput); byte[] h264 = new byte[h264length]; System.arraycopy(h264frame, 0, h264, 0, h264length); file_test.write(head); file_test.write(h264);//write selice } file_test.close(); } catch (Exception e) { e.printStackTrace(); } } private void ReadSize(int h264length,DataInputStream dataInput) throws IOException, InterruptedException{ int read = 0; int temp = 0; while(read<h264length) { temp= dataInput.read(h264frame, read, h264length-read); Log.e(TAG, String.format("h264frame %d,%d,%d", h264length,read,h264length-read)); if(temp==-1) { Log.e(TAG, "no data get wait for data coming....."); Thread.sleep(2000); continue; } read += temp; } } //从 fd文件中找到SPS And PPS private byte[] SPS; private byte[] PPS; private int StartMdatPlace = 0; private void findSPSAndPPS() throws Exception{ File file = new File(fd); FileInputStream fileInput = new FileInputStream(file); int length = (int)file.length(); byte[] data = new byte[length]; fileInput.read(data); final byte[] mdat = new byte[]{0x6D,0x64,0x61,0x74}; final byte[] avcc = new byte[]{0x61,0x76,0x63,0x43}; for(int i=0 ; i<length; i++){ if(data[i] == mdat[0] && data[i+1] == mdat[1] && data[i+2] == mdat[2] && data[i+3] == mdat[3]){ StartMdatPlace = i+4;//find mdat break; } } Log.e(TAG, "StartMdatPlace:"+StartMdatPlace); //记录到xml文件里 String mdatStr = String.format("mdata_%d%d.mdat",videoWidth,videoHeight); Editor editor = sharedPreferences.edit(); editor.putInt(mdatStr, StartMdatPlace); editor.commit(); for(int i=0 ; i<length; i++){ if(data[i] == avcc[0] && data[i+1] == avcc[1] && data[i+2] == avcc[2] && data[i+3] == avcc[3]){ int sps_start = i+3+7;//其中i+3指到avcc的c,再加7跳过6位AVCDecoderConfigurationRecord参数 //sps length and sps data byte[] sps_3gp = new byte[2];//sps length sps_3gp[1] = data[sps_start]; sps_3gp[0] = data[sps_start + 1]; int sps_length = bytes2short(sps_3gp); Log.e(TAG, "sps_length :" + sps_length); sps_start += 2;//skip length SPS = new byte[sps_length]; System.arraycopy(data, sps_start, SPS, 0, sps_length); //save sps FileOutputStream file_out = MainActivity.this.openFileOutput( String.format("%d%d.sps",videoWidth,videoHeight), Context.MODE_PRIVATE); file_out.write(SPS); file_out.close(); //pps length and pps data int pps_start = sps_start + sps_length + 1; byte[] pps_3gp =new byte[2]; pps_3gp[1] = data[pps_start]; pps_3gp[0] =data[pps_start+1]; int pps_length = bytes2short(pps_3gp); Log.e(TAG, "PPS LENGTH:"+pps_length); pps_start+=2; PPS = new byte[pps_length]; System.arraycopy(data, pps_start, PPS,0,pps_length); //Save PPS file_out = MainActivity.this.openFileOutput( String.format("%d%d.pps",videoWidth,videoHeight), Context.MODE_PRIVATE); file_out.write(PPS); file_out.close(); break; } } } //计算长度 public short bytes2short(byte[] b) { short mask=0xff; short temp=0; short res=0; for(int i=0;i<2;i++) { res<<=8; temp=(short)(b[1-i]&mask); res|=temp; } return res; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); if(mMediaRecorderRecording) { releaseMediaRecorder(); try { lss.close(); receiver.close(); sender.close(); } catch (IOException e) { Log.e(TAG, e.toString()); } mMediaRecorderRecording = false; } finish(); } }