4.5 AVformatContext成员很多,在本项目中,只用到以下几个
1、AVIOContext * pb; char filename[1024];
文件IO上下文,可以自定义读写格式 ,自定义读写或者从内存中读,都需要用到AVIOContext
文件名,要把打开的文件名存下来(会断开重连)
2、unsigned int nb_streams; 核心
存数组大小
3、AVStream **streams; 核心
存音视频信息
4、int64_t duration; 获取总长度 ,duration表示媒体文件总长,以AV_TIME_BASE基数为单位,表示1秒钟有多少个单位。其实就是微秒值。可以用宏//AV_TIME_BASE
5、int64_t bit_rate; 比特率,8比特1个字节
6、void avformat_close_input(AVFormatContext **s); 清理封装的上下文——关闭输入的上下文,传指针地址,清理指针空间后,把指针置0
7、其他记录
调试,debug可以看具体信息
4.6 AVStream
1、AVCodecContext *codec;用来做解码器,但是已过时
2、AVRational time_base; 时间基数,他是一个分数
3、int64_t duration; 多少分之一秒,假定time_base=1000分之一,duration就是毫秒
得到毫秒:duration *((double)time_base.num/(double)time_base.den)*1000,den是分母(确保分母不为0),num是分子
4、int64_t nb_frames;
5、AVRational avg_frame_rate; 帧率AVRational 表示有理数
6、**AVCodecParameters codecpar;(音视频参数),用来替代[AVCodecContext codec]
AVCodecParameters *codecpar
1、enum AVMediaType codec_type; 表示编码类型,标识是音频还是视频
2、enum AVCodecID codec_id ; 标识编码格式,是264或者265等
3、unit32_t codec_tag; 用4个字节表示各种编码器
4、int format; 格式,像素格式,或者音频采样格式
5、int width;int height; 视频宽高
6、unit64_t channel_layout; 声道
int channel; 声道数
int sample_rate;样本率
int frame_size;帧大小
4.11av_read_frame 读取帧数据
在读帧和解码时要关注内存问题,ffmpeg的复杂就在于此,会出现内存泄漏
C++在调用函数时候要考虑传入的参数,产生什么空间,空间是怎么清理
该函数有两个参数:
AVFormatContext *s——文件格式上下文
AVPacket *pkt——Packer传了一个指针,需要考虑一些问题,传的这个指针要不要考虑指针空间,要不要预先分配好地址;第二次调用的时候上次的空间怎么处理,比如每次都读的都是同一帧,同一个AVPacket进来,会怎么处理。
注意,pkt不能传null,必须是一个空间
每次传同一个对象进来不会清理旧的内存空间,会开辟新的地址空间使用,导致内存不断增加
return 0 表示成功,<0 on error or end of file
AVPacket 成员
AVBufferRef buf ; 存储引用计数
int64_t pts; // pts (num/den); 显示时间
int64_t dts;// 解码时间
unit8_t* data; 指向AVBufferRef 再分配的空间
int size; 用接口删除
AVPacket 函数 ——空间申请赋值清理用到的函数
AVPacker * av_packet_alloc(void) ; 空间创建并初始化;创建对象,申请堆上的空间,需要释放
AVPacket * av_packet_clone(const AVPacket * str) ; 空间复制,创建并引用计数
int av_packer_ref(AVPacket *dst,const AVPacket * str); 手动引用加1,是内部做封装或者做处理的时候,手动地把原始空间加到目标空间,要确保AVPacket 已经创建并且初始化好。等同于前面的clone
av_packet_unref(AVPacket *pkt) ; 减少引用,把packet引用计数减1,减到0就会删掉
void av_packet_free(AVPacket **pkt);清空对象并减引用计数
void av_init_packet(AVPacket *pkt);默认值,初始化接口
int av_packet_from_data(AVPacket *pkt, unit8_t *data,int size); 自定义转成avpacket 的包
int av_copy_packet(AVPacket *dst,const AVPacket *src); attribute_deprecated 早期的函数,已经被抛弃
4.12av_seek_frame函数 进度条拖动操作
函数原型
int av_seek_frame(AVFormatContext *s, // 封装格式上下文
int stream_index, // 索引,针对音频和视频都可以 -1 default (默认视频)
int64_t timestamp, // 时间戳,移动到哪个时间位置,与AVStream.time_base时间基数一样
int flags); // 标识位。表示移的方法
Q:一段媒体中,有音视频,选择哪个来移动
A:用视频来做seek,选择关键帧
用音频来做seek有这样的问题:音频没有b帧
视频有b帧,比如第7秒不是关键帧,则解不出来,视频必须移动到关键帧的位置,所以stream_index选择videostream视频
Q:时间戳近似问题
A:当前位置和进度条的比例✖总时间得到位置,
av_seek_frame flag
#define AVSEEK_FLAG_BACKWARD 1 // seek backward 往后(后指的是时间早的)走
#define AVSEEK_FLAG_BYTE 2 // seek based on position in bytes
#define AVSEEK_FLAG_ANY 4 // seek to any frame,even non-keyframes 找最近(后)的一帧,但是一般不这么做,都要找关键帧,不是关键帧会花屏
#define AVSEEK_FLAG_FRAME 8 // seek based on frame number 往后找关键帧
音频和视频顺序:一般是一帧图像后跟2帧音频
时间一开始是负数?
设置断点调试,发现一开始就是负值,原始数据就是负值,表示在0之前,要预先处理
整个解封装过程代码
整个解封装代码
#include <iostream>
#include<thread> //线程
extern "C" {
#include "libavformat/avformat.h"
}
using namespace std;
#pragma comment(lib,"avformat.lib")<