FFmpeg获取音视频流信息

文章目录

  • 前言
  • 一、需求
  • 二、源码
  • 三、运行结果


前言

本文记录用 FFmpeg 获取视频流+音频流的信息(编码格式、分辨率、帧率、播放时长…),所用的工程基于上个博客编译成功的工程:使用FFmpeg4.3.1的SDK官方开发包编译ffmpeg.c


一、需求

我们经常需要知道一个媒体文件所包含的媒体流的信息,比如文件格式、播放时长、码率、视音频编码格式,视频分辨率,帧率,音频属性等信息。

如何使用 FFmpeg API 获取这些信息呢?

  • 媒体容器封装格式
  • 文件播放时长
  • 文件平均码率(视频+音频)
  • 视频属性(编码器名称、视频分辨率、帧率、编码码率)
  • 音频属性(编码器名称、采样率、声道数、编码码率)

二、源码

ffmepg.h 文件中添加我们自定义的结构体,我们后面会利用 ffmepg 的 API 函数将音视频流信息填充到各个字段:

typedef struct __AVGeneralMediaInfo {
    char filepath[1024];    // 文件路径
    int64_t duration;       // 时长,单位:微秒 time_base:1,000,000
    int64_t totalBitrate;   // 总码率
    int videoStreamIndex;   // 视频流索引
    int audioStreamIndex;   // 音频流索引

    char videoCodecName[256]; 
    int width;              // 视频宽
    int height;             // 视频高
    double frameRate;       // 视频帧率

    char audioCodecName[256];
    int sampleRate;         // 采样率
    int channels;           // 声道数
} AVGeneralMediaInfo;

void get_avgeneral_mediainfo(AVGeneralMediaInfo* avmi, const char* filepath);

ffmepg.c 文件中添加获取音视频流的基本信息的接口

// 封装:查找解码器
// type:[0:video, 1:audio]
void get_decoder_name(AVGeneralMediaInfo *avmi, AVFormatContext *avFmtctx, int type) 
{
    int nindex = -1;
    if (type == 0) {    // video
        nindex = avmi->videoStreamIndex;
    }
    else if (type == 1) {   // aduio
        nindex = avmi->audioStreamIndex;
    }

    if (nindex >= 0) {
        AVCodecContext* avcodecCtx = NULL;
        AVCodec *avcodec = NULL;

        avcodecCtx = avFmtctx->streams[nindex]->codec;
        avcodec = avcodec_find_decoder(avcodecCtx->codec_id);
        if (type == 0) {    // video
            strcpy(avmi->videoCodecName, avcodec->name);
            printf("videoCodecName = %s\n", avmi->videoCodecName);
        }
        else if (type == 1) {
            strcpy(avmi->audioCodecName, avcodec->long_name);
            printf("audioCodecName = %s\n", avmi->audioCodecName);
        }
    }
}

// 获取音视频流的基本信息
void get_avgeneral_mediainfo(AVGeneralMediaInfo *avmi, const char *filepath)
{
    int ret = -1;
    int i = 0;

    AVFormatContext* avFmtCtx = NULL;   // 大管家
    if (avmi == NULL || filepath == NULL) {
        return;
    }

    // 1.打开音视频文件或网络流
    ret = avformat_open_input(&avFmtCtx, filepath, NULL, NULL);
    if (ret < 0) {
        printf("error avformat_open_input:%s\n", filepath);
        return;
    }

    // 2.打印音视频流信息
    av_dump_format(avFmtCtx, 0, filepath, 0);

    // 3.继续深入,读取更多的字段
    avmi->duration = avFmtCtx->duration;        // 时长
    avmi->totalBitrate = avFmtCtx->bit_rate;    // 总码率
    printf("duration = %lld, totalBitrate = %lld\n", 
        avmi->duration, 
        avmi->totalBitrate);

    // 分别读取音视频流,更多的参数
    for (i = 0; i < avFmtCtx->nb_streams; i++) {
        AVStream* avstmp = avFmtCtx->streams[i];    // 拿到具体的一路流
        if (avstmp->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            avmi->videoStreamIndex = i;
            avmi->width = avstmp->codec->width;
            avmi->height = avstmp->codec->height;

            // 视频帧率:avg_frame_rate
            // fps:frames per second
            if (avstmp->avg_frame_rate.num != 0 
                && avstmp->avg_frame_rate.den != 0) {
                avmi->frameRate = (double)avstmp->avg_frame_rate.num / (double)avstmp->avg_frame_rate.den;
            }
            printf("width = %d, height = %d, frameRate = %.3lf\n", 
                avmi->width,
                avmi->height,
                avmi->frameRate);
        }
        else if (avstmp->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            avmi->audioStreamIndex = i;
            avmi->channels = avstmp->codec->channels;
            avmi->sampleRate = avstmp->codec->sample_rate;

            printf("channel = %d, sampleRate = %d\n", 
                avmi->channels, 
                avmi->sampleRate);
        }
    }

    // 读取具体的解码器
    // avcodec_find_decoder()
    // 视频解码器
    get_decoder_name(avmi, avFmtCtx, 0);
    
    // 音频解码器
    get_decoder_name(avmi, avFmtCtx, 1);

    // release
    avformat_close_input(&avFmtCtx);
}

