我是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。
谢谢你们!
答案 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_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
答案 2 :(得分:0)
Dimitri Podborski 的回答很好!但是这种方法有一个小问题。如果你查看av_read_frame
函数的代码,你会发现可能有两种情况:
format_context->flags & AVFMT_FLAG_GENPTS == true
- 那么好,这个方法有效format_context->flags & AVFMT_FLAG_GENPTS == false
- 然后流的丢弃字段将被忽略,av_read_frame 将读取所有数据包。所以,很明显,您应该使用 AVDISCARD_ALL 方法,但如果没有 GENPTS 标志 - 回退到经典的流索引检查。