音视频-H264编码封装- MP4格式转Annex B格式

目录

1:H264语法结构回顾

2:H264编码补充介绍

3:MP4模式转Annex B模式输出到文件示例


1:H264语法结构回顾

在之前文章里介绍过H264的语法结构。

传送门: 视音频-H264 编码NALU语法结构简介 

2:H264编码补充介绍

   H.264视频编码标准中两种常见的封装方式:annexb模式和mp4模式。

  1. annexb模式

    • 这是传统的封装模式,其中每个视频帧的数据(NALUnit)在传输时以0x000001或0x0000001作为分隔符。
    • SPS(Sequence Parameter Set)和PPS(Picture Parameter Set)等关键信息通常会周期性地在视频流内部重复,特别是在关键帧(I帧)之前。
    • 这种模式下,视频帧的数据相对独立,不依赖于容器格式。
  2. mp4模式

    • 在这种模式下,SPS、PPS和其他相关信息被封装到了容器(如MP4、MKV)中,而不是直接在视频流内部传输。
    • 每个视频帧之前通常有一个4字节的长度字段,用于指示下一个视频帧的长度。
    • 在这种模式下,视频帧的数据是按照容器格式进行封装和传输的。

因为一些解码器只支持annexb模式,所以有时候需要将mp4格式的视频数据转换为annexb模式。在FFmpeg中,可以使用h264_mp4toannexb_filter过滤器来进行这种转换。

实现大概流程如下:

3:MP4模式转Annex B模式输出到文件示例

(填充sps pps 到H264头部信息 并输出H264文件)

        这段代码的主要功能是从MP4文件中提取H264视频流,使用bitstream过滤器将H264从MP4格式转换为AnnexB格式,并将转换后的数据写入到一个新的文件中。代码使用了FFmpeg库中的函数来处理媒体文件和Packet。程序首先查找输入媒体文件中的视频流,然后读取每个Packet,如果Packet属于视频流,则使用bitstream过滤器进行处理,并将处理后的数据包写入到输出文件中。如果出错或到达文件末尾,则退出循环并释放所有资源。

