package com.jutong.live.pusher; import java.util.Iterator; import java.util.List; import android.app.Activity; import android.content.Context; import android.graphics.ImageFormat; import android.hardware.Camera; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.PreviewCallback; import android.hardware.Camera.Size; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import com.jutong.live.jni.PusherNative; import com.jutong.live.param.VideoParam; public class VideoPusher extends Pusher implements Callback, PreviewCallback { private final static String TAG = "VideoPusher"; private boolean mPreviewRunning; private Camera mCamera; private SurfaceHolder mHolder; private VideoParam mParam; private byte[] buffer; private byte[] raw; private Activity mActivity; private int screen; private final static int SCREEN_PORTRAIT = 0; private final static int SCREEN_LANDSCAPE_LEFT = 90; private final static int SCREEN_LANDSCAPE_RIGHT = 270; public VideoPusher(Activity activity, SurfaceHolder surfaceHolder, VideoParam param, PusherNative pusherNative) { super(pusherNative); mActivity = activity; mParam = param; mHolder = surfaceHolder; surfaceHolder.addCallback(this); } @Override public void startPusher() { startPreview(); mPusherRuning = true; } @Override public void stopPusher() { mPusherRuning = false; } @Override public void release() { mPusherRuning = false; mActivity = null; stopPreview(); } public void switchCamera() { if (mParam.getCameraId() == CameraInfo.CAMERA_FACING_BACK) { mParam.setCameraId(CameraInfo.CAMERA_FACING_FRONT); } else { mParam.setCameraId(CameraInfo.CAMERA_FACING_BACK); } stopPreview(); startPreview(); } private void stopPreview() { if (mPreviewRunning && mCamera != null) { mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; mPreviewRunning = false; } } @SuppressWarnings("deprecation") private void startPreview() { if (mPreviewRunning) { return; } try { mCamera = Camera.open(mParam.getCameraId()); Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewFormat(ImageFormat.NV21); setPreviewSize(parameters); setPreviewFpsRange(parameters); setPreviewOrientation(parameters); mCamera.setParameters(parameters); buffer = new byte[mParam.getWidth() * mParam.getHeight() * 3 / 2]; raw = new byte[mParam.getWidth() * mParam.getHeight() * 3 / 2]; mCamera.addCallbackBuffer(buffer); mCamera.setPreviewCallbackWithBuffer(this); mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); mPreviewRunning = true; } catch (Exception ex) { ex.printStackTrace(); if (null != mListener) { mListener.onErrorPusher(-100); } } } private void setPreviewSize(Camera.Parameters parameters) { List<Integer> supportedPreviewFormats = parameters .getSupportedPreviewFormats(); for (Integer integer : supportedPreviewFormats) { System.out.println("支持:" + integer); } List<Size> supportedPreviewSizes = parameters .getSupportedPreviewSizes(); Size size = supportedPreviewSizes.get(0); Log.d(TAG, "支持 " + size.width + "x" + size.height); int m = Math.abs(size.height * size.width - mParam.getHeight() * mParam.getWidth()); supportedPreviewSizes.remove(0); Iterator<Size> iterator = supportedPreviewSizes.iterator(); while (iterator.hasNext()) { Size next = iterator.next(); Log.d(TAG, "支持 " + next.width + "x" + next.height); int n = Math.abs(next.height * next.width - mParam.getHeight() * mParam.getWidth()); if (n < m) { m = n; size = next; } } mParam.setHeight(size.height); mParam.setWidth(size.width); parameters.setPreviewSize(mParam.getWidth(), mParam.getHeight()); Log.d(TAG, "预览分辨率 width:" + size.width + " height:" + size.height); } private void setPreviewFpsRange(Camera.Parameters parameters) { int range[] = new int[2]; parameters.getPreviewFpsRange(range); Log.d(TAG, "预览帧率 fps:" + range[0] + " - " + range[1]); } private void setPreviewOrientation(Camera.Parameters parameters) { CameraInfo info = new CameraInfo(); Camera.getCameraInfo(mParam.getCameraId(), info); int rotation = mActivity.getWindowManager().getDefaultDisplay() .getRotation(); screen = 0; switch (rotation) { case Surface.ROTATION_0: screen = SCREEN_PORTRAIT; mNative.setVideoOptions(mParam.getHeight(), mParam.getWidth(), mParam.getBitrate(), mParam.getFps()); break; case Surface.ROTATION_90: // 横屏 左边是头部(home键在右边) screen = SCREEN_LANDSCAPE_LEFT; mNative.setVideoOptions(mParam.getWidth(), mParam.getHeight(), mParam.getBitrate(), mParam.getFps()); break; case Surface.ROTATION_180: screen = 180; break; case Surface.ROTATION_270:// 横屏 头部在右边 screen = SCREEN_LANDSCAPE_RIGHT; mNative.setVideoOptions(mParam.getWidth(), mParam.getHeight(), mParam.getBitrate(), mParam.getFps()); break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + screen) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - screen + 360) % 360; } mCamera.setDisplayOrientation(result); // // 如果是竖屏 设置预览旋转90度,并且由于回调帧数据也需要旋转所以宽高需要交换 // if (mContext.getResources().getConfiguration().orientation == // Configuration.ORIENTATION_PORTRAIT) { // mNative.setVideoOptions(mParam.getHeight(), mParam.getWidth(), // mParam.getBitrate(), mParam.getFps()); // parameters.set("orientation", "portrait"); // mCamera.setDisplayOrientation(90); // } else { // mNative.setVideoOptions(mParam.getWidth(), mParam.getHeight(), // mParam.getBitrate(), mParam.getFps()); // parameters.set("orientation", "landscape"); // mCamera.setDisplayOrientation(0); // } } @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mHolder = holder; stopPreview(); startPreview(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { stopPreview(); } @Override public void onPreviewFrame(byte[] data, Camera camera) { if (mPusherRuning) { switch (screen) { case SCREEN_PORTRAIT: portraitData2Raw(data); break; case SCREEN_LANDSCAPE_LEFT: raw = data; break; case SCREEN_LANDSCAPE_RIGHT: landscapeData2Raw(data); break; } mNative.fireVideo(raw); } camera.addCallbackBuffer(buffer); } private void landscapeData2Raw(byte[] data) { int width = mParam.getWidth(), height = mParam.getHeight(); int y_len = width * height; int k = 0; // y数据倒叙插入raw中 for (int i = y_len - 1; i > -1; i--) { raw[k] = data[i]; k++; } // System.arraycopy(data, y_len, raw, y_len, uv_len); // v1 u1 v2 u2 // v3 u3 v4 u4 // 需要转换为: // v4 u4 v3 u3 // v2 u2 v1 u1 int maxpos = data.length - 1; int uv_len = y_len >> 2; // 4:1:1 for (int i = 0; i < uv_len; i++) { int pos = i << 1; raw[y_len + i * 2] = data[maxpos - pos - 1]; raw[y_len + i * 2 + 1] = data[maxpos - pos]; } } private void portraitData2Raw(byte[] data) { // if (mContext.getResources().getConfiguration().orientation != // Configuration.ORIENTATION_PORTRAIT) { // raw = data; // return; // } int width = mParam.getWidth(), height = mParam.getHeight(); int y_len = width * height; int uvHeight = height >> 1; // uv数据高为y数据高的一半 int k = 0; if (mParam.getCameraId() == CameraInfo.CAMERA_FACING_BACK) { for (int j = 0; j < width; j++) { for (int i = height - 1; i >= 0; i--) { raw[k++] = data[width * i + j]; } } for (int j = 0; j < width; j += 2) { for (int i = uvHeight - 1; i >= 0; i--) { raw[k++] = data[y_len + width * i + j]; raw[k++] = data[y_len + width * i + j + 1]; } } } else { for (int i = 0; i < width; i++) { int nPos = width - 1; for (int j = 0; j < height; j++) { raw[k] = data[nPos - i]; k++; nPos += width; } } for (int i = 0; i < width; i += 2) { int nPos = y_len + width - 1; for (int j = 0; j < uvHeight; j++) { raw[k] = data[nPos - i - 1]; raw[k + 1] = data[nPos - i]; k += 2; nPos += width; } } } } }