我最初尝试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)
其中inputSurface
和outputSurface
是https://developer.android.com/reference/android/media/MediaCodec,我传递给Surface我使用(如何)在活动中显示视频。输出缓冲区将不会出现onOutputBufferAvailable
(因为surface
首先使用它们),onInputBufferAvailable
都没有。
现在问题是:我究竟如何构建包含视频缓冲区的Surface
,以及如何在活动中显示MediaPlayer
对于输出,我只需创建一个Surface
并传递给MediaPlayer
和MediaCodec
,但是输入呢?无论如何,我是否需要输入ByteBuffer
,而Surface
仅用于将其他输出用作输入?
答案 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(前一帧字节)包含敏感数据,务必先馈入解码器