我最初尝试了如何在Andoid exoplayer中播放原始的NAL单元?,但我注意到我必须以低级别进行操作。
我找到了这个简单的MediaCodec示例。正如您所看到的,它是一个线程,可以在传递给它的表面上播放文件。
请注意这些行。
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的文章,我认为我现在更好地理解了它。根据https://developer.android.com/reference/android/media/MediaCodec,一切都依赖于这种类型的东西。 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)
在此情况下,我将inputSurface
和outputSurface
作为Surface传递给MediaPlayer,然后使用它来在活动中显示视频。但是输出缓冲区不会在onOutputBufferAvailable
(因为surface
首先使用它们),也不会在onInputBufferAvailable
中出现。
现在的问题是:我应该如何构造包含视频缓冲区的Surface
,并将MediaPlayer
显示到活动中
对于输出,可以简单地创建一个Surface
并将其传递给MediaPlayer
和MediaCodec
,但输入呢?我是否仍然需要ByteBuffer
输入,并且Surface
只是用作其他输出的输入?
readAt(long position, byte[] buffer, int offset, int size)
。 - PPP