summaryrefslogtreecommitdiffstats
path: root/libhb/nal_units.c
diff options
context:
space:
mode:
authorRodeo <[email protected]>2014-04-07 22:04:03 +0000
committerRodeo <[email protected]>2014-04-07 22:04:03 +0000
commit2e6f73542dcc281dc19b2f1347cfe7992e12ed13 (patch)
tree36e6e2733c80231c74780e9faabd34684a8ef149 /libhb/nal_units.c
parent6fd2143293a671266ced4771d42ec99493ca9ffc (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.c195
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;
+}