// Copyright 2019 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "codec.h" #include #include "buffer.h" #include "utils.h" namespace easymedia { Codec::Codec() { memset(&config, 0, sizeof(config)); } Codec::~Codec() { } std::shared_ptr Codec::GetExtraData(void** data, size_t* size) { if (data && size && extra_data) { *data = extra_data->GetPtr(); *size = extra_data->GetValidSize(); } return extra_data; } bool Codec::SetExtraData(void* data, size_t size, bool realloc) { if (!realloc) { if (!extra_data) { extra_data = std::make_shared(); if (!extra_data) { return false; } } extra_data->SetPtr(data); extra_data->SetValidSize(size); extra_data->SetSize(size); extra_data->SetUserData(nullptr); return true; } if (!data || size == 0) { return false; } auto extradata = MediaBuffer::Alloc(size); if (!extradata || extradata->GetSize() < size) { LOG_NO_MEMORY(); return false; } memcpy(extradata->GetPtr(), data, size); extradata->SetValidSize(size); extra_data = extradata; return true; } bool Codec::Init() { return false; } // Copy from ffmpeg. static const uint8_t* find_startcode_internal(const uint8_t* p, const uint8_t* end) { const uint8_t* a = p + 4 - ((intptr_t)p & 3); for (end -= 3; p < a && p < end; p++) { if (p[0] == 0 && p[1] == 0 && p[2] == 1) { return p; } } for (end -= 3; p < end; p += 4) { uint32_t x = *(const uint32_t*)p; // if ((x - 0x01000100) & (~x) & 0x80008000) // little endian // if ((x - 0x00010001) & (~x) & 0x00800080) // big endian if ((x - 0x01010101) & (~x) & 0x80808080) { // generic if (p[1] == 0) { if (p[0] == 0 && p[2] == 1) { return p; } if (p[2] == 0 && p[3] == 1) { return p + 1; } } if (p[3] == 0) { if (p[2] == 0 && p[4] == 1) { return p + 2; } if (p[4] == 0 && p[5] == 1) { return p + 3; } } } } for (end += 3; p < end; p++) { if (p[0] == 0 && p[1] == 0 && p[2] == 1) { return p; } } return end + 3; } const uint8_t* find_nalu_startcode(const uint8_t* p, const uint8_t* end) { const uint8_t* out = find_startcode_internal(p, end); if (p < out && out < end && !out[-1]) { out--; } return out; } std::list> split_h264_separate(const uint8_t* buffer, size_t length, int64_t timestamp) { std::list> l; const uint8_t* p = buffer; const uint8_t* end = p + length; const uint8_t *nal_start = nullptr, *nal_end = nullptr; nal_start = find_nalu_startcode(p, end); // 00 00 01 or 00 00 00 01 size_t start_len = (nal_start[2] == 1 ? 3 : 4); for (;;) { if (nal_start == end) { break; } nal_start += start_len; nal_end = find_nalu_startcode(nal_start, end); size_t size = nal_end - nal_start + start_len; uint8_t nal_type = (*nal_start) & 0x1F; uint32_t flag; switch (nal_type) { case 7: case 8: flag = MediaBuffer::kExtraIntra; break; default: flag = 0; } // not extraIntra? if (!flag) { break; } auto sub_buffer = MediaBuffer::Alloc(size); if (!sub_buffer) { LOG_NO_MEMORY(); // fatal error l.clear(); return l; } memcpy(sub_buffer->GetPtr(), nal_start - start_len, size); sub_buffer->SetValidSize(size); sub_buffer->SetUserFlag(flag); sub_buffer->SetUSTimeStamp(timestamp); sub_buffer->SetType(Type::Video); l.push_back(sub_buffer); nal_start = nal_end; } return std::move(l); } std::list> split_h265_separate(const uint8_t* buffer, size_t length, int64_t timestamp) { std::list> l; const uint8_t* p = buffer; const uint8_t* end = p + length; const uint8_t *nal_start = nullptr, *nal_end = nullptr; nal_start = find_nalu_startcode(p, end); // 00 00 01 or 00 00 00 01 size_t start_len = (nal_start[2] == 1 ? 3 : 4); for (;;) { if (nal_start == end) { break; } nal_start += start_len; nal_end = find_nalu_startcode(nal_start, end); size_t size = nal_end - nal_start + start_len; uint8_t nal_type = ((*nal_start) & 0x7E) >> 1; uint32_t flag; switch (nal_type) { case 32: case 33: case 34: flag = MediaBuffer::kExtraIntra; break; default: flag = 0; } // not extraIntra? if (!flag) { break; } auto sub_buffer = MediaBuffer::Alloc(size); if (!sub_buffer) { LOG_NO_MEMORY(); // fatal error l.clear(); return l; } memcpy(sub_buffer->GetPtr(), nal_start - start_len, size); sub_buffer->SetValidSize(size); sub_buffer->SetUserFlag(flag); sub_buffer->SetUSTimeStamp(timestamp); l.push_back(sub_buffer); nal_start = nal_end; } return std::move(l); } static void* FindNaluByType(std::shared_ptr& mb, int nal_type, int& size, CodecType c_type) { if ((c_type != CODEC_TYPE_H264) && (c_type != CODEC_TYPE_H265)) { LOG("ERROR: %s failed! Invalid codec type\n", __func__); return NULL; } void* target_nalu = NULL; const uint8_t* start = (uint8_t*)mb->GetPtr(); const uint8_t* end = start + mb->GetValidSize(); const uint8_t *nal_start = nullptr, *nal_end = nullptr; nal_start = nal_end = find_nalu_startcode(start, end); // 00 00 01 or 00 00 00 01 int start_len = (nal_start[2] == 1 ? 3 : 4); int nal_size = 0; uint8_t type = 0; for (;;) { if (nal_start == end) { break; } nal_start = nal_end; nal_end = find_nalu_startcode(nal_start + start_len, end); nal_size = nal_end - nal_start; if (c_type == CODEC_TYPE_H264) { type = *(nal_start + start_len) & 0x1F; } else { type = (*(nal_start + start_len) & 0x7E) >> 1; } if (type == nal_type) { size = nal_size; target_nalu = (void*)nal_start; break; } } return target_nalu; } void* GetVpsFromBuffer(std::shared_ptr& mb, int& size, CodecType c_type) { if (c_type != CODEC_TYPE_H265) { return NULL; } return FindNaluByType(mb, 32, size, c_type); } void* GetSpsFromBuffer(std::shared_ptr& mb, int& size, CodecType c_type) { int nalu_type = 0; if (c_type == CODEC_TYPE_H265) { nalu_type = 33; } else if (c_type == CODEC_TYPE_H264) { nalu_type = 7; } else { return NULL; } return FindNaluByType(mb, nalu_type, size, c_type); } void* GetPpsFromBuffer(std::shared_ptr& mb, int& size, CodecType c_type) { int nalu_type = 0; if (c_type == CODEC_TYPE_H265) { nalu_type = 34; } else if (c_type == CODEC_TYPE_H264) { nalu_type = 8; } else { return NULL; } return FindNaluByType(mb, nalu_type, size, c_type); } void* GetSpsPpsFromBuffer(std::shared_ptr& mb, int& size, CodecType c_type) { void* sps_ptr = NULL; int sps_size = 0; void* pps_ptrt = NULL; int pps_size = 0; // get sps from buffer sps_ptr = GetSpsFromBuffer(mb, sps_size, c_type); if (!sps_ptr) { return NULL; } // get pps from buffer pps_ptrt = GetPpsFromBuffer(mb, pps_size, c_type); if (!pps_ptrt) { return NULL; } size = sps_size + pps_size; return sps_ptr; } void* GetVpsSpsPpsFromBuffer(std::shared_ptr& mb, int& size, CodecType c_type) { void* vps_ptr = NULL; int vps_size = 0; void* sps_ptr = NULL; int sps_size = 0; void* pps_ptrt = NULL; int pps_size = 0; // get vps from buffer vps_ptr = GetVpsFromBuffer(mb, vps_size, c_type); if (!vps_ptr) { return NULL; } // get sps from buffer sps_ptr = GetSpsFromBuffer(mb, sps_size, c_type); if (!sps_ptr) { return NULL; } // get pps from buffer pps_ptrt = GetPpsFromBuffer(mb, pps_size, c_type); if (!pps_ptrt) { return NULL; } size = vps_size + sps_size + pps_size; return vps_ptr; } void* GetSeiFromBuffer(std::shared_ptr& mb, int& size, CodecType c_type) { int nalu_type = 0; if (c_type == CODEC_TYPE_H265) { nalu_type = 39; } else if (c_type == CODEC_TYPE_H264) { nalu_type = 6; } else { return NULL; } return FindNaluByType(mb, nalu_type, size, c_type); } // Last nalu must be IDR otherwise will occure error. void* GetIntraFromBuffer(std::shared_ptr& mb, int& size, CodecType c_type) { void* idr_ptr = NULL; int nalu_type = 0; if (c_type == CODEC_TYPE_H265) { nalu_type = 19; } else if (c_type == CODEC_TYPE_H264) { nalu_type = 5; } else { return NULL; } idr_ptr = FindNaluByType(mb, nalu_type, size, c_type); size = mb->GetValidSize() - (int)((uint8_t*)idr_ptr - (uint8_t*)mb->GetPtr()); return idr_ptr; } } // namespace easymedia