当前位置: 首页 > 知识库问答 >
问题:

如何在Android MediaCodec中播放raw NAL单元

田阳泽
2023-03-14

我最初试过如何在Andoid Exoplayer中玩原始的NAL单位?但我注意到我得在低级工作。

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 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();
 codec.setInputSurface(Surface inputSurface) 
 codec.setOutputSurface(Surface outputSurface) 

所以现在的问题是:我如何构造包含视频缓冲区的Surface以及我如何将MediaPlayer显示到活动中

对于输出,我可以简单地创建一个Surface并传递给MediaPlayerMediaCodec,但是输入呢?我是否需要bytebuffer作为输入,而surface只是用于使用其他输出作为输入?

共有1个答案

长孙逸仙
2023-03-14

您首先需要移除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(第一个帧字节)包含敏感数据,必须先输入解码器

 类似资料:
  • 我在服务器中创建了一个XML文件。它包含和标记。我获取标记并将它们作为链接显示在中。当用户点击链接时,我想播放电台应用程序中的文件。 下面是我的XML文件: 我不知道下一个活动中的链接怎么玩。 :

  • 我用WordPress开发了一个网站。我在主页上放了一段视频,它在Chrome,Mozila和IE中运行良好,但在Safari中运行良好。我已经使用了html5视频标签,有没有人可以建议我如何在Safari播放MP4视频?这是我正在使用的代码播放视频在我的网站。 上面的代码工作得很好,但在Safari中除外。有什么flash播放器解决方案播放MP4视频吗?

  • 问题内容: 我尝试pygame播放wav文件,如下所示: 但是它改变了声音,我不知道为什么!我阅读了此链接解决方案,但无法解决播放wave文件的问题! 对于此解决方案,我不知道应该导入什么? 对于这个解决方案/ dev / dsp在新版本的linux中不存在: 而当我尝试pyglet它给我这个错误: 问题答案: 您可以使用PyAudio。我的Linux上的一个示例可以正常工作:

  • 我正在使用图书馆(https://github.com/Gagravarr/VorbisJava). 我想从OGG文件中获取PCM数据,并使用AudioTrack播放。使用AudioTrack对我来说是一项要求,因为我以后需要在播放时连接多个PCM数据,以获得最流畅的播放效果。 正如您在下面看到的,我试图用与文件匹配的数据设置AudioTrack,用库读取文件的内容,并将其直接写入AudioTra

  • 我试图实现一个播放/暂停按钮,用于连接到ARKit场景中节点的位置音频。我希望用户能够暂停音频,然后在以后的时间从暂停点恢复它。因此不好,因为这会在重新连接音频时将其重置到起始位置。 我能找到的唯一与SCNAudioPlayers相关联的暂停方法深藏在下面: 有人知道这能不能做到吗? 提前谢了。

  • 我已经创建了一个使用媒体播放器播放音频的应用程序,但即使其他应用程序(如youtube)开始播放视频,我的播放器也不会停止。 如何停止音频,如果其他应用程序开始播放音频。 我用过mediaPlayer。start();播放音频。 任何帮助都将不胜感激。