diff --git a/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java b/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java index 5f2bd32..c360d81 100644 --- a/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java +++ b/app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java @@ -139,8 +139,10 @@ public class USBCameraActivity extends AppCompatActivity implements CameraDialog mUVCCameraView = (CameraViewInterface) mTextureView; mUVCCameraView.setCallback(this); mCameraHelper = UVCCameraHelper.getInstance(); + mCameraHelper.setDefaultPreviewSize(320,240); mCameraHelper.initUSBMonitor(this, mUVCCameraView, listener); + mCameraHelper.setOnPreviewFrameListener(new AbstractUVCCameraHandler.OnPreViewResultListener() { @Override public void onPreviewResult(byte[] nv21Yuv) { @@ -232,6 +234,7 @@ public class USBCameraActivity extends AppCompatActivity implements CameraDialog Log.i(TAG,"save path:" + path); } }); + break; case R.id.menu_recording: if (mCameraHelper == null || !mCameraHelper.isCameraOpened()) { diff --git a/app/src/main/res/layout/activity_usbcamera.xml b/app/src/main/res/layout/activity_usbcamera.xml index fc839fb..1d7e7b4 100644 --- a/app/src/main/res/layout/activity_usbcamera.xml +++ b/app/src/main/res/layout/activity_usbcamera.xml @@ -21,7 +21,8 @@ android:id="@+id/camera_view" android:layout_below="@id/toolbar" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:layout_centerInParent="true" android:layout_centerHorizontal="true" android:layout_centerVertical="true" /> diff --git a/libusbcamera/src/main/java/com/jiangdg/usbcamera/UVCCameraHelper.java b/libusbcamera/src/main/java/com/jiangdg/usbcamera/UVCCameraHelper.java index 386b54a..44ca652 100644 --- a/libusbcamera/src/main/java/com/jiangdg/usbcamera/UVCCameraHelper.java +++ b/libusbcamera/src/main/java/com/jiangdg/usbcamera/UVCCameraHelper.java @@ -34,8 +34,6 @@ public class UVCCameraHelper { private int previewHeight = 480; public static int MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS; public static int MODE_CONTRAST = UVCCamera.PU_CONTRAST; - //0-YUYV - private static final int PREVIEW_FORMAT = 0; private static UVCCameraHelper mCameraHelper; // USB Manager @@ -135,9 +133,9 @@ public class UVCCameraHelper { mCameraHandler = null; } // initialize camera handler -// cameraView.setAspectRatio(previewWidth / (float)previewHeight); +// mCamViewWrf.get().setAspectRatio(previewWidth / (float)previewHeight); mCameraHandler = UVCCameraHandler.createHandler(mActivityWrf.get(), mCamViewWrf.get(), 2, - previewWidth, previewHeight, PREVIEW_FORMAT); + previewWidth, previewHeight, UVCCamera.FRAME_FORMAT_YUYV); } public void updateResolution(int width, int height) { @@ -150,9 +148,9 @@ public class UVCCameraHelper { mCameraHandler.release(); mCameraHandler = null; } -// cameraView.setAspectRatio(previewWidth / (float)previewHeight); +// mCamViewWrf.get().setAspectRatio(previewWidth / (float)previewHeight); mCameraHandler = UVCCameraHandler.createHandler(mActivityWrf.get(), mCamViewWrf.get(), 2, - previewWidth, previewHeight, PREVIEW_FORMAT); + previewWidth, previewHeight, UVCCamera.FRAME_FORMAT_YUYV); openCamera(mCtrlBlock); startPreview(mCamViewWrf.get()); } @@ -299,4 +297,20 @@ public class UVCCameraHelper { return null; return mCameraHandler.getSupportedPreviewSizes(); } + + public void setDefaultPreviewSize(int defaultWidth,int defaultHeight) { + if(mUSBMonitor != null) { + throw new IllegalStateException("setDefaultPreviewSize should be call before initMonitor"); + } + this.previewWidth = defaultWidth; + this.previewHeight = defaultHeight; + } + + public int getPreviewWidth() { + return previewWidth; + } + + public int getPreviewHeight() { + return previewHeight; + } } diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/UVCCamera.java b/libusbcamera/src/main/java/com/serenegiant/usb/UVCCamera.java index 6dd30e2..2c0e935 100644 --- a/libusbcamera/src/main/java/com/serenegiant/usb/UVCCamera.java +++ b/libusbcamera/src/main/java/com/serenegiant/usb/UVCCamera.java @@ -58,8 +58,8 @@ public class UVCCamera { public static final int PIXEL_FORMAT_YUV = 1; public static final int PIXEL_FORMAT_RGB565 = 2; public static final int PIXEL_FORMAT_RGBX = 3; - public static final int PIXEL_FORMAT_YUV420SP = 4; - public static final int PIXEL_FORMAT_NV21 = 5; // = YVU420SemiPlanar + public static final int PIXEL_FORMAT_YUV420SP = 4; // NV12 + public static final int PIXEL_FORMAT_NV21 = 5; // = YVU420SemiPlanar,NV21,但是保存到jpg颜色失真 //-------------------------------------------------------------------------------- public static final int CTRL_SCANNING = 0x00000001; // D0: Scanning Mode @@ -127,7 +127,7 @@ public class UVCCamera { private UsbControlBlock mCtrlBlock; protected long mControlSupports; // カメラコントロールでサポートしている機能フラグ protected long mProcSupports; // プロセッシングユニットでサポートしている機能フラグ - protected int mCurrentFrameFormat = FRAME_FORMAT_MJPEG; + protected int mCurrentFrameFormat = FRAME_FORMAT_YUYV; protected int mCurrentWidth = DEFAULT_PREVIEW_WIDTH, mCurrentHeight = DEFAULT_PREVIEW_HEIGHT; protected float mCurrentBandwidthFactor = DEFAULT_BANDWIDTH; protected String mSupportedSize; diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java b/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java index c1c0027..5286b42 100644 --- a/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java +++ b/libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java @@ -530,9 +530,9 @@ public abstract class AbstractUVCCameraHandler extends Handler { if ((mUVCCamera == null) || mIsPreviewing) return; try { mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor); - // 获取USB Camera预览数据 - mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); - + // 获取USB Camera预览数据,使用NV21颜色会失真 +// mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); + mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP); } catch (final IllegalArgumentException e) { // try { // // fallback to YUV mode diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandler.java b/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandler.java index 2d01f1c..4403ee8 100644 --- a/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandler.java +++ b/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandler.java @@ -42,7 +42,7 @@ public class UVCCameraHandler extends AbstractUVCCameraHandler { final Activity parent, final CameraViewInterface cameraView, final int width, final int height) { - return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH); + return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_YUYV, UVCCamera.DEFAULT_BANDWIDTH); } /** @@ -58,7 +58,7 @@ public class UVCCameraHandler extends AbstractUVCCameraHandler { final Activity parent, final CameraViewInterface cameraView, final int width, final int height, final float bandwidthFactor) { - return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, bandwidthFactor); + return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_YUYV, bandwidthFactor); } /** @@ -74,7 +74,7 @@ public class UVCCameraHandler extends AbstractUVCCameraHandler { final Activity parent, final CameraViewInterface cameraView, final int encoderType, final int width, final int height) { - return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH); + return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_YUYV, UVCCamera.DEFAULT_BANDWIDTH); } /** diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandlerMultiSurface.java b/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandlerMultiSurface.java index b85aaa0..428908c 100644 --- a/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandlerMultiSurface.java +++ b/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandlerMultiSurface.java @@ -43,7 +43,7 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler { final Activity parent, final CameraViewInterface cameraView, final int width, final int height) { - return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH); + return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_YUYV, UVCCamera.DEFAULT_BANDWIDTH); } /** @@ -59,7 +59,7 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler { final Activity parent, final CameraViewInterface cameraView, final int width, final int height, final float bandwidthFactor) { - return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, bandwidthFactor); + return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_YUYV, bandwidthFactor); } /** @@ -75,7 +75,7 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler { final Activity parent, final CameraViewInterface cameraView, final int encoderType, final int width, final int height) { - return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH); + return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_YUYV, UVCCamera.DEFAULT_BANDWIDTH); } /** diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/H264EncodeConsumer.java b/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/H264EncodeConsumer.java index 01f860d..9310ce1 100644 --- a/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/H264EncodeConsumer.java +++ b/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/H264EncodeConsumer.java @@ -1,10 +1,6 @@ package com.serenegiant.usb.encoder.biz; -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.nio.ByteBuffer; - +import android.annotation.SuppressLint; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecList; @@ -14,7 +10,13 @@ import android.os.Bundle; import android.os.Environment; import android.util.Log; -/** 对YUV视频流进行编码 +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.nio.ByteBuffer; + +/** + * 对YUV视频流进行编码 * Created by jiangdongguo on 2017/5/6. */ @@ -33,19 +35,19 @@ public class H264EncodeConsumer extends Thread { private boolean isExit = false; private boolean isEncoderStart = false; - private MediaFormat mFormat; + private MediaFormat mFormat; private static String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test2.h264"; private BufferedOutputStream outputStream; final int millisPerframe = 1000 / 20; long lastPush = 0; private OnH264EncodeResultListener listener; - private int mWidth ; - private int mHeight ; + private int mWidth; + private int mHeight; private MediaFormat newFormat; private WeakReference mMuxerRef; private boolean isAddKeyFrame = false; - public interface OnH264EncodeResultListener{ + public interface OnH264EncodeResultListener { void onEncodeResult(byte[] data, int offset, int length, long timestamp); } @@ -54,17 +56,13 @@ public class H264EncodeConsumer extends Thread { this.listener = listener; } - public H264EncodeConsumer(){ - - } - - public H264EncodeConsumer(int width,int height){ + public H264EncodeConsumer(int width, int height) { this.mWidth = width; this.mHeight = height; } - public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer){ - this.mMuxerRef = new WeakReference<>(mMuxer); + public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer) { + this.mMuxerRef = new WeakReference<>(mMuxer); Mp4MediaMuxer muxer = mMuxerRef.get(); if (muxer != null && newFormat != null) { muxer.addTrack(newFormat, true); @@ -74,13 +72,10 @@ public class H264EncodeConsumer extends Thread { private ByteBuffer[] inputBuffers; private ByteBuffer[] outputBuffers; - public void setRawYuv(byte[] yuvData,int width,int height){ - if (! isEncoderStart) + public void setRawYuv(byte[] yuvData, int width, int height) { + if (!isEncoderStart) return; - // 根据编码器支持转换颜色空间格式 - // 即 nv21 ---> YUV420sp(21) - // nv21 ---> YUV420p (19) - if(mWidth != width || mHeight != height){ + if (mWidth != width || mHeight != height) { mWidth = width; mHeight = height; return; @@ -96,7 +91,8 @@ public class H264EncodeConsumer extends Thread { Thread.sleep(time / 2); } // 将数据写入编码器 - feedMediaCodecData(yuvData); + + feedMediaCodecData(nv12ToNV21(yuvData, mWidth, mHeight)); if (time > 0) Thread.sleep(time / 2); @@ -106,13 +102,13 @@ public class H264EncodeConsumer extends Thread { } } - private void feedMediaCodecData(byte[] data){ - if (! isEncoderStart) + private void feedMediaCodecData(byte[] data) { + if (!isEncoderStart) return; int bufferIndex = -1; - try{ + try { bufferIndex = mMediaCodec.dequeueInputBuffer(0); - }catch (IllegalStateException e){ + } catch (IllegalStateException e) { e.printStackTrace(); } if (bufferIndex >= 0) { @@ -129,15 +125,16 @@ public class H264EncodeConsumer extends Thread { } } - public void exit(){ + public void exit() { isExit = true; } + @SuppressLint("WrongConstant") @Override public void run() { - if(!isEncoderStart){ + if (!isEncoderStart) { startMediaCodec(); - } + } // 休眠200ms,等待音频线程开启 // 否则视频第一秒会卡住 try { @@ -147,7 +144,7 @@ public class H264EncodeConsumer extends Thread { } // 如果编码器没有启动或者没有图像数据,线程阻塞等待 - while(!isExit){ + while (!isExit) { MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = 0; byte[] mPpsSps = new byte[0]; @@ -162,7 +159,7 @@ public class H264EncodeConsumer extends Thread { } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { synchronized (H264EncodeConsumer.this) { newFormat = mMediaCodec.getOutputFormat(); - if(mMuxerRef != null){ + if (mMuxerRef != null) { Mp4MediaMuxer muxer = mMuxerRef.get(); if (muxer != null) { muxer.addTrack(newFormat, true); @@ -202,35 +199,35 @@ public class H264EncodeConsumer extends Thread { if (sync) { System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length); outputBuffer.get(h264, mPpsSps.length, bufferInfo.size); - if(listener != null){ - listener.onEncodeResult(h264, 0,mPpsSps.length + bufferInfo.size, bufferInfo.presentationTimeUs / 1000); + if (listener != null) { + listener.onEncodeResult(h264, 0, mPpsSps.length + bufferInfo.size, bufferInfo.presentationTimeUs / 1000); } // 添加视频流到混合器 - if(mMuxerRef != null){ + if (mMuxerRef != null) { Mp4MediaMuxer muxer = mMuxerRef.get(); if (muxer != null) { muxer.pumpStream(outputBuffer, bufferInfo, true); } isAddKeyFrame = true; } - if(DEBUG) - Log.i(TAG,"关键帧 h264.length = "+h264.length+";mPpsSps.length="+mPpsSps.length + if (DEBUG) + Log.i(TAG, "关键帧 h264.length = " + h264.length + ";mPpsSps.length=" + mPpsSps.length + " bufferInfo.size = " + bufferInfo.size); } else { outputBuffer.get(h264, 0, bufferInfo.size); - if(listener != null){ - listener.onEncodeResult(h264, 0,bufferInfo.size, bufferInfo.presentationTimeUs / 1000); + if (listener != null) { + listener.onEncodeResult(h264, 0, bufferInfo.size, bufferInfo.presentationTimeUs / 1000); } // 添加视频流到混合器 - if(isAddKeyFrame && mMuxerRef != null){ + if (isAddKeyFrame && mMuxerRef != null) { Mp4MediaMuxer muxer = mMuxerRef.get(); if (muxer != null) { muxer.pumpStream(outputBuffer, bufferInfo, true); } } - if(DEBUG) - Log.i(TAG,"普通帧 h264.length = "+h264.length+ " bufferInfo.size = " + bufferInfo.size); + if (DEBUG) + Log.i(TAG, "普通帧 h264.length = " + h264.length + " bufferInfo.size = " + bufferInfo.size); } mMediaCodec.releaseOutputBuffer(outputBufferIndex, false); } @@ -261,7 +258,6 @@ public class H264EncodeConsumer extends Thread { mMediaCodec.start(); - isEncoderStart = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + 1) { inputBuffers = outputBuffers = null; @@ -277,25 +273,27 @@ public class H264EncodeConsumer extends Thread { } } - private void stopMediaCodec(){ + private void stopMediaCodec() { isEncoderStart = false; - if(mMediaCodec != null){ + if (mMediaCodec != null) { mMediaCodec.stop(); mMediaCodec.release(); - Log.d(TAG,"关闭视频编码器"); + Log.d(TAG, "关闭视频编码器"); } } private static final int FRAME_RATE = 15; private static final float BPP = 0.50f; + private int calcBitRate() { - final int bitrate = (int)(BPP * FRAME_RATE * mWidth * mHeight); + final int bitrate = (int) (BPP * FRAME_RATE * mWidth * mHeight); Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f)); return bitrate; } /** * select the first codec that match a specific MIME type + * * @param mimeType * @return null if no codec matched */ @@ -307,7 +305,7 @@ public class H264EncodeConsumer extends Thread { for (int i = 0; i < numCodecs; i++) { final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); - if (!codecInfo.isEncoder()) { // skipp decoder + if (!codecInfo.isEncoder()) { // skipp decoder continue; } // select first codec that match a specific MIME type and color format @@ -327,6 +325,7 @@ public class H264EncodeConsumer extends Thread { /** * select color format available on specific codec and we can use. + * * @return 0 if no colorFormat is matched */ protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) { @@ -356,12 +355,14 @@ public class H264EncodeConsumer extends Thread { * color formats that we can use in this class */ protected static int[] recognizedFormats; + static { - recognizedFormats = new int[] { -// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar, + recognizedFormats = new int[]{ +// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar, +// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar, +// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar, MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, -// MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface, }; } @@ -374,4 +375,87 @@ public class H264EncodeConsumer extends Thread { } return false; } + + + private byte[] nv21ToI420(byte[] data, int width, int height) { + byte[] ret = new byte[width * height * 3 / 2]; + int total = width * height; + + ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量 + ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量 + ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量 + // NV21 YYYYYYYY VUVU + bufferY.put(data, 0, total); + for (int i = total; i < data.length; i += 2) { + bufferV.put(data[i]); + bufferU.put(data[i + 1]); + } + + return ret; + } + + private byte[] nv12ToI420(byte[] data, int width, int height) { + byte[] ret = new byte[width * height * 3 / 2]; + int total = width * height; + + ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量 + ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量 + ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量 + + // NV12 YYYYYYYY UVUV + bufferY.put(data, 0, total); + for (int i = total; i < data.length; i += 2) { + bufferU.put(data[i]); + bufferV.put(data[i + 1]); + } + return ret; + } + + private byte[] nv12ToNv21(byte[] data, int width, int height) { + byte[] ret = new byte[width * height * 3 / 2]; + int total = width * height; + + ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量 + ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量 + ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量 + + // NV12 YYYYYYYY UVUV + bufferY.put(data, 0, total); + for (int i = total; i < data.length; i += 2) { + bufferU.put(data[i]); + bufferV.put(data[i + 1]); + } + return ret; + } + + // YYYYYYYY UVUV(nv21)--> YYYYYYYY VUVU(nv12) + private byte[] nv21ToNV12(byte[] nv21, int width, int height) { + byte[] ret = new byte[width * height * 3 /2]; + int framesize = width * height; + int i = 0, j = 0; + // 拷贝Y分量 + System.arraycopy(nv21, 0,ret , 0, framesize); + // 拷贝UV分量 + for (j = framesize; j < nv21.length; j += 2) { + ret[j+1] = nv21[j+1]; + ret[j] = nv21[j]; + } + return ret; + } + + // YYYYYYYY UVUV(nv12)--> YYYYYYYY VUVU(nv21) + private byte[] nv12ToNV21(byte[] nv12, int width, int height) { + byte[] ret = new byte[width * height * 3 /2]; + int framesize = width * height; + int i = 0, j = 0; + // 拷贝Y分量 + System.arraycopy(nv12, 0,ret , 0, framesize); + // 拷贝UV分量 + for (j = framesize; j < nv12.length; j += 2) { + ret[j] = nv12[j+1]; + ret[j+1] = nv12[j]; + } + return ret; + } + }