音视频系列四:ffmpeg之获取音视频帧数据

阅读: 评论:0

音视频系列四:ffmpeg之获取音视频帧数据

音视频系列四:ffmpeg之获取音视频帧数据

title: 音视频系列四:ffmpeg之获取音视频帧数据

categories:[ffmpeg]

tags:[音视频编程]

date: 2021/11/29

作者:hackett 微信公众号:加班猿

一、AVFrame解码视频

1.先贴一个ffmpeg解析flv文件20帧数据后的截图,AVFrame是包含码流参数较多的结构体,结构体源码位于libavcodec/avcodec.h中

完整代码:

#include <stdio.h>#ifdef __cplusplus
extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endifint openCodecContext(const AVFormatContext* pFormatCtx, int* pStreamIndex, enum AVMediaType type, AVCodecContext** ppCodecCtx) {int streamIdx = -1;// 获取流下标for (int i = 0; i < pFormatCtx->nb_streams; i++) {if (pFormatCtx->streams[i]->codec->codec_type == type) {streamIdx = i;break;}}if (streamIdx == -1) {printf("find video stream failed!n");exit(-1);}// 寻找解码器AVCodecContext* pCodecCtx = pFormatCtx->streams[streamIdx]->codec;AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if (NULL == pCodec) {printf("avcode find decoder failed!n");exit(-1);}//打开解码器if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {printf("avcode open failed!n");exit(-1);}*ppCodecCtx = pCodecCtx;*pStreamIndex = streamIdx;return 0;
}int main(void)
{AVFormatContext* pInFormatCtx = NULL;AVCodecContext* pVideoCodecCtx = NULL;AVCodecContext* pAudioCodecCtx = NULL;AVPacket* pPacket = NULL;AVFrame* pFrame = NULL;int ret;/* 支持本地文件和网络url */const char streamUrl[] = "./ouput_1min.flv";/* 1. 注册 */av_register_all();pInFormatCtx = avformat_alloc_context();/* 2. 打开流 */if (avformat_open_input(&pInFormatCtx, streamUrl, NULL, NULL) != 0) {printf("Couldn't open input stream.n");return -1;}/* 3. 获取流的信息 */if (avformat_find_stream_info(pInFormatCtx, NULL) < 0) {printf("Couldn't find stream information.n");return -1;}int videoStreamIdx = -1;int audioStreamIdx = -1;/* 4. 寻找并打开解码器 */openCodecContext(pInFormatCtx, &videoStreamIdx, AVMEDIA_TYPE_VIDEO, &pVideoCodecCtx);openCodecContext(pInFormatCtx, &audioStreamIdx, AVMEDIA_TYPE_AUDIO, &pAudioCodecCtx);pPacket = av_packet_alloc();pFrame = av_frame_alloc();int cnt = 20; // 读取20帧数据(音频和视频)while (cnt--) {/* 5. 读流数据, 未解码的数据存放于pPacket */ret = av_read_frame(pInFormatCtx, pPacket);if (ret < 0) {printf("av_read_frame errorn");break;}/* 6. 解码, 解码后的数据存放于pFrame *//* 视频解码 */if (pPacket->stream_index == videoStreamIdx) {avcodec_decode_video2(pVideoCodecCtx, pFrame, &ret, pPacket);if (ret == 0) {printf("video decodec error!n");continue;}printf("* * * * * * video * * * * * * * * *n");printf("___height: [%d]n", pFrame->height);printf("____width: [%d]n", pFrame->width);printf("pict_type: [%d]n", pFrame->pict_type);printf("key_frame: [%d]n", pFrame->key_frame); // 视频关键帧  1 -> 是 0 -> 否printf("___format: [%d]n", pFrame->format);printf("* * * * * * * * * * * * * * * * * * *nn");}/* 音频解码 */if (pPacket->stream_index == audioStreamIdx) {avcodec_decode_audio4(pAudioCodecCtx, pFrame, &ret, pPacket);if (ret < 0) {printf("audio decodec error!n");continue;}printf("* * * * * * audio * * * * * * * * * *n");printf("____nb_samples: [%d]n", pFrame->nb_samples);printf("__samples_rate: [%d]n", pFrame->sample_rate);printf("channel_layout: [%lu]n", pFrame->channel_layout);printf("________format: [%d]n", pFrame->format);printf("* * * * * * * * * * * * * * * * * * *nn");}av_packet_unref(pPacket); /* 将缓存空间的引用计数-1,并将Packet中的其他字段设为初始值。如果引用计数为0,自动的释放缓存空间 */}/* 释放资源 */av_frame_free(&pFrame);av_packet_free(&pPacket);avcodec_close(pVideoCodecCtx);avcodec_close(pAudioCodecCtx);avformat_close_input(&pInFormatCtx);return 0;
}

