概述

有很多封装格式的H264码流采用的是AVCC格式。有些硬件编码器仅支持ANNEX B格式的H264码流,因此就需要做一次中转,本示例主要展示如何转换。

代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>#define NALU_TYPE_SLICE 1
#define NALU_TYPE_DPA 2
#define NALU_TYPE_DPB 3
#define NALU_TYPE_DPC 4
#define NALU_TYPE_IDR 5
#define NALU_TYPE_SEI 6
#define NALU_TYPE_SPS 7
#define NALU_TYPE_PPS 8
#define NALU_TYPE_AUD 9
#define NALU_TYPE_EOSEQ 10
#define NALU_TYPE_EOSTREAM 11
#define NALU_TYPE_FILL 12/*
H264帧由NALU头和NALU主体组成。
NALU头由一个字节组成,它的语法如下:+---------------+|0|1|2|3|4|5|6|7|+-+-+-+-+-+-+-+-+|F|NRI|  Type   |+---------------+F: 1个比特.forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.NRI: 2个比特.nal_ref_idc. 取00~11,似乎指示这个NALU的重要性,如00的NALU解码器可以丢弃它而不影响图像的回放,0~3,取值越大,表示当前NAL越重要,需要优先受到保护.如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,本句法元素必需大于0。Type: 5个比特.nal_unit_type. 这个NALU单元的类型,1~12由H.264使用,24~31由H.264以外的应用使用.*/
typedef struct {uint8_t nal_type:5;uint8_t nal_ref_idc:2;uint8_t forbidden_zero_bit:1;
} H264Hdr;int parase_nalu_hdr(uint8_t *nal_hdr, int nal_len) {const char *type_str = NULL;H264Hdr *hdr = (H264Hdr *)nal_hdr;printf("### DUMP nalu hdr(hdr:0x%02x, len=%d):\n",*nal_hdr, nal_len);printf("forbidden_zero_bit:%d\n", hdr->forbidden_zero_bit);printf("nal_ref_idc:%d\n", hdr->nal_ref_idc);switch (hdr->nal_type) {case NALU_TYPE_SLICE:type_str = "P/B Slice";break;case NALU_TYPE_AUD:type_str = "AUD Slice";break;case NALU_TYPE_DPA:type_str = "DPA Slice";break;case NALU_TYPE_DPB:type_str = "DPB Slice";break;case NALU_TYPE_DPC:type_str = "DPC Slice";break;case NALU_TYPE_EOSEQ:type_str = "EOS seq Slice";break;case NALU_TYPE_EOSTREAM:type_str = "EOS stream Slice";break;case NALU_TYPE_FILL:type_str = "FILL Slice";break;case NALU_TYPE_IDR:type_str = "IDR Slice";break;case NALU_TYPE_PPS:type_str = "PPS Slice";break;case NALU_TYPE_SPS:type_str = "SPS Slice";break;case NALU_TYPE_SEI:type_str = "SEI Slice";break;default:type_str = "Unknow Slice";printf("Warn: nal type:%d\n", hdr->nal_type);break;}printf("nal_type:%s\n", type_str);return 0;
}/*bits:  8   version ( always 0x01 )8   avc profile ( sps[0][1] )8   avc compatibility ( sps[0][2] )8   avc level ( sps[0][3] )6   reserved ( all bits on )2   NALULengthSizeMinusOne  // 这个值是(前缀长度-1),值如果是3,那前缀就是4,因为4-1=33   reserved ( all bits on )5   number of SPS NALUs (usually 1)
repeated once per SPS:16     SPS sizevariable   SPS NALU data8   number of PPS NALUs (usually 1)
repeated once per PPS16    PPS sizevariable PPS NALU data*/typedef struct {uint8_t version;uint8_t avc_profile;uint8_t avc_compatibility;uint8_t avc_level;uint8_t reserved0:6;uint8_t nalu_len:2;
} AvccExtraHdr;#define SPS_CNT_MASK 0x1Fint parase_avcc_extra(char *buff, int size) {uint8_t sps_cnt;uint16_t sps_size;uint8_t pps_cnt;uint16_t pps_size;uint8_t *offset = NULL;uint8_t extra_to_anexb[1024] = {0x00, 0x00, 0x00, 0x01, 0x00};if (!buff || (size < 7))return -1;AvccExtraHdr *hdr = (AvccExtraHdr *)buff;printf("version:0x%02x\n", hdr->version);printf("avc_profile:0x%02x\n", hdr->avc_profile);printf("avc_compatibility:0x%02x\n", hdr->avc_compatibility);printf("avc_level:0x%02x\n", hdr->avc_level);printf("reserved0:0x%02x\n", hdr->reserved0);printf("nalu_len:0x%02x\n", hdr->nalu_len);FILE *file_out = fopen("annexb.h264", "wb");if (!file_out) {printf("ERROR: open annexb.h264 failed!\n");return -1;}fseek(file_out, 0, SEEK_SET);offset = (uint8_t *)buff + sizeof(AvccExtraHdr);sps_cnt = *offset & SPS_CNT_MASK;offset++;printf("sps_cnt:%d\n", sps_cnt);for (int i = 0; i < sps_cnt; i++) {// 网络字节序转为主机字节序sps_size = ntohs(*((uint16_t *)offset));offset += 2;printf("+++ FLC-DBG: write anexb hdr to annexb.h264...\n");fwrite(extra_to_anexb, 1, 4, file_out);printf("+++ FLC-DBG: write sps:0x%02x to annexb.h264...\n", *offset);fwrite(offset, 1, sps_size, file_out);printf("[%d] sps_size:%d, sps_data:", i, sps_size);for (int j = 0; j < sps_size; j++)printf("0x%02x ", *(offset++));printf("\n");}pps_cnt = *(offset++);printf("pps_cnt:%d\n", pps_cnt);for (int i = 0; i < sps_cnt; i++) {// 网络字节序转为主机字节序pps_size = ntohs(*((uint16_t *)offset));offset += 2;printf("+++ FLC-DBG: write anexb hdr to annexb.h264...\n");fwrite(extra_to_anexb, 1, 4, file_out);printf("+++ FLC-DBG: write sps:0x%02x to annexb.h264...\n", *offset);fwrite(offset, 1, pps_size, file_out);printf("[%d] pps_size:%d, pps_data:", i, pps_size);for (int j = 0; j < pps_size; j++)printf("0x%02x ", *(offset++));printf("\n");}fclose(file_out);return 0;
}#define PARASE_MODE_EXTRA 1
#define PARASE_MODE_STREAM 2int main(int argc, char *argv[]) {int mode = 0;if (argc < 3) {printf("ERROR: Invalid args\n");printf("Usage: \n\t1.%s -extra avcc_extra.bin\n", argv[0]);printf("\t2.%s -stream avcc_stream.bin\n", argv[0]);return -1;}if (!strcmp(argv[1], "-extra"))mode = PARASE_MODE_EXTRA;else if (!strcmp(argv[1], "-stream"))mode = PARASE_MODE_STREAM;else {printf("ERROR: Mode invalid\n");return -1;}printf("# InFile:%s\n", argv[2]);FILE *file = fopen(argv[2], "r");if (!file) {printf("ERROR: open %s failed!\n", argv[2]);return -1;}fseek(file, 0, SEEK_END);int flen = ftell(file);if (flen <= 0) {printf("ERROR: file length invalid! flen:%d\n", flen);fclose(file);return -1;}uint8_t *buffer = (uint8_t *)malloc(flen + 1);if (!buffer) {printf("ERROR: malloc %d Bytes failed!\n", flen);fclose(file);return -1;}fseek(file, 0, SEEK_SET);int ret = fread(buffer, 1, flen, file);if (ret <= 0) {printf("ERROR: read file %d Bytes error!\n", flen);fclose(file);free(buffer);return -1;}fclose(file);if (mode == PARASE_MODE_EXTRA) {printf("#### PARASE AVCC EXTRA DATA ####\n");ret = parase_avcc_extra(buffer, ret);if (ret < 0)printf("ERROR: parase avcc data failed!\n");} else {printf("#### PARASE AVCC STREAM ####\n");// 解析avcc h264码流,长度默认:4字节,可根据extra data的nalu_len来计算。int offset = 0;int nal_len = 0;while (offset < flen) {nal_len = ntohl(*((uint32_t *)(buffer + offset)));// 将avcc的4字节长度转换为annex b的“00 00 00 01”分隔符。*(buffer + offset) = 0;*(buffer + offset + 1) = 0;*(buffer + offset + 2) = 0;*(buffer + offset + 3) = 1;offset += 4;parase_nalu_hdr((uint8_t *)(buffer + offset), nal_len);offset += nal_len;}// 追加形式打开文件,保存转换后的Stream。FILE *file_out = fopen("annexb.h264", "a+");if (!file_out) {printf("ERROR: open annexb.h264 failed!\n");fclose(file);free(buffer);return -1;}fwrite(buffer, 1, flen, file_out);fclose(file_out);}free(buffer);return ret;
}

