Нарушение доступа в нативном коде с аппаратным ускорением декодера 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;
    }
}

Ответы на вопрос(2)

Ваш ответ на вопрос