Loading... # FFmpeg的API使用篇(一):获取视频信息 ## 1、前言 从这本文开始,我将逐步深入FFmpeg API的使用方法,而不仅仅使用ffmpeg命令行这种虽然简便强大但是有一定局限性的方法。使用命令行我们可以轻松完成自己的一部分特定工作,但是如果我们要使用FFmpeg将处理的结果返回给其他用户,或者我们的项目中需要嵌入FFmpeg,那么我们就必须要学会使用FFmpeg提供的API写代码完成工作。 回到本文主要内容。首先,我们对视频进行分析之前经常需要知道媒体文件所包含的视频流信息,比如文件格式、播放时长、视频码率、视频帧率、视频编解码格式、音频码率、音频采样率等等信息。如果使用命令行,那么只需要`ffmpeg -i input.mp4`即可获取以上信息。但是,如何使用FFmpeg 提供的API来获取这些信息,这就是本文要讲述的内容。 首先,放一张运行结果图。从图中可以看出已经获取了以上提到的信息。  ## 2、代码环境 我使用`windows10 + qt creator`的开发环境,编译环境为`MSVC2017`。 需要说明的是,在使用FFmpeg API写代码之前先将要用到的FFmpeg添加到qt项目的工程中。B站有一个视频讲解怎么配置环境,讲解的还算清楚,可以作为参考。如果有遇到任何问题,欢迎评论区留言,我会及时回复。 [B站 qt creator+windows+ffmpeg配置](https://www.bilibili.com/video/BV1zN411d7st?p=4) ## 3、代码 我将上述提到的工作封装成了一个类,如果你环境搭建好了,那么可以直接将代码复制,然后运行。先跑起来看到效果,然后再仔细研究代码。 - 类头文件代码(videoinformation.h) ```cpp // Copyright (c) 2021 LucasNan <nanche@mail.hfut.edu.cn> <www.kevinnan.org.cn> // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #ifndef VIDEOINFORMATION_H #define VIDEOINFORMATION_H #include <iostream> #include <string> extern "C"{ #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" } /* 视频有哪些信息? * (1) 总体 * - 格式 o * - 编解码器 * - 文件大小 * - 时长 o * - 总体码率模式 * - 总体码率 * - 日期 * (2) 对于视频 * - ID o * - 格式 o * - 码率 o * - 最大码率 * - 宽度 o * - 高度 o * - 帧率模式 * - 帧率 o * - 色彩空间 * - 色度抽样 * - 位深 * - 流大小 o * (3) 对于音频 * - 格式 o * - 码率 o * - 声道数 o * - 采样率 o * - 压缩模式 * - 流大小 o */ class VideoInformation { public: VideoInformation(); ~VideoInformation(){ avformat_close_input(&input_AVFormat_context_); } //@brief: 得到视频信息 //@param: file_path: 文件路径 //@ret : void //@birth: created by LucasNan on 20210218 void getVideoInfo(std::string file_path); //@brief: 得到格式 //@param: void //@ret : 视频格式 //@birth: created by LucasNan on 20210219 std::string getFormat(){ return this->format_; } //@brief: 得到视频长度 //@param: void //@ret : 视频长度 //@birth: created by LucasNan on 20210219 std::string getDuration(){ return this->duration_; } //@brief: 得到视频帧率 //@param: void //@ret : 视频帧率 //@birth: created by LucasNan on 20210219 int getFrameRate(){ return this->frame_rate_; } //@brief: 得到视频码率 //@param: void //@ret : 视频平均码率 //@birth: created by LucasNan on 20210219 int getVideoAverageBitRate(){ return this->video_average_bit_rate_; } //@brief: 得到视频宽度 //@param: void //@ret : 视频宽度 //@birth: created by LucasNan on 20210219 int getWidth(){ return this->width_; } //@brief: 得到视频高度 //@param: void //@ret : 视频高度 //@birth: created by LucasNan on 20210219 int getHeight(){ return this->height_; } //@brief: 得到视频流大小 //@param: void //@ret : 视频流大小 //@birth: created by LucasNan on 20210219 float getVideoSize(){ return this->video_size_; } //@brief: 得到视频编码格式 //@param: void //@ret : 视频编码格式 //@birth: created by LucasNan on 20210219 std::string getVideoFormat(){ return this->video_format_; } std::string getAudioFormat(){ return this->audio_format_; } //@brief: 得到音频平均码率 //@param: void //@ret : 音频平均码率 //@birth: created by LucasNan on 20210219 int getAudioAverageBitRate(){ return this->audio_average_bit_rate_; } //@brief: 得到音频通道数 //@param: void //@ret : 音频通道数 //@birth: created by LucasNan on 20210219 int getChannelNumbers(){ return this->channel_nums; } //@brief: 得到音频采样率 //@param: void //@ret : 音频采样率 //@birth: created by LucasNan on 20210219 int getSampleRate(){ return this->sample_rate_; } //@brief: 得到音频大小 //@param: void //@ret : 音频大小 //@birth: created by LucasNan on 20210219 float getAudioSize(){ return this->audio_size_; } private: AVFormatContext* input_AVFormat_context_; //流数 unsigned int stream_numbers_; //视频流索引号 unsigned int video_stream_index_; //音频流索引号 unsigned int audio_stream_index_; //(1) 总体 std::string format_; //格式 std::string duration_; //时长 //(2) 对于视频 int frame_rate_; //帧率 int video_average_bit_rate_; //平均码率 int width_; //视频宽度 int height_; //视频高度 float video_size_; //视频流大小 std::string video_format_; //视频编码格式 //(3) 对于音频 std::string audio_format_; //音频编码格式 int audio_average_bit_rate_; //音频平均码率 int channel_nums; //声道数 int sample_rate_; //采样率 float audio_size_; //音频流大小 }; #endif // VIDEOINFORMATION_H ``` - 类实现代码(videoinformation.cpp) ```cpp // Copyright (c) 2021 LucasNan <nanche@mail.hfut.edu.cn> <www.kevinnan.org.cn> // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #include "videoinformation.h" VideoInformation::VideoInformation() { //获取动态内存 input_AVFormat_context_ = avformat_alloc_context(); //input_AVFormat_context_ = NULL; } void VideoInformation::getVideoInfo(std::string file_path){ if(avformat_open_input(&input_AVFormat_context_, file_path.c_str(), 0, NULL) < 0){ std::cout<<"file open error!"<<std::endl; return; } if(avformat_find_stream_info(input_AVFormat_context_, NULL) < 0){ printf("error"); return ; } //得到流数量 this->stream_numbers_ = input_AVFormat_context_->nb_streams; //计算视频长度 int hours, mins, secs; secs = input_AVFormat_context_->duration / 1000000; mins = secs / 60; secs %= 60; hours = mins / 60; mins %= 60; //格式化视频长度 char duration_foramt_[128]; sprintf(duration_foramt_, "%d:%d:%d", hours, mins, secs); this->duration_ = duration_foramt_; //av_dump_format(input_AVFormat_context_, 0, file_path.c_str(), 0); //得到输入视频的封装格式 std::cout<<"format: "<<input_AVFormat_context_->streams[0]->codecpar->format<<std::endl; std::cout<<"bit rate: "<<input_AVFormat_context_->bit_rate/1000.0<<std::endl; AVInputFormat* infoFormat = input_AVFormat_context_->iformat; this->format_ = infoFormat->name; //分别遍历视频的流 for(unsigned int i = 0; i < stream_numbers_; i++){ //取出一路流,并生成AVStream对象 AVStream* input_stream = input_AVFormat_context_->streams[i]; //AVDictionaryEntry *lang = av_dict_get(input_stream->metadata, "language", NULL, 0); //std::cout<<"ddd: "<<lang->value<<std::endl; //判断是否为视频流 if(input_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){ //avg_frame_rate -> AVRational(有理数), //avg_frame_rate.num : 分子 //avg_frame_rate.den : 母 //得到视频帧率 this->frame_rate_ = input_stream->avg_frame_rate.num / input_stream->avg_frame_rate.den; //取出视频流中的编码参数部分, 生成AVCodecParamters对象 AVCodecParameters* codec_par = input_stream->codecpar; //利用编码参数对象AVCdecParamters得到视频宽度,高度,码率,视频大小 this->width_ = codec_par->width; this->height_ = codec_par->height; this->video_average_bit_rate_ = codec_par->bit_rate/1000; this->video_size_ = this->video_average_bit_rate_ * secs / (8.0*1024); //利用avcodec_paramters_to_context()函数产生AVCodecContext对象 //input_stream->codec已经被淘汰,不推荐使用这种方式生成AVCodecContext AVCodecContext* avctx_video; avctx_video = avcodec_alloc_context3(NULL); int ret = avcodec_parameters_to_context(avctx_video, codec_par); if (ret < 0) { avcodec_free_context(&avctx_video); return; } //使用AVCodecContext得到视频编码格式(不推荐) char buf[128]; avcodec_string(buf, sizeof(buf), avctx_video, 0); //使用AVCodecParameters得到视频编码方式 this->video_format_ = avcodec_get_name((codec_par->codec_id)); //判断是否为音频流 }else if(input_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){ //生成AVcodecParamters对象 AVCodecParameters* codec_par = input_stream->codecpar; AVCodecContext* avctx_audio; avctx_audio = avcodec_alloc_context3(NULL); int ret = avcodec_parameters_to_context(avctx_audio, codec_par); if(ret < 0){ avcodec_free_context(&avctx_audio); return; } this->audio_format_ = avcodec_get_name(avctx_audio->codec_id); this->audio_average_bit_rate_ = codec_par->bit_rate / 1000; this->channel_nums = codec_par->channels; this->sample_rate_ = codec_par->sample_rate; this->audio_size_ = this->audio_average_bit_rate_ * secs / (8.0*1024); } } } ``` - main.cpp代码 ```cpp // Copyright (c) 2021 LucasNan <nanche@mail.hfut.edu.cn> <www.kevinnan.org.cn> // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #include <QCoreApplication> #include <iostream> #include "videoinformation.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); //const char* file = "F:/google/durant.mp4"; const char* file = "F:/google/Beautiful-Ambient-Mix.mp4"; VideoInformation* v1 = new VideoInformation(); v1->getVideoInfo(file); std::cout<<"duration: "<<v1->getDuration()<<std::endl<< "format: "<<v1->getFormat()<<std::endl<< "video frame rate: "<<v1->getFrameRate()<<std::endl<< "video bit rate: "<<v1->getVideoAverageBitRate()<<std::endl<< "video format: "<<v1->getVideoFormat()<<std::endl<< "video width: "<<v1->getWidth()<<std::endl<< "video height: "<<v1->getHeight()<<std::endl<< "video size: "<<v1->getVideoSize()<<std::endl<< "audio bit rate: "<<v1->getAudioAverageBitRate()<<std::endl<< "audio format: "<<v1->getAudioFormat()<<std::endl<< "audio size: "<<v1->getAudioSize()<<std::endl<< "audio smaple rate: "<<v1->getSampleRate()<<std::endl<< "audio channel numbers: "<<v1->getChannelNumbers()<<std::endl; return a.exec(); } ``` ## 4、为什么要加extern"C"关键字? 最后,我要谈一个问题,为什么使用FFmpeg,要使用extern "C"关键字包含FFmpeg的头文件。 在 C 语言中编译出来的签名是 _decoder,而在 C++ 语言中,一般编译器的生成则类似于 _decode_float_float。虽然在编译阶段是没有问题的,但是在链接阶段,如果不加 extern "C" 关键字的话,那么将会链接 _decoder_float_float 这个方法签名;而如果加了 extern "C" 关键字的话,那么寻找的方法签名就是 _decoder。而 FFmpeg 就是 C 语言书写的,编译 FFmpeg 的时候所产生的方法签名都是 C 语言类型的签名,所以在 C 中引用 FFmpeg 必须要加 extern "C" 关键字。 Last modification:June 15th, 2021 at 06:04 pm © 允许规范转载