FFmpeg代码编程获取视频信息

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: FFmpeg代码编程获取视频信息

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
目录
相关文章
|
6月前
|
Web App开发 编解码 安全
视频会议技术 入门探究:WebRTC、Qt与FFmpeg在视频编解码中的应用
视频会议技术 入门探究:WebRTC、Qt与FFmpeg在视频编解码中的应用
619 4
|
6月前
|
存储 编解码 数据处理
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(三)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
189 0
|
6月前
|
存储 编解码 数据处理
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(二)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
209 0
|
4天前
|
编解码 监控 网络协议
如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频
本文详细介绍了如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频。内容涵盖环境搭建、编码配置、服务器端与客户端实现等方面,适合视频监控系统和直播平台等应用场景。通过具体命令和示例代码,帮助读者快速上手并实现目标。
15 6
|
3月前
|
编解码 Linux
CentOS安装ffmpeg并转码视频为mp4
CentOS安装ffmpeg并转码视频为mp4
138 0
|
5月前
|
Python
Python使用ffmpeg下载m3u8拼接为视频
Python使用ffmpeg下载m3u8拼接为视频
|
21天前
|
Java 数据安全/隐私保护
Java ffmpeg 实现视频加文字/图片水印功能
【10月更文挑战第22天】在 Java 中使用 FFmpeg 实现视频加文字或图片水印功能,需先安装 FFmpeg 并添加依赖(如 JavaCV)。通过构建 FFmpeg 命令行参数,使用 `drawtext` 滤镜添加文字水印,或使用 `overlay` 滤镜添加图片水印。示例代码展示了如何使用 JavaCV 实现文字水印。
|
26天前
|
计算机视觉 Python
FFMPEG学习笔记(一): 提取视频的纯音频及无声视频
本文介绍了如何使用FFmpeg工具从视频中提取纯音频和无声视频。提供了具体的命令行操作,例如使用`ffmpeg -i input.mp4 -vn -c:a libmp3lame output.mp3`来提取音频,以及`ffmpeg -i input.mp4 -c:v copy -an output.mp4`来提取无声视频。此外,还包含了一个Python脚本,用于批量处理视频文件,自动提取音频和生成无声视频。
37 1
|
28天前
|
缓存 监控 计算机视觉
视频监控笔记(三):opencv结合ffmpeg获取rtsp摄像头相关信息
本文介绍了如何使用OpenCV结合FFmpeg获取RTSP摄像头信息,包括网络架构、视频监控系统组成、以及如何读取和显示网络摄像头视频流。
38 1
|
29天前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
144 0
下一篇
无影云桌面