mpeg2 ts android ffmpeg openmax

Установка выглядит следующим образом:

  1. Многоадресный сервер 1000Mbs, UDP, Mpeg2-TS часть 1 (H.222), транслирующий прямые телеканалы.
  2. Четырехъядерный 1.5Ghz Android 4.2.2 GLES 2.0 рендерер.
  3. Библиотека FFMpeg.
  4. Eclipse Kepler, Android SDK / NDK и т. Д. Работает на Windows 8.1.
  5. Экран вывода 1920 x 1080, я использую текстуру 2048 x 1024 и получаю от 35 до 45 кадров в секунду.

Приложение:

  1. Поток рендерера работает непрерывно и обновляет одну текстуру, загружая сегменты в gpu, когда медиа-изображения готовы.
  2. Поток обработчика медиа, загружает и обрабатывает медиа с сервера / или локального хранилища.
  3. Видеопоток (ы), один для буферизации пакетов UDP и другой для декодирования пакетов в кадры.

Я подключаю ffmpeg к потоку UDP просто отлично, и пакеты буферизируются и, казалось бы, прекрасно декодируются. Пакетных буферов достаточно, нет перегрузок. Проблема, с которой я сталкиваюсь, заключается в том, что она, похоже, разбивает кадры (т.е. воспроизводит только 1 из каждых множества кадров). Я понимаю, что мне нужно различать кадры I/P/B, но в данный момент, подняв руки, я не понимаю. Я даже пытался взломать, чтобы обнаружить I кадры безрезультатно. Плюс, я только рендеринг кадров менее чем на четверть экрана. Так что я не использую полноэкранное декодирование.

Декодированные кадры также хранятся в отдельных буферах, чтобы вырезать разрывы страниц. Количество буферов я тоже изменил, с 1 на 10 без удачи.

Из того, что я нашел об OpenMax IL, он обрабатывает только MPeg2-TS Part 3 (H.264 и AAC), но вы можете использовать свой собственный декодер. Я понимаю, что вы можете добавить свой собственный компонент декодирования к нему. Стоит ли мне пробовать этот маршрут или мне следует продолжить с ffmpeg?

Декодер кадра (только средство визуализации будет преобразовывать и масштабировать кадры, когда они будут готовы) /* * Эта функция будет проходить через пакеты и продолжать декодирование * до тех пор, пока кадр не будет готов первым или из пакетов */

while (packetsUsed[decCurrent])
{
    hack_for_i_frame:
    i = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packets[decCurrent]);
    packetsUsed[decCurrent] = 0; // finished with this one
    i = packets[decCurrent].flags & 0x0001;
    decCurrent++;
    if (decCurrent >= MAXPACKETS) decCurrent = 0;
    if (frameFinished)
    {
        ready_pFrame = pFrame;
        frameReady = true;  // notify renderer
        frameCounter++;
        if (frameCounter>=MAXFRAMES) frameCounter = 0;
        pFrame = pFrames[frameCounter];
        return 0;
    }
    else if (i)
        goto hack_for_i_frame;
}

return 0;

Программа чтения пакетов (порождается как pthread) void *mainPacketReader(void *voidptr) { int res;

while ( threadState == TS_RUNNING )
{
    if (packetsUsed[prCurrent])
    {
        LOGE("Packet buffer overflow, dropping packet...");
        av_read_frame( pFormatCtx, &packet );
    }
    else if ( av_read_frame( pFormatCtx, &packets[prCurrent] ) >= 0 )
    {
        if ( packets[prCurrent].stream_index == videoStream )
        {
            packetsUsed[prCurrent] = 1; // flag as used
            prCurrent++;
            if ( prCurrent >= MAXPACKETS )
            {
                prCurrent = 0;
            }
        }

        // here check if the packet is audio and add to audio buffer
    }
}
return NULL;

И рендерер просто делает эту // текстуру уже связанной до вызова этой функции

if ( frameReady == false ) return;

AVFrame *temp;  // set to frame 'not' currently being decoded
temp = ready_pFrame;

sws_scale(sws_ctx,(uint8_t const* const *)temp->data,
        temp->linesize, 0, pCodecCtx->height,
        pFrameRGB->data, pFrameRGB->linesize);

glTexSubImage2D(GL_TEXTURE_2D, 0,
        XPOS, YPOS, WID, HGT,
        GL_RGBA, GL_UNSIGNED_BYTE, buffer);

frameReady = false;

В прошлом у libvlc тоже были проблемы с синхронизацией звука, так что я решил пойти с ffmpeg и выполнить всю работу с ослом с нуля.

Если у кого-нибудь есть указания, как остановить прерывистость воспроизведения видео (прекрасно работает в VLC-плеере) или, возможно, другой путь к неудаче, это было бы очень полезно.

РЕДАКТИРОВАТЬ Я удалил взлом для I-кадра (совершенно бесполезно). Переместите функцию sws_scale из средства визуализации в декодер пакетов. И я оставил поток чтения пакетов udp в покое.

В то же время я также изменил приоритет потоков чтения пакетов и потоков декодера пакетов в режиме реального времени. С тех пор я не теряю кучу пропущенных пакетов.

1 ответ

Решение

(после окончательного выяснения, где была кнопка ответа)

Взлом I-Frame был совершенно бесполезен, и чтобы сэкономить на перегрузке потока в рендере, sws_scale был перемещен в поток декодера.

Я также отошел от этого, полностью избавившись от sws_scale и загрузив каждый отдельный кадр YUV в gpu и используя фрагментный шейдер для преобразования в rgb.

Любой, кто интересуется шейдером для конвертации YUV в RGB, вот он и очень простой:

Вершинный шейдер

attribute vec4 qt_Vertex;
attribute vec2 qt_InUVCoords;
attribute vec4 qt_InColor;
uniform mat4 qt_OrthoMatrix;

varying vec2 qt_TexCoord0;
varying vec4 qt_OutColor;

void main(void)
{
    gl_Position = qt_OrthoMatrix * qt_Vertex;
    qt_TexCoord0 = qt_InUVCoords;
    qt_OutColor = qt_InColor;
}

Фрагмент шейдера:

precision mediump float;

uniform sampler2D qt_TextureY;
uniform sampler2D qt_TextureU;
uniform sampler2D qt_TextureV;

varying vec4 qt_OutColor;

varying vec2 qt_TexCoord0;

const float num1 = 1.403;   // line 1

const float num2 = 0.344;   // line 2
const float num3 = 0.714;

const float num4 = 1.770;   // line 3
const float num5 = 1.0;

const float half1 = 0.5;

void main(void)
{
    float y = texture2D(qt_TextureY, qt_TexCoord0).r;
    float u = texture2D(qt_TextureU, qt_TexCoord0).r - half1;
    float v = texture2D(qt_TextureV, qt_TexCoord0).r - half1;

    gl_FragColor = vec4( y + num1 * v,
                         y - num2 * u - num3 * v,
                         y + num4 * u, num5) * qt_OutColor;
}
Другие вопросы по тегам