2.简单介绍一下流程中的各个函数的意义:

av_register_all():注册FFmpeg所有编解码器。

avformat_open_input():打开流的AVFormatContext。

avformat_find_stream_info():获取流的信息。

avcodec_find_encoder():查找编码器。

avcodec_open2():打开编码器。

av_read_frame():读流数据。

avcodec_decode_video2():视频解码。

av_write_frame():将编码后的视频码流写入文件。

av_packet_unref():将缓存空间的引用计数-1,并将Packet中的其他字段设为初始值。如果引用计数为0,自动的释放缓存空间。

二、AVFrame 数据结构

AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息。

这里源码的注释太冗长所以省略了。

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8uint8_t *data[AV_NUM_DATA_POINTERS];int linesize[AV_NUM_DATA_POINTERS];uint8_t **extended_data;int width, height;int nb_samples;int format;int key_frame;enum AVPictureType pict_type;AVRational sample_aspect_ratio;int64_t pts;
#if FF_API_PKT_PTSattribute_deprecatedint64_t pkt_pts;
#endifint64_t pkt_dts;int coded_picture_number;int display_picture_number;int quality;void *opaque;
#if FF_API_ERROR_FRAMEattribute_deprecateduint64_t error[AV_NUM_DATA_POINTERS];
#endifint repeat_pict;int interlaced_frame;int top_field_first;int palette_has_changed;int64_t reordered_opaque;int sample_rate;uint64_t channel_layout;AVBufferRef *buf[AV_NUM_DATA_POINTERS];AVBufferRef **extended_buf;int        nb_extended_buf;AVFrameSideData **side_data;int            nb_side_data;
#define AV_FRAME_FLAG_CORRUPT       (1 << 0)
#define AV_FRAME_FLAG_DISCARD   (1 << 2)int flags;enum AVColorRange color_range;enum AVColorPrimaries color_primaries;enum AVColorTransferCharacteristic color_trc;enum AVColorSpace colorspace;enum AVChromaLocation chroma_location;int64_t best_effort_timestamp;int64_t pkt_pos;int64_t pkt_duration;AVDictionary *metadata;int decode_error_flags;
#define FF_DECODE_ERROR_INVALID_BITSTREAM   1
#define FF_DECODE_ERROR_MISSING_REFERENCE   2
#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE  4
#define FF_DECODE_ERROR_DECODE_SLICES       8int channels;int pkt_size;
#if FF_API_FRAME_QPattribute_deprecatedint8_t *qscale_table;attribute_deprecatedint qstride;attribute_deprecatedint qscale_type;attribute_deprecatedAVBufferRef *qp_table_buf;
#endifAVBufferRef *hw_frames_ctx;AVBufferRef *opaque_ref;size_t crop_top;size_t crop_bottom;size_t crop_left;size_t crop_right;AVBufferRef *private_ref;} AVFrame;

接下来集中看常用的一些结构成员:

2.1 data

 uint8_t *data[AV_NUM_DATA_POINTERS]; // 解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)

data 是一个指针数组,数组的每一个元素是一个指针,指向视频中图像的某一 plane 或音频中某一声道的 plane。

2.2 linesize

int linesize[AV_NUM_DATA_POINTERS]; // data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽

对于视频来说,linesize 每个元素是一个图像 plane 中一行图像的大小(字节数)。注意有对齐要求

对于音频来说,linesize 每个元素是一个音频 plane 的大小(字节数)

linesize 可能会因性能上的考虑而填充一些额外的数据,因此 linesize 可能比实际对应的音视频数据尺寸要大。

2.3 width, height;

int width, height; // 视频帧宽和高()

2.4 nb_samples

int nb_samples; // 音频帧中单个声道中包含的采样点数。

2.5 format

int format; // 解码后原始数据类型

对于视频帧,此值对应于enum AVPixelFormat

