如何在Android MediaCodec

时间:2018-06-08 03:36:30

标签: android video h.264 codec

我最初尝试How to play raw NAL units in Andoid exoplayer?,但我注意到我将不得不做低级别的事情。

我发现了simple MediaCodec example。正如您所看到的,它是一个在传递给它的表面上播放文件的线程。

注意行

mExtractor = new MediaExtractor();
mExtractor.setDataSource(filePath);

看起来我应该创建自己的MediaExtractor,而不是从文件中提取视频单元,它将使用我将提供的缓冲区中的h264 NAL单元。

然后我可以致电mExtractor.setDataSource(MediaDataSource dataSource),参见MediaDataSource

它有readAt(long position, byte[] buffer, int offset, int size)

这是它读取NAL单位的地方。但是,我应该如何通过它们?我没有关于需要读取的缓冲区结构的信息。

我应该在其中传递带有NAL单位的byte[] buffer,如果是,请使用哪种格式?偏移量是多少?如果它是一个缓冲区,我不应该只删除读取的行,因此没有偏移或大小?

顺便说一句,h264 NAL单元是流媒体单元,它们来自RTP数据包,而不是文件。我将通过C ++将它们存储起来并将它们存储在缓冲区中,尝试传递给mediaExtractor。

更新

我一直在阅读很多关于MediaCodec的内容,我想我更了解它。根据{{​​3}},一切都依赖于这种类型的东西:

 MediaCodec codec = MediaCodec.createByCodecName(name);
 MediaFormat mOutputFormat; // member variable
 codec.setCallback(new MediaCodec.Callback() {
   @Override
   void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
     ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
     // fill inputBuffer with valid data
     …
     codec.queueInputBuffer(inputBufferId, …);
   }

   @Override
   void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
     ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
     MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
     // bufferFormat is equivalent to mOutputFormat
     // outputBuffer is ready to be processed or rendered.
     …
     codec.releaseOutputBuffer(outputBufferId, …);
   }

   @Override
   void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
     // Subsequent data will conform to new format.
     // Can ignore if using getOutputFormat(outputBufferId)
     mOutputFormat = format; // option B
   }

   @Override
   void onError(…) {
     …
   }
 });
 codec.configure(format, …);
 mOutputFormat = codec.getOutputFormat(); // option B
 codec.start();
 // wait for processing to complete
 codec.stop();
 codec.release();

如您所见,我可以传递输入缓冲区并获得解码输出缓冲区。确切的字节格式仍然是一个谜,但我认为它是如何工作的。同样根据同一篇文章,ByteBuffer的用法很慢,Surface是首选。它们自动消耗输出缓冲区。虽然没有关于如何做到这一点的教程,但文章中有一节说它几乎相同,所以我想我只需要添加额外的行

 codec.setInputSurface(Surface inputSurface) 
 codec.setOutputSurface(Surface outputSurface) 

其中inputSurfaceoutputSurfacehttps://developer.android.com/reference/android/media/MediaCodec,我传递给Surface我使用(如何)在活动中显示视频。输出缓冲区将不会出现onOutputBufferAvailable(因为surface首先使用它们),onInputBufferAvailable都没有。

现在问题是:我究竟如何构建包含视频缓冲区的Surface,以及如何在活动中显示MediaPlayer

对于输出,我只需创建一个Surface并传递给MediaPlayerMediaCodec,但是输入呢?无论如何,我是否需要输入ByteBuffer,而Surface仅用于将其他输出用作输入?

1 个答案:

答案 0 :(得分:0)

首先,您需要删除NAL单元,并将原始H264字节输入此方法,以防您从文件中读取数据,因此无需删除任何内容,因为您没有使用数据包,只需输入数据此方法的字节数:

 rivate void initDecoder(){
    try {
        writeHeader = true;
        if(mDecodeMediaCodec != null){
            try{
                mDecodeMediaCodec.stop();
            }catch (Exception e){}
            try{
                mDecodeMediaCodec.release();
            }catch (Exception e){}
        }
        mDecodeMediaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
       //MIME_TYPE = video/avc    in your case
        mDecodeMediaCodec.configure(format,mSurfaceView.getHolder().getSurface(),
                null,
                0);
        mDecodeMediaCodec.start();
        mDecodeInputBuffers = mDecodeMediaCodec.getInputBuffers();
    } catch (IOException e) {
        e.printStackTrace();
        mLatch.trigger();
    }
}


   private void decode(byte[] data){


            try {

                MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                int inputBufferIndex = mDecodeMediaCodec.dequeueInputBuffer(1000);//
                if (inputBufferIndex >= 0) {
                    ByteBuffer buffer = mDecodeInputBuffers[inputBufferIndex];
                    buffer.clear();
                    buffer.put(data);
                    mDecodeMediaCodec.queueInputBuffer(inputBufferIndex,
                            0,
                            data.length,
                            packet.sequence / 1000,
                            0);

                    data = null;
                    //decodeDataBuffer.clear();
                    //decodeDataBuffer = null;
                }

                int outputBufferIndex = mDecodeMediaCodec.dequeueOutputBuffer(info,
                        1000);
                do {
                    if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                        //no output available yet
                    } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        //encodeOutputBuffers = mDecodeMediaCodec.getOutputBuffers();
                    } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        format = mDecodeMediaCodec.getOutputFormat();
                        //mediaformat changed
                    } else if (outputBufferIndex < 0) {
                        //unexpected result from encoder.dequeueOutputBuffer
                    } else {

                        mDecodeMediaCodec.releaseOutputBuffer(outputBufferIndex,
                                true);

                        outputBufferIndex = mDecodeMediaCodec.dequeueOutputBuffer(info,
                                0);

                    }
                } while (outputBufferIndex > 0);

    }

请不要忘记iFrame(前一帧字节)包含敏感数据,务必先馈入解码器