写点什么

音视频开发 _ 获取媒体文件的详细信息

作者:DS小龙哥
  • 2023-04-21
    重庆
  • 本文字数:10193 字

    阅读完需:约 33 分钟

一、前言

做音视频开发过程中,经常需要获取媒体文件的详细信息。


比如:获取视频文件的总时间、帧率、尺寸、码率等等信息。 获取音频文件的的总时间、帧率、码率,声道等信息。 这篇文章贴出 2 个我封装好的函数,直接调用就能获取媒体信息返回,copy 过去就能使用,非常方便。


如果要获取详细信息,可以使用ffprobe实现,也可以调用 ffmpeg 函数直接打开视频解析获取。


下面会演示两种方式,一种直接调用 ffprobe.exe实现,一种是调用 ffmpeg 函数直接打开视频解析获取。


如果调用ffprobe.exe实现,可以编译 ffmpeg 源码,以静态方式编译ffprobe.exe,这样调用起来比较方便,不需要带任何的依赖库。


下面 调用ffprobe.exe以 JSON 形式输出媒体文件的详细信息。


ffprobe -v quiet -of json -i D:/123.mp4  -show_streams
复制代码


执行之后直接通过 JSON 格式输出:


C:\Users\11266>ffprobe -v quiet -of json -i D:/123.mp4  -show_streams{    "streams": [        {            "index": 0,            "codec_name": "aac",            "codec_long_name": "AAC (Advanced Audio Coding)",            "profile": "LC",            "codec_type": "audio",            "codec_time_base": "1/88200",            "codec_tag_string": "mp4a",            "codec_tag": "0x6134706d",            "sample_fmt": "fltp",            "sample_rate": "88200",            "channels": 2,            "channel_layout": "stereo",            "bits_per_sample": 0,            "r_frame_rate": "0/0",            "avg_frame_rate": "0/0",            "time_base": "1/44100",            "start_pts": 0,            "start_time": "0.000000",            "duration_ts": 4141046,            "duration": "93.901270",            "bit_rate": "127948",            "max_bit_rate": "132760",            "nb_frames": "4045",            "disposition": {                "default": 1,                "dub": 0,                "original": 0,                "comment": 0,                "lyrics": 0,                "karaoke": 0,                "forced": 0,                "hearing_impaired": 0,                "visual_impaired": 0,                "clean_effects": 0,                "attached_pic": 0,                "timed_thumbnails": 0            },            "tags": {                "creation_time": "2015-04-30T02:43:22.000000Z",                "language": "und",                "handler_name": "GPAC ISO Audio Handler"            }        },        {            "index": 1,            "codec_name": "h264",            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",            "profile": "Main",            "codec_type": "video",            "codec_time_base": "2349/70450",            "codec_tag_string": "avc1",            "codec_tag": "0x31637661",            "width": 1280,            "height": 720,            "coded_width": 1280,            "coded_height": 720,            "has_b_frames": 0,            "sample_aspect_ratio": "1:1",            "display_aspect_ratio": "16:9",            "pix_fmt": "yuv420p",            "level": 51,            "chroma_location": "left",            "refs": 1,            "is_avc": "true",            "nal_length_size": "4",            "r_frame_rate": "25/1",            "avg_frame_rate": "35225/2349",            "time_base": "1/30000",            "start_pts": 0,            "start_time": "0.000000",            "duration_ts": 2816400,            "duration": "93.880000",            "bit_rate": "582474",            "bits_per_raw_sample": "8",            "nb_frames": "1409",            "disposition": {                "default": 1,                "dub": 0,                "original": 0,                "comment": 0,                "lyrics": 0,                "karaoke": 0,                "forced": 0,                "hearing_impaired": 0,                "visual_impaired": 0,                "clean_effects": 0,                "attached_pic": 0,                "timed_thumbnails": 0            },            "tags": {                "creation_time": "2015-04-30T02:43:23.000000Z",                "language": "und",                "handler_name": "GPAC ISO Video Handler"            }        }    ]}
复制代码


如果只是想要得到 媒体的总时长、尺寸信息,那么执行下面命令即可:


C:\Users\11266>ffprobe -i D:/123.mp4ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developers  built with gcc 9.2.1 (GCC) 20200122  configuration: --disable-static --enable-shared --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt  libavutil      56. 31.100 / 56. 31.100  libavcodec     58. 54.100 / 58. 54.100  libavformat    58. 29.100 / 58. 29.100  libavdevice    58.  8.100 / 58.  8.100  libavfilter     7. 57.100 /  7. 57.100  libswscale      5.  5.100 /  5.  5.100  libswresample   3.  5.100 /  3.  5.100  libpostproc    55.  5.100 / 55.  5.100Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'D:/123.mp4':  Metadata:    major_brand     : mp42    minor_version   : 0    compatible_brands: mp42isom    creation_time   : 2015-04-30T02:43:22.000000Z  Duration: 00:01:33.90, start: 0.000000, bitrate: 715 kb/s    Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 88200 Hz, stereo, fltp, 127 kb/s (default)    Metadata:      creation_time   : 2015-04-30T02:43:22.000000Z      handler_name    : GPAC ISO Audio Handler    Stream #0:1(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 582 kb/s, 15 fps, 25 tbr, 30k tbn, 20000k tbc (default)    Metadata:      creation_time   : 2015-04-30T02:43:23.000000Z      handler_name    : GPAC ISO Video Handler
复制代码

二、调用 ffprobe 获取媒体信息

下面利用 Qt 编写代码调用ffprobe可执行文件,解析媒体信息输出。


下面封装了 2 个函数,完整媒体信息的解析返回。

【1】获取尺寸和时长

//媒体信息struct MEDIA_INFO{  int width;  //宽度  int  height; //高度  qint64 duration;//视频总时长--毫秒 };

//获取视频的尺寸和总时间信息struct MEDIA_INFO GetVideo_SizeInfo(QString file){ int w, h; struct MEDIA_INFO info = {0,0,0};
//拼接ffmpge的路径 QString cmd = QString("%1 -i \"%2\"").arg(FFPROBE_NAME).arg(file);

QProcess process; process.setProcessChannelMode(QProcess::MergedChannels); process.start(cmd.toUtf8()); process.waitForFinished(); process.waitForReadyRead();
//qDebug() << "cmd:" << cmd;
if (process.exitCode() == 0) { log_printf(QString("Run Success"));
QString qba = process.readAll();
QByteArray utf8_str = qba.toUtf8();
// Match duration QRegularExpression reDuration("Duration: (\\d{2}:\\d{2}:\\d{2}\\.\\d{2})"); QRegularExpressionMatch matchDuration = reDuration.match(utf8_str);
if (matchDuration.hasMatch()) { QString duration = matchDuration.captured(1);
// "00:06:37.15" qDebug() << "视频总时间:" << duration; int hour=duration.section(":", 0, 0).toInt(); int minute = duration.section(":", 1, 1).toInt(); int second = duration.section(":", 2, 3).section(".",0,0).toInt(); int ms = duration.section(":", 2, 3).section(".", 1, 1).toInt(); info.duration= hour * 60 * 60 *1000 + minute * 60 *1000 + second*1000 + ms; } else { qDebug() << "No duration match found."; }
// Match resolution QRegularExpression reResolution("\\d{3,4}x\\d{3,4}"); QRegularExpressionMatch matchResolution = reResolution.match(utf8_str);
if (matchResolution.hasMatch()) { QString resolution = matchResolution.captured(0); //qDebug() << "视频尺寸:" << resolution; //qDebug() << "视频尺寸--w:" << resolution.section("x", 0, 0); //qDebug() << "视频尺寸--h:" << resolution.section("x", 1, 1);
info.width = resolution.section("x", 0, 0).toInt(); info.height = resolution.section("x", 1, 1).toInt(); } else { qDebug() << "No resolution match found."; }
} else { log_printf(QString("Run ERROR")); return info; }
return info;}
复制代码

【2】获取媒体详细并解析出来

// 定义用于存储解析结果的结构体struct Stream {  int index;  QString codecName;  QString codecLongName;  QString profile;  QString codecType;  QString codecTimeBase;  QString codecTagString;  QString codecTag;  int width;  int height;  int codedWidth;  int codedHeight;  bool hasBFrames;  QString pixFmt;  int level;  QString colorRange;  QString colorSpace;  QString colorTransfer;  QString colorPrimaries;  QString chromaLocation;  int refs;  bool isAVC;  int nalLengthSize;  QString rFrameRate;  QString avgFrameRate;  QString timeBase;  qint64 startPts;  QString startTime;  qint64 durationTs;  QString duration;  int bitRate;  int bitsPerRawSample;  int nbFrames;  struct Disposition {    int defaultValue;    int dub;    int original;    int comment;    int lyrics;    int karaoke;    int forced;    int hearingImpaired;    int visualImpaired;    int cleanEffects;    int attachedPic;    int timedThumbnails;  } disposition;  struct Tags {    QString language;    QString handlerName;  } tags;};


//解析存放媒体信息的JSON结构QVector<Stream> DecodeMediaInfo(QString mediafile){ QByteArray byte_data=mediafile.toUtf8();
//获取媒体信息 QByteArray jsonStr = GetMediaInfo(byte_data.data());

// 将json字符串转换为json文档对象 QJsonDocument doc = QJsonDocument::fromJson(jsonStr);
// 获取顶层json对象 QJsonObject topLevelObj = doc.object();
// 获取streams数组 QJsonArray streamsArray = topLevelObj.value("streams").toArray();
// 遍历streams数组,将每个元素转换为Stream结构体 QVector<Stream> streamVec; for (const QJsonValue & streamValue : streamsArray) { QJsonObject streamObj = streamValue.toObject();
// 创建新的Stream实例,并设置属性值 Stream stream; stream.index = streamObj.value("index").toInt(); stream.codecName = streamObj.value("codec_name").toString(); stream.codecLongName = streamObj.value("codec_long_name").toString(); stream.profile = streamObj.value("profile").toString(); stream.codecType = streamObj.value("codec_type").toString(); stream.codecTimeBase = streamObj.value("codec_time_base").toString(); stream.codecTagString = streamObj.value("codec_tag_string").toString(); stream.codecTag = streamObj.value("codec_tag").toString(); stream.width = streamObj.value("width").toInt(); stream.height = streamObj.value("height").toInt(); stream.codedWidth = streamObj.value("coded_width").toInt(); stream.codedHeight = streamObj.value("coded_height").toInt(); stream.hasBFrames = streamObj.value("has_b_frames").toBool(); stream.pixFmt = streamObj.value("pix_fmt").toString(); stream.level = streamObj.value("level").toInt(); stream.colorRange = streamObj.value("color_range").toString(); stream.colorSpace = streamObj.value("color_space").toString(); stream.colorTransfer = streamObj.value("color_transfer").toString(); stream.colorPrimaries = streamObj.value("color_primaries").toString(); stream.chromaLocation = streamObj.value("chroma_location").toString(); stream.refs = streamObj.value("refs").toInt(); stream.isAVC = streamObj.value("is_avc").toBool(); stream.nalLengthSize = streamObj.value("nal_length_size").toInt(); stream.rFrameRate = streamObj.value("r_frame_rate").toString(); stream.avgFrameRate = streamObj.value("avg_frame_rate").toString(); stream.timeBase = streamObj.value("time_base").toString(); stream.startPts = streamObj.value("start_pts").toInt(); stream.startTime = streamObj.value("start_time").toString(); stream.durationTs = streamObj.value("duration_ts").toInt(); stream.duration = streamObj.value("duration").toString(); stream.bitRate = streamObj.value("bit_rate").toInt(); stream.bitsPerRawSample = streamObj.value("bits_per_raw_sample").toInt(); stream.nbFrames = streamObj.value("nb_frames").toInt();
// 解析disposition对象 QJsonObject dispositionObj = streamObj.value("disposition").toObject(); stream.disposition.defaultValue = dispositionObj.value("default").toInt(); stream.disposition.dub = dispositionObj.value("dub").toInt(); stream.disposition.original = dispositionObj.value("original").toInt(); stream.disposition.comment = dispositionObj.value("comment").toInt(); stream.disposition.lyrics = dispositionObj.value("lyrics").toInt(); stream.disposition.karaoke = dispositionObj.value("karaoke").toInt(); stream.disposition.forced = dispositionObj.value("forced").toInt(); stream.disposition.hearingImpaired = dispositionObj.value("hearing_impaired").toInt(); stream.disposition.visualImpaired = dispositionObj.value("visual_impaired").toInt(); stream.disposition.cleanEffects = dispositionObj.value("clean_effects").toInt(); stream.disposition.attachedPic = dispositionObj.value("attached_pic").toInt(); stream.disposition.timedThumbnails = dispositionObj.value("timed_thumbnails").toInt();
// 解析tags对象 QJsonObject tagsObj = streamObj.value("tags").toObject(); stream.tags.language = tagsObj.value("language").toString(); stream.tags.handlerName = tagsObj.value("handler_name").toString();
// 将Stream实例添加到vector中 streamVec.append(stream);
// 打印解析结果 for (const Stream & stream : streamVec) { qDebug() << "Index:" << stream.index << "Codec Name:" << stream.codecName << "Codec Long Name:" << stream.codecLongName << "Profile:" << stream.profile << "Codec Type:" << stream.codecType << "Codec Time Base:" << stream.codecTimeBase << "Codec Tag String:" << stream.codecTagString << "Codec Tag:" << stream.codecTag << "Width:" << stream.width << "Height:" << stream.height << "Coded Width:" << stream.codedWidth << "Coded Height:" << stream.codedHeight << "Has B Frames:" << stream.hasBFrames << "Pixel Format:" << stream.pixFmt << "Level:" << stream.level << "Color Range:" << stream.colorRange << "Color Space:" << stream.colorSpace << "Color Transfer:" << stream.colorTransfer << "Color Primaries:" << stream.colorPrimaries << "Chroma Location:" << stream.chromaLocation << "Refs:" << stream.refs << "Is AVC:" << stream.isAVC << "NAL Length Size:" << stream.nalLengthSize << "R Frame Rate:" << stream.rFrameRate << "Avg Frame Rate:" << stream.avgFrameRate << "Time Base:" << stream.timeBase << "Start PTS:" << stream.startPts << "Start Time:" << stream.startTime << "Duration TS:" << stream.durationTs << "Duration:" << stream.duration << "Bitrate:" << stream.bitRate << "Bits per Raw Sample:" << stream.bitsPerRawSample << "Number of Frames:" << stream.nbFrames << "Disposition Default Value:" << stream.disposition.defaultValue << "Disposition Dub:" << stream.disposition.dub << "Disposition Original:" << stream.disposition.original << "Disposition Comment:" << stream.disposition.comment << "Disposition Lyrics:" << stream.disposition.lyrics << "Disposition Karaoke:" << stream.disposition.karaoke << "Disposition Forced:" << stream.disposition.forced << "Disposition Hearing Impaired:" << stream.disposition.hearingImpaired << "Disposition Visual Impaired:" << stream.disposition.visualImpaired << "Disposition Clean Effects:" << stream.disposition.cleanEffects << "Disposition Attached Pic:" << stream.disposition.attachedPic << "Disposition Timed Thumbnails:" << stream.disposition.timedThumbnails << "Tags Language:" << stream.tags.language << "Tags Handler Name:" << stream.tags.handlerName; } } return streamVec;}
复制代码

三、调用 ffmpeg 函数获取媒体信息

如果在程序里不方便调用ffprobe.exe,那么也可以直接调用 ffmpeg 的函数,打开视频、音频解析媒体数据。

【1】获取视频信息

下面给出代码:


#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libavutil/dict.h>
int main(){ AVFormatContext *format_ctx = NULL; int ret;
// 打开视频文件 ret = avformat_open_input(&format_ctx, "video.mp4", NULL, NULL); if (ret != 0) { printf("无法打开视频文件\n"); return -1; }
// 获取视频文件中每个流的详细信息 ret = avformat_find_stream_info(format_ctx, NULL); if (ret < 0) { printf("无法获取视频流信息\n"); return -1; }
// 输出视频流的详细信息 for (int i = 0; i < format_ctx->nb_streams; i++) { AVStream *stream = format_ctx->streams[i]; AVCodecParameters *params = stream->codecpar; AVRational time_base = stream->time_base; printf("流%d:\n", i); printf(" 时间基数:%d/%d\n", time_base.num, time_base.den); printf(" 编码器ID:%d\n", params->codec_id); printf(" 视频宽度:%d\n", params->width); printf(" 视频高度:%d\n", params->height); printf(" 帧率:%d/%d\n", stream->avg_frame_rate.num, stream->avg_frame_rate.den); }
// 获取元数据(metadata) AVDictionaryEntry *tag = NULL; while ((tag = av_dict_get(format_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { printf("%s=%s\n", tag->key, tag->value); }
// 关闭输入文件 avformat_close_input(&format_ctx); return 0;}
复制代码


这个代码片段可以在 Linux 或 Windows 操作系统上编译,并且需要在编译时链接 FFmpeg 库。

【2】获取视频、音频详细信息

#include <stdio.h>#include <libavformat/avformat.h>
int main(int argc, char **argv) {
AVFormatContext *fmt_ctx = NULL; AVDictionaryEntry *tag = NULL;
// 打开输入媒体文件 if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) < 0) { fprintf(stderr, "Cannot open input file\n"); return -1; }
// 获取媒体文件信息 if (avformat_find_stream_info(fmt_ctx, NULL) < 0) { fprintf(stderr, "Cannot find stream information\n"); avformat_close_input(&fmt_ctx); return -1; }
// 输出媒体文件信息 printf("File: %s\n", argv[1]); printf("Format: %s\n", fmt_ctx->iformat->name); printf("Duration: %lld seconds\n", fmt_ctx->duration / AV_TIME_BASE);
for (int i = 0; i < fmt_ctx->nb_streams; i++) { AVStream *stream = fmt_ctx->streams[i];
const char *type = "Unknown"; if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { type = "Video"; printf("\n%s Stream #%d:\n", type, i); printf("Codec: %s\n", avcodec_get_name(stream->codecpar->codec_id)); printf("Resolution: %dx%d\n", stream->codecpar->width, stream->codecpar->height); printf("Frame Rate: %.2f fps\n", av_q2d(stream->avg_frame_rate)); printf("Bit Rate: %lld kbps\n", stream->codecpar->bit_rate / 1000); } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { type = "Audio"; printf("\n%s Stream #%d:\n", type, i); printf("Codec: %s\n", avcodec_get_name(stream->codecpar->codec_id)); printf("Sample Rate: %d Hz\n", stream->codecpar->sample_rate); printf("Channels: %d\n", stream->codecpar->channels); printf("Bit Rate: %lld kbps\n", stream->codecpar->bit_rate / 1000); }
// 输出流的元数据信息 while ((tag = av_dict_get(stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { printf("%s=%s\n", tag->key, tag->value); } }
// 关闭输入媒体文件 avformat_close_input(&fmt_ctx);
return 0;}
复制代码


使用方法:


  1. 将示例代码保存为ffprobe.c文件。

  2. 在命令行中进入该文件所在目录,执行以下命令进行编译:


gcc -o ffprobe ffprobe.c -lavformat -lavcodec -lavutil
复制代码


  1. 执行以下命令获取视频或音频文件的全部参数信息:


./ffprobe [input_file]
复制代码


其中,[input_file]是输入的视频或音频文件路径。


例如,执行以下命令获取test.mp4视频文件的全部参数信息:


./ffprobe test.mp4
复制代码


发布于: 刚刚阅读数: 4
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022-01-06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
音视频开发_获取媒体文件的详细信息_三周年连更_DS小龙哥_InfoQ写作社区