使用MediaCodec进行转码会导致绿色和混乱的视频

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用MediaCodec进行转码会导致绿色和混乱的视频相关的知识,希望对你有一定的参考价值。

我试图通过使用MediaCodec解码和重新编码视频来减少视频文件的大小。但是,多路复用器生成的新视频文件都是messed up。你能告诉我我做错了什么吗?

我的代码是从这个CTS修改的,有两个主要区别:

  1. 我在异步模式下使用编解码器与回调。
  2. 我没有使用辅助类,而是将encoder.createInputSurface()生成的表面直接传递给decoder.configure()。

我试图通过将surfaceView.getHolder()。getSurface()传递给decoder.configure()将视频解码为SurfaceView,并正确播放视频。我认为编码器/复用器部件可能有问题。

这是我的代码:

解码器回调:

private class VideoDecodeCallBack extends MediaCodec.Callback {

    @Override
    public void onInputBufferAvailable(MediaCodec codec, int index) {
        ByteBuffer buffer = codec.getInputBuffer(index);
        if (extractorDone) {
            return;
        }

        int size = videoExtractor.readSampleData(buffer, 0);
        long pts = videoExtractor.getSampleTime();
        if (size >= 0) {
            codec.queueInputBuffer(index, 0, size, pts, videoExtractor.getSampleFlags());
        }
        extractorDone = !videoExtractor.advance();
        if (extractorDone) {
            codec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
        }
    }

    @Override
    public void onOutputBufferAvailable(
            MediaCodec codec, int index, MediaCodec.BufferInfo info) {
        if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
            codec.releaseOutputBuffer(index, false);
            return;
        }

        boolean render = info.size != 0;
        codec.releaseOutputBuffer(index, render);

        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
            videoEncoder.signalEndOfInputStream();
        }

    }

    @Override
    public void onError(
            MediaCodec codec, MediaCodec.CodecException e) {

    }

    @Override
    public void onOutputFormatChanged(
            MediaCodec codec, MediaFormat format) {
    }

编码器回调:

private class VideoEncodeCallBack extends MediaCodec.Callback {

    @Override
    public void onInputBufferAvailable(MediaCodec codec, int index) {
    }

    @Override
    public void onOutputBufferAvailable(
            MediaCodec codec, int index, MediaCodec.BufferInfo info) {
        ByteBuffer buffer = codec.getOutputBuffer(index);

        if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
            codec.releaseOutputBuffer(index, false);
            return;
        }

        if (info.size != 0) {
            muxer.writeSampleData(outputVideoTrack, buffer, info);
        }

        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
            muxer.stop();
            muxer.release();
        }

        codec.releaseOutputBuffer(index, false);
    }

    @Override
    public void onError(
            MediaCodec codec, MediaCodec.CodecException e) {

    }

    @Override
    public void onOutputFormatChanged(
            MediaCodec codec, MediaFormat format) {
        outputVideoTrack = muxer.addTrack(format);
        muxer.start();
    }
}

我如何配置编解码器:

public void configure(String input, String output) throws IOException {
    // Extractors
    videoExtractor = new MediaExtractor();
    videoExtractor.setDataSource(input);

    int videoTrack = getAndSelectVideoTrackIndex(videoExtractor);

    MediaFormat videoFormat = videoExtractor.getTrackFormat(videoTrack);
    String mime = videoFormat.getString(MediaFormat.KEY_MIME);

    // Codecs
    videoDecoder = MediaCodec.createDecoderByType(mime);
    videoEncoder = MediaCodec.createEncoderByType(mime);

    videoDecoder.setCallback(new VideoDecodeCallBack(), videoDecodeThreadHandler);
    videoEncoder.setCallback(new VideoEncodeCallBack(), videoEncodeThreadHandler);

    videoEncoder.configure(createOutputFormat(), null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    Surface surface = videoEncoder.createInputSurface();
    videoDecoder.configure(videoFormat, surface, null, 0);

    // Muxer
    muxer = new MediaMuxer(output, OutputFormat.MUXER_OUTPUT_MPEG_4);
}

所需的输出格式:

private static MediaFormat createOutputFormat() {
    MediaFormat result = MediaFormat.createVideoFormat("video/avc", 540, 960);
    result.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    result.setInteger(MediaFormat.KEY_BIT_RATE, 1300000);
    result.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
    result.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
    return result;
}

完整的代码是here

答案

我也有这个问题。我的猜测是,当解码器直接渲染到编码器的输入表面时,android无法重新缩放帧。

解决方案可以是使用openGL进行重新缩放。

这个项目做到了。 https://github.com/hoolrory/AndroidVideoSamples/blob/master/CommonVideoLibrary/src/com/roryhool/commonvideolibrary/VideoResampler.java

(我没有运行这个项目,但我实现了类似的东西,所以它应该工作)

以上是关于使用MediaCodec进行转码会导致绿色和混乱的视频的主要内容,如果未能解决你的问题,请参考以下文章

android 采集摄像头预览帧,使用opencv和MediaCodec直接录制水印滤镜视频

android 采集摄像头预览帧,使用opencv和MediaCodec直接录制水印滤镜视频

Http请求时URL中的中文编码

如何在没有 MediaExtractor 的情况下为 H264 使用 MediaCodec

使用 MediaCodec 和 Surface 进行 Android 编码

php 图片用base64转码完的文本比以前还大 是为啥?