解封装

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;
}


到这里已经从文件中读取到数据了,读取到一帧数据,就发送一帧数据给解码对象处理.

下一章记录解码过程!!!!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

07-03 13:57