FFmpeg H264增加SEI_c++ h264 add sei-程序员宅基地

技术标签: H264 SEI FFMPEG  FFMPEG  

先看使用场景:
https://blog.csdn.net/lsheevyfg/article/details/80951415
https://www.jianshu.com/p/4d9120dfcd69
参考文章:
https://blog.csdn.net/ab7936573/article/details/74135909

FFMPEG使用版本为3.2.2
直接上代码:
EvHeade.h

#ifdef __cplusplus
extern "C"
{
    
#endif
 
#include "libavcodec\avcodec.h"
#include "libavformat\avformat.h"
#include "libavutil\avutil.h"
 
#ifdef __cplusplus
}
#endif
 
 
#ifdef _WIN32
 
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avdevice.lib")
#pragma comment(lib,"avfilter.lib")
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"postproc.lib")
#pragma comment(lib,"swresample.lib")
#pragma comment(lib,"swscale.lib")
 
#endif

sei_packet.h

#ifndef SEI_PACKET_H
#define SEI_PACKET_H
 
#include <stdint.h>
 
uint32_t reversebytes(uint32_t value);
uint32_t get_sei_packet_size(uint32_t size);
int fill_sei_packet(unsigned char * packet, bool isAnnexb, const char * content, uint32_t size);
int get_sei_content(unsigned char * packet, uint32_t size, char * buffer, int *count);
#pragma once

#endif

sei_packet.cpp

#include "sei_packet.h"
#include <stdio.h>
#include <string.h>
 
#define min(X,Y) ((X) < (Y) ? (X) : (Y))
 
#define UUID_SIZE 16
 
//FFMPEG uuid
//static unsigned char uuid[] = { 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef };
//self UUID
static unsigned char uuid[] = {
     0x54, 0x80, 0x83, 0x97, 0xf0, 0x23, 0x47, 0x4b, 0xb7, 0xf7, 0x4f, 0x32, 0xb5, 0x4e, 0x06, 0xac };
 
//开始码
static unsigned char start_code[] = {
    0x00,0x00,0x00,0x01};
 
