1. 程式人生 > >h264增加SEI段資訊新增自定義資訊

h264增加SEI段資訊新增自定義資訊

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);

#endif

#pragma once

size_t get_sei_packet_size(size_t size);

int fill_sei_packet(unsigned char * packet, bool isAnnexb, const char * content, size_t size);

int get_sei_content(unsigned char * packet, size_t size, char * buffer, int *count);

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;
}