解封装
1.常用函数:
- av_register_all():注册所有组件
- avformat_network_init() : 初始化网络环境
- avformat_open_input(...) : 打开输入的视频文件
- avformat_find_stream_info(...) : 获取视频文件信息
- av_find_best_stream(...) : 获取音视频或者字幕的stream_index
- av_read_frame(...) : 从输入文件读取一帧压缩数据
2.结构体:
- AVFormatContext :
https://blog.csdn.net/qq_25333681/article/details/80428852
AVFormatContext是API中直接接触到的结构体,位于avformat.h中,是音视频数据,也就是音视频文件的一种抽象和封装,
该文件包含了多路流,包括音频流、视频流、字幕流等。该结构体的使用,贯穿了ffmpeg使用的整个流程。
- AVStream
https://www.jianshu.com/p/f6e409d62407
是存储每一个视频/音频流信息的结构体,位于avformat.h文件中
int index; //在AVFormatContext中的索引,这个数字是自动生成的,可以通过这个数字从AVFormatContext::streams表中索引到该流。
int id;//流的标识,依赖于具体的容器格式。解码:由libavformat设置。编码:由用户设置,如果未设置则由libavformat替换。
AVCodecContext *codec;//指向该流对应的AVCodecContext结构,调用avformat_open_input时生成。
AVRational time_base;//这是表示帧时间戳的基本时间单位(以秒为单位)。该流中媒体数据的pts和dts都将以这个时间基准为粒度。
int64_t start_time;//流的起始时间,以流的时间基准为单位。如需设置,100%确保你设置它的值真的是第一帧的pts。
int64_t duration;//解码:流的持续时间。如果源文件未指定持续时间,但指定了比特率,则将根据比特率和文件大小估计该值。
int64_t nb_frames; //此流中的帧数(如果已知)或0。
enum AVDiscard discard;//选择哪些数据包可以随意丢弃,不需要去demux。
AVRational sample_aspect_ratio;//样本长宽比(如果未知,则为0)。
AVDictionary *metadata;//元数据信息。
AVRational avg_frame_rate;//平均帧速率。解封装:可以在创建流时设置为libavformat,也可以在avformat_find_stream_info()中设置。封装:可以由调用者在avformat_write_header()之前设置。
AVPacket attached_pic;//附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面。
int probe_packets;//编解码器用于probe的包的个数。
int codec_info_nb_frames;//在av_find_stream_info()期间已经解封装的帧数。
int request_probe;//流探测状态,1表示探测完成,0表示没有探测请求,rest 执行探测。
int skip_to_keyframe;//表示应丢弃直到下一个关键帧的所有内容。
int skip_samples;//在从下一个数据包解码的帧开始时要跳过的采样数。
int64_t start_skip_samples;//如果不是0,则应该从流的开始跳过的采样的数目。
int64_t first_discard_sample;//如果不是0,则应该从流中丢弃第一个音频样本。
int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];//内部数据,从pts生成dts。
int64_t last_dts_for_order_check;
uint8_t dts_ordered;
uint8_t dts_misordered;//内部数据,用于分析dts和检测故障mpeg流。
AVRational display_aspect_ratio;//显示宽高比。
- AVPacket
https://www.jianshu.com/p/bb6d3905907e
AVPacket是ffmpeg中很重要的一个数据结构,它保存了解封装之后,解码之前的数据(仍然时压缩后的数据)和关于这些数据的一些附加信息,如显示时间戳(pts),解码时间戳(dts),数据时长(duration),所在流媒体的索引(stream_index)等等.
对于视频来说,AVPacket通常包含一个压缩的Frame;而音频则有可能包含多个压缩的Frame。并且,一个packet也有可能是空的,不包含任何压缩数据,只包含边缘数据side data(容器提供的关于packet的一些附加信息,例如,在编码结束的时候更新一些流的参数)。
AVPacket的大小是公共的ABI(Public ABI)一部分,这样的结构体在FFmpeg很少,由此也可见AVPacket的重要性,它可以被分配在栈空间上(可以使用语句AVPacket pkt;在栈空间定义一个Packet),并且除非libavcodec和libavformat有很大改动,不然不会在AVPacket中添加新的字段.
3.解封装步骤
①初始化
//ffmpeg 注册所有封装器
av_register_all();
//注册所有的解码器
avcodec_register_all();
//初始化网络
avformat_network_init();
②打开文件 avformat_open_input();
mux.lock();
// 打开文件
int ret = avformat_open_input(&avFormatContext, url, 0, 0);
if (ret != 0) {
mux.unlock();
char buf[1024] = {0};
//打印失败信息
av_strerror(ret, buf, sizeof(buf));
XLOGE("=============>> avformat_open_input failed!");
return false;
}
//读取文件信息
ret = avformat_find_stream_info(avFormatContext, 0);
if (ret != 0) {
mux.unlock();
char buf[1024] = {0};
av_strerror(ret, buf, sizeof(buf));
XLOGE("=============>> avformat_find_stream_info %s failed!", url);
return false;
}
isOPen = true;
// 文件时长转换单位
this->totalMs = avFormatContext->duration / (AV_TIME_BASE / 1000);
mux.unlock();
③开启线程,循环读取文件中数据 av_read_frame();
//循环读取文件数据(调用Read()函数,每次读取一帧数据)
void IDemux::Main() {
while (!isExit) {
if (IsPausing()) {
XSleep(2);
continue;
}
XData d = Read();
// 将读取到的数据发送出去(发送给解码对象)
if (d.size > 0) {
observable->Notify(d);
continue;
} else {
XSleep(2);
}
}
};
//读取一帧数据,数据由调用者清理
XData FFDemux::Read() {
mux.lock();
if (!avFormatContext) {
mux.unlock();
return XData();
}
XData data;
AVPacket *avPacket = av_packet_alloc();
int ret = av_read_frame(avFormatContext, avPacket);
if (ret != 0) {
mux.unlock();
//释放内存
av_packet_free(&avPacket);
return XData();
}
//转换pts
avPacket->pts = avPacket->pts *
(1000 * r2d(avFormatContext->streams[avPacket->stream_index]->time_base));
avPacket->dts = avPacket->dts *
(1000 * r2d(avFormatContext->streams[avPacket->stream_index]->time_base));
data.data = (unsigned char *) avPacket;
data.size = avPacket->size;
if (avPacket->stream_index == videoStream) {
data.decodeType = DECODE_TYPE_VIDEO; //视频数据
} else if (avPacket->stream_index == audioStream) {
data.decodeType = DECODE_TYPE_AUDIO; //音频数据
} else {
mux.unlock();
av_packet_free(&avPacket);
return XData();
}
mux.unlock();
return data;
}
到这里已经从文件中读取到数据了,读取到一帧数据,就发送一帧数据给解码对象处理.
下一章记录解码过程!!!!