Files
obs-studio/deps/libcaption/src/cea708.c
Matthew Szatmary a8517f3698 libobs: Update libcaption library
(This commit also modifies deps/libcaption)

Closes obsproject/obs-studio#1354
2018-07-08 01:14:27 -07:00

284 lines
11 KiB
C

/**********************************************************************************************/
/* The MIT License */
/* */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining a copy */
/* of this software and associated documentation files (the "Software"), to deal */
/* in the Software without restriction, including without limitation the rights */
/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
/* copies of the Software, and to permit persons to whom the Software is */
/* furnished to do so, subject to the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be included in */
/* all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
/* THE SOFTWARE. */
/**********************************************************************************************/
#include "cea708.h"
#include <float.h>
#include <memory.h>
#include <stdio.h>
int cea708_cc_count(user_data_t* data)
{
return data->cc_count;
}
uint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_cc_type_t* type)
{
(*valid) = data->cc_data[index].cc_valid;
(*type) = data->cc_data[index].cc_type;
return data->cc_data[index].cc_data;
}
int cea708_init(cea708_t* cea708, double timestamp)
{
memset(cea708, 0, sizeof(cea708_t));
cea708->country = country_united_states;
cea708->provider = t35_provider_atsc;
cea708->user_identifier = GA94;
cea708->user_data_type_code = 3;
cea708->directv_user_data_length = 0;
cea708->user_data.process_em_data_flag = 0;
cea708->user_data.process_cc_data_flag = 1;
cea708->user_data.additional_data_flag = 0;
cea708->user_data.em_data = 0xFF;
cea708->user_data.cc_count = 0;
cea708->timestamp = timestamp;
return 1;
}
void cea708_parse_user_data_type_strcture(const uint8_t* data, size_t size, user_data_t* user_data)
{
memset(user_data, 0, sizeof(user_data_t));
user_data->process_em_data_flag = !!(data[0] & 0x80);
user_data->process_cc_data_flag = !!(data[0] & 0x40);
user_data->additional_data_flag = !!(data[0] & 0x20);
user_data->cc_count = (data[0] & 0x1F);
user_data->em_data = data[1];
data += 2, size -= 2;
for (int i = 0; 3 < size && i < (int)user_data->cc_count; ++i, data += 3, size -= 3) {
user_data->cc_data[i].marker_bits = data[0] >> 3;
user_data->cc_data[i].cc_valid = data[0] >> 2;
user_data->cc_data[i].cc_type = data[0] >> 0;
user_data->cc_data[i].cc_data = data[1] << 8 | data[2];
}
}
// 00 00 00 06 C1 FF FC 34 B9 FF : onCaptionInfo.
libcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708)
{
if (3 > size) {
goto error;
}
// I think the first few bytes need to be handled in mpeg
cea708->country = (itu_t_t35_country_code_t)(data[0]);
cea708->provider = (itu_t_t35_provider_code_t)((data[1] << 8) | data[2]);
cea708->user_identifier = 0;
cea708->user_data_type_code = 0;
data += 3, size -= 3;
if (t35_provider_atsc == cea708->provider) {
if (4 > size) {
goto error;
}
cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
data += 4, size -= 4;
}
// where country and provider are zero
// Im not sure what this extra byte is. It sonly seesm to come up in onCaptionInfo
// h264 spec seems to describe this
if (0 == cea708->provider && 0 == cea708->country) {
if (1 > size) {
goto error;
}
data += 1, size -= 1;
} else if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) {
if (1 > size) {
goto error;
}
cea708->user_data_type_code = data[0];
data += 1, size -= 1;
}
if (t35_provider_direct_tv == cea708->provider) {
if (1 > size) {
goto error;
}
cea708->directv_user_data_length = data[0];
data += 1, size -= 1;
}
if (3 == cea708->user_data_type_code && 2 <= size) {
cea708_parse_user_data_type_strcture(data, size, &cea708->user_data);
} else if (4 == cea708->user_data_type_code) {
// additional_CEA_608_data
} else if (5 == cea708->user_data_type_code) {
// luma_PAM_data
} else {
// ATSC_reserved_user_data
}
return LIBCAPTION_OK;
error:
return LIBCAPTION_ERROR;
}
libcaption_stauts_t cea708_parse_h262(const uint8_t* data, size_t size, cea708_t* cea708)
{
if (!data || 7 > size) {
return LIBCAPTION_ERROR;
}
cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
cea708->user_data_type_code = data[4];
if (3 == cea708->user_data_type_code) {
cea708_parse_user_data_type_strcture(data + 5, size - 5, &cea708->user_data);
}
return LIBCAPTION_OK;
}
int cea708_add_cc_data(cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data)
{
if (31 <= cea708->user_data.cc_count) {
return 0;
}
cea708->user_data.cc_data[cea708->user_data.cc_count].marker_bits = 0x1F;
cea708->user_data.cc_data[cea708->user_data.cc_count].cc_valid = valid;
cea708->user_data.cc_data[cea708->user_data.cc_count].cc_type = type;
cea708->user_data.cc_data[cea708->user_data.cc_count].cc_data = cc_data;
++cea708->user_data.cc_count;
return 1;
}
int cea708_render(cea708_t* cea708, uint8_t* data, size_t size)
{
int i;
size_t total = 0;
data[0] = cea708->country;
data[1] = cea708->provider >> 8;
data[2] = cea708->provider >> 0;
total += 3;
data += 3;
size -= 3;
if (t35_provider_atsc == cea708->provider) {
data[0] = cea708->user_identifier >> 24;
data[1] = cea708->user_identifier >> 16;
data[2] = cea708->user_identifier >> 8;
data[3] = cea708->user_identifier >> 0;
total += 4;
data += 4;
size -= 4;
}
if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) {
data[0] = cea708->user_data_type_code;
total += 1;
data += 1;
size -= 1;
}
if (t35_provider_direct_tv == cea708->provider) {
data[0] = cea708->directv_user_data_length;
total += 1;
data += 1;
size -= 1;
}
data[1] = cea708->user_data.em_data;
data[0] = (cea708->user_data.process_em_data_flag ? 0x80 : 0x00)
| (cea708->user_data.process_cc_data_flag ? 0x40 : 0x00)
| (cea708->user_data.additional_data_flag ? 0x20 : 0x00)
| (cea708->user_data.cc_count & 0x1F);
total += 2;
data += 2;
size -= 2;
for (i = 0; i < (int)cea708->user_data.cc_count; ++i) {
data[0] = (cea708->user_data.cc_data[i].marker_bits << 3) | (cea708->user_data.cc_data[i].cc_valid << 2) | cea708->user_data.cc_data[i].cc_type;
data[1] = cea708->user_data.cc_data[i].cc_data >> 8;
data[2] = cea708->user_data.cc_data[i].cc_data >> 0;
total += 3;
data += 3;
size -= 3;
}
data[0] = 0xFF; //marker bits
return (int)(total + 1);
}
cc_data_t cea708_encode_cc_data(int cc_valid, cea708_cc_type_t type, uint16_t cc_data)
{
cc_data_t data = { 0x1F, cc_valid, type, cc_data };
return data;
}
void cea708_dump(cea708_t* cea708)
{
int i;
fprintf(stderr, "itu_t_t35_country_code_t %d\n", cea708->country);
fprintf(stderr, "itu_t_t35_provider_code_t %d\n", cea708->provider);
fprintf(stderr, "user_identifier %c%c%c%c\n",
(cea708->user_identifier >> 24) & 0xFF, (cea708->user_identifier >> 16) & 0xFF,
(cea708->user_identifier >> 8) & 0xFF, cea708->user_identifier & 0xFF);
fprintf(stderr, "user_data_type_code %d\n", cea708->user_data_type_code);
fprintf(stderr, "directv_user_data_length %d\n", cea708->directv_user_data_length);
fprintf(stderr, "user_data.process_em_data_flag %d\n", cea708->user_data.process_em_data_flag);
fprintf(stderr, "user_data.process_cc_data_flag %d\n", cea708->user_data.process_cc_data_flag);
fprintf(stderr, "user_data.additional_data_flag %d\n", cea708->user_data.additional_data_flag);
fprintf(stderr, "user_data.cc_count %d\n", cea708->user_data.cc_count);
fprintf(stderr, "user_data.em_data %d\n", cea708->user_data.em_data);
for (i = 0; i < (int)cea708->user_data.cc_count; ++i) {
int valid;
cea708_cc_type_t type;
uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type);
if (valid && cc_type_ntsc_cc_field_1 == type) {
eia608_dump(cc_data);
} else {
fprintf(stderr, "user_data.cc_data[%d] cc_valid: %s, cc_type: %d, cc_data: %04x\n", i, cea708->user_data.cc_data[i].cc_valid ? "true" : "false", cea708->user_data.cc_data[i].cc_type, cea708->user_data.cc_data[i].cc_data);
}
}
}
libcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708)
{
int i, count = cea708_cc_count(&cea708->user_data);
libcaption_stauts_t status = LIBCAPTION_OK;
if (GA94 == cea708->user_identifier) {
for (i = 0; i < count; ++i) {
int valid;
cea708_cc_type_t type;
uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type);
if (valid && cc_type_ntsc_cc_field_1 == type) {
status = libcaption_status_update(status, caption_frame_decode(frame, cc_data, cea708->timestamp));
}
}
}
return status;
}