先給大家說下軟件的功能吧.
拿出android手機然後採集實時的圖片數據,並對這些採集的圖片數據進行H264編碼,然後根據協議上傳到公司的服務器上。
那麼現在大家分析下,如果要實現這些功能,有哪些技術瓶頸:
1.Camera參數設置,設置不正確,無法看到視頻,而且最主要的是有的時候你用的設置在公司的測試機上運行正常在其他手機上就看不到視頻了。
2.如何獲取採集的實時圖片數據
3.攝像頭打開後大家發現沒圖片是倒置的,原因何在,如何解決倒置的問題呢?
4.H264壓縮編碼
5.H264編碼後的圖片顯示爲灰白色的,爲什麼呢?
請看我下面的代碼,我將爲大家詳細的解釋上面的情況出現的理由和解決方案:
-
package com.camera.CameraPreview;
-
-
import java.io.IOException;
-
-
import android.app.Activity;
-
import android.content.pm.ActivityInfo;
-
import android.hardware.Camera;
-
import android.hardware.Camera.PreviewCallback;
-
import android.os.Bundle;
-
import android.view.SurfaceHolder;
-
import android.view.Window;
-
import android.view.SurfaceHolder.Callback;
-
import android.view.SurfaceView;
-
-
public class PreviewActivity extends Activity implements Callback, PreviewCallback {
-
-
-
private SurfaceView mSurface ;
-
-
private SurfaceHolder mHolder;
-
-
-
/** Called when the activity is first created. */
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
-
requestWindowFeature(Window.FEATURE_NO_TITLE);
-
//and set the preview to landscape mode.
-
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
-
-
setContentView(R.layout.main);
-
-
mSurface = (SurfaceView)this.findViewById(R.id.SurfacePrewView);
-
mHolder = mSurface.getHolder();
-
mHolder.addCallback(this);
-
-
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//該代碼放在addCallback前還是addCallback後對代碼Camera有沒有影響
-
}
-
-
-
@Override
-
public void surfaceChanged(SurfaceHolder holder, int format, int width,
-
int height) {
-
Camera.Parameters parame = mCamera.getParameters();
-
parame.setPreviewSize(320,480);
-
-
mCamera.startPreview();
-
}
-
-
-
-
private Camera mCamera ;
-
-
@Override
-
public void surfaceCreated(SurfaceHolder holder) {
-
if(mCamera!=null)
-
{
-
mCamera.release();
-
mCamera = null ;
-
}
-
//<uses-permission android:name="android.permission.CAMERA" />
-
//必須添加此權限,否則DDMS便會提示你:can not connect to Camera service
-
mCamera = Camera.open();
-
-
try {
-
mCamera.setPreviewDisplay(mHolder);//此代碼必須添加視頻纔會顯示出來
-
mCamera.setPreviewCallback(this);
-
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
-
-
@Override
-
public void surfaceDestroyed(SurfaceHolder holder) {
-
if(mCamera != null)
-
{
-
mCamera.release();
-
mCamera = null ;
-
}
-
}
-
-
-
@Override
-
public void onPreviewFrame(byte[] data, Camera camera) {
-
// TODO Auto-generated method stub
-
-
//get the prew frame here,the data of default is YUV420_SP
-
//you should change YUV420_SP to YUV420_P
-
-
}
-
-
-
-
- }
1. 爲了確保在所有的平臺上你都能夠看到畫面,SurfaceCreated() SurfaceChanged() SurfaceDestory()請一定要按照上面的寫法寫:
SurfaceCreated中注意
mCamera.setPreviewDisplay(mHolder);//此代碼必須添加視頻纔會顯示出來
mCamera.setPreviewCallback(this);//設置預覽幀的接口,就是通過這個接口,我們來獲得預覽幀的數據的
2.圖像是倒的如何解決?
//and set the preview to landscape mode.
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
在SetContentView之前調用此代碼,理由大家自己看API文檔,解釋的麻煩.
3.public void onPreviewFrame(byte[] data, Camera camera)
該接口爲獲取視頻預覽幀的接口,傳遞進來的data,默認是YUV420SP的,H264編碼的源默認是YUV420的,將他們轉換一下再編碼圖像就正常了;
轉換函數:
-
private byte[] changeYUV420SP2P(byte[]data,int length){
-
int width = 176;
-
int height = 144;
-
byte[] str = new byte[length];
-
System.arraycopy(data, 0, str, 0,width*height);
-
-
int strIndex = width*height;
-
-
for(int i = width*height+1; i < length ;i+=2)
-
{
-
str[strIndex++] = data[i];
-
}
-
-
-
for(int i = width*height;i<length;i+=2)
-
{
-
str[strIndex++] = data[i];
-
}
-
return str;
- }
4.編解碼庫
關於Android的編解碼庫因爲涉及到公司的版權問題,請大家原諒我無法給出庫了,不過這些都是從X264中提取出來的,是在Windows主機上搭建了一個linux的虛擬機,然後在linux虛擬機下編寫的NDK開源庫,X264這塊因爲最近一直在弄Sipdroid的代碼,暫時還無法騰出精力來管理