diff options
author | Rodeo <[email protected]> | 2014-04-07 22:04:03 +0000 |
---|---|---|
committer | Rodeo <[email protected]> | 2014-04-07 22:04:03 +0000 |
commit | 2e6f73542dcc281dc19b2f1347cfe7992e12ed13 (patch) | |
tree | 36e6e2733c80231c74780e9faabd34684a8ef149 /libhb/nal_units.c | |
parent | 6fd2143293a671266ced4771d42ec99493ca9ffc (diff) |
libhb: add generic code for converting NAL
unit bitstreams (H.264, HEVC) to and from
Annex B format.
Only used by QSV for now, but could
always come in handy down the road.
May also fix an issue introduced
by the recent QSV cleanup patchset.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6156 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/nal_units.c')
-rw-r--r-- | libhb/nal_units.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/libhb/nal_units.c b/libhb/nal_units.c new file mode 100644 index 000000000..fc87b7798 --- /dev/null +++ b/libhb/nal_units.c @@ -0,0 +1,195 @@ +/* nal_units.c + * + * Copyright (c) 2003-2014 HandBrake Team + * This file is part of the HandBrake source code. + * Homepage: <http://handbrake.fr/>. + * 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 <stdint.h> +#include <string.h> + +#include "common.h" +#include "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; +} |