问题
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:
format_context->flags & AVFMT_FLAG_GENPTS == true
- then OK, the approach worksformat_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