

























之前已经实现过获取不同的视音频轨道信息,然后指定索引进行播放,相当于已经实现了一部分功能,但是还不全,vlc播放器上面就可以看到直接切换节目,而不是切换视音频轨道索引,每个节目都有属于自己的视音频轨道,而且可能有多个,比如节目1可能有一个视频两个音轨,一个中文一个英文,之前就没有考虑到这块,导致有些文件播放的时候错乱,画面和声音对不上,其实就是画面是节目1,而声音确是节目2的。查阅资料得知,ffmpeg本身就有接口可以获取到节目的信息,包括中文的节目名称等,我说怎么之前获取流的信息的时候,有个nb_streams表示流的数量,当时就看到还有个nb_programs以及nb_chapters,一开始以为nb是牛逼的简写,原来是number的简写,nb_programs就是有多少个节目,然后再根据数量逐个取出对应的AVProgram对象,获取对应的信息即可。
这里需要注意的是,AVProgram对象有个stream_index对象数组,这里存放的当前节目中视音频索引在全局AVFormatContext对象的流中的索引,这里需要取出来判断是属于音频还是视频,而且还可能有其他的比如DATA数据的轨道,这些不需要的就去掉。有了每个节目的所属视音频索引信息,切换的时候,只需要设置当前需要解码的索引即可。AVFormatContext对象在通过av_read_frame读取的时候,其实所有视频流数据都会读取出来,用户按照需要选择那些去解码即可,所以只需要切换这个索引即可,判断packet的index是不是当前选中节目的视音频轨道索引即可。效果非常好,实时切换瞬间,非常ok。


//节目信息
struct ProgramInfo {
int id;
QString name;
QString provider;
QList<int> audioTracks;
QList<int> videoTracks;
//重载打印输出格式
friend QDebug operator << (QDebug debug, const ProgramInfo &programInfo) {
QStringList list;
list << QString("节目编号: %1").arg(programInfo.id);
list << QString("节目名称: %1").arg(programInfo.name);
list << QString("节目发布: %1").arg(programInfo.provider);
QStringList audios;
foreach (int track, programInfo.audioTracks) {
audios << QString::number(track);
}
QStringList videos;
foreach (int track, programInfo.videoTracks) {
videos << QString::number(track);
}
list << QString("音频索引: %1").arg(audios.join("|"));
list << QString("视频索引: %1").arg(videos.join("|"));
debugx << list.join(" ");
return debug;
}
};
//指定轨道播放: ffplay -vst 0 -ast 1 f:/mp4/ts/1.ts 中途可以按 alt+c 切换节目
//获取详细信息: ffprobe -v quiet -show_programs -print_format json input.ts
//ts流有多个节目/每个节目有多个视频或音频/可能存在一个视频和多个音频
QList<ProgramInfo> FFmpegThreadHelper::readProgramInfo(AVFormatContext *formatCtx, QList<int> &audioTracks, QList<int> &videoTracks)
{
audioTracks.clear();
videoTracks.clear();
QList<ProgramInfo> programs;
//获取视音频索引集合
for (int i = 0; i < formatCtx->nb_streams; ++i) {
AVMediaType type = FFmpegHelper::getMediaType(formatCtx->streams[i]);
if (type == AVMEDIA_TYPE_AUDIO) {
audioTracks << i;
} else if (type == AVMEDIA_TYPE_VIDEO) {
videoTracks << i;
}
}
//1个节目下面有多个视频/转换成多个节目
if (formatCtx->nb_programs == 1 && videoTracks.count() > 1) {
TrackHelper::getProgram(programs, audioTracks, videoTracks);
return programs;
}
//获取节目流信息
for (int i = 0; i < formatCtx->nb_programs; ++i) {
ProgramInfo program;
AVProgram *avprogram = formatCtx->programs[i];
program.id = avprogram->id;
AVDictionaryEntry *tag = NULL;
while ((tag = av_dict_get(avprogram->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
QString key = tag->key;
QString value = tag->value;
if (key == "service_name") {
program.name = value;
} else if (key == "service_provider") {
program.provider = value;
}
}
int number = avprogram->nb_stream_indexes;
for (int j = 0; j < number; ++j) {
int index = avprogram->stream_index[j];
AVMediaType type = FFmpegHelper::getMediaType(formatCtx->streams[index]);
if (type == AVMEDIA_TYPE_AUDIO) {
program.audioTracks << index;
} else if (type == AVMEDIA_TYPE_VIDEO) {
program.videoTracks << index;
}
}
programs << program;
}
return programs;
}
#include "trackhelper.h"
void TrackHelper::checkProgram(QList<ProgramInfo> &programs)
{
int count = programs.count();
for (int i = 0; i < count; ++i) {
if (programs.at(i).name.isEmpty()) {
programs[i].name = "节目";
}
if (programs.at(i).provider.isEmpty()) {
programs[i].provider = "发布";
}
}
}
void TrackHelper::getProgram(QList<ProgramInfo> &programs, const QList<int> &audioTracks, const QList<int> &videoTracks)
{
//每个视频对应多少个音轨/必须是整数倍/非整数倍统一到一个节目
int audioCount = audioTracks.count();
int videoCount = videoTracks.count();
int number = audioCount / videoCount;
if (audioCount % videoCount != 0) {
return;
}
//没有借口获取节目流/有接口获取视音频轨道/通过规律自动组合
int index = 0;
programs.clear();
for (int i = 0; i < videoCount; ++i) {
ProgramInfo program;
program.id = i;
program.name = QString("节目%1").arg(i + 1);
program.provider = QString("发布%1").arg(i + 1);
program.videoTracks << videoTracks.at(i);
for (int j = 0; j < number; ++j) {
program.audioTracks << audioTracks.at(index++);
}
programs << program;
}
}
void TrackHelper::getProgramOne(QList<ProgramInfo> &programs, const QList<int> &audioTracks, const QList<int> &videoTracks)
{
programs.clear();
if (audioTracks.count() == 0 && videoTracks.count() == 0) {
return;
}
ProgramInfo program;
program.id = 0;
program.name = "节目";
program.provider = "发布";
program.audioTracks = audioTracks;
program.videoTracks = videoTracks;
programs << program;
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。