Я изначально пробовал Как играть в сырые NAL-подразделения в Andoid exoplayer? но я заметил, что мне нужно делать что-то на низком уровне.
Я нашел этот простой пример MediaCodec. Как вы можете видеть, это поток, который воспроизводит файл на поверхности, переданной ему.
Обратите внимание на строки
mExtractor = new MediaExtractor();
mExtractor.setDataSource(filePath);
Похоже, что я должен создать свой собственный MediaExtractor
который вместо извлечения видеоустройств из файла будет использовать единицы NAL h264 из буфера, который я предоставил.
Затем я могу вызвать 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();
Как вы можете видеть, я могу передавать входные буферы и получать декодированные выходные буферы. Точные форматы байтов все еще являются mistery, но я думаю, что это работает. Также, согласно той же статье, использование ByteBuffer
происходит медленно, а Surface
- предпочтительным. Они автоматически потребляют выходные буферы. Хотя нет учебника о том, как это сделать, есть раздел в статье, который говорит, что он почти идентичен, поэтому я думаю, мне просто нужно добавить дополнительные строки
codec.setInputSurface(Surface inputSurface)
codec.setOutputSurface(Surface outputSurface)
Где inputSurface
и outputSurface
- это Surface, которые я outputSurface
MediaPlayer, который я использую (как) для отображения видео в активности. И выходные буферы просто не будут onOutputBufferAvailable
(потому что surface
их потребляет в первую очередь), ни onInputBufferAvailable
.
Итак, теперь вопросы: как именно я создаю Surface
который содержит видео-буфер, и как отображать MediaPlayer
в активности
Для вывода я могу просто создать Surface
и перейти к MediaPlayer
и MediaCodec
, но как насчет ввода? Нужно ли мне ByteBuffer
для ввода в любом случае, и Surface
будет просто использовать другие выходы в качестве входных данных?