背景
最近整理视频编解码的代码,早前在jetson上封装了jetson multimedia作为视频编解码的类,供其他同事和其他组使用,但该解码接口有一个问题,无法首先获取视频宽高信息,更无法直接获取视频的帧率、比特率等信息。
解决方法
- 使用ffmpeg库,命令行参数不适合代码集成
- 使用ffmpeg的API接口进行封装
源码实现
cpp
// ffmpeg_videoinfo.h
#ifndef FFMPEG_VIDEOINFO_H
#define FFMPEG_VIDEOINFO_H
#include <iostream>
#include <memory>
struct VideoAsset {
float width;
float height;
float fps;
float bitrate; /** bit per second */
float duration; /** seconds */
};
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int GetVideoInfo(const char *in_file, std::shared_ptr<VideoAsset> &info);
#ifdef __cplusplus
};
#endif
#endif // FFMPEG_VIDEOINFO_H
cpp
// ffmpeg_videoinfo.cpp
#include "ffmpeg_videoinfo.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
int GetVideoInfo(const char *in_file, std::shared_ptr<VideoAsset> &info){
// 注册所有格式和编解码器
av_register_all();
// 创建一个格式上下文(Format Context)
AVFormatContext* formatContext = nullptr;
if (avformat_open_input(&formatContext, in_file, nullptr, nullptr) != 0) {
fprintf(stderr, "Could not open input file '%s'", in_file);
return -1;
}
// 获取流信息
if (avformat_find_stream_info(formatContext, nullptr) < 0) {
fprintf(stderr, "Could not find stream information '%s'", in_file);
return -1;
}
// 查找视频流
int videoStreamIndex = -1;
for (unsigned i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1) {
fprintf(stderr, "Could not find video stream '%s'", in_file);
return -1;
}
// 获取视频流的编码参数
AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
AVStream* videoStream = formatContext->streams[videoStreamIndex];
info->fps = av_q2d(videoStream->avg_frame_rate);
info->width = codecParameters->width;
info->height = codecParameters->height;
int64_t totalSize = 0;
int64_t totalDuration = 0;
AVPacket packet;
while (av_read_frame(formatContext, &packet) >= 0) {
if (packet.stream_index == 0) {
totalSize += packet.size;
totalDuration += packet.duration;
}
av_packet_unref(&packet);
}
AVRational timeBase = videoStream->time_base; /** 时间基 */
info->duration = (float)totalDuration * av_q2d(timeBase);
info->bitrate = (totalDuration > 0) ? (totalSize * 8.0 / info->duration) : 0.0;
// 清理
avformat_close_input(&formatContext);
return 0;
}
#ifdef __cplusplus
};
#endif
cpp
// 测试脚本 test_single_videoinfo.cpp
#include "ffmpeg_videoinfo.h"
int main(int argc, char **argv) {
if(argc<2){
return 1;
}
const std::string &input_h264=argv[1];
std::shared_ptr<VideoAsset> video_info_ptr = std::make_shared<VideoAsset>();
// VideoAsset video_info;
int status_code = GetVideoInfo(input_h264.c_str(), video_info_ptr);
if (status_code < 0) {
printf("GetVideoInfo failed\n");
return -1;
}
printf("bitrate:%f, duration:%f, fps:%f, height:%f, width:%f\n",
video_info_ptr->bitrate, video_info_ptr->duration,
video_info_ptr->fps, video_info_ptr->height,
video_info_ptr->width);
return 1;
}
bash
# CMakelist.txt核心
add_executable(test_video_info test_single_videoinfo.cpp ffmpeg_videoinfo.h ffmpeg_videoinfo.cpp)
target_compile_features(test_video_info PRIVATE cxx_std_14)
target_link_libraries(test_video_info avcodec avutil avformat)
bash
# 测试
./test_video_info /data/videos/l4t.h264
# 输出信息示例
[h264 @ 0xaaaac14b16a0] Stream #0: not enough frames to estimate rate; consider increasing probesize
bitrate:30681866.000000, duration:164.490005, fps:20.000000, height:2160.000000, width:3840.000000
后记
本人对ffmpeg接口并不熟悉,以上根据文档及搜索结果进行的实现,不敢保证没有bug,如果各位遇到问题,可以留言交流