240 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| // FFMPEG Video Encoder Integration for OBS Studio
 | |
| // Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included in all
 | |
| // copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
| // SOFTWARE.
 | |
| 
 | |
| #include "hevc.hpp"
 | |
| 
 | |
| using namespace streamfx::encoder::codec;
 | |
| 
 | |
| enum class nal_unit_type : uint8_t { // 6 bits
 | |
| 	TRAIL_N        = 0,
 | |
| 	TRAIL_R        = 1,
 | |
| 	TSA_N          = 2,
 | |
| 	TSA_R          = 3,
 | |
| 	STSA_N         = 4,
 | |
| 	STSA_R         = 5,
 | |
| 	RADL_N         = 6,
 | |
| 	RADL_R         = 7,
 | |
| 	RASL_N         = 8,
 | |
| 	RASL_R         = 9,
 | |
| 	RSV_VCL_N10    = 10,
 | |
| 	RSV_VCL_R11    = 11,
 | |
| 	RSV_VCL_N12    = 12,
 | |
| 	RSV_VCL_R13    = 13,
 | |
| 	RSV_VCL_N14    = 14,
 | |
| 	RSV_VCL_R15    = 15,
 | |
| 	BLA_W_LP       = 16,
 | |
| 	BLA_W_RADL     = 17,
 | |
| 	BLA_N_LP       = 18,
 | |
| 	IDR_W_RADL     = 19,
 | |
| 	IDR_N_LP       = 20,
 | |
| 	CRA            = 21,
 | |
| 	RSV_IRAP_VCL22 = 22,
 | |
| 	RSV_IRAP_VCL23 = 23,
 | |
| 	RSV_VCL24      = 24,
 | |
| 	RSV_VCL25      = 25,
 | |
| 	RSV_VCL26      = 26,
 | |
| 	RSV_VCL27      = 27,
 | |
| 	RSV_VCL28      = 28,
 | |
| 	RSV_VCL29      = 29,
 | |
| 	RSV_VCL30      = 30,
 | |
| 	RSV_VCL31      = 31,
 | |
| 	VPS            = 32,
 | |
| 	SPS            = 33,
 | |
| 	PPS            = 34,
 | |
| 	AUD            = 35,
 | |
| 	EOS            = 36,
 | |
| 	EOB            = 37,
 | |
| 	FD             = 38,
 | |
| 	PREFIX_SEI     = 39,
 | |
| 	SUFFIX_SEI     = 40,
 | |
| 	RSV_NVCL41     = 41,
 | |
| 	RSV_NVCL42     = 42,
 | |
| 	RSV_NVCL43     = 43,
 | |
| 	RSV_NVCL44     = 44,
 | |
| 	RSV_NVCL45     = 45,
 | |
| 	RSV_NVCL46     = 46,
 | |
| 	RSV_NVCL47     = 47,
 | |
| 	UNSPEC48       = 48,
 | |
| 	UNSPEC49       = 49,
 | |
| 	UNSPEC50       = 50,
 | |
| 	UNSPEC51       = 51,
 | |
| 	UNSPEC52       = 52,
 | |
| 	UNSPEC53       = 53,
 | |
| 	UNSPEC54       = 54,
 | |
| 	UNSPEC55       = 55,
 | |
| 	UNSPEC56       = 56,
 | |
| 	UNSPEC57       = 57,
 | |
| 	UNSPEC58       = 58,
 | |
| 	UNSPEC59       = 59,
 | |
| 	UNSPEC60       = 60,
 | |
| 	UNSPEC61       = 61,
 | |
| 	UNSPEC62       = 62,
 | |
| 	UNSPEC63       = 63,
 | |
| };
 | |
| 
 | |
| struct hevc_nal_unit_header {
 | |
| 	bool          zero_bit : 1;
 | |
| 	nal_unit_type nut : 6;
 | |
| 	uint8_t       layer_id : 6;
 | |
| 	uint8_t       temporal_id_plus1 : 3;
 | |
| };
 | |
| 
 | |
| struct hevc_nal {
 | |
| 	hevc_nal_unit_header* header;
 | |
| 	std::size_t           size = 0;
 | |
| 	uint8_t*              data = nullptr;
 | |
| };
 | |
| 
 | |
| bool is_nal(uint8_t* data, uint8_t* end)
 | |