ffmpeg431_test.cpp 文件内容如下:

#include <iostream>
extern "C"
{
#include "ffmpeg.h"
}

int main(int argc, char** argv)
{
	AVGeneralMediaInfo* avmi = new AVGeneralMediaInfo();
	if (avmi) {
		get_avgeneral_mediainfo(avmi, "SampleVideo_1280x720_20mb.mp4");

		delete avmi;
		avmi = NULL;
	}
}

三、运行结果

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'SampleVideo_1280x720_20mb.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    creation_time   : 1970-01-01T00:00:00.000000Z
    encoder         : Lavf53.24.2
  Duration: 00:01:57.31, bitrate: N/A
    Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 1280x720, 1048 kb/s, 25 fps, 25 tbr, 12800 tbn (default)
    Metadata:
      creation_time   : 1970-01-01T00:00:00.000000Z
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 48000 Hz, 6 channels, 383 kb/s (default)
    Metadata:
      creation_time   : 1970-01-01T00:00:00.000000Z
      handler_name    : SoundHandler
duration = 117312000, totalBitrate = 0
width = 1280, height = 720, frameRate = 25.000
channel = 6, sampleRate = 48000
videoCodecName = h264
audioCodecName = AAC (Advanced Audio Coding)

使用 MediaInfo 打开 SampleVideo_1280x720_20mb.mp4 可以看到与上面打印对应的参数
在这里插入图片描述


我的qq:2442391036,欢迎交流!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/306720.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

扩展边界opencv

扩展图像的边缘&#xff08;如上边增加50像素&#xff09;通常是通过添加额外的像素行来实现的 使用cv2.copyMakeBorder函数 valueborder_color指定了边框的颜色 import cv2 import numpy as np# 读取图像 image cv2.imread(th.jpg)# 设置边框宽度 top_border_width 50 # …

序列模型(4)—— Scaling Laws

本文介绍 LLM 训练过程中重要的 Scaling Laws&#xff0c;这是一个经验规律&#xff0c;指出了固定训练成本&#xff08;总计算量FLOPs&#xff09; C C C 时&#xff0c;如何调配模型规模&#xff08;参数量&#xff09; N N N 和训练 Token 数据量 D D D&#xff0c;才能实现…

【Emgu.CV教程】4.3、无缝融合应用之SeamlessClone()

SeamlessClone()函数才是真正的无缝克隆&#xff0c;它可以将一张小一点的图片&#xff0c;复制到另一张大一点的图片中&#xff0c;并且复制的位置可以用户自己定义&#xff0c;先看一下它的函数介绍&#xff1a; public static void SeamlessClone(IInputArray src, // 输入…

【STM32】| 01——常用外设 | USART

系列文章目录 【STM32】| 01——常用外设 | USART 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 前言1. 基础理论1.1 并行通信和串行通信1.2 同步通信和异步通信1.3 单工/半双工/全双工1.4 电平信号(RS232/TTL)和差分信号(RS485)1.5 端口(COM) 2. 串口理论2.1 串口物理…

Android - CrashHandler 全局异常捕获器

官网介绍如下&#xff1a;Thread.UncaughtExceptionHandler (Java Platform SE 8 ) 用于线程因未捕获异常而突然终止时调用的处理程序接口。当线程由于未捕获异常而即将终止时&#xff0c;Java虚拟机将使用thread . getuncaughtexceptionhandler()查询该线程的UncaughtExceptio…

cisp难不难?cisp如何备考通过率高?

*CISP 全称为Certified Information Security Professional&#xff0c;是国际上广受欢迎的信息安全专业认证之一。 对于许多信息安全领域的从业者来说&#xff0c;CISP认证是他们职业生涯中的重要一步。那么&#xff0c;CISP难不难呢?如何备考通过率更高呢?接下来&#xf…

electron自定义窗口和右键菜单样式

前言 electron默认沿用系统UI&#xff0c;并没有提供很多接口供使用者定制样式&#xff0c;如果想要完全自定义的样式&#xff0c;目前我能想到的方案只能是通过前端自定义样式&#xff0c;然后通过进程通信来实现系统基础功能&#xff1a;最大/小化、关闭、拖动窗口等。 效果…

关于java的冒泡排序

关于java的冒泡排序 我们前面的文章中了解到了数组的方法类Arrays&#xff0c;我们本篇文章来了解一下最出名的排序算法之一&#xff0c;冒泡排序&#xff01;&#x1f600; 冒泡排序的代码还是非常简单的&#xff0c;两层循环&#xff0c;外层冒泡轮数&#xff0c;里层依次比…

