参考博客:用FFmpeg获取视频流+音频流的信息(编码格式、分辨率、帧率、播放时长...)_zhoubotong2012的博客-CSDN博客_ffmpeg获取视频编码格式
参考博客:用AVCodecParameters代替AVCodecContext_luotuo44的博客-CSDN博客_avcodec_parameters_to_context
我是在 Qt 里跑的,所以路径用了 QString 传递,然后 FFmpeg 使用的 4.2 版本进行测试。对于相关函数的含义,一般 FFmpeg 源文件有注释。
(2020-12-30 修改)之前用的 AVFormatContext 来获取的比特率,如果是视频文件这就不能作为音频的比特率了,所以改为了 AVCodecContext 来获取。不过有些文件的 AVCodecContext 可能获取不到比特率信息,这时候再使用 AVFormatContext 提供的信息。
(2022-08-25 修改)之前用av_get_bytes_per_sample(guard.codecCtx->sample_fmt)获取采样精度,因为参数枚举AVSampleFormat并不对应文件实际的采样精度,所以读取出来的信息如24bit时会识别成32bit,现在用av_get_bits_per_sample(guard.codecParam->codec_id)来获取。
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/dict.h>
}
#include <QString>
#include <QDebug>
//通过一个guard对象来确保资源释放
struct AudioInfoGuard {
//格式化I/O上下文
AVFormatContext *formatCtx = NULL;
//解码器
AVCodec *codec = NULL;
//解码器上下文
AVCodecContext *codecCtx = NULL;
//参数信息
AVCodecParameters *codecParam = NULL;
~AudioInfoGuard() {
if(codecCtx){
avcodec_free_context(&codecCtx);
}
if(formatCtx){
avformat_close_input(&formatCtx);
avformat_free_context(formatCtx);
}
}
};
//是在Qt中使用的,所以传递的QString
bool getAudioInfo(const QString &filepath)
{
//用的utf8编码,这里转换下
QByteArray temp=filepath.toUtf8();
const char *path=temp.constData();
//const char *filepath="D:/Download/12.wav";
//借助析构函数来释放
AudioInfoGuard guard;
//打开输入流并读取头
//流要使用avformat_close_input关闭
//成功时返回=0
int result=avformat_open_input(&guard.formatCtx, path, NULL, NULL);
if (result!=0||guard.formatCtx==NULL){
return false;
}
//读取文件获取流信息,把它存入AVFormatContext中
//正常时返回>=0
if (avformat_find_stream_info(guard.formatCtx, NULL) < 0) {
return false;
}
//获取元信息,曲名,歌手等
//AVDictionaryEntry *tag = NULL;
//while (tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))
//{
// qDebug()<<tag->key<<tag->value;
//}
qDebug()<<"filepath"<<filepath;
//时长
//duration/AV_TIME_BASE单位为秒
qDebug()<<"duration"<<guard.formatCtx->duration/(AV_TIME_BASE/1000.0)<<"ms";
//文件格式,如wav
qDebug()<<"format"<<guard.formatCtx->iformat->name<<":"<<guard.formatCtx->iformat->long_name;
//这是容器的比特率
qDebug()<<"bit rate"<<guard.formatCtx->bit_rate<<"bps";
qDebug()<<"n stream"<<guard.formatCtx->nb_streams;
for (unsigned int i = 0; i < guard.formatCtx->nb_streams; i++)
{
#if 1
//AVStream是存储每一个视频/音频流信息的结构体
AVStream *in_stream = guard.formatCtx->streams[i];
//类型为音频
if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
//参数信息
guard.codecParam = in_stream->codecpar;
//查找具有匹配编解码器ID的已注册解码器
//失败返回NULL
guard.codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
if(guard.codec==NULL){
return false;
}
//分配AVCodecContext并将其字段设置为默认值
//需要使用avcodec_free_context释放生成的对象
//如果失败,则返回默认填充或者 NULL
guard.codecCtx = avcodec_alloc_context3(guard.codec);
if(guard.codecCtx==NULL){
return false;
}
//根据编码器填充上下文参数
//事实上codecpar包含了大部分解码器相关的信息,这里是直接从AVCodecParameters复制到AVCodecContext
//成功时返回值>=0
if(avcodec_parameters_to_context(guard.codecCtx, in_stream->codecpar)<0){
return false;
}
//某些AVCodecContext字段的访问器,已弃用
//av_codec_set_pkt_timebase(codec_ctx, in_stream->time_base);
//打开解码器
//使用给定的AVCodec初始化AVCodecContext
//在之前必须使用avcodec_alloc_context3()分配上下文
//成功时返回值=0
if(avcodec_open2(guard.codecCtx, guard.codec, nullptr)!=0){
return false;
}
//采样率
qDebug()<<"sample rate"<<guard.codecParam->sample_rate;
//通道数
qDebug()<<"channels"<<guard.codecParam->channels;
//采样精度
qDebug()<<"sample bit"<<av_get_bits_per_sample(guard.codecParam->codec_id);
//音频的比特率
qDebug()<<"bit rate"<<guard.codecCtx->bit_rate;
//编码,如pcm
qDebug()<<"codec name"<<guard.codec->name<<":"<<guard.codec->long_name;
return true;
}
#else
//新的版本这种获取方式已弃用
AVStream *in_stream = fmt_ctx->streams[i];
AVCodecContext *avctx=in_stream->codec;
if (avctx->codec_type == AVMEDIA_TYPE_VIDEO){
//视频信息略
}else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO){
//音频信息
qDebug()<<"sample rate"<<guard.codecParam->sample_rate;
qDebug()<<"channels"<<guard.codecParam->channels;
qDebug()<<"sample bit"<<av_get_bits_per_sample(guard.codecParam->codec_id);
AVCodec *codec=avcodec_find_decoder(avctx->codec_id);
if(codec==NULL){
return;
}
qDebug()<<"codec name"<<codec->name<<":"<<codec->long_name;
return true;
}
#endif
}
return false;
}