Android背景视频录制

时间:2015-05-08 11:29:23

标签: android video encoding h.264 decoding

我正在为自定义Android平台开发录制服务。当应用程序启动时,它将开始在后台录制视频。不幸的是,这个应用程序在硬件上运行,阻止我使用视频录制。 我对此问题的解决方案是拍摄图像并将其保存在循环缓冲区中,当事件发生时,它将停止将图像提供给缓冲区并将它们放在视频中。

我遇到的问题是,当我将图片保存到视频时,我只会收到嘈杂的green screen

我的代码基于此示例:Using MediaCodec to save series of images as Video

注意:我也无法使用MediaMux,我正在开发 API级别< 18

我将引导您完成我采取的步骤。在创建服务时我只需打开相机,我就在SurfaceTexture上设置预览,并在调用PreviewCallback时将图像添加到缓冲区。

private Camera mCamera;
private String mTimeStamp;
SurfaceTexture mSurfaceTexture;

private CircularBuffer<ByteArrayOutputStream> mCircularBuffer;
private static final int MAX_BUFFER_SIZE = 200;
private int mWidth = 720;
private int mHeight = 480;

@Override
public void onCreate() {
    try {
        mCircularBuffer = new CircularBuffer(MAX_BUFFER_SIZE);
        mTimeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        mSurfaceTexture = new SurfaceTexture(10);

        mCamera = getCameraInstance();
        Parameters parameters = mCamera.getParameters();
        parameters.setJpegQuality(20);
        parameters.setPictureSize(mWidth, mHeight);
        mCamera.setParameters(parameters);
        mCamera.setPreviewTexture(mSurfaceTexture);
        mCamera.startPreview();
        mCamera.setPreviewCallback(mPreviewCallback);
    } catch (IOException e) {
        Log.d(TAG, "IOException: " + e.getMessage());
    } catch (Exception e) {
        Log.d(TAG, "Exception: " + e.getMessage());
    }
}

private PreviewCallback mPreviewCallback = new PreviewCallback() {

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, mWidth, mHeight, null);
        Rect rectangle = new Rect(0, 0, mWidth, mHeight);
        yuvImage.compressToJpeg(rectangle, 20, out);
        mCircularBuffer.add(out);
    }
};

所有这一切都有效,当我将字节数组转换为jpg时,它们都是正确的图像文件。

现在,当事件发生时,服务将被销毁,最后200张图片需要放在彼此后面并转换为mp4。我首先根据上面链接中提供的代码将其保存到H264。然后使用mp4parser将该文件转换为mp4。

@Override
public void onDestroy() {
    super.onDestroy();
    mCamera.stopPreview();
    saveFileToH264("video/avc");
    convertH264ToMP4();
}

private void saveFileToH264(String MIMETYPE) {
    MediaCodec codec = MediaCodec.createEncoderByType(MIMETYPE);
    MediaFormat mediaFormat = null;

    int height = mCamera.getParameters().getPictureSize().height;
    int width = mCamera.getParameters().getPictureSize().width;

    Log.d(TAG, height + ", " + width);

    mediaFormat = MediaFormat.createVideoFormat(MIMETYPE, width, height);

    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1000000);
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
    codec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    codec.start();

    ByteBuffer[] inputBuffers = codec.getInputBuffers();
    ByteBuffer[] outputBuffers = codec.getOutputBuffers();
    boolean sawInputEOS = false;
    int inputBufferIndex = -1, outputBufferIndex = -1;
    BufferInfo info = null;

    try {
        File file = new File("/sdcard/output.h264");
        FileOutputStream fstream2 = new FileOutputStream(file);
        DataOutputStream dos = new DataOutputStream(fstream2);

        // loop through buffer and get image output streams
        for (int i = 0; i < MAX_BUFFER_SIZE; i++) {
            ByteArrayOutputStream out = mCircularBuffer.getData(i);
            byte[] dat = out.toByteArray();

            long WAITTIME = 50;

            inputBufferIndex = codec.dequeueInputBuffer(WAITTIME);
            int bytesread = MAX_BUFFER_SIZE - 1 - i;

            int presentationTime = 0;

            if (bytesread <= 0)
                sawInputEOS = true;

            if (inputBufferIndex >= 0) {
                if (!sawInputEOS) {
                    int samplesiz = dat.length;
                    inputBuffers[inputBufferIndex].put(dat);
                    codec.queueInputBuffer(inputBufferIndex, 0, samplesiz, presentationTime, 0);
                    presentationTime += 100;

                    info = new BufferInfo();
                    outputBufferIndex = codec.dequeueOutputBuffer(info, WAITTIME);
                    Log.i("BATA", "outputBufferIndex=" + outputBufferIndex);
                    if (outputBufferIndex >= 0) {
                        byte[] array = new byte[info.size];
                        outputBuffers[outputBufferIndex].get(array);

                        if (array != null) {
                            try {
                                dos.write(array);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        codec.releaseOutputBuffer(outputBufferIndex, false);
                        inputBuffers[inputBufferIndex].clear();
                        outputBuffers[outputBufferIndex].clear();

                        if (sawInputEOS)
                            break;
                    }
                } else {
                    codec.queueInputBuffer(inputBufferIndex, 0, 0, presentationTime,
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM);

                    info = new BufferInfo();
                    outputBufferIndex = codec.dequeueOutputBuffer(info, WAITTIME);

                    if (outputBufferIndex >= 0) {
                        byte[] array = new byte[info.size];
                        outputBuffers[outputBufferIndex].get(array);

                        if (array != null) {
                            try {
                                dos.write(array);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        codec.releaseOutputBuffer(outputBufferIndex, false);
                        inputBuffers[inputBufferIndex].clear();
                        outputBuffers[outputBufferIndex].clear();
                        break;
                    }
                }
            }
        }
        codec.flush();

        try {
            fstream2.close();
            dos.flush();
            dos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        codec.stop();
        codec.release();
        codec = null;
    } catch (FileNotFoundException e) {
        Log.d(TAG, "File not found: " + e.getMessage());
    } catch (Exception e) {
        Log.d(TAG, "Exception: " + e.getMessage());
    }
}

private void convertH264ToMP4() {
    try {
        DataSource videoFile = new FileDataSourceImpl("/sdcard/output.h264");

        H264TrackImpl h264Track = new H264TrackImpl(videoFile, "eng", 5, 1);
        // 5fps. you can play with timescale and timetick to get non integer fps, 23.967 is
        // 24000/1001

        Movie movie = new Movie();

        movie.addTrack(h264Track);

        Container out = new DefaultMp4Builder().build(movie);

        FileOutputStream fos = new FileOutputStream(new File("/sdcard/output.mp4"));
        out.writeContainer(fos.getChannel());
        fos.flush();
        fos.close();
        Log.d(TAG, "Video saved to sdcard");
    } catch (Exception e) {
        Log.d(TAG, "No file was saved");
    }
}

我很确定问题出在saveFileToH264代码中。我在上面提供的链接上读过一篇文章,这可能是一个跨步和/或对齐问题(?)。我没有编码/解码的经验,所以我不知道如何解决这个问题。如果有人能提供帮助,那将非常感激!

注意:我知道代码不是最优的,我仍然需要添加更多支票和诸如此类的东西,但我首先想要获得一个有效的视频。

0 个答案:

没有答案