/* nal_units.c * * Copyright (c) 2003-2019 HandBrake Team * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include #include "handbrake/common.h" #include "handbrake/nal_units.h" static const uint8_t hb_annexb_startcode[] = { 0x00, 0x00, 0x00, 0x01, }; size_t hb_nal_unit_write_annexb(uint8_t *buf, const uint8_t *nal_unit, const size_t nal_unit_size) { if (buf != NULL) { memcpy(buf, hb_annexb_startcode, sizeof(hb_annexb_startcode)); memcpy(buf + sizeof(hb_annexb_startcode), nal_unit, nal_unit_size); } return sizeof(hb_annexb_startcode) + nal_unit_size; } size_t hb_nal_unit_write_isomp4(uint8_t *buf, const uint8_t *nal_unit, const size_t nal_unit_size) { int i; uint8_t length[4]; // 4-byte length replaces Annex B start code prefix if (buf != NULL) { for (i = 0; i < sizeof(length); i++) { length[i] = (nal_unit_size >> (8 * (sizeof(length) - 1 - i))) & 0xff; } memcpy(buf, &length[0], sizeof(length)); memcpy(buf + sizeof(length), nal_unit, nal_unit_size); } return sizeof(length) + nal_unit_size; } uint8_t* hb_annexb_find_next_nalu(const uint8_t *start, size_t *size) { uint8_t *nal = NULL; uint8_t *buf = (uint8_t*)start; uint8_t *end = (uint8_t*)start + *size; /* Look for an Annex B start code prefix (3-byte sequence == 1) */ while (end - buf > 3) { if (!buf[0] && !buf[1] && buf[2] == 1) { nal = (buf += 3); // NAL unit begins after start code break; } buf++; } if (nal == NULL) { *size = 0; return NULL; } /* * Start code prefix found, look for the next one to determine the size * * A 4-byte sequence == 1 is also a start code, so check for a 3-byte * sequence == 0 too (start code emulation prevention will prevent such a * sequence from occurring outside of a start code prefix) */ while (end - buf > 3) { if (!buf[0] && !buf[1] && (!buf[2] || buf[2] == 1)) { end = buf; break; } buf++; } *size = end - nal; return nal; } hb_buffer_t* hb_nal_bitstream_annexb_to_mp4(const uint8_t *data, const size_t size) { hb_buffer_t *out; uint8_t *buf, *end; size_t out_size, buf_size; out_size = 0; buf_size = size; buf = (uint8_t*)data; end = (uint8_t*)data + size; while ((buf = hb_annexb_find_next_nalu(buf, &buf_size)) != NULL) { out_size += hb_nal_unit_write_isomp4(NULL, buf, buf_size); buf_size = end - buf; } out = hb_buffer_init(out_size); if (out == NULL) { hb_error("hb_nal_bitstream_annexb_to_mp4: hb_buffer_init failed"); return NULL; } out_size = 0; buf_size = size; buf = (uint8_t*)data; end = (uint8_t*)data + size; while ((buf = hb_annexb_find_next_nalu(buf, &buf_size)) != NULL) { out_size += hb_nal_unit_write_isomp4(out->data + out_size, buf, buf_size); buf_size = end - buf; } return out; } static size_t mp4_nal_unit_length(const uint8_t *data, const size_t nal_length_size, size_t *nal_unit_length) { uint8_t i; /* In MP4, NAL units are preceded by a 2-4 byte length field */ for (i = 0, *nal_unit_length = 0; i < nal_length_size; i++) { *nal_unit_length |= data[i] << (8 * (nal_length_size - 1 - i)); } return nal_length_size; } hb_buffer_t* hb_nal_bitstream_mp4_to_annexb(const uint8_t *data, const size_t size, const uint8_t nal_length_size) { hb_buffer_t *out; uint8_t *buf, *end; size_t out_size, nal_size; out_size = 0; buf = (uint8_t*)data; end = (uint8_t*)data + size; while (end - buf > nal_length_size) { buf += mp4_nal_unit_length(buf, nal_length_size, &nal_size); if (end - buf < nal_size) { hb_log("hb_nal_bitstream_mp4_to_annexb: truncated bitstream" " (remaining: %lu, expected: %lu)", end - buf, nal_size); return NULL; } out_size += hb_nal_unit_write_annexb(NULL, buf, nal_size); buf += nal_size; } out = hb_buffer_init(out_size); if (out == NULL) { hb_error("hb_nal_bitstream_mp4_to_annexb: hb_buffer_init failed"); return NULL; } out_size = 0; buf = (uint8_t*)data; end = (uint8_t*)data + size; while (end - buf > nal_length_size) { buf += mp4_nal_unit_length(buf, nal_length_size, &nal_size); out_size += hb_nal_unit_write_annexb(out->data + out_size, buf, nal_size); buf += nal_size; } return out; }