java - 媒体编解码器工作不顺利

标签 java android video media android-mediacodec

使用这个网址,我在下面编写了代码将 onpreviewframe 数据编码为 mp4 视频,并且我使用了一个线程很好地完成了这项工作,但似乎它无法正常工作。

private void initCodec() {
    String root =  Environment.getExternalStorageDirectory().toString();
    File myDir = new File(root + "/Vocalist");
    if(!myDir.exists()) {
        myDir.mkdirs();
    }
    try {
        File file = new File (myDir, "myVideo.mp4");
        if(file.exists()){
            file.delete();
        }
        fos = new FileOutputStream(file, false);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }try {
        mMediaCodec = MediaCodec.createEncoderByType("video/avc");
    }
    catch (Exception e){

    }
    MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc",
            320,
            240);
    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 500000);
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
    mMediaCodec.configure(mediaFormat,
            null,
            null,
            MediaCodec.CONFIGURE_FLAG_ENCODE);
    mMediaCodec.start();
    inputBuffers = mMediaCodec.getInputBuffers();
    outputBuffers = mMediaCodec.getOutputBuffers();

}

 private synchronized void encode(byte[] dataInput)
{
    byte[] data = dataInput;
    inputBuffers = mMediaCodec.getInputBuffers();// here changes
    outputBuffers = mMediaCodec.getOutputBuffers();

    int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);

    if (inputBufferIndex >= 0) {
        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
        inputBuffer.clear();
        inputBuffer.put(data);
        mMediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
    } else {
        return;
    }

    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
    Log.i("tag", "outputBufferIndex-->" + outputBufferIndex);
    do {
        if (outputBufferIndex >= 0) {
            ByteBuffer outBuffer = outputBuffers[outputBufferIndex];
            System.out.println("buffer info-->" + bufferInfo.offset + "--"
                    + bufferInfo.size + "--" + bufferInfo.flags + "--"
                    + bufferInfo.presentationTimeUs);
            byte[] outData = new byte[bufferInfo.size];
            outBuffer.get(outData);
            try {
                if (bufferInfo.offset != 0) {
                    fos.write(outData, bufferInfo.offset, outData.length
                            - bufferInfo.offset);
                } else {
                    fos.write(outData, 0, outData.length);
                }
                fos.flush();
                Log.i("camera", "out data -- > " + outData.length);
                mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo,
                        0);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            outputBuffers = mMediaCodec.getOutputBuffers();
        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            MediaFormat format = mMediaCodec.getOutputFormat();
        }
    } while (outputBufferIndex >= 0);
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

        if (mHolder.getSurface() == null) {
            return;
        }

        try {

            initCodec();
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(final byte[] bytes, Camera camera) {
                    if (recording == true) {
                        if(mThread.isAlive())
                            encode(bytes);

                    }
                }
            });






        } catch (Exception e) {
            Log.d("TAG", "Error starting camera preview: " + e.getMessage());
        }
    }
}
  public void newOpenCamera() {
    if (mThread == null) {
        mThread = new CameraHandlerThread();
    }

    synchronized (mThread) {
        mThread.openCamera();
    }
}

private static void oldOpenCamera() {
    try {
        c = Camera.open(1);
        Camera.Parameters parameters = c.getParameters();
        parameters.set("orientation", "portrait");
        parameters.setJpegQuality(100);
        parameters.setPreviewFormat(ImageFormat.NV21);
        parameters.setPreviewSize(320, 240);
        c.setParameters(parameters);
    }
    catch (RuntimeException e) {
        Log.e("camera", "failed to open front camera");
    }
}

public CameraHandlerThread mThread = null;
public static class CameraHandlerThread extends HandlerThread {
    Handler mHandler = null;


    CameraHandlerThread() {
        super("CameraHandlerThread");
        start();
        mHandler = new Handler(getLooper());
    }

    synchronized void notifyCameraOpened() {
        notify();

    }

    public void openCamera() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                oldOpenCamera();
                notifyCameraOpened();
            }
        });
    }
}

我将 onpreviewframe 数据转换为视频,但在第一秒后视频播放不流畅。我该怎么办?

最佳答案

首先,您没有使用帧转发计时信息:

mMediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0)

因此,当您使缓冲区出列时,您的 BufferInfo.presentationTimeUs 将始终为零。

其次,您似乎没有使用 MediaMuxer,这意味着您只是将原始 H.264 流写入文件。这不是“.mp4”;它根本不包括时间信息。许多视频播放器甚至不知道如何处理普通的 H.264。

将文件包装为 .mp4,并使用相机的帧定时,应该会产生更好的结果。

您的代码结构似乎假设它可以提供一帧输入并获得一帧输出,但情况并非总是如此。您希望保持输入已满,并在输出可用时耗尽输出。

您可以在 bigflake 上找到更多信息和一些示例代码并在 Grafika .

关于java - 媒体编解码器工作不顺利,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36102488/

相关文章:

python - ffmpeg 将输出保存到 python 变量并在不写入多个文件的情况下进行进一步的 ffmpeg 操作

java - Shanks 算法在某组数字上神秘地失败了

java - 主服务线程上的 BlockingQueue

ruby - 我可以以编程方式将 Webm 文件中的音频插入另一个 Webm 文件吗?

android - 如何禁用以编程方式长按主页按钮启动谷歌?

java - 在 java 文件中将 CheckBox View 定义为全局变量会使应用程序在 Android Studio 中终止

Android OpenGL 视频捕获

java - 包含排除原理中递归函数对其自身求和

java - 什么时候必须在方法声明中使用 "throws"字?

java - 创建未知数量的 Java 节点