FFmpeg使用代码获取视频信息
在写代码之前,我门先用命令来查看一下视频信息:
输入:ffprobe 1080P.mp4
即可反馈如下媒体信息:
这些信息其实都可以使用ffmpeg来获取。有时候,我们在推流视频文件,或者排查问题的时候,就需要实时的查看这些信息了,所以这一关铁定是需要过的。接下来一起看一下如何使用编码一步一步的得到我们想要的信息。
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '1080P.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf58.29.100 description : Packed by Bilibili XCoder v2.0.2 Duration: 00:03:46.53, start: 0.000000, bitrate: 3087 kb/s Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 2951 kb/s, 30 fps, 30 tbr, 16k tbn (default) Metadata: handler_name : VideoHandler vendor_id : [0][0][0][0] Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default) Metadata: handler_name : SoundHandler vendor_id : [0][0][0][0]
单纯的去解析一个媒体文件时的流程如下:
1、初步定义计划
先来分析一下内部大概流程,然后;写出来伪代码,后面来补充代码:
- 1、创建并分配一个AVFormatContext内存空间,然后做一些初始化工作;
- 2、解析并打印数据;
- 3、释放资源
我们输入的是一个媒体文件url
输出是直接打印媒体信息;
/** * @brief 解析媒体文件,并打印相关媒体信息 * * @param fileName 输入源,如:/视频/1080P.mp4 */ void show_media_info(char *fileName) { int ret = -1; // 1、创建并分配一个AVFormatContext内存空间,然后做一些初始化工作; AVFormatContext *ctx = NULL; ctx = createAVFormatContext(fileName); if( NULL == ctx) { printf("createAVFormatContext error \n"); return; } //2、解析并打印数据; parseMediaInfo(ctx); //3、释放资源 releaseAll(ctx); }
2、初步解析媒体文件,分配AVFormatContext内存空间
本函数来完成初步的初始化工作,此函数就是为了得到一个全面的AVFormatContext媒体上下文;
AVFormatContext *createAVFormatContext(char *fileName) { int ret = -1; // 解复用器上下文 AVFormatContext *ctx = NULL; // 1、分配解复用器内存 ctx = avformat_alloc_context(); if( NULL == ctx) { printf("avformat_alloc_context error \n"); return NULL; } // 2、打开媒体文件 ret = avformat_open_input(&ctx, fileName, NULL, NULL); if(ret < 0) { printf("avformat_open_input error! \n"); return NULL; } // 3、读取媒体流的部分数据包,获取码流信息 ret = avformat_find_stream_info(ctx, NULL); if(ret < 0) { printf("avformat_find_stream_info error! \n"); return NULL; } return ctx; }
3、解析数据
解析数据函数原型:
// 2、解析数据 void parseMediaInfo(AVFormatContext *ctx) { }
为了更好的补充此函数,我们先分析一下需要拿到的关键信息有哪些?
在使用ffprobe 1080P.mp4的时候,打印的信息如下:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '1080P.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf58.29.100 description : Packed by Bilibili XCoder v2.0.2 Duration: 00:03:46.53, start: 0.000000, bitrate: 3087 kb/s Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 2951 kb/s, 30 fps, 30 tbr, 16k tbn (default) Metadata: handler_name : VideoHandler vendor_id : [0][0][0][0] Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default) Metadata: handler_name : SoundHandler vendor_id : [0][0][0][0]
我们提取一些关键信息分别如下:
视频:
- 1、流索引为0;
- 2、编码格式为h264,High模式;
- 3、布局为yuv420p;
- 4、图像尺寸为1920x1080;
- 5、码率平均为2951kb/s;
- 6、帧率为30fps;
- 7、tbr为30;
- 8、tbn为16k;
- 9、视频时长;
关于tbn,tbc,tbr:
来源于网络
- tbn: 对应容器中的时间基。 值是AVStream.time_base的倒数
- tbc: 对应编解码器中的时间基。 值是AVCodecContext.time_base的倒数
- tbr: 从视频流中猜算得到,可能是帧率或场率(帧率的2倍)
音频流:
- 1、流索引为1;
- 2、编码格式为AAC,LC模式;
- 3、44.1K Hz;
- 4、stereo 立体声;
- 5、音频存储格式:fltp;
- 6、128 kb/s;
1、解析流索引:
// 2、解析数据 /** * @brief * * // 视频: // 1、流索引为0; // 2、编码格式为h264,High模式; // 3、布局为yuv420p; // 4、图像尺寸为1920x1080; // 5、码率平均为2951kb/s; // 6、帧率为30fps; // 7、tbr为30; // 8、tbn为16k; // 音频流: // 1、流索引为1; // 2、编码格式为AAC,LC模式; // 3、44.1K Hz; // 4、stereo 立体声; // 5、音频存储格式:fltp; // 6、128 kb/s; * * @param ctx */ void parseMediaInfo(AVFormatContext *ctx) { int video_index = -1; int audio_index = -1; #if 1 // 方式1: 解析流索引,新版的ffmpeg常用 audio_index = av_find_best_stream(ctx, AVMEDIA_TYPE_AUDIO, -1,-1,NULL,0); video_index = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1,-1,NULL,0); printf("audio_index=%d \n",audio_index); //audio_index=1 printf("video_index=%d \n",video_index); //video_index=0 #else // 方式2: 旧版的时候经常用 for (int i = 0; i < ctx->nb_streams; i++) { if(ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audio_index = i; } else if(ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_index = i; } } printf("audio_index=%d \n",audio_index); //audio_index=1 printf("video_index=%d \n",video_index); //video_index=0 #endif // 获取每路流的保存结构体 AVStream *videoStream = ctx->streams[video_index]; AVStream *audioStream = ctx->streams[audio_index]; // 打印媒体文件url printf("media url=%s \n", ctx->url); // 打印流数量 printf("media stream number=%d \n", ctx->nb_streams); //码率的计算 int bitRate = ctx->bit_rate / 1024; printf("media bitRate=%d kbps\n", bitRate); // 视频总时长 // ctx->durationd的解释: Duration of the stream, in AV_TIME_BASE fractional seconds printf("视频总时长: %lld 微秒 \n", ctx->duration);//226534000 微秒 // (ctx->duration / AV_TIME_BASE) 得到总秒数 int total_secondes = ctx->duration / AV_TIME_BASE; printf("视频总时长: %d 秒 \n", total_secondes);//226 秒 // 对时间进行换算: int h,m,s; h = total_secondes / 3600; //时 m = (total_secondes % 3600 ) / 60; //分 s = total_secondes % 60; //秒 printf("媒体文件总时长: %d:%d:%d \n",h,m,s); //视频总时长: 0:3:46 // 获取视频流信息 由videoStream得到 printf("################[ Video Info ]#####################\n"); printf("视频流索引: %d \n", videoStream->index); // 视频帧率 double frameRate = av_q2d(videoStream->avg_frame_rate); printf("视频帧率: %lf \n", frameRate); // 视频类型 可以自行扩充,这里以h264和mpeg4为例 if(AV_CODEC_ID_H264 == videoStream->codecpar->codec_id) { printf("视频编码格式为 H264 \n"); }else if(AV_CODEC_ID_MPEG4 == videoStream->codecpar->codec_id) { printf("视频编码格式为 MPEG4 \n"); }else{ printf("视频编码格式暂时还不知道 \n"); } // 获取视频流总时长 if(AV_NOPTS_VALUE != videoStream->duration) { int video_time = videoStream->duration * av_q2d(videoStream->time_base); printf("video_time : %02d:%02d:%02d\n" , (video_time / 3600), (video_time % 3600) / 60, (video_time % 60) ); }else{ printf("video duration unknown ! \n"); } // 获取视频流信息 由audioStream得到 printf("################[ Audio Info ]#####################\n"); printf("音频流索引: %d \n", audioStream->index); // 音频编解码器的采样率 单位Hz printf("音频编解码器的采样率 单位Hz : %d Hz \n",audioStream->codecpar->sample_rate); //音频采样格式 if(AV_SAMPLE_FMT_FLTP == audioStream->codecpar->format) { printf("音频采样格式 : FLTP \n"); } else if(AV_SAMPLE_FMT_S16P == audioStream->codecpar->format) { printf("音频采样格式 : S16P \n"); }else{ printf("音频采样格式暂时还不知道 \n"); } // 音频信道数目 printf("音频信道数目:%d \n",audioStream->codecpar->channels); // 获取音频流总时长 if(AV_NOPTS_VALUE != audioStream->duration) { int audio_time = audioStream->duration * av_q2d(audioStream->time_base); printf("audio_time : %02d:%02d:%02d\n" , (audio_time / 3600), (audio_time % 3600) / 60, (audio_time % 60) ); }else{ printf("audio duration unknown ! \n"); } }
4、释放资源
// 3、释放资源 void releaseAll(AVFormatContext *ctx) { if(ctx) { avformat_free_context(ctx); } }
5、执行效果:
media url=/home/zhenghui/视频/1080P.mp4 media stream number=2 media bitRate=3015 kbps 视频总时长: 226534000 微秒 视频总时长: 226 秒 媒体文件总时长: 0:3:46 ################[ Video Info ]##################### 视频流索引: 0 视频帧率: 29.999914 视频编码格式为 H264 video_time : 00:03:46 ################[ Audio Info ]##################### 音频流索引: 1 音频编解码器的采样率 单位Hz : 44100 Hz 音频采样格式 : FLTP 音频信道数目:2 audio_time : 00:03:46 *******************************[ exit ]*********************************
6、总代码
代码位置:
https://gitee.com/truedei/avos.git