how to change stream index in libavformat

最后都变了- 提交于 2021-02-18 12:44:05

问题


I'm a newbie in ffmpeg. I have a problem when some media has multiple audio streams. Suppose in MKV file, it has three audio streams (MP3, WMA and WMAPro)

How do I change the stream index when demuxing using:

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

So I'm searching something like change_stream_index(int streamindex), and when I call that function (suppose change_stream_index(2)), the next call to av_read_frame will demux WMAPro frame instead of MP3.

Thanks guys!


回答1:


Well, at first you check for the number of streams within the input. Then you write them in some buffer(in my case I only have 2 streams, but you can easily expand that)

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);
    }

Since you don't know in which order the streams come in, you'll also have to check for the name of the codec: ptrFormatContext->streams[i]->codec->codec_name and then save the index for the regarding target_format. Then you can just access the stream through the given index:

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
        }
    }

I just copied some extracts from a program of mine but this will hopefully help you to understand how to select streams from the input. I did not post complete code, only excerpts, so you will have to do some initialization etc on your own, but if you have any questions I'll gladly help you!




回答2:


I was facing the same problem today and in my case I deal with a mp4 file with 80 tracks and obviously if you just need to demux a single track you don't want to skip up to 79 packets each time you want to process a single packet from a selected stream.

The solution for me was setting the discard attribute of all streams which I'm not interested in to AVDISCARD_ALL. For example in order to select only a single stream with index 71 you can do this:

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;
}

after this you can call av_seek_frame or av_read_frame and only track 71 is processed.

Just for the reference, here is the list of all available discard types:

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



回答3:


The answer by Dimitri Podborski is good! But there's a small issue with that approach. If you inspect the code of av_read_frame function, you'll find that there can be two cases:

  1. format_context->flags & AVFMT_FLAG_GENPTS == true - then OK, the approach works
  2. format_context->flags & AVFMT_FLAG_GENPTS == false - then the discard field of a stream will be ignored, and av_read_frame will read all the packets.

So, obviously, you should go with the AVDISCARD_ALL approach, but in case of absence of the GENPTS flag - fallback to a classic stream index inspection.



来源:https://stackoverflow.com/questions/8713091/how-to-change-stream-index-in-libavformat

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!