uint32_t reversebytes(uint32_t value) {
    
	return (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 |
		(value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24;
}
 
uint32_t get_sei_nalu_size(uint32_t content)
{
    
	//SEI payload size
	uint32_t sei_payload_size = content + UUID_SIZE;
	//NALU + payload类型 + 数据长度 + 数据
	uint32_t sei_size = 1 + 1 + (sei_payload_size / 0xFF + (sei_payload_size % 0xFF != 0 ? 1 : 0)) + sei_payload_size;
	//截止码
	uint32_t tail_size = 2;
	if (sei_size % 2 == 1)
	{
    
		tail_size -= 1;
	}
	sei_size += tail_size;
 
	return sei_size;
}
 
uint32_t get_sei_packet_size(uint32_t size)
{
    
	return get_sei_nalu_size(size) + 4;
}
 
int fill_sei_packet(unsigned char * packet,bool isAnnexb, const char * content, uint32_t size)
{
    
	unsigned char * data = (unsigned char*)packet;
	unsigned int nalu_size = (unsigned int)get_sei_nalu_size(size);
	uint32_t sei_size = nalu_size;
	//大端转小端
	nalu_size = reversebytes(nalu_size);
 
	//NALU开始码
	unsigned int * size_ptr = &nalu_size;
	if (isAnnexb)
	{
    
		memcpy(data, start_code, sizeof(unsigned int));
	}
	else
	{
    
		memcpy(data, size_ptr, sizeof(unsigned int));
	}
	data += sizeof(unsigned int);
 
	unsigned char * sei = data;
	//NAL header
	*data++ = 6; //SEI
	//sei payload type
	*data++ = 5; //unregister
	size_t sei_payload_size = size + UUID_SIZE;
	//数据长度
	while (true)
	{
    
		*data++ = (sei_payload_size >= 0xFF ? 0xFF : (char)sei_payload_size);
		if (sei_payload_size < 0xFF) break;
		sei_payload_size -= 0xFF;
	}
 
	//UUID
	memcpy(data, uuid, UUID_SIZE);
	data += UUID_SIZE;
	//数据
	memcpy(data, content, size);
	data += size;
 
	//tail 截止对齐码
	if (sei + sei_size - data == 1)
	{
    
		*data = 0x80;
	}
	else if (sei + sei_size - data == 2)
	{
    
		*data++ = 0x00;
		*data++ = 0x80;
	}
 
	return true;
}
 
int get_sei_buffer(unsigned char * data, uint32_t size, char * buffer, int *count)
{
    
	unsigned char * sei = data;
	int sei_type = 0;
	unsigned sei_size = 0;
	//payload type
	do {
    
		sei_type += *sei;
	} while (*sei++ == 255);
	//数据长度
	do {
    
		sei_size += *sei;
	} while (*sei++ == 255);
 
	//检查UUID
	if (sei_size >= UUID_SIZE && sei_size <= (data + size - sei) &&
		sei_type == 5 && memcmp(sei, uuid, UUID_SIZE) == 0)
	{
    
		sei += UUID_SIZE;
		sei_size -= UUID_SIZE;
 
		if (buffer != NULL && count != NULL)
		{
    
			if (*count > (int)sei_size)
			{
    
				memcpy(buffer, sei, sei_size);
			}
		}
		if (count != NULL)
		{
    
			*count = sei_size;
		}
		return sei_size;
	}
	return -1;
}
 
int get_sei_content(unsigned char * packet, uint32_t size,char * buffer,int *count)
{
    
	unsigned char ANNEXB_CODE_LOW[] = {
     0x00,0x00,0x00,0x01 };
	unsigned char ANNEXB_CODE[] = {
     0x00,0x00,0x00,0x01 };
 
	unsigned char *data = packet;
	bool isAnnexb = false;
	if ((size > 3 && memcmp(data, ANNEXB_CODE_LOW,3) == 0) ||
		(size > 4 && memcmp(data, ANNEXB_CODE,4) == 0)
		)
	{
    
		isAnnexb = true;
	}
	//暂时只处理MP4封装,annexb暂为处理
	if (isAnnexb)
	{
    
		while (data < packet + size) {
    
			if ((packet + size - data) > 4 && data[0] == 0x00 && data[1] == 0x00)
			{
    
				int startCodeSize = 2;
				if (data[2] == 0x01)
				{
    
					startCodeSize = 3;
				}
				else if(data[2] == 0x00 && data[3] == 0x01)
				{
    
					startCodeSize = 4;
				}
				if (startCodeSize == 3 || startCodeSize == 4)
				{
    
					if ((packet + size - data) > (startCodeSize + 1) && 
						(data[startCodeSize] & 0x1F) == 6)
					{
    
						//SEI
						unsigned char * sei = data + startCodeSize + 1;
 
						int ret = get_sei_buffer(sei, (packet + size - sei), buffer, count);
						if (ret != -1)
						{
    
							return ret;
						}
					}
					data += startCodeSize + 1;
				}
				else
				{
    
					data += startCodeSize + 1;
				}
			}
			else
			{
    
				data++;
			}
		}
	}
	else
	{
    
		//当前NALU
		while (data < packet + size) {
    
			//MP4格式起始码/长度
			unsigned int *length = (unsigned int *)data;
			int nalu_size = (int)reversebytes(*length);
			//NALU header
			if ((*(data + 4) & 0x1F) == 6)
			{
    
				//SEI
				unsigned char * sei = data + 4 + 1;
 
				int ret = get_sei_buffer(sei, min(nalu_size,(packet + size - sei)),buffer,count);
				if (ret != -1)
				{
    
					return ret;
				}
			}
			data += 4 + nalu_size;
		}
	}
	return -1;
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/passionkk/article/details/102804023

智能推荐

ASP.NET MVC 5 - 给数据模型添加校验器_.net模型验证枚举类型-程序员宅基地

文章浏览阅读2.4k次。ASP.NET MVC 的核心设计信条之一是DRY: "不要重复自己(DRY --Don’t Repeat Yourself)"。ASP.NET MVC鼓励您指定功能或者行为,只做一次,然后将它应用到应用程序的各个地方。这可以减少您需要编写的代码量,并减少代码出错率,易于代码维护。给ASP.NET MVC 和 Entity Framework Code First 提供验证支持是 DRY 信条的一次伟大实践。您可以在一个地方 (模型类) 中以声明的方式指定验证规则,这个规则会在应用程序中的任何地方执行。_.net模型验证枚举类型

thymeleaf读取配置文件问题_thymeleaf国际化#{login.test}读不到配置文件-程序员宅基地

文章浏览阅读4k次。写好了一个正常的properties文件,然后使用 thymeleaf在页面进行取值结果:??user.name_zh_CN??这是为什么呢,原来这里的properties文件名必须是messages,这是spring boot规定的国际化文件名..._thymeleaf国际化#{login.test}读不到配置文件

matlab误差传播和算法稳定性_误差传递可以用什么软件计算-程序员宅基地

文章浏览阅读9.6k次,点赞6次,收藏23次。算法描述: 方案二:递推公式结果:y(1)=0.212647 y(2)=0.071838 y(3)=0.065374 y(4)=0.046157 y(5)=0.038461 y(6)=0.032051 y(7)=0.027701 y(8)=0.02432..._误差传递可以用什么软件计算

使用fastjson对Json的基本操作_fastjson tojson-程序员宅基地

文章浏览阅读1.8k次,点赞6次,收藏44次。fastjson是阿里的一款对Json处理的工具,使用前需要引入对应的jar包下载地址本人感觉还是非常强悍的,比Gson使用起来更加的强大package json;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import com...._fastjson tojson

dll 导出函数转发_#pragma comment(linker, "/export-程序员宅基地

文章浏览阅读1.2k次。前言有时候我们导出的dll仅仅想导出一些函数声明,然后有其他dll进行实现,我们成这里导出声明为:导出函数转发https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160# pragram comment_#pragma comment(linker, "/export

jQuery实现Tab选项卡_用jquery写tab选项卡-程序员宅基地

文章浏览阅读1.8k次。<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <link rel="icon" href="../../../img/sup.ico" type="image/x-ioc"/&..._用jquery写tab选项卡

随便推点

第三章 MQ队列管理器搭建之(三)_amq3681e: ha 子系统已在管理复制的数据队列管理器“gwo.qm”-程序员宅基地

文章浏览阅读402次。版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本文链接:https://blog.csdn.net/sjz64472418/article/details/8517182MQ集群及网关队列管理器的搭建描述:如上图所示,为MQ的集群搭建部署图。CLUSTERA、CLUSTERB分别是两个集群,其中Qm1-Qm3、Ga..._amq3681e: ha 子系统已在管理复制的数据队列管理器“gwo.qm”

怎么通过视图修改数据mysql_数据库修改”视图数据,将职工“w010”与客户“c110”签订合同的金额从16万修改为1-程序员宅基地

文章浏览阅读4.6k次,点赞2次,收藏2次。视图并不在数据库中实际存在,行和列数据来自定义视图的查询总使用的表,并且是在使用视图时动态生成的。安全:使用视图的用户只能访问他们被允许的结果集,对表的权限管理并不能限制到某个行某个列,但是通过视图就可以简单的实现。简单:使用视图的用户完全不需要关系后面对应的表结构、关联条件和筛选条件,对用户来说已经是过滤好的符合条件的结果集。2.视图不是表,不保存数据,只是一张虚拟的表,源表的数据发生变化后,视图的结果也同步发生变化。视图的可更新性和视图中查询的定义有关系,一下类型的视图是不可更新的。_数据库修改”视图数据,将职工“w010”与客户“c110”签订合同的金额从16万修改为1

mysql错误:java.sql.SQLException: The server time zone value ‘xxx‘ is unrecognized or represents 解决方法_mysql 5.7 createsqlexception(sqlerror.java:120) ~[-程序员宅基地

文章浏览阅读289次。错误内容:java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specific time zo_mysql 5.7 createsqlexception(sqlerror.java:120) ~[mysql-connector-j-8.0.32.j

C++面向对象编程之——构造函数(一)_在面向对象的编程中,什么是构造函数-程序员宅基地

文章浏览阅读742次,点赞2次,收藏2次。之前学习C++面向对象编程,只知道构造函数是创建实例对象时被自动调用;然后陆续看了一些书,知识点比较分散,现对构造函数进行一个全面总结,以便更加深入理解构造函数。目录 构造函数是什么? 构造函数形式? 默认构造函数 构造函数初始化列表1.构造函数是什么?构造函数:类中通过一个或者多个特殊的成员函数来控制其对象初始化过程。构造函数的任务是初始化类中的数据成员,无论何..._在面向对象的编程中,什么是构造函数

给中国学生的第七封信:21世纪最需要的7种人才_从死记硬背 到 融会贯通 几重境界-程序员宅基地

文章浏览阅读1.6k次。给中国学生的第七封信:21世纪最需要的7种人才人才的标准从来都不是一成不变的。在东方的战国时代和西方的骑士时代里,最受器重的是力敌万夫的勇士和巧舌善辩的谋臣;在中国的科举时代里,靠着“死记硬背”和“八股文章”而金榜题名的书生最容易出人头地;在西方工业革命风起云涌的日子里,善于用机器的力量改变世界的发明家以及那些精通专业、埋头苦干的工程师成了所有人才中的佼佼者;即便是在刚刚过去的20世纪中,_从死记硬背 到 融会贯通 几重境界

CodeBlocks集成Objective-C开发_vs code 支持 object-c 语法-程序员宅基地

文章浏览阅读1.4k次。转自 http://kingj.iteye.com/blog/1558869Objective-C是苹果软件的编程语言,想要上机学习、调试,有一个集成开发环境(IDE)方便很多。有三类方法搭建Objective-C的集成开发环境:1) 使用苹果的平台,集成开发环境使用Xcode。但如果没有苹果平台,想在Windows环境下学习Objective-C,可以采用以下两种方法:_vs code 支持 object-c 语法

推荐文章

热门文章

相关标签