用法

1、先从extra信息中解析出sps跟pps。

./main -extra avcc_extradata.bin

2、转换avcc的码流为annex B格式。

./main -stream avcc_stream.bin

完整资源包下载

https://download.csdn.net/download/lyy901135/12347498

H264 AVCC 格式转 ANNEX B格式相关推荐

  1. H.264流媒体协议格式中的Annex B格式和AVCC格式深度解析

    版权声明:本文为CSDN博主「Chucky_Hu」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/Roma ...

  2. <整理总结>H264/265码流数据包格式分析(带mp4v2封装H264/265为MP4的源码示例)

    H264/265码流数据包格式分析 前言: 一.H.264码流解析 I帧P帧B帧说明: 二.H.265码流解析 三.主要源码 前言: 最近在学习使用MP4v2将H264/H265码流以及AAC音频封装 ...

  3. Android多媒体:H264格式文件转MP4格式文件

    项目有Rtsp实时视频播放功能 ,现在要实现边播放边保存的功能要求.我在音视频方面技术处理白痴水平,所以 只能简单地实现先把h264格式的视频流保存到SD卡,再把对应地h264文件转码成MP4格式文件 ...

  4. 视频格式转化(将MP4格式转换成ogg格式)

    一.音频视频编码解码工具  www.ffmpeg.org(开源网址) 二.进入windows中进行下载windows版本的文件 三.选择您需要的版本,我这里下载的是Download FFmpeg 32 ...

  5. 3D电影,左右格式转红蓝格式

    使用stereo3d滤镜可以方便的实现3D电影左右格式(或上下格式)到红蓝格式的转换(需要较新版本的ffmpeg和ffplay). 如使用ffmpeg生成红蓝格式,原视频1280x720,即左右各为6 ...

  6. 音视频封装格式转换器(支持avi格式转换),基于FFmpeg4.1实现(音视频学习笔记二)

    之前参照雷霄骅博士的最简单的基于FFMPEG的封装格式转换器(无编解码)的博客和FFmpeg官网的example,实现一个简单的封装格式转换器.但是后来我发现我想从mp4格式转换成avi格式的时候会报 ...

  7. php pkcs 1格式的公钥,解说--2--微信支付RSA公钥PKCS1格式转化成PKCS8格式的公钥

    最近在开发一个功能:微信自动转账给个人用户(个人微信零钱.银行卡) 今天只讲RSA公钥PKCS1格式转化成PKCS8格式的公钥 先说说解决过程(一路心酸,一万个······): 1.昨晚开始转格式,未 ...

  8. pandas将dataframe中的年、月、日数据列合并成完整日期字符串、并使用to_datetime将字符串格式转化为日期格式

    pandas将dataframe中的年.月.日数据列合并成完整日期字符串.并使用to_datetime将字符串格式转化为日期格式 目录

  9. python使用openCV加载图像、并将BGR格式转换成HSV格式、定义HSV格式中需要分离颜色的掩码(掩模)区间(mask)、并使用mask信息进行颜色分离、BGR格式的图像转化为RGB、并可视化

    python使用openCV加载图像.并将BGR格式转换成HSV格式.定义HSV格式中需要分离颜色的掩码(掩模)区间(mask).并使用mask信息进行颜色分离.将BGR格式的图像转化为RGB.可视化 ...

