libobs: Add caption support for HEVC

Add embedded caption support for HEVC by slightly modifying the H.264
SEI to be an HEVC SEI (2 byte SEI NAL header).
This commit is contained in:
JoHn BoWeRs
2024-03-13 13:55:00 -06:00
committed by Lain
parent f9bc9a8b12
commit 33eb95c274
+63 -6
View File
@@ -1520,6 +1520,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
size_t size;
long ref = 1;
bool avc = false;
bool hevc = false;
/* Instead of exiting early for unsupported codecs, we will continue
* processing to allow the freeing of caption data even if the captions
@@ -1527,6 +1528,10 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
* the given codec. */
if (strcmp(out->encoder->info.codec, "h264") == 0) {
avc = true;
#ifdef ENABLE_HEVC
} else if (strcmp(out->encoder->info.codec, "hevc") == 0) {
hevc = true;
#endif
}
DARRAY(uint8_t) out_data;
@@ -1543,6 +1548,31 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
return false;
}
#ifdef ENABLE_HEVC
uint8_t hevc_nal_header[2];
if (hevc) {
size_t nal_header_index_start = 4;
// Skip past the annex-b start code
if (memcmp(out->data, nal_start + 1, 3) == 0) {
nal_header_index_start = 3;
} else if (memcmp(out->data, nal_start, 4) == 0) {
nal_header_index_start = 4;
} else {
/* We shouldn't ever see this unless we start getting
* packets without annex-b start codes. */
blog(LOG_DEBUG,
"Annex-B start code not found. We may not "
"generate a valid HEVC NAL unit header "
"for our caption");
return false;
}
/* We will use the same 2 byte NAL unit header for the CC SEI,
* but swap the NAL types out. */
hevc_nal_header[0] = out->data[nal_header_index_start];
hevc_nal_header[1] = out->data[nal_header_index_start + 1];
}
#endif
sei_init(&sei, 0.0);
da_init(out_data);
@@ -1606,13 +1636,40 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
ctrack->caption_head = next;
}
if (avc) {
if (avc || hevc) {
data = malloc(sei_render_size(&sei));
size = sei_render(&sei, data);
/* TODO: SEI should come after AUD/SPS/PPS,
* but before any VCL */
da_push_back_array(out_data, nal_start, 4);
da_push_back_array(out_data, data, size);
if (avc) {
/* TODO: SEI should come after AUD/SPS/PPS,
* but before any VCL */
da_push_back_array(out_data, nal_start, 4);
da_push_back_array(out_data, data, size);
#ifdef ENABLE_HEVC
} else if (hevc) {
/* Only first NAL, VPS/PPS/SPS should use the 4 byte
start code, SEIs use 3 byte version */
da_push_back_array(out_data, nal_start + 1, 3);
/* nal_unit_header( ) {
* forbidden_zero_bit f(1)
* nal_unit_type u(6)
* nuh_layer_id u(6)
* nuh_temporal_id_plus1 u(3)
* }
*/
const uint8_t prefix_sei_nal_type = 39;
/* The first bit is always 0, so we just need to
* save the last bit off the original header and
* add the SEI NAL type. */
uint8_t first_byte = (prefix_sei_nal_type << 1) |
(0x01 & hevc_nal_header[0]);
hevc_nal_header[0] = first_byte;
/* The HEVC NAL unit header is 2 byte instead of
* one, otherwise everything else is the
* same. */
da_push_back_array(out_data, hevc_nal_header, 2);
da_push_back_array(out_data, &data[1], size - 1);
#endif
}
free(data);
}
@@ -1622,7 +1679,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
out->data = (uint8_t *)out_data.array + sizeof(ref);
out->size = out_data.num - sizeof(ref);
sei_free(&sei);
if (avc) {
if (avc || hevc) {
return true;
}
return false;