Как преобразовать YV12 в COLOR_FormatYUV420SemiPlanar?

Я пытаюсь закодировать видео .h264 с помощью MediaCodec и Camera (onPreviewFrame). Я застрял при преобразовании цветового пространства из YV12 (с камеры) в COLOR_FormatYUV420SemiPlanar (нужен кодеру).

Изменить: я заметил, что это может быть ошибка в MediaCodec, поскольку следующий код работает на других устройствах:

public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final  byte[] output, final int width, final int height) {
    /*
     * COLOR_TI_FormatYUV420PackedSemiPlanar is NV12
     * We convert by putting the corresponding U and V bytes together (interleaved).
     */
    final int frameSize = width * height;
    final int qFrameSize = frameSize / 4;

    System.arraycopy(input, 0, output, 0, frameSize); // Y

    for (int i = 0; i < qFrameSize; i++) {
        output[frameSize + i * 2] = input[frameSize + i + qFrameSize]; // Cb (U)
        output[frameSize + i * 2 + 1] = input[frameSize + i]; // Cr (V)
    }
    return output;
}

Вот результат, который я получаю (кажется, что биты цвета имеют некоторое смещение):

введите здесь описание изображения

Редактирование 2. Размер кадра – 1280 x 720, устройство – Samsung s5 (SM-G900V) с OMX.qcom.video.encoder.avc под управлением Android Lollipop 5.0 (API 21).

Примечание. Я знаю о COLOR_FormatSurface, но мне нужно, чтобы это работало на API 16.


person user2880229    schedule 08.12.2015    source источник


Ответы (2)


Если это работает на устройстве Qualcomm до Android 4.3, вам необходимо выровнять начало плоскости U/V по границе 2048 байт. Что-то вроде этого может сработать:

public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final  byte[] output, final int width, final int height) {
    final int frameSize = width * height;
    final int alignedFrameSize = (frameSize + 2047)/2048*2048;
    final int qFrameSize = frameSize / 4;

    System.arraycopy(input, 0, output, 0, frameSize); // Y

    for (int i = 0; i < qFrameSize; i++) {
        output[alignedFrameSize + i * 2] = input[frameSize + i + qFrameSize]; // Cb (U)
        output[alignedFrameSize + i * 2 + 1] = input[frameSize + i]; // Cr (V)
    }
    return output;
}

Это довольно известная проблема; до Android 4.3 входные форматы кодировщиков строго не тестировались, поэтому кодировщики могли делать все, что хотели. (Осторожно, кодировщики Samsung будут вести себя еще хуже.) См. https://code.google.com/p/android/issues/detail?id=37769, чтобы узнать о других известных проблемах.

person mstorsjo    schedule 08.12.2015
comment
Спасибо за ответ, но, к сожалению, это не решило проблему. Я добавил некоторые характеристики устройства и размера кадра (Редактировать 2). Результат выглядит точно так же, также я читал, что эта проблема с выравниванием не влияет на видео 720p. stackoverflow.com/questions/20699009/ - person user2880229; 08.12.2015
comment
У вас явно есть какая-то проблема, когда вы не начинаете с правильного места на УФ-плоскости. Сколько байтов данных вы получили для кадра? Точно 1280*720*1.5? - person fadden; 11.12.2015

Вы можете попробовать это

public byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height)
{
    for (int i = 0; i < height; i++)
        System.arraycopy(input, yStride * i, output, yStride * i, yStride); // Y

    for (int i = 0; i < halfHeight; i++) {
        for (int j = 0; j < halfWidth; j++) {
            output[ySize + (i * halfWidth + j) * 2] = input[ySize + cSize + i * cStride + j]; // Cb (U)
            output[ySize + (i * halfWidth + j) * 2 + 1] = input[ySize + i * cStride + j]; // Cr (V)
        }
    }

    return output;
}
person Teocci    schedule 12.04.2017