Сначала я попробовал Как играть в необработанные NAL-единицы в экзоплеере Andoid? , но я заметил, что мне придется делать что-то на низком уровне.
Я нашел это простой пример MediaCodec .Как видите, это поток, который воспроизводит файл на поверхности, переданной ему.
Обратите внимание на строки
mExtractor = new MediaExtractor();
mExtractor.setDataSource(filePath);
Похоже, я должен создать свой собственный MediaExtractor
, который вместо извлечения видеоблоков из файла будет использовать модули HAL64 NALиз буфера, который я предоставлю.
Затем я могу позвонить mExtractor.setDataSource(MediaDataSource dataSource)
, см. MediaDataSource
Он имеет readAt(long position, byte[] buffer, int offset, int size)
Это гдеон читает единицы NAL.Тем не менее, как я должен передать их?У меня нет информации о структуре буфера, который нужно прочитать.
Должен ли я передать byte[] buffer
с единицами NAL в нем, и если да, то в каком формате?Для чего смещение?Если это буфер, не следует ли мне просто стереть строки, которые были прочитаны и, следовательно, не имеют смещения или размера?
Кстати, блоки 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
s является медленным, и Surface
s являются предпочтительными.Они потребляют выходные буферы автоматически.Хотя учебника о том, как это сделать, не существует, в статье есть раздел, в котором говорится, что он почти идентичен, поэтому я думаю, что мне просто нужно добавить дополнительные строки
codec.setInputSurface(Surface inputSurface)
codec.setOutputSurface(Surface outputSurface)
, где inputSurface
и outputSurface
Поверхность с, которые я передаю MediaPlayer , которые я использую (как) для отображения видео в действии.И выходные буферы просто не будут включаться onOutputBufferAvailable
(потому что surface
потребляет их в первую очередь), а также onInputBufferAvailable
.
Так что теперь возникают вопросы: как именно я могу построить Surface
, который содержит видеобуфер, и как мне отобразить MediaPlayer
в активности
Для вывода я могу просто создать Surface
и перейти к MediaPlayer
и MediaCodec
а как насчет ввода?Нужно ли мне ByteBuffer
для входа в любом случае, а Surface
просто для использования других выходов в качестве входов?