配合ffmpeg接口获取视频音频媒体信息详解
作者:DS小龙哥
这篇文章主要为大家介绍了配合ffmpeg接口获取视频音频媒体信息详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
一、前言
做音视频开发过程中,经常需要获取媒体文件的详细信息。
比如:获取视频文件的总时间、帧率、尺寸、码率等等信息。 获取音频文件的的总时间、帧率、码率,声道等信息。 这篇文章贴出2个我封装好的函数,直接调用就能获取媒体信息返回,cop过去就能使用,非常方便。
如果要获取详细信息,可以使用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": "h364", "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.mp4 ffprobe 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.100 Input #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: h364 (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; }
使用方法:
- 将示例代码保存为
ffprobe.c
文件。 - 在命令行中进入该文件所在目录,执行以下命令进行编译:
gcc -o ffprobe ffprobe.c -lavformat -lavcodec -lavutil
- 执行以下命令获取视频或音频文件的全部参数信息:
./ffprobe [input_file]
其中,[input_file]
是输入的视频或音频文件路径。
例如,执行以下命令获取test.mp4
视频文件的全部参数信息:
./ffprobe test.mp4
以上就是配合ffmpeg接口获取视频音频媒体信息详解的详细内容,更多关于ffmpeg接口获取视频音频的资料请关注脚本之家其它相关文章!