• MPEG 解封装



    MPEG 解封装


    因为工作需求,需要将MP4/MKV中的H264/H265裸流给拉出来单独保存。
    网上搜索以后前人已经有了一些探索,比如:
    最简单的基于FFmpeg的封装格式处理:视音频分离器简化版
    从flv文件中提取h264码流(使用av_bsf_send_packet和av_bsf_receive_packet)

    流程

    大概流程就是拆包,拿到一帧帧的AVPacket以后转存到文件中就行,但测试发现MP4和MKV中的packet没有SPS和PPS头,所以我们就需要使用h264_mp4toannexbhevc_mp4toannexb这两个bitstream filter来对packet进行处理。

    h264_mp4toannexb

    @section h264_mp4toannexb
    
    Convert an H.264 bitstream from length prefixed mode to start code
    prefixed mode (as defined in the Annex B of the ITU-T H.264
    specification).
    
    This is required by some streaming formats, typically the MPEG-2
    transport stream format (muxer @code{mpegts}).
    
    For example to remux an MP4 file containing an H.264 stream to mpegts
    format with @command{ffmpeg}, you can use the command:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    贴上h264_mp4toannexb的相关信息,简单来说就是将bitstream从length prefixed mode 转为 start code prefixed mode,更为具体的信息可以参考H.264的附录B(annexb)

    hevc_mp4toannexb

    @section hevc_mp4toannexb
    
    Convert an HEVC/H.265 bitstream from length prefixed mode to start code
    prefixed mode (as defined in the Annex B of the ITU-T H.265
    specification).
    
    This is required by some streaming formats, typically the MPEG-2
    transport stream format (muxer @code{mpegts}).
    
    For example to remux an MP4 file containing an HEVC stream to mpegts
    format with @command{ffmpeg}, you can use the command:
    
    @example
    ffmpeg -i INPUT.mp4 -codec copy -bsf:v hevc_mp4toannexb OUTPUT.ts
    @end example
    
    Please note that this filter is auto-inserted for MPEG-TS (muxer
    @code{mpegts}) and raw HEVC/H.265 (muxer @code{h265} or
    @code{hevc}) output formats.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    H.265情况与H.264情况类似。

    代码

    主要使用av_bsf_send_packetav_bsf_receive_packet来实现。

    int mp4toannexb(DEMUXER_T *demuxer, AVPacket *pkt)
    {
        int ret;
    
        ret = av_bsf_send_packet(demuxer->bsf_ctx, pkt);
        if (ret < 0) {
            printf("bsf send packet failed, errno: %d \n", ret);
            return -1;
        }
    
        for(;;) {
            ret = av_bsf_receive_packet(demuxer->bsf_ctx, pkt);
            if (AVERROR_EOF == ret || AVERROR(EAGAIN) == ret) {
                return 0;
            }
            if (ret < 0) {
                printf("Could not receive packet, errno: %d \n", ret);
                return -1;
            }
        }
    }
    
    int create_filter(DEMUXER_T *demuxer) {
        int ret, codec_id = demuxer->ctx->streams[demuxer->video_index]->codecpar->codec_id;
        const AVBitStreamFilter *filter;
    
        switch (codec_id) {
        case AV_CODEC_ID_H264:
            filter = av_bsf_get_by_name("h264_mp4toannexb");
            break;
        case AV_CODEC_ID_HEVC:
            filter = av_bsf_get_by_name("hevc_mp4toannexb");
            break;
        default:
            printf("Unspport Codec id: %d \n", codec_id);
            return -1;
        }
    
        if (NULL == filter) {
            printf("Could not create filter \n");
            return -1;
        }
    
        ret = av_bsf_alloc(filter, &demuxer->bsf_ctx);
        if (ret < 0) {
            printf("Could not alloc bitstream filter \n");
            return -1;
        }
    
        // avcodec_parameters_from_context
        ret = avcodec_parameters_copy(demuxer->bsf_ctx->par_in, demuxer->ctx->streams[demuxer->video_index]->codecpar);
        if (ret < 0) {
            printf("Parameter copy filed, errno: %d \n", ret);
            goto L_ERR_PARAMETERS_COPY;
        }
    
        ret = av_bsf_init(demuxer->bsf_ctx);
        if (ret < 0) {
            printf("BSF init failed, errno: %d \n", ret);
            goto L_ERR_BSF_INIT;
        }
    
        return 0;
    
    L_ERR_BSF_INIT:
    L_ERR_PARAMETERS_COPY:
        av_bsf_free(&demuxer->bsf_ctx);
        return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    完整的代码已上传GitHub(mpeg_demuxer)。

    ffmpeg命令行操作

    代码写完后发现其实一条命令就能解决问题,-_-||

    ffmpeg -i h264.mp4 -c:v copy -bsf:v h264_mp4toannexb -an out.h264
    
    • 1

    写在最后

    一般来说只有H.264和hevc有裸流,其他编码格式一般没有。
    比如mkv格式的vp9,我们一般将它转为ivf格式。

    ffmpeg -i input.mkv -c copy stream.vp9.ivf
    ffmpeg -i input.webm -c copy stream.ivf
    
    • 1
    • 2
  • 相关阅读:
    山东省创新型中小企业认证解读
    VMware 与戴尔正式“分手”
    CSS相关
    Servlet【方法使用】
    gitbook在centos上安装
    Cent OS安装中文字体
    设计原则之显式依赖关系
    如何通俗理解超级浏览器?可以用于哪些场景?有哪些品牌?
    关于共识算法Raft的常见误解
    终于有清华大佬把“:JVM入门以及Class格式”给大家讲解透彻了
  • 原文地址:https://blog.csdn.net/raykwok1150/article/details/126016273