FFMEG libavcodec декодер затем перекодирует видео проблемы - PullRequest
0 голосов
/ 03 ноября 2018

Я пытаюсь использовать библиотеку libavcodec в FFMpeg для декодирования и перекодирования видео h264.

У меня работает декодирующая часть (нормально обращается к окну SDL), но когда я пытаюсь перекодировать кадры, я получаю неверные данные в сэмплах перекодированного видео.

Вот фрагмент кода моей логики кодирования.

EncodeResponse H264Codec::EncodeFrame(AVFrame* pFrame, StreamCodecContainer* pStreamCodecContainer, AVPacket* pPacket)
{
    int result = 0;

    result = avcodec_send_frame(pStreamCodecContainer->pEncodingCodecContext, pFrame);

    if(result < 0)
    {
        return EncodeResponse::Fail;
    }

    while (result >= 0)
    {
        result = avcodec_receive_packet(pStreamCodecContainer->pEncodingCodecContext, pPacket);

        // If the encoder needs more frames to create a packed then return and wait for
        // method to be called again upon a new frame been present.
        // Else check if we have failed to encode for some reason.
        // Else a packet has successfully been returned, then write it to the file.
        if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
        {
            // Higher level logic, dedcodes next frame from source
            // video then calls this method again.
            return EncodeResponse::SendNextFrame;
        }
        else if (result < 0)
        {
            return EncodeResponse::Fail;
        }
        else
        {
            // Prepare packet for muxing.
            if (pStreamCodecContainer->codecType == AVMEDIA_TYPE_VIDEO)
            {
                av_packet_rescale_ts(m_pPacket, pStreamCodecContainer->pEncodingCodecContext->time_base, 
                                     m_pDecodingFormatContext->streams[pStreamCodecContainer->streamIndex]->time_base);
            }

            m_pPacket->stream_index = pStreamCodecContainer->streamIndex;

            int result = av_interleaved_write_frame(m_pEncodingFormatContext, m_pPacket);

            av_packet_unref(m_pPacket);
        }
    }

    return EncodeResponse::EncoderEndOfFile;
}

Странное поведение: я замечаю, что прежде чем я получу первый пакет из avcodec_receive_packet, мне нужно отправить более 50 кадров в avcodec_send_frame.

Я построил отладочную сборку FFMpeg и вошел в код. Я заметил, что AVERROR (EAGAIN) возвращается avcodec_receive_packet из-за следующего в x264encoder :: encode в encoder.c

    if( h->frames.i_input <= h->frames.i_delay + 1 - h->i_thread_frames )
    {
        /* Nothing yet to encode, waiting for filling of buffers */
        pic_out->i_type = X264_TYPE_AUTO;
        return 0;
    }

По какой-то причине в моем кодовом контексте (h) никогда не было фреймов. Я потратил много времени, пытаясь отладить ffmpeg и определить, что я делаю неправильно. Но достигли предела моих знаний видео кодеков (что мало).

Я тестирую это с видео, в котором нет звука, чтобы уменьшить сложность.

Я создал урезанную версию своего приложения и предоставил самостоятельный проект (со встроенными зависимостями ffmpeg и SDL). Надеюсь, это может помочь любому, кто хочет помочь мне:).

Ссылка на проект https://github.com/maxhap/video-codec


Изучив инициализацию кодера, я обнаружил, что должен установить кодек AV_CODEC_FLAG_GLOBAL_HEADER перед вызовом avcodec_open2

pStreamCodecContainer->pEncodingCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

Это изменение привело к тому, что перекодированный блок moov выглядел гораздо более опасным (для его анализа использовался MP4Box.js). Тем не менее, видео по-прежнему не воспроизводится правильно, выходное видео имеет серые рамки при запуске при воспроизведении в VLC и не воспроизводится на других проигрывателях.

С тех пор я пытался создать контекст кодирования с помощью примера кода, а не использовать параметры декодирования моего кодека. Это привело к исправлению ошибки / данных или проблемы с кодировкой. Тем не менее, мои времена DTS масштабируются до огромных цифр

Вот мой новый кодек init

if (pStreamCodecContainer->codecType == AVMEDIA_TYPE_VIDEO) 
{
    pStreamCodecContainer->pEncodingCodecContext->height = pStreamCodecContainer->pDecodingCodecContext->height;
    pStreamCodecContainer->pEncodingCodecContext->width = pStreamCodecContainer->pDecodingCodecContext->width;
    pStreamCodecContainer->pEncodingCodecContext->sample_aspect_ratio = pStreamCodecContainer->pDecodingCodecContext->sample_aspect_ratio;

    /* take first format from list of supported formats */
    if (pStreamCodecContainer->pEncodingCodec->pix_fmts)
    {
        pStreamCodecContainer->pEncodingCodecContext->pix_fmt = pStreamCodecContainer->pEncodingCodec->pix_fmts[0];
    }
    else
    {
        pStreamCodecContainer->pEncodingCodecContext->pix_fmt = pStreamCodecContainer->pDecodingCodecContext->pix_fmt;
    }

    /* video time_base can be set to whatever is handy and supported by encoder */      
    pStreamCodecContainer->pEncodingCodecContext->time_base = av_inv_q(pStreamCodecContainer->pDecodingCodecContext->framerate);
    pStreamCodecContainer->pEncodingCodecContext->sample_aspect_ratio = pStreamCodecContainer->pDecodingCodecContext->sample_aspect_ratio;
}
else 
{
    pStreamCodecContainer->pEncodingCodecContext->channel_layout = pStreamCodecContainer->pDecodingCodecContext->channel_layout;
    pStreamCodecContainer->pEncodingCodecContext->channels = 
        av_get_channel_layout_nb_channels(pStreamCodecContainer->pEncodingCodecContext->channel_layout);

    /* take first format from list of supported formats */
    pStreamCodecContainer->pEncodingCodecContext->sample_fmt = pStreamCodecContainer->pEncodingCodec->sample_fmts[0];
    pStreamCodecContainer->pEncodingCodecContext->time_base = AVRational{ 1, pStreamCodecContainer->pEncodingCodecContext->sample_rate };
}

Есть идеи, почему мое время DTS неправильно масштабируется?


Мне удалось исправить смещение DTS, используя значение time_base непосредственно из потоков декодирования.

So

pStreamCodecContainer->pEncodingCodecContext->time_base = m_pDecodingFormatContext->streams[pStreamCodecContainer->streamIndex]->time_base

вместо

pStreamCodecContainer->pEncodingCodecContext->time_base = av_inv_q(pStreamCodecContainer->pDecodingCodecContext->framerate);

Я создам ответ на основе всех моих находок.

1 Ответ

0 голосов
/ 07 ноября 2018

Чтобы исправить первоначальную проблему с поврежденным moov-боксом, мне пришлось добавить флаг AV_CODEC_FLAG_GLOBAL_HEADER в контекст кодека кодирования перед вызовом avcodec_open2.

encCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

Следующей проблемой были плохо масштабированные значения DTS в закодированном пакете, это вызывало побочный эффект конечной длительности mp4, составляющей сотни часов. Чтобы исправить это, мне пришлось изменить временную базу контекста кодирования, чтобы она соответствовала временной базе потоков контекста декодирования. Это отличается от использования av_inv_q(framerate), как предложено в примере перекодирования avcodec.

encCodecContext->time_base = decCodecFormatContext->streams[streamIndex]->time_base;
...