| {
 | |
| 	std::size_t s = static_cast<size_t>(end - data);
 | |
| 	if (s < 4)
 | |
| 		return false;
 | |
| 
 | |
| 	if (*data != 0x0)
 | |
| 		return false;
 | |
| 	if (*(data + 1) != 0x0)
 | |
| 		return false;
 | |
| 	if (*(data + 2) != 0x0)
 | |
| 		return false;
 | |
| 	if (*(data + 3) != 0x1)
 | |
| 		return false;
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool seek_to_nal(uint8_t*& data, uint8_t* end)
 | |
| {
 | |
| 	if (data > end)
 | |
| 		return false;
 | |
| 
 | |
| 	for (; data <= end; data++) {
 | |
| 		if (is_nal(data, end)) {
 | |
| 			return true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| std::size_t get_nal_size(uint8_t* data, uint8_t* end)
 | |
| {
 | |
| 	uint8_t* ptr = data + 4;
 | |
| 	if (!seek_to_nal(ptr, end)) {
 | |
| 		return static_cast<size_t>(end - data);
 | |
| 	}
 | |
| 	return static_cast<size_t>(ptr - data);
 | |
| }
 | |
| 
 | |
| bool is_discard_marker(uint8_t* data, uint8_t* end)
 | |
| {
 | |
| 	std::size_t s = static_cast<size_t>(end - data);
 | |
| 	if (s < 4)
 | |
| 		return false;
 | |
| 
 | |
| 	if (*data != 0x0)
 | |
| 		return false;
 | |
| 	if (*(data + 1) != 0x0)
 | |
| 		return false;
 | |
| 
 | |
| 	if (*(data + 2) == 0x3) {
 | |
| 		// Discard marker only if the next byte is not 0x0, 0x1, 0x2 or 0x3.
 | |
| 		if (*(data + 3) != 0x0)
 | |
| 			return false;
 | |
| 		if (*(data + 3) != 0x1)
 | |
| 			return false;
 | |
| 		if (*(data + 3) != 0x2)
 | |
| 			return false;
 | |
| 		if (*(data + 3) != 0x3)
 | |
| 			return false;
 | |
| 
 | |
| 		return true;
 | |
| 	} else {
 | |
| 		if (*(data + 2) == 0x0)
 | |
| 			return true;
 | |
| 		if (*(data + 2) == 0x1)
 | |
| 			return true;
 | |
| 		if (*(data + 2) == 0x2)
 | |
| 			return true;
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool should_discard_nal(uint8_t* data, uint8_t* end)
 | |
| {
 | |
| 	if (data > end)
 | |
| 		return true;
 | |
| 
 | |
| 	for (; data <= end; data++) {
 | |
| 		if (is_discard_marker(data, end))
 | |
| 			return true;
 | |
| 	}
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| void progress_parse(uint8_t*& ptr, uint8_t* end, size_t& sz)
 | |
| {
 | |
| 	ptr += sz;
 | |
| 	sz = get_nal_size(ptr, end);
 | |
| }
 | |
| 
 | |
| void hevc::extract_header_sei(uint8_t* data, std::size_t sz_data, std::vector<uint8_t>& header,
 | |
| 							  std::vector<uint8_t>& sei)
 | |
| {
 | |
| 	uint8_t* ptr = data;
 | |
| 	uint8_t* end = data + sz_data;
 | |
| 
 | |
| 	// Reserve enough memory to store the entire packet data if necessary.
 | |
| 	header.reserve(sz_data);
 | |
| 	sei.reserve(sz_data);
 | |
| 
 | |
| 	if (!seek_to_nal(ptr, end)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	for (std::size_t nal_sz = get_nal_size(ptr, end); nal_sz > 0; progress_parse(ptr, end, nal_sz)) {
 | |
| 		if (should_discard_nal(ptr + 4, ptr + nal_sz)) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		hevc_nal nal;
 | |
| 		nal.header = reinterpret_cast<hevc_nal_unit_header*>(ptr + 4);
 | |
| 		nal.size   = nal_sz - 4 - 2;
 | |
| 		nal.data   = ptr + 4 + 2;
 | |
| 
 | |
| 		switch (nal.header->nut) {
 | |
| 		case nal_unit_type::VPS:
 | |
| 		case nal_unit_type::SPS:
 | |
| 		case nal_unit_type::PPS:
 | |
| 			header.insert(header.end(), ptr, ptr + nal_sz);
 | |
| 			break;
 | |
| 		case nal_unit_type::PREFIX_SEI:
 | |
| 		case nal_unit_type::SUFFIX_SEI:
 | |
| 			sei.insert(sei.end(), ptr, ptr + nal_sz);
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 |