4

我是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。

多谢你们!

4

3 回答 3

1

好吧,首先您检查输入中的流数。然后你把它们写在一些缓冲区中(在我的例子中,我只有 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
        }
    }

我刚刚从我的一个程序中复制了一些摘录,但这希望能帮助您了解如何从输入中选择流。我没有发布完整的代码,只是摘录,所以你必须自己做一些初始化等,但如果你有任何问题,我很乐意帮助你!

于 2012-01-20T23:57:37.610 回答
1

我今天遇到了同样的问题,在我的情况下,我处理一个包含 80 个音轨的 mp4 文件,显然,如果你只需要解复用一个音轨,你不想每次处理单个音轨时跳过多达 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_frameorav_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
于 2019-01-23T20:49:09.103 回答
0

Dimitri Podborski 的回答很好!但是这种方法有一个小问题。如果你检查av_read_frame函数的代码,你会发现可能有两种情况:

  1. format_context->flags & AVFMT_FLAG_GENPTS == true- 然后好的,该方法有效
  2. format_context->flags & AVFMT_FLAG_GENPTS == false- 然后流的丢弃字段将被忽略,av_read_frame 将读取所有数据包。

因此,显然,您应该使用 AVDISCARD_ALL 方法,但在没有 GNPTS 标志的情况下 - 回退到经典的流索引检查。

于 2021-01-28T07:49:12.750 回答