enum AVPixelFormat {  AV_PIX_FMT_NONE = -1,  AV_PIX_FMT_YUV420P,   ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)  AV_PIX_FMT_YUYV422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr  AV_PIX_FMT_RGB24,     ///< packed RGB 8:8:8, 24bpp,   AV_PIX_FMT_BGR24,     ///< packed RGB 8:8:8, 24bpp,   AV_PIX_FMT_YUV422P,   ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)  AV_PIX_FMT_YUV444P,   ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)  AV_PIX_FMT_YUV410P,   ///< planar YUV 4:1:0,  9bpp, (1 Cr & Cb sample per 4x4 Y samples)  AV_PIX_FMT_YUV411P,   ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)  AV_PIX_FMT_GRAY8,     ///<        Y        ,  8bpp  AV_PIX_FMT_MONOWHITE, ///<        Y        ,  1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb  AV_PIX_FMT_MONOBLACK, ///<        Y        ,  1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb  AV_PIX_FMT_PAL8,      ///< 8 bit with PIX_FMT_RGB32 palette  AV_PIX_FMT_YUVJ420P,  ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of PIX_FMT_YUV420P and setting (省略)  
}  

对于音频帧,此值对应于enum AVSampleFormat

enum AVSampleFormat {  AV_SAMPLE_FMT_NONE = -1,  AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits  AV_SAMPLE_FMT_S16,         ///< signed 16 bits  AV_SAMPLE_FMT_S32,         ///< signed 32 bits  AV_SAMPLE_FMT_FLT,         ///< float  AV_SAMPLE_FMT_DBL,         ///< double  AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar  AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar  AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar  AV_SAMPLE_FMT_FLTP,        ///< float, planar  AV_SAMPLE_FMT_DBLP,        ///< double, planar AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically  
};  

2.6 key_frame

int key_frame; // 是否是关键帧

2.7 pict_type

enum AVPictureType pict_type; // 帧类型(I,)

视频帧类型(I、B、P 等)

enum AVPictureType {AV_PICTURE_TYPE_NONE = 0, ///< UndefinedAV_PICTURE_TYPE_I,     ///< IntraAV_PICTURE_TYPE_P,     ///< PredictedAV_PICTURE_TYPE_B,     ///< Bi-dir predictedAV_PICTURE_TYPE_S,     ///< S(GMC)-VOP MPEG-4AV_PICTURE_TYPE_SI,    ///< Switching IntraAV_PICTURE_TYPE_SP,    ///< Switching PredictedAV_PICTURE_TYPE_BI,    ///< BI type
};

2.8 sample_aspect_ratio

AVRational sample_aspect_ratio; // 视频宽高比(16:9,)

2.9 pts

int64_t pts; // 显示时间戳 单位是 time_base

2.10 pkt_pts

int64_t pkt_pts; 

此 frame 对应的 packet 中的解码时间戳。是从对应 packet(解码生成此 frame)中拷贝 DTS 得到此值。
如果对应的 packet 中只有 dts 而未设置 pts,则此值也是此 frame 的 pts。

2.11 coded_picture_number

int coded_picture_number; // 编码帧序号

2.12 display_picture_number

int display_picture_number; // 显示帧序号

2.13 interlaced_frame

int interlaced_frame; // 是否是隔行扫描

2.14 sample_rate

int sample_rate; // 音频采样率

2.15 buf

AVBufferRef *buf[AV_NUM_DATA_POINTERS]; 

此帧的数据可以由 AVBufferRef 管理,AVBufferRef 提供 AVBuffer 引用机制

AVBuffer 是 FFmpeg 中很常用的一种缓冲区,缓冲区使用引用计数(reference-counted)机制

2.16 pkt_pos

int64_t pkt_pos; // 最后一个扔进解码器的 packet 在输入文件中的位置偏移量

2.17 pkt_duration

int64_t pkt_duration;// 对应 packet 的时长,单位是 AVStream->time_base

2.18 channels

int channels;// 音频声道数量

2.19 pkt_size

int pkt_size;// 对应 packet 的大小

2.20 crop_

size_t crop_top;
size_t crop_bottom;
size_t crop_left;
size_t crop_right;

用于视频帧图像裁切。四个值分别为从 frame 的上/下/左/右边界裁切的像素数。

如果你觉得文章还不错,可以给个"三连",文章同步到以下个人微信公众号[加班猿]

我是hackett,我们下期见

本文发布于:2024-02-03 02:14:21,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170689766247967.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:音视频   帧数   系列   ffmpeg
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23