【发布时间】:2018-06-08 03:36:29
【问题描述】:
我最初尝试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 的内容,并且我认为我对它的理解更好。根据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();
如您所见,我可以传递输入缓冲区并获取解码的输出缓冲区。确切的字节格式仍然是个谜,但我认为它是这样工作的。同样根据同一篇文章,ByteBuffers 的使用速度较慢,首选Surfaces。它们会自动消耗输出缓冲区。虽然没有关于如何做的教程,但文章中有一节说它几乎相同,所以我想我只需要添加额外的行
codec.setInputSurface(Surface inputSurface)
codec.setOutputSurface(Surface outputSurface)
其中inputSurface 和outputSurface 是Surfaces,我将其传递给MediaPlayer,我使用(如何)在活动中显示视频。并且输出缓冲区根本不会出现onOutputBufferAvailable(因为surface首先消耗它们),onInputBufferAvailable也不会出现。
所以现在的问题是:我究竟如何构造一个包含视频缓冲区的Surface,以及如何在活动中显示MediaPlayer
对于输出,我可以简单地创建一个Surface 并传递给MediaPlayer 和MediaCodec,但是输入呢?我是否需要ByteBuffer 作为输入,而Surface 只是用于使用其他输出作为输入?
【问题讨论】:
-
您不需要使用 MediaExtractor。您需要做的就是删除 rtp 标头和起始代码并将数据复制到 MediaCodec 的输入缓冲区中
-
@TomerNuni 我应该使用哪种方法?我只在 MediaDataSource 中找到了
readAt(long position, byte[] buffer, int offset, int size) -
您需要将原始数据复制到 mediaCodec 的输入缓冲区中。您可以通过将其设置为输出表面来直接渲染到表面。无需使用 MediaPlayer。
-
@TomerNuni 我需要使用哪个活动视图对象?它有办法向它输入一个表面吗?