美文网首页
音视频流媒体开发【七】-FFmpeg解封装流程

音视频流媒体开发【七】-FFmpeg解封装流程

作者: AlanGe | 来源:发表于2023-02-22 19:45 被阅读0次

    音视频流媒体开发-目录

    解封装

    封装格式相关函数

    ◼ avformat_alloc_context(); 负责申请一个AVFormatContext结构的内存,并进行简单初始化
    ◼ avformat_free_context(); 释放该结构里的所有东西以及该结构本身
    ◼ avformat_close_input(); 关闭解复用器。关闭后就不再需要使用avformat_free_context 进行释放。
    ◼ avformat_open_input(); 打开输入视频文件
    ◼ avformat_find_stream_info(); 获取视频文件信息
    ◼ av_read_frame(); 读取音视频包
    ◼ avformat_seek_file(); 定位文件
    ◼ av_seek_frame(); 定位文件

    解封装流程

    FFmpeg数据结构之间的关系

    区分不同的码流

    ◼ AVMEDIA_TYPE_VIDEO视频流
    video_index = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,-1,-1, NULL, 0)

    ◼ AVMEDIA_TYPE_AUDIO音频流
    audio_index = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,-1,-1, NULL, 0)

    AVPacket 里面也有一个index的字段

    重点

    avformat_open_input和avformat_find_stream_info分别用于打开一个流和分析流信息。

    在初始信息不足的情况下(比如FLV和H264文件),avformat_find_stream_info接口需要在内部调用read_frame_internal接口读取流数据(音视频帧),然后再分析后,设置核心数据结构AVFormatContext。

    由于需要读取数据包,avformat_find_stream_info接口会带来很大的延迟。

    分离AAC文件

    分离H264文件

    案例

    创建工程07-01-ffmpeg-demux
    将ffmpeg-4.2.1-win32-dev放到工程目录

    07-01-ffmpeg-demux.pro
    
    TEMPLATE = app
    CONFIG += console
    CONFIG -= app_bundle
    CONFIG -= qt
    
    SOURCES += main.c
    
    win32 {
    INCLUDEPATH += $$PWD/ffmpeg-4.2.1-win32-dev/include
    LIBS += $$PWD/ffmpeg-4.2.1-win32-dev/lib/avformat.lib   \
            $$PWD/ffmpeg-4.2.1-win32-dev/lib/avcodec.lib    \
            $$PWD/ffmpeg-4.2.1-win32-dev/lib/avdevice.lib   \
            $$PWD/ffmpeg-4.2.1-win32-dev/lib/avfilter.lib   \
            $$PWD/ffmpeg-4.2.1-win32-dev/lib/avutil.lib     \
            $$PWD/ffmpeg-4.2.1-win32-dev/lib/postproc.lib   \
            $$PWD/ffmpeg-4.2.1-win32-dev/lib/swresample.lib \
            $$PWD/ffmpeg-4.2.1-win32-dev/lib/swscale.lib
    }
    
    main.c
    
    #include <stdio.h>
    #include <libavformat/avformat.h>
    
    
    int main(int argc, char **argv)
    {
        //打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
    //    avformat_network_init();
    
        const char *default_filename = "believe.mp4";
    
        char *in_filename = NULL;
    
        if(argv[1] == NULL)
        {
            in_filename = default_filename;
        }
        else
        {
            in_filename = argv[1];
        }
        printf("in_filename = %s\n", in_filename);
    
        //AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
        AVFormatContext *ifmt_ctx = NULL;           // 输入文件的demux
    
        int videoindex = -1;        // 视频索引
        int audioindex = -1;        // 音频索引
    
    
        // 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接
        int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
        if (ret < 0)  //如果打开媒体文件失败,打印失败原因
        {
            char buf[1024] = { 0 };
            av_strerror(ret, buf, sizeof(buf) - 1);
            printf("open %s failed:%s\n", in_filename, buf);
            goto failed;
        }
    
        ret = avformat_find_stream_info(ifmt_ctx, NULL);
        if (ret < 0)  //如果打开媒体文件失败,打印失败原因
        {
            char buf[1024] = { 0 };
            av_strerror(ret, buf, sizeof(buf) - 1);
            printf("avformat_find_stream_info %s failed:%s\n", in_filename, buf);
            goto failed;
        }
    
        //打开媒体文件成功
        printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);
        av_dump_format(ifmt_ctx, 0, in_filename, 0);
        printf_s("\n==== av_dump_format finish =======\n\n");
        // url: 调用avformat_open_input读取到的媒体文件的路径/名字
        printf("media name:%s\n", ifmt_ctx->url);
        // nb_streams: nb_streams媒体流数量
        printf("stream number:%d\n", ifmt_ctx->nb_streams);
        // bit_rate: 媒体文件的码率,单位为bps
        printf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));
        // 时间
        int total_seconds, hour, minute, second;
        // duration: 媒体文件时长,单位微妙
        total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE;  // 1000us = 1ms, 1000ms = 1秒
        hour = total_seconds / 3600;
        minute = (total_seconds % 3600) / 60;
        second = (total_seconds % 60);
        //通过上述运算,可以得到媒体文件的总时长
        printf("total duration: %02d:%02d:%02d\n", hour, minute, second);
        printf("\n");
        /*
         * 老版本通过遍历的方式读取媒体文件视频和音频的信息
         * 新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果
         */
        for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++)
        {
            AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流
            //如果是音频流,则打印音频的信息
            if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
            {
                printf("----- Audio info:\n");
                // index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识
                printf("index:%d\n", in_stream->index);
                // sample_rate: 音频编解码器的采样率,单位为Hz
                printf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);
                // codecpar->format: 音频采样格式
                if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format)
                {
                    printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");
                }
                else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format)
                {
                    printf("sampleformat:AV_SAMPLE_FMT_S16P\n");
                }
                // channels: 音频信道数目
                printf("channel number:%d\n", in_stream->codecpar->channels);
                // codec_id: 音频压缩编码格式
                if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id)
                {
                    printf("audio codec:AAC\n");
                }
                else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id)
                {
                    printf("audio codec:MP3\n");
                }
                else
                {
                    printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);
                }
                // 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
                if(in_stream->duration != AV_NOPTS_VALUE)
                {
                    int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
                    //将音频总时长转换为时分秒的格式打印到控制台上
                    printf("audio duration: %02d:%02d:%02d\n",
                           duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));
                }
                else
                {
                    printf("audio duration unknown");
                }
    
                printf("\n");
    
                audioindex = i; // 获取音频的索引
            }
            else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type)  //如果是视频流,则打印视频的信息
            {
                printf("----- Video info:\n");
                printf("index:%d\n", in_stream->index);
                // avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧
                printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));
                if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式
                {
                    printf("video codec:MPEG4\n");
                }
                else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式
                {
                    printf("video codec:H264\n");
                }
                else
                {
                    printf("video codec_id:%d\n", in_stream->codecpar->codec_id);
                }
                // 视频帧宽度和帧高度
                printf("width:%d height:%d\n", in_stream->codecpar->width,
                       in_stream->codecpar->height);
                //视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
                if(in_stream->duration != AV_NOPTS_VALUE)
                {
                    int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);
                    printf("video duration: %02d:%02d:%02d\n",
                           duration_video / 3600,
                           (duration_video % 3600) / 60,
                           (duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上
                }
                else
                {
                    printf("video duration unknown");
                }
    
                printf("\n");
                videoindex = i;
            }
        }
    
        AVPacket *pkt = av_packet_alloc();
    
        int pkt_count = 0;
        int print_max_count = 10;
        printf("\n-----av_read_frame start\n");
        while (1)
        {
            ret = av_read_frame(ifmt_ctx, pkt);
            if (ret < 0)
            {
                printf("av_read_frame end\n");
                break;
            }
    
            if(pkt_count++ < print_max_count)
            {
                if (pkt->stream_index == audioindex)
                {
                    printf("audio pts: %lld\n", pkt->pts);
                    printf("audio dts: %lld\n", pkt->dts);
                    printf("audio size: %d\n", pkt->size);
                    printf("audio pos: %lld\n", pkt->pos);
                    printf("audio duration: %lf\n\n",
                           pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));
                }
                else if (pkt->stream_index == videoindex)
                {
                    printf("video pts: %lld\n", pkt->pts);
                    printf("video dts: %lld\n", pkt->dts);
                    printf("video size: %d\n", pkt->size);
                    printf("video pos: %lld\n", pkt->pos);
                    printf("video duration: %lf\n\n",
                           pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));
                }
                else
                {
                    printf("unknown stream_index:\n", pkt->stream_index);
                }
            }
    
            av_packet_unref(pkt);
        }
    
        if(pkt)
            av_packet_free(&pkt);
    failed:
        if(ifmt_ctx)
            avformat_close_input(&ifmt_ctx);
    
    
        getchar(); //加上这一句,防止程序打印完信息马上退出
        return 0;
    }
    
    

    构建项目
    将 believe.flv和believe.mp4 拷贝到build-07-01-ffmpeg-demux-Desktop_Qt_5_10_1_MinGW_32bit-Debug

    设置

    运行

    in_filename = believe.flv
    
    ==== av_dump_format in_filename:believe.flv ===
    Input #0, flv, from 'believe.flv':
      Metadata:
        major_brand     : isom
        minor_version   : 512
        compatible_brands: isomiso2avc1mp41
        comment         : www.ieway.cn
        encoder         : Lavf58.29.100
      Duration: 00:03:42.53, start:
    
    video dts: 81
    video size: 580
    video pos: 68691
    video duration: 0.066000
    
    audio pts: 85
    audio dts: 85
    audio size: 341
    audio pos: 69291
    audio duration: 0.021000
    
    audio pts: 107
    audio dts: 107
    audio size: 342
    audio pos: 69649
    audio duration: 0.021000
    
    audio pts: 128
    audio dts: 128
    audio size: 341
    audio pos: 70008
    audio duration: 0.021000
    
    video pts: 147
    video dts: 147
    video size: 11289
    video pos: 70366
    video duration: 0.066000
    
    av_read_frame end
    

    设置

    运行

    相关文章

      网友评论

          本文标题:音视频流媒体开发【七】-FFmpeg解封装流程

          本文链接:https://www.haomeiwen.com/subject/mclbkdtx.html