#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/log.h>// 定义一个全局的缓冲区,用于存储错误信息
static char err_buff[128] = {0};// 函数:获取错误信息
// 参数:errnum - 错误码
// 返回值:指向错误信息字符串的指针
static char* av_get_error(int errnum)
{// 使用av_strerror函数将错误码转换为可读的错误信息,并存储在err_buff中av_strerror(errnum, err_buff, 128);return err_buff;
}int main(int argc, char **argv)
{// 定义FFmpeg的格式上下文,用于存储输入文件的多媒体流信息AVFormatContext *ifmt_ctx = NULL;// 存储视频流的索引int             videoindex = -1;// 定义AVPacket,用于存储编解码数据包AVPacket        *pkt = NULL;// 用于存储函数调用的返回值int             ret = -1;// 标记文件是否读取结束int             file_end = 0;// 检查命令行参数数量,至少需要输入文件和输出文件两个参数if(argc < 3){printf("usage: %s input outfile\n", argv[0]);return -1;}// 打开输出文件FILE *outfp=fopen(argv[2],"wb");printf("in:%s out:%s\n", argv[1], argv[2]);// 分配格式上下文ifmt_ctx = avformat_alloc_context();if (!ifmt_ctx){printf("Could not allocate context.\n");return -1;}// 打开输入文件,准备读取多媒体流ret = avformat_open_input(&ifmt_ctx,argv[1], NULL, NULL);if(ret != 0){printf("avformat_open_input: %s\n", av_get_error(ret));return -1;}// 扫描文件,直到找到所有流的信息ret = avformat_find_stream_info(ifmt_ctx, NULL);if(ret < 0){printf("avformat_find_stream_info: %s\n", av_get_error(ret));avformat_close_input(&ifmt_ctx);return -1;}// 查找最佳的视频流videoindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if(videoindex == -1){avformat_close_input(&ifmt_ctx);return -1;}// 分配一个AVPacketpkt = av_packet_alloc();av_init_packet(pkt);// 获取H.264 MP4到AnnexB比特流过滤器const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");// 分配一个比特流过滤器上下文AVBSFContext *bsf_ctx = NULL;av_bsf_alloc(bsfilter, &bsf_ctx);// 将视频流的编解码参数复制到过滤器的输入参数中avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codecpar);// 初始化比特流过滤器av_bsf_init(bsf_ctx);// 循环读取数据包,直到文件结束file_end = 0;while (0 == file_end){// 从格式上下文中读取一个数据包if((ret = av_read_frame(ifmt_ctx, pkt)) < 0){// 如果没有更多的数据包,则标记文件结束file_end = 1;printf("read end: ret:%d\n", ret);}// 如果读取到的数据包属于视频流if(ret == 0 && pkt->stream_index == videoindex){// 存储原始数据包的大小int input_size = pkt->size;// 计数器,用于统计输出的数据包数量int out_pkt_count = 0;// 通过比特流过滤器发送数据包if (av_bsf_send_packet(bsf_ctx, pkt) != 0){av_packet_unref(pkt);continue;}av_packet_unref(pkt); // 释放pkt,因为过滤器会使用它// 从过滤器接收转换后的数据包,并写入输出文件while(av_bsf_receive_packet(bsf_ctx, pkt) == 0){out_pkt_count++;// 将数据包的内容写入输出文件size_t size = fwrite(pkt->data, 1, pkt->size, outfp);if(size != pkt->size){printf("fwrite failed-> write:%u, pkt_size:%u\n", size, pkt->size);}av_packet_unref(pkt); // 释放数据包}// 如果原始数据包较大,但是只得到了一个输出数据包,打印警告信息if(out_pkt_count >= 2){printf("curent pkt(size:%d), only get 1 out pkt, get %d pkts\n",input_size, out_pkt_count);}}else{// 如果读取到的数据包不是视频流,则释放它if(ret == 0)av_packet_unref(pkt);}}// 关闭输出文件if(outfp)fclose(outfp);// 释放比特流过滤器上下文if(bsf_ctx)av_bsf_free(&bsf_ctx);// 释放AVPacketif(pkt)av_packet_free(&pkt);// 关闭输入的格式上下文if(ifmt_ctx)avformat_close_input(&ifmt_ctx);// 打印完成信息printf("finish\n");return 0;
}

相关函数如下:

  1. av_bsf_get_by_name("h264_mp4toannexb")

    • 功能:获取指定名称的比特流过滤器。
    • 参数:比特流过滤器的名称。
    • 返回值:指向 AVBitStreamFilter 结构的指针,如果找不到指定名称的过滤器,则返回 NULL。
  2. av_bsf_alloc(bsfilter, &bsf_ctx)

    • 功能:为指定的比特流过滤器分配上下文内存。
    • 参数:
      • bsfilter:要分配上下文的比特流过滤器。
      • bsf_ctx:用于存储分配的过滤器上下文的指针。
    • 返回值:成功返回 0,失败返回负值错误代码。
  3. avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codecpar)

    • 功能:复制给定的编解码器参数到过滤器上下文的输入参数中。
    • 参数:
      • bsf_ctx->par_in:过滤器上下文的输入参数。
      • ifmt_ctx->streams[videoindex]->codecpar:要复制的编解码器参数。
    • 返回值:无。
  4. av_bsf_init(bsf_ctx)

    • 功能:初始化指定的比特流过滤器上下文。
    • 参数:要初始化的过滤器上下文。
    • 返回值:成功返回 0,失败返回负值错误代码。
  5. av_bsf_send_packet(bsf_ctx, pkt)

    • 功能:发送数据包给比特流过滤器进行处理。
    • 参数:
      • bsf_ctx:要发送数据包的比特流过滤器上下文。
      • pkt:要发送的数据包。
    • 返回值:成功返回 0,表示数据包被成功发送给过滤器;否则返回负值错误代码。
  6. av_bsf_receive_packet(bsf_ctx, pkt)

    • 功能:从比特流过滤器接收处理后的数据包。
    • 参数:
      • bsf_ctx:要从中接收数据包的比特流过滤器上下文。
      • pkt:用于存储接收到的数据包。
    • 返回值:成功返回 0,表示成功接收到数据包;否则返回负值错误代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/836055.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