伺服电机:编码器原理与分类

什么是编码器&#xff1f; 编码器是将旋转位置的改变转换为电气信号。 编码器是伺服系统闭环控制不可缺少的部件&#xff0c;编码器应用在轴的闭环控制和大多数的自动化控制中。编码器为闭环控制提供位置或速度的实际测量值。 一、编码器的分类 从编码器的原理和产生的信号类…

Cypress.io:快速简单可靠的浏览器测试工具 | 开源日报 No.142

cypress-io/cypress Stars: 45.5k License: MIT Cypress.io 是一个快速、简单和可靠的浏览器测试工具&#xff0c;可以用于任何在浏览器中运行的内容。它支持 Mac、Linux 和 Windows 系统&#xff0c;并提供了安装指南。 hrvach/deskhop Stars: 4.1k License: GPL-3.0 DeskH…

ansible基础概念

一、【写在前面】 前面断更了几天&#xff0c;笔者被流感给干倒了&#xff0c;去拍了个核磁&#xff0c;给我脑子干成脱髓鞘了&#xff0c;也不知道是之前新冠导致的还是如何&#xff0c;哎要变成愚蠢的低级动物了……稍微恢复一点体力&#xff0c;今天赶快来博客水一水文章。…

代码随想录-刷题第五十二天

300. 最长递增子序列 题目链接&#xff1a;300. 最长递增子序列 思路&#xff1a;动态规划五步曲&#xff1a; dp[i]表示从0到i&#xff0c;以nums[i]结尾的最长递增子序列的长度。 递推公式&#xff1a;if(nums[i]>nums[j]) dp[i] max(dp[i], dp[j] 1) 位置i的最长升序…

MFC 多文档视图架构

目录 多文档视图架构 模仿多文档视图架构 执行流程 多文档视图架构 一个多文档视图架构运行后会是下面的样子&#xff1a; 内部的子框架窗口就相当于一个单文档视图架构&#xff0c;多文档视图架构就相当于在外面套一层框架窗口。 特点&#xff1a;可以管理多个文档(可以有…

Armv8-R AArch32 architecture概念学习

提示 该博客主要为个人学习&#xff0c;通过阅读官网手册整理而来&#xff08;个人觉得阅读官网的英文文档非常有助于理解各个IP特性&#xff09;。若有不对之处请参考参考文档&#xff0c;以官网文档为准。阅读该文章&#xff0c;可以先查看AArch64 Exception Model学习&…

SSM 基础知识点

1. IoC IoC—Inversion of Control&#xff0c;即“控制反转”&#xff0c;不是什么技术&#xff0c;而是一种设计思想。在 Java 开发中&#xff0c;IoC 意味着将你设计好的对象交给容器控制&#xff0c;而不是传统的在你的对象内部直接控制。 谁控制谁&#xff0c;控制什么&…

C语言中关于函数递归的理解

递归的概念&#xff1a;如果一个对象部分包含它自己,或者利用自己定义自己,则称这个对象是递归的;如果 一个过程直接或间接调用自己,则称这个过程是一个递归过程。递归的主要思考方式在于&#xff1a;将大事化小 我们先看一个例子 题目&#xff1a;输入一个无符号数&#xff0…

软考高级系统架构设计师考试经验分享

文章目录 1. 软考介绍&#xff08;1&#xff09;什么是软考&#xff08;2&#xff09;软考的作用&#xff08;3&#xff09;软考各科目的难度&#xff08;4&#xff09;考试时间&#xff08;5&#xff09;考试形式 2.系统架构设计师备考经验&#xff08;1&#xff09;辅导资料&…

【2024系统架构设计】 系统架构设计师第二版-嵌入式系统架构设计理论与实践

目录 一 嵌入式系统软件架构的原理 二 嵌入式系统软件架构的设计方法 三 案例分析 一 嵌入式系统软件架构的原理 🚀嵌入式系统的典型架构可以分为

51单片机四位数码管计算器 Proteus仿真程序

目录 概要 仿真图 部分代码 资料下载地址&#xff1a;51单片机四位数码管计算器 Proteus仿真程序 概要 1.系统通过4x4的矩阵键盘输入数字及运算符。 2.可以进行4位十进制数以内的加法运算&#xff0c;如果计算结果超过4位十进制数&#xff0c;则屏幕显示E 3.可以进行加法以外…

c语言:输入成绩,统计不及格人数|练习题

一、题目 输入学生成绩&#xff0c;统计不及格的学生人数 二、代码截图【带注释】 三、源代码【带注释】 #include <stdio.h> //题目&#xff1a;输入成绩&#xff0c;统计不及格人数 //思考分析 //1、由于学生人数是未知数&#xff0c;所以可以在输入时&#xff0c;以0…
最新文章