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

时间:2018-01-23 11:10:46

标签: android video mediacodec

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

我的代码已从此CTS修改,但有两个主要区别:

  1. 我在回调中使用异步模式的编解码器。
  2. 我没有使用辅助类,而是将encoder.createInputSurface()生成的表面直接传递给decoder.configure()。
  3. 我试图通过将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

1 个答案:

答案 0 :(得分:1)

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

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

这个项目就是这么做的。 https://github.com/hoolrory/AndroidVideoSamples/blob/master/CommonVideoLibrary/src/com/roryhool/commonvideolibrary/VideoResampler.java

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