泽众财务RPA机器人常见五个应用场景

泽众RPA&#xff08;即机器人流程自动化&#xff0c;Robotic Process Automation, RPA&#xff09;解决方案是依托于各类先进信息技术手段的虚拟劳动力 &#xff08;数字劳动力&#xff09;&#xff0c;根据预先设定的程序操作指令对任务进行自动化处理&#xff0c;实现业务流程…

网络安全等级保护的发展历程

1994年国务院147号令第一次提出&#xff0c;计算机信息系统实行安全等级保护&#xff0c;这也预示着等保的起步。 2007年《信息安全等级保护管理办法》的发布之后。是等保在各行业深耕落地的时代。 2.0是等保版本的俗称&#xff0c;不是等级。等保共分为五级&#xff0c;二级…

JeeSite V5.7.0 发布,Java快速开发平台,Vite5、多项重构重磅升级

JeeSite V5.7.0 发布&#xff0c;Java快速开发平台&#xff0c;Vite5、多项重构重磅升级 升级内容 新增 参数配置 IP 地址黑白名单过滤器动态参数 新增 侧边栏是否展开第一个菜单的开关 first-open 新增 AesTypeHandler 处理字段数据加密解密或脱敏 新增 JsonTypeHandler …

AI换脸原理(7)——人脸分割参考文献TernausNet: 源码解析

1、介绍 这篇论文相对来说比较简单,整体是通过使用预训练的权重来提高U-Net的性能,实现对UNet的改进。该方法也是DeepFaceLab官方使用的人脸分割方法。在介绍篇我们已经讲过了UNet的网络结构和设计,在进一步深入了解TernausNet之前,我们先简单回顾下UNet。 U-Net的主要结构…

网络基础(三)——网络层

目录 IP协议 1、基本概念 2、协议头格式 2.1、报头和载荷如何有效分离 2.2、如果超过了MAC的规定&#xff0c;IP应该如何做呢&#xff1f; 2.3、分片会有什么影响 3、网段划分 4、特殊的ip地址 5、ip地址的数量限制 6、私有ip地址和公网ip地址 7、路由 IP协议 网络…

Docker尚硅谷_高级篇

Docker尚硅谷 高级篇一、Dockerfile1.1 Dockerfile1.2 构建过程1.3 Dockerfile保留字1.3 构建镜像1.4 虚悬镜像 二、Docker发布微服务2.1 搭建SpringBoot项目2.2 发布微服务项目到Docker容器 三、Docker网络3.1 Docker网络3.2 docker网络命令3.3 Docker网络模式3.4 docker03.5 …

sql注入之bool盲注

目录 盲注步骤 1、进入靶场 2、如下图所示输入&#xff1f;id1‘ 判断此时存在注入点 3、判断列数 ​编辑 4、开始盲注 普通的python脚本 代码思想 结果 二分查找python脚本 二分查找算法思想简介 二分查找与普通查找的主要差距 代码思想 代码 结果​编辑 下面以…

后端项目开发笔记

Maven打包与JDK版本不对应解决方法 我这里使用jdk8。 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configurat…

趣味软件-吃什么(Eat What)?

&#x1f354;&#x1f35c;&#x1f355; 你是否也有这样的日常烦恼&#xff1f; 每天的“世纪难题”——今天吃什么&#xff1f; &#x1f570;️ 饭点到了&#xff0c;脑袋空空&#xff0c;选择困难症大爆发&#xff01; &#x1f46b; 和女朋友约会&#xff0c;却不知道她的…

