Нарушение доступа в нативном коде с аппаратным ускорением декодера Android MediaCodec
Я намерен использовать Android MediaCodec для декодирования видеопотока, а затем использовать выходные изображения для дальнейшей обработки изображений в собственном коде.
Платформа: ASUS tf700t android 4.1.1. Тестовый поток: H.264 full HD @ 24 frm / s
С Tegra-3 SoC внутри я рассчитываю на аппаратную поддержку декодирования видео. Функционально мое приложение ведет себя так, как и ожидалось: я действительно могу получить доступ к изображениям декодера и правильно их обработать. Тем не менее, я испытываю очень высокую загрузку процессора декодера.
В следующих экспериментах нагрузка на процесс / нить измеряется какtop -m 32 -t " в оболочке adb. Чтобы получить надежный выход из "Топ"все 4 ядра процессора принудительно активируются, выполняя несколько потоков, зацикливающихся навсегда с самым низким приоритетом. Это подтверждают неоднократные казниcat / sys / devices / system / cpu / cpu [0-3] / online ", Для простоты, есть только декодирование видео, без звука; и нет никакого контроля времени, поэтому декодер работает так быстро, как может.
Первый эксперимент: запустите приложение, вызвав функцию обработки JNI, но все дальнейшие вызовы обработки закомментированы. Результаты:
пропускная способность: 25 об / мин1% загрузка потока VideoDecoder приложения24% загрузка потока Binder_3 процесса / системы / bin / mediaserverКажется, что скорость декодирования ограничена ЦП (25% от четырехъядерного ЦП) ... При включении обработки вывода декодированные изображения корректны и приложение работает. Единственная проблема: слишком высокая загрузка процессора для декодирования.
После множества экспериментов я подумал о том, чтобы дать MediaCodec поверхность, чтобы нарисовать его результат. Во всех остальных аспектах код идентичен. Результаты:
пропускная способность 55 фрм / с (приятно !!)2% загрузка потока VideoDecoder приложения1% загрузка потокового медиа-сервера процесса / системы / бина / медиа-сервераДействительно, видео показано на предоставленной поверхности. Поскольку загрузка процессора практически отсутствует, это должно быть аппаратно ускорено ...
Кажется, что de MediaCodec использует аппаратное ускорение, только если предоставляется Surface?
Все идет нормально. Я уже был склонен использовать Поверхность в качестве обходного пути (не обязательно, но в некоторых случаях даже приятно иметь). Но, если поверхность предоставлена, я не могу получить доступ к выводимым изображениям! Результатом является нарушение прав доступа в нативном коде.
Это действительно озадачивает меня! Я не видел в документации никаких ограничений доступа или чего-либо ещеhttp://developer.android.com/reference/android/media/MediaCodec.html, Также ничего в этом направлении не было упомянуто в презентации Google I / Ohttp://www.youtube.com/watch?v=RQws6vsoav8.
Итак: как использовать аппаратный ускоренный декодер Android MediaCodec и получать доступ к изображениям в собственном коде? Как избежать нарушения доступа? Любая помощь приветствуется! Также любое объяснение или подсказка.
Я уверен, что MediaExtractor и MediaCodec используются должным образом, так как приложение функционально нормально (до тех пор, пока я не предоставляю Surface). Это все еще довольно экспериментально, и хороший список API находится в списке задач ;-)
Обратите внимание, что единственное различие между двумя экспериментами - это переменная mSurface: null или фактическая поверхность в "mDecoder.configure (mediaFormat, mSurface, null, 0); "
Код инициализации:
mExtractor = new MediaExtractor();
mExtractor.setDataSource(mPath);
// Locate first video stream
for (int i = 0; i < mExtractor.getTrackCount(); i++) {
mediaFormat = mExtractor.getTrackFormat(i);
String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
Log.i(TAG, String.format("Stream %d/%d %s", i, mExtractor.getTrackCount(), mime));
if (streamId == -1 && mime.startsWith("video/")) {
streamId = i;
}
}
if (streamId == -1) {
Log.e(TAG, "Can't find video info in " + mPath);
return;
}
mExtractor.selectTrack(streamId);
mediaFormat = mExtractor.getTrackFormat(streamId);
mDecoder = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));
mDecoder.configure(mediaFormat, mSurface, null, 0);
width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
Log.i(TAG, String.format("Image size: %dx%d format: %s", width, height, mediaFormat.toString()));
JniGlue.decoutStart(width, height);
Декодер цикла (работает в отдельном потоке):
ByteBuffer[] inputBuffers = mDecoder.getInputBuffers();
ByteBuffer[] outputBuffers = mDecoder.getOutputBuffers();
while (!isEOS && !Thread.interrupted()) {
int inIndex = mDecoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
// Valid buffer returned
int sampleSize = mExtractor.readSampleData(inputBuffers[inIndex], 0);
if (sampleSize < 0) {
Log.i(TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM");
mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
mExtractor.advance();
}
}
int outIndex = mDecoder.dequeueOutputBuffer(info, 10000);
if (outIndex >= 0) {
// Valid buffer returned
ByteBuffer buffer = outputBuffers[outIndex];
JniGlue.decoutFrame(buffer, info.offset, info.size);
mDecoder.releaseOutputBuffer(outIndex, true);
} else {
// Some INFO_* value returned
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.i(TAG, "RunDecoder: INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = mDecoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.i(TAG, "RunDecoder: New format " + mDecoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
// Timeout - simply ignore
break;
default:
// Some other value, simply ignore
break;
}
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "RunDecoder: OutputBuffer BUFFER_FLAG_END_OF_STREAM");
isEOS = true;
}
}