如何在libavformat中更改流索引

时间:2012-01-03 13:36:20

标签: 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。

谢谢你们!

3 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

我今天遇到了同样的问题,就我而言,我处理的是一个包含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_frameav_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

答案 2 :(得分: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 方法,但如果没有 GENPTS 标志 - 回退到经典的流索引检查。