【问题标题】:Picking a video, decoding it, changing its fps, encoding and saving using mediacodec选择一个视频,解码它,改变它的 fps,使用 mediacodec 编码和保存
【发布时间】:2018-11-19 09:31:16
【问题描述】:

想从设备中挑选一个视频并对其进行解码以更改其帧速率,然后对其进行编码并将其保存到设备中。使用 MediaCodec 怎么可能?查阅了很多文档,但找不到方法。我有以下用于解码的代码。这对我的目的有什么好处。如果是,如何使用该解码数据以更改的 fps 保存它。

 MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 1080, 720);
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 2500000);
            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
            try {
                decoder = MediaCodec.createDecoderByType("video/avc");
            } catch (IOException e) {
                Log.d("Error", "Fail to create MediaCodec: " + e.toString());
            }



            ///Commenting for testing...
            /*
            // Pass the decoded data to the surface to display
            decoder.configure(mediaFormat, null, null, 0);
            //decoder.configure(mediaFormat, null, null, 0);
            decoder.start();

            */
            ///Commenting for testing...



            // new BufferInfo();

            ByteBuffer[] inputBuffers = decoder.getInputBuffers();
            ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
            if (null == inputBuffers) {
                Log.d("Error", "null == inputBuffers");
            }
            if (null == outputBuffers) {
                Log.d("Error", "null == outbputBuffers 111");
            }

            FileInputStream file = null;
            try {
                file = new FileInputStream(data.getData().getPath().toString());
            } catch (FileNotFoundException e) {
                Log.d("Error", "open file error: " + e.toString());
                return;
            }
            int read_size = -1;
            int mCount = 0;

            for (; ; ) {
                byte[] h264 = null;
                try {
                    byte[] length_bytes = new byte[4];
                    read_size = file.read(length_bytes);
                    if (read_size < 0) {
                        Log.d("Error", "read_size<0 pos1");
                        break;
                    }
                    int byteCount = bytesToInt(length_bytes, 0);

                    //Changed to .length
                    //int byteCount=length_bytes.length;

                    Log.d("Error", "byteCount: " + byteCount);

                    h264 = new byte[byteCount];
                    read_size = file.read(h264, 0, byteCount);
                    // Log.d("Error", "read_size: " + read_size);
                    if (read_size < 0) {
                        Log.d("Error", "read_size<0 pos2");
                        break;
                    }
                    // Log.d("Error", "pos: " + file.)
                } catch (IOException e) {
                    Log.d("Error", "read_size 2: " + read_size);
                    Log.d("Error", "e.toStrinig(): " + e.toString());
                    break;
                }

                int inputBufferIndex = decoder.dequeueInputBuffer(-1);
                if (inputBufferIndex >= 0) {
                    ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                    inputBuffer.clear();
                    inputBuffer.put(h264);
                    // long sample_time = ;
                    decoder.queueInputBuffer(inputBufferIndex, 0, h264.length, mCount * 1000000 / 20, 0);
                    ++mCount;
                } else {
                    Log.d("Error", "dequeueInputBuffer error");
                }

                ByteBuffer outputBuffer = null;
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
                while (outputBufferIndex >= 0) {
                    outputBuffer = outputBuffers[outputBufferIndex];
                    decoder.releaseOutputBuffer(outputBufferIndex, true);
                    outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
                }

                // Pass the decoded data to the surface to display
                decoder.configure(mediaFormat,mPreview.getHolder().getSurface() , null, 0);
                //decoder.configure(mediaFormat, null, null, 0);


                decoder.start();

                if (outputBufferIndex >= 0) {
                    decoder.releaseOutputBuffer(outputBufferIndex, false);
                } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    outputBuffers = decoder.getOutputBuffers();
                    Log.d("Error", "outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED");
                } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    // Subsequent data will conform to new format.
                    Log.d("Error", "outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED");
                }

                try {
                    Thread.sleep(1000/20);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }




            }
    }
}
public int bytesToInt(byte[] src, int offset) {
    int value;
    value = (int) ((src[offset] & 0xFF)
            | ((src[offset+1] & 0xFF)<<8)
            | ((src[offset+2] & 0xFF)<<16)
            | ((src[offset+3] & 0xFF)<<24));
    return value;
}

【问题讨论】:

  • 解码、重采样、编码、保存。那至少是4个问题。当每个帖子有一个问题时,Stack-overflow 效果最好,否则对于志愿者来说,它的工作量太大,无法给出对社区有价值的答案。
  • 如果我们把这个问题当作 4 个问题来问,我们会得到不同类型的答案。我正在为所有这 4 个问题寻找一个完整的解决方案。
  • Stackoverflow 不是代码编写服务。它是帮助您编写代码的资源。这里没有人会为您提供完整的解决方案。
  • 如果你不能回答,请不要理会我的问题。
  • 我只是在我的问题中包含了我需要的不同步骤,而不是问一个广泛的问题,例如如何使用媒体编解码器更改本地视频的 FPS。在 stackoverflow 上有几个像这样的广泛问题,我已经看到了完整的解决方案。所以请不要太打扰........

标签: android video encoding decoding android-mediacodec


【解决方案1】:

您可以查看DecodeEditEncode,这是使用表面进行解码和重新编码的一个很好的起点(解码器的输出表面 -> 编码器的输入表面)。

特别看一下这个方法

private void editVideoData(VideoChunks inputData, MediaCodec decoder,
            OutputSurface outputSurface, InputSurface inputSurface, MediaCodec encoder,
            VideoChunks outputData)

您必须遵循的工作流程类似于以下内容:

  • 提取视频轨道 (MediaExtractor)

  • 提供解码器输入缓冲区

  • 将解码帧渲染到表面

  • 渲染时,编码器将获取帧(您也必须设置时间戳)
  • 使用 MediaMuxer 将编码器帧与音轨混合。

额外链接:一些例子

ExtractDecodeEditEncodeMuxTest

VideoResample.java(很有意思)

【讨论】:

    猜你喜欢
    • 2015-12-24
    • 2013-12-26
    • 2016-03-12
    • 2019-06-21
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多