HackMyVM-Minimal

目录 信息收集 arp nmap nikto whatweb WEB web信息收集 gobuster 文件包含漏洞 提权 web信息收集 main方法 question_1 question_2 question_3 prize.txt 软连接 信息收集 arp ┌──(root?0x00)-[~/HackMyVM] └─# arp-scan -l Interface: eth0, type: E…

.NET_NLog

步骤 1. 添加依赖 ①Microsoft.Extensions.DependencyInjection ②NLog.Extensions.Logging&#xff08;或Microsoft.Extensions.Logging.___&#xff09; Tutorial NLog/NLog Wiki GitHub 2.添加nlog.config文件(默认名称, 可改为其他名称, 但需要另行配置) 文件的基础…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt 驾校管理 设计与实现

一.项目介绍 系统角色&#xff1a;管理员、驾校教练、学员 管理员&#xff1a; 个人中心&#xff1a;修改密码以及个人信息修改 学员管理&#xff1a;维护学员信息&#xff0c;维护学员成绩信息 驾校教练管理&#xff1a;驾校教练信息的维护 驾校车辆管理&…

【数据结构初阶】直接插入排序

最近浅学了直接插入排序&#xff0c;写个博客做笔记&#xff01;笔记功能除外若能对读者老爷有所帮助最好不过了&#xff01; 直接插入排序是插入排序的一种&#xff0c;那么介绍直接插入排序之前先介绍一下常见的排序算法&#xff01; 目录 1.常见的排序算法 2.直接插入排…

57. 【Android教程】相机:Camera

相机现在已经不仅仅是手机必备神器了&#xff0c;甚至相机的拍照质量已经是很多人买手机的首选条件了。而对于相机而言主要有两大功能&#xff1a;拍照片和拍视频。Android 为此两种方式&#xff1a; 相机 intent相机 API 本节我们就一起来看看相机的具体用法。 1. 打开 Camer…

C# Linq中的自定义排序

1.开发过程中&#xff0c;会遇到OrderBy/OrderByDescending排序无法满足的情况&#xff0c;此时就需要自定义排序&#xff0c;按照想要的排序规则取排序&#xff0c;比如订单的状态等等。 2.自定义泛型比较器代码如下&#xff1a; /// <summary>/// 自定义泛型比较器(用…

train_gpt2_fp32.cu - layernorm_forward_kernel3

源码 __global__ void layernorm_forward_kernel3(float* __restrict__ out, float* __restrict__ mean, float* __restrict__ rstd,const float* __restrict__ inp, const float* __restrict__ weight,const float* __restrict__ bias, int N, int C) {cg::thread_block bl…

【北京迅为】《iTOP-3588从零搭建ubuntu环境手册》-第8章 安装编译所需要的依赖包

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

多个文件 import 的相同模块里的对象

多个文件 import 的相同模块里的对象&#xff0c;是否永远都是同一个对象&#xff1f; 在store的index.js中 import vue from ‘vue’ import Vuex from ‘vuex’ 并配置有关对象 然后再app.vue中配置vm 在不同的文件中 import一个vue对象&#xff0c;在任何情况下&#…

vue2项目升级到vue3经历分享5

写到第5篇了&#xff0c;解决了很多问题&#xff0c;还有一些需要调整 1 el-input-number指令兼容性调整 下面这个可编辑的表格&#xff0c;全是0&#xff0c;于是需要一个指令&#xff0c;让它自己实现如果是0&#xff0c;就置空&#xff1b;如果是数字就是格式化为千分位&…

使用docker安装seafile

使用docker安装seafile 1 介绍seafile Seafile 是一款开源的企业云盘&#xff0c;支持全平台&#xff08;浏览器、Windows、Mac、Linux、Android、IPhone等&#xff09;客户端。Seafile 内置协同文档 SeaDoc &#xff0c;让协作撰写、管理和发布文档更便捷。最重要的这是国产…