FFmpeg代码编程获取视频信息

简介: 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
目录
相关文章
|
1月前
|
存储 编解码 算法
深度探索:使用FFmpeg实现视频Logo的添加与移除(二)
深度探索:使用FFmpeg实现视频Logo的添加与移除
42 0
|
1月前
|
编解码 监控 计算机视觉
QT5.14.2 视频分帧:QT与FFmpeg的高效结合
QT5.14.2 视频分帧:QT与FFmpeg的高效结合
|
1月前
|
存储 编解码 数据处理
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(三)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
35 0
|
1月前
|
存储 编解码 数据处理
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(二)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
38 0
|
1月前
|
存储 编解码 调度
剖析ffmpeg视频解码播放:时间戳的处理
剖析ffmpeg视频解码播放:时间戳的处理
51 0
|
1月前
|
存储 编解码 vr&ar
用C++实现视频编码器:FFmpeg与SDL技术结合,轻松编写高效编解码器
用C++实现视频编码器:FFmpeg与SDL技术结合,轻松编写高效编解码器
74 0
|
3月前
|
编解码
ffmpeg 裁剪视频命令 时间 修改分辨率
ffmpeg 裁剪视频命令 时间 修改分辨率
148 0
|
1月前
|
编解码 算法 vr&ar
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换(二)
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换
30 1
|
1月前
|
存储 编解码 算法
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换(一)
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换
63 1
|
1月前
|
设计模式 存储 缓存
【ffmpeg C++ 播放器优化实战】优化你的视频播放器:使用策略模式和单例模式进行视频优化
【ffmpeg C++ 播放器优化实战】优化你的视频播放器:使用策略模式和单例模式进行视频优化
55 0