最新文章

  1. php http传参数,http - PHP的URL传参数(英文句号变成了下划线)的问题,求解释。...
  2. 两大开源游戏引擎Cocos2d-x及OGEngine对比分析
  3. 安装python的第一个曲折
  4. LeetCode Algorithm 530. 二叉搜索树的最小绝对差
  5. .net 实现文件下载2
  6. ubuntu16.04 远程控制win10
  7. JS-封装js让一个div或者img的移动
  8. Kinect for Windows V2和V1对比开发___彩色数据获取并用OpenCV2.4.10显示
  9. Use Visual Studio Code to create and run Transact-SQL scripts for SQL Server
  10. 红米Note 7 Pro在印度首销迅速售罄
  11. Tensorflow2.0数据和部署(三)——基于Tensorflow数据服务的数据管道
  12. Cadence Orcad Capture CIS原理图数据库的基本使用方法与技巧图文教程
  13. 动作捕捉——从模型到动画个人流程记录
  14. java实现录屏功能
  15. 百度地图api调用时json.loads()报错ValueError: No JSON object could be decoded解决
  16. 病毒、蠕虫和木马的区别
  17. 计算机的规格介绍英语,电脑常见的一些英文(主板名称型号英文缩写)
  18. 会议录音转文字的软件有哪些?这款软件试试看
  19. CC2640R2f片上OAD工程简单说明
  20. 6论文降重小技巧(建议收藏)

热门文章

  1. Centos 8 常用指令汇总
  2. AW9523B IIC驱动
  3. async与awite详解
  4. 即食燕窝怎么吃?即食燕窝一天吃多少?即食燕窝是直接吃吗?
  5. json对象的遍历(C++)
  6. OpenCV cmake配置项BUILD_opencv_world的说明
  7. mysql导入数据的格式转换_mysql数据库导入导出csv格式实现zblog转换帝国cms教程记录...
  8. 基于C语言控制台程序的简易MP3音乐播放器
  9. Ten Googol
  10. 腾讯自研云原生数据库CynosDB发布 兼容MySQL和PostgreSQL