как изменить индекс потока в libavformat

Я новичок в ffmpeg. У меня проблема, когда некоторые носители имеют несколько аудиопотоков. Предположим, в файле MKV есть три аудиопотока (MP3, WMA и WMAPro).

Как изменить индекс потока при демультиплексировании с помощью:

AVPacket inputPacket;
ret = av_read_frame(avInputFmtCtx, &inputPacket)

Итак, я ищу что-то вроде change_stream_index(int ​​streamindex), и когда я вызываю эту функцию (предположим, change_stream_index(2)), следующий вызов av_read_frame будет демультиплексировать кадр WMAPro вместо MP3.

Спасибо, парни!


person BobAlmond    schedule 03.01.2012    source источник


Ответы (3)


Ну, сначала вы проверяете количество потоков на входе. Затем вы записываете их в какой-то буфер (в моем случае у меня только 2 потока, но вы можете легко их расширить)

ptrFormatContext = avformat_alloc_context();

    if(avformat_open_input(&ptrFormatContext, filename, NULL, NULL) != 0 )
    {
        qDebug("Error opening the input");
        exit(-1);
    }
    if(av_find_stream_info( ptrFormatContext) < 0)
    {
        qDebug("Could not find any stream info");
        exit(-2);
    }
    dump_format(ptrFormatContext, 0, filename, (int) NULL);

    for(i=0; i<ptrFormatContext->nb_streams; i++)
    {
        switch(ptrFormatContext->streams[i]->codec->codec_type)
        {
        case AVMEDIA_TYPE_VIDEO:
        {
            if(videoStream < 0) videoStream = i;
            break;
        }
        case AVMEDIA_TYPE_AUDIO:
        {
            if(audioStream < 0) audioStream = i;
        }
        }
    }
    if(audioStream == -1)
    {
        qDebug("Could not find any audio stream");
        exit(-3);
    }
    if(videoStream == -1)
    {
        qDebug("Could not find any video stream");
        exit(-4);
    }

Поскольку вы не знаете, в каком порядке приходят потоки, вам также придется проверить имя кодека: ptrFormatContext->streams[i]->codec->codec_name, а затем сохранить индекс для соответствующего target_format. Затем вы можете просто получить доступ к потоку через заданный индекс:

while(av_read_frame(ptrFormatContext,&ptrPacket) >= 0)
    {
        if(ptrPacket.stream_index == videoStream)
        {
            //decode the video stream to raw format
            if(avcodec_decode_video2(ptrCodecCtxt, ptrFrame, &frameFinished, &ptrPacket) < 0)
            {
                qDebug("Error decoding the Videostream");
                exit(-13);
            }
            if(frameFinished)
            {
                printf("%s\n", (char*) ptrPacket.data);
//encode the video stream to target format
//                av_free_packet(&ptrPacket);
            }
        }
        else if (ptrPacket.stream_index == audioStream)
        {
            //decode the audio stream to raw format
//            if(avcodec_decode_audio3(aCodecCtx, , ,&ptrPacket) < 0)
//            {
//                qDebug("Error decoding the Audiostream");
//                exit(-14);
//            }
            //encode the audio stream to target format
        }
    }

Я только что скопировал некоторые выдержки из своей программы, но, надеюсь, это поможет вам понять, как выбирать потоки из ввода. Я не публиковал полный код, только выдержки, поэтому вам придется выполнить некоторую инициализацию и т. д. самостоятельно, но если у вас есть какие-либо вопросы, я с радостью вам помогу!

person mmoment    schedule 20.01.2012

Сегодня я столкнулся с той же проблемой, и в моем случае я имею дело с файлом mp4 с 80 дорожками, и, очевидно, если вам просто нужно демультиплексировать одну дорожку, вы не хотите пропускать до 79 пакетов каждый раз, когда вы хотите обработать один пакет из выбранного потока.

Решением для меня было установить для атрибута discard всех потоков, которые мне не интересны, значение AVDISCARD_ALL. Например, чтобы выбрать только один поток с индексом 71, вы можете сделать это:

int32_t stream_index = 71;
for(int32_t i = 0; i<pFormatContext->nb_streams; i++)
{
  if(stream_index != i) pFormatContext->streams[i]->discard = AVDISCARD_ALL;
}

после этого вы можете вызвать av_seek_frame или av_read_frame и обрабатывается только трек 71.

Просто для справки, вот список всех доступных типов сброса:

AVDISCARD_NONE    =-16, ///< discard nothing
AVDISCARD_DEFAULT =  0, ///< discard useless packets like 0 size packets in avi
AVDISCARD_NONREF  =  8, ///< discard all non reference
AVDISCARD_BIDIR   = 16, ///< discard all bidirectional frames
AVDISCARD_NONINTRA= 24, ///< discard all non intra frames
AVDISCARD_NONKEY  = 32, ///< discard all frames except keyframes
AVDISCARD_ALL     = 48, ///< discard all
person Dimitri Podborski    schedule 23.01.2019

Ответ Дмитрия Подборского хорош! Но есть небольшая проблема с таким подходом. Если вы изучите код функции av_read_frame, вы обнаружите, что может быть два случая:

  1. format_context->flags & AVFMT_FLAG_GENPTS == true - тогда ок, подход работает
  2. format_context->flags & AVFMT_FLAG_GENPTS == false - тогда поле discard потока будет игнорироваться, а av_read_frame прочитает все пакеты.

Итак, очевидно, вам следует использовать подход AVDISCARD_ALL, но в случае отсутствия флага GENPTS - откат к классической проверке индекса потока.

person stolpa4    schedule 28.01.2021