mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-06 15:38:53 +00:00
closedcaption: move CC buffering to helper object
Move most of the interesting code from ccconverter to this new helper object. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3211>
This commit is contained in:
parent
06a20f9243
commit
088597b430
5 changed files with 775 additions and 886 deletions
|
@ -31,6 +31,8 @@ GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
typedef struct cdp_fps_entry cdp_fps_entry;
|
typedef struct cdp_fps_entry cdp_fps_entry;
|
||||||
|
|
||||||
|
#define VAL_OR_0(v) ((v) ? (*(v)) : 0)
|
||||||
|
|
||||||
static const struct cdp_fps_entry cdp_fps_table[] = {
|
static const struct cdp_fps_entry cdp_fps_table[] = {
|
||||||
{0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ },
|
{0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ },
|
||||||
{0x2f, 24, 1, 25, 22, 2},
|
{0x2f, 24, 1, 25, 22, 2},
|
||||||
|
@ -333,3 +335,573 @@ convert_cea708_cdp_to_cc_data (GstObject * dbg_obj,
|
||||||
/* skip everything else we don't care about */
|
/* skip everything else we don't care about */
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CC_DATA_EXTRACT_TOO_MANY_FIELD1 -2
|
||||||
|
#define CC_DATA_EXTRACT_TOO_MANY_FIELD2 -3
|
||||||
|
|
||||||
|
static gint
|
||||||
|
cc_data_extract_cea608 (const guint8 * cc_data, guint cc_data_len,
|
||||||
|
guint8 * cea608_field1, guint * cea608_field1_len,
|
||||||
|
guint8 * cea608_field2, guint * cea608_field2_len)
|
||||||
|
{
|
||||||
|
guint i, field_1_len = 0, field_2_len = 0;
|
||||||
|
|
||||||
|
if (cea608_field1_len) {
|
||||||
|
field_1_len = *cea608_field1_len;
|
||||||
|
*cea608_field1_len = 0;
|
||||||
|
}
|
||||||
|
if (cea608_field2_len) {
|
||||||
|
field_2_len = *cea608_field2_len;
|
||||||
|
*cea608_field2_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cc_data_len % 3 != 0) {
|
||||||
|
GST_WARNING ("Invalid cc_data buffer size %u. Truncating to a multiple "
|
||||||
|
"of 3", cc_data_len);
|
||||||
|
cc_data_len = cc_data_len - (cc_data_len % 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cc_data_len / 3; i++) {
|
||||||
|
guint8 byte0 = cc_data[i * 3 + 0];
|
||||||
|
guint8 byte1 = cc_data[i * 3 + 1];
|
||||||
|
guint8 byte2 = cc_data[i * 3 + 2];
|
||||||
|
gboolean cc_valid = (byte0 & 0x04) == 0x04;
|
||||||
|
guint8 cc_type = byte0 & 0x03;
|
||||||
|
|
||||||
|
GST_TRACE ("0x%02x 0x%02x 0x%02x, valid: %u, type: 0b%u%u", byte0, byte1,
|
||||||
|
byte2, cc_valid, (cc_type & 0x2) == 0x2, (cc_type & 0x1) == 0x1);
|
||||||
|
|
||||||
|
if (cc_type == 0x00) {
|
||||||
|
if (!cc_valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cea608_field1 && cea608_field1_len) {
|
||||||
|
if (*cea608_field1_len + 2 > field_1_len) {
|
||||||
|
GST_WARNING ("Too many cea608 input bytes %u for field 1",
|
||||||
|
*cea608_field1_len + 2);
|
||||||
|
return CC_DATA_EXTRACT_TOO_MANY_FIELD1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte1 != 0x80 || byte2 != 0x80) {
|
||||||
|
cea608_field1[(*cea608_field1_len)++] = byte1;
|
||||||
|
cea608_field1[(*cea608_field1_len)++] = byte2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cc_type == 0x01) {
|
||||||
|
if (!cc_valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cea608_field2 && cea608_field2_len) {
|
||||||
|
if (*cea608_field2_len + 2 > field_2_len) {
|
||||||
|
GST_WARNING ("Too many cea608 input bytes %u for field 2",
|
||||||
|
*cea608_field2_len + 2);
|
||||||
|
return CC_DATA_EXTRACT_TOO_MANY_FIELD2;
|
||||||
|
}
|
||||||
|
if (byte1 != 0x80 || byte2 != 0x80) {
|
||||||
|
cea608_field2[(*cea608_field2_len)++] = byte1;
|
||||||
|
cea608_field2[(*cea608_field2_len)++] = byte2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* all cea608 packets must be at the beginning of a cc_data */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_cmpint (i * 3, <=, cc_data_len);
|
||||||
|
|
||||||
|
GST_LOG ("Extracted cea608-1 of length %u and cea608-2 of length %u, "
|
||||||
|
"ccp_offset %i", VAL_OR_0 (cea608_field1_len),
|
||||||
|
VAL_OR_0 (cea608_field2_len), i * 3);
|
||||||
|
|
||||||
|
return i * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
gint
|
||||||
|
drop_ccp_from_cc_data (guint8 * cc_data, guint cc_data_len)
|
||||||
|
{
|
||||||
|
return cc_data_extract_cea608 (cc_data, cc_data_len, NULL, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFAULT_MAX_BUFFER_TIME (100 * GST_MSECOND)
|
||||||
|
|
||||||
|
struct _CCBuffer
|
||||||
|
{
|
||||||
|
GstObject parent;
|
||||||
|
GArray *cea608_1;
|
||||||
|
GArray *cea608_2;
|
||||||
|
GArray *cc_data;
|
||||||
|
/* used for tracking which field to write across output buffer boundaries */
|
||||||
|
gboolean last_cea608_written_was_field1;
|
||||||
|
|
||||||
|
GstClockTime max_buffer_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (CCBuffer, cc_buffer, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
CCBuffer *
|
||||||
|
cc_buffer_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (cc_buffer_get_type (), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cc_buffer_init (CCBuffer * buf)
|
||||||
|
{
|
||||||
|
buf->cea608_1 = g_array_new (FALSE, FALSE, sizeof (guint8));
|
||||||
|
buf->cea608_2 = g_array_new (FALSE, FALSE, sizeof (guint8));
|
||||||
|
buf->cc_data = g_array_new (FALSE, FALSE, sizeof (guint8));
|
||||||
|
|
||||||
|
buf->max_buffer_time = DEFAULT_MAX_BUFFER_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cc_buffer_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
CCBuffer *buf = GST_CC_BUFFER (object);
|
||||||
|
|
||||||
|
g_array_unref (buf->cea608_1);
|
||||||
|
buf->cea608_1 = NULL;
|
||||||
|
g_array_unref (buf->cea608_2);
|
||||||
|
buf->cea608_2 = NULL;
|
||||||
|
g_array_unref (buf->cc_data);
|
||||||
|
buf->cc_data = NULL;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (cc_buffer_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cc_buffer_class_init (CCBufferClass * buf_class)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = (GObjectClass *) buf_class;
|
||||||
|
|
||||||
|
gobject_class->finalize = cc_buffer_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove padding bytes from a cc_data packet. Returns the length of the new
|
||||||
|
* data in @cc_data */
|
||||||
|
static guint
|
||||||
|
compact_cc_data (guint8 * cc_data, guint cc_data_len)
|
||||||
|
{
|
||||||
|
gboolean started_ccp = FALSE;
|
||||||
|
guint out_len = 0;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
if (cc_data_len % 3 != 0) {
|
||||||
|
GST_WARNING ("Invalid cc_data buffer size");
|
||||||
|
cc_data_len = cc_data_len - (cc_data_len % 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cc_data_len / 3; i++) {
|
||||||
|
gboolean cc_valid = (cc_data[i * 3] & 0x04) == 0x04;
|
||||||
|
guint8 cc_type = cc_data[i * 3] & 0x03;
|
||||||
|
|
||||||
|
if (!started_ccp && (cc_type == 0x00 || cc_type == 0x01)) {
|
||||||
|
if (cc_valid) {
|
||||||
|
/* copy over valid 608 data */
|
||||||
|
cc_data[out_len++] = cc_data[i * 3];
|
||||||
|
cc_data[out_len++] = cc_data[i * 3 + 1];
|
||||||
|
cc_data[out_len++] = cc_data[i * 3 + 2];
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cc_type & 0x10)
|
||||||
|
started_ccp = TRUE;
|
||||||
|
|
||||||
|
if (!cc_valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cc_type == 0x00 || cc_type == 0x01) {
|
||||||
|
GST_WARNING ("Invalid cc_data. cea608 bytes after cea708");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_data[out_len++] = cc_data[i * 3];
|
||||||
|
cc_data[out_len++] = cc_data[i * 3 + 1];
|
||||||
|
cc_data[out_len++] = cc_data[i * 3 + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG ("compacted cc_data from %u to %u", cc_data_len, out_len);
|
||||||
|
|
||||||
|
return out_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
calculate_n_cea608_doubles_from_time_ceil (CCBuffer * buf, GstClockTime ns)
|
||||||
|
{
|
||||||
|
/* cea608 has a maximum bitrate of 60000/1001 * 2 bytes/s */
|
||||||
|
guint ret = gst_util_uint64_scale_ceil (ns, 120000, 1001 * GST_SECOND);
|
||||||
|
|
||||||
|
return GST_ROUND_UP_2 (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
calculate_n_cea708_doubles_from_time_ceil (CCBuffer * buf, GstClockTime ns)
|
||||||
|
{
|
||||||
|
/* ccp has a maximum bitrate of 9600000/1001 bits/s */
|
||||||
|
guint ret = gst_util_uint64_scale_ceil (ns, 9600000 / 8, 1001 * GST_SECOND);
|
||||||
|
|
||||||
|
return GST_ROUND_UP_2 (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
push_internal (CCBuffer * buf, const guint8 * cea608_1,
|
||||||
|
guint cea608_1_len, const guint8 * cea608_2, guint cea608_2_len,
|
||||||
|
const guint8 * cc_data, guint cc_data_len)
|
||||||
|
{
|
||||||
|
guint max_cea608_bytes;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (buf, "pushing cea608-1: %u cea608-2: %u ccp: %u",
|
||||||
|
cea608_1_len, cea608_2_len, cc_data_len);
|
||||||
|
max_cea608_bytes =
|
||||||
|
calculate_n_cea608_doubles_from_time_ceil (buf, buf->max_buffer_time);
|
||||||
|
|
||||||
|
if (cea608_1_len > 0) {
|
||||||
|
if (cea608_1_len + buf->cea608_1->len > max_cea608_bytes) {
|
||||||
|
GST_WARNING_OBJECT (buf, "cea608 field 1 overflow, dropping all "
|
||||||
|
"previous data, max %u, attempted to hold %u", max_cea608_bytes,
|
||||||
|
cea608_1_len + buf->cea608_1->len);
|
||||||
|
g_array_set_size (buf->cea608_1, 0);
|
||||||
|
}
|
||||||
|
g_array_append_vals (buf->cea608_1, cea608_1, cea608_1_len);
|
||||||
|
}
|
||||||
|
if (cea608_2_len > 0) {
|
||||||
|
if (cea608_2_len + buf->cea608_2->len > max_cea608_bytes) {
|
||||||
|
GST_WARNING_OBJECT (buf, "cea608 field 2 overflow, dropping all "
|
||||||
|
"previous data, max %u, attempted to hold %u", max_cea608_bytes,
|
||||||
|
cea608_2_len + buf->cea608_2->len);
|
||||||
|
g_array_set_size (buf->cea608_2, 0);
|
||||||
|
}
|
||||||
|
g_array_append_vals (buf->cea608_2, cea608_2, cea608_2_len);
|
||||||
|
}
|
||||||
|
if (cc_data_len > 0) {
|
||||||
|
guint max_cea708_bytes =
|
||||||
|
calculate_n_cea708_doubles_from_time_ceil (buf, buf->max_buffer_time);
|
||||||
|
if (cc_data_len + buf->cc_data->len > max_cea708_bytes) {
|
||||||
|
GST_WARNING_OBJECT (buf, "ccp data overflow, dropping all "
|
||||||
|
"previous data, max %u, attempted to hold %u", max_cea708_bytes,
|
||||||
|
cc_data_len + buf->cc_data->len);
|
||||||
|
g_array_set_size (buf->cea608_2, 0);
|
||||||
|
}
|
||||||
|
g_array_append_vals (buf->cc_data, cc_data, cc_data_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cc_buffer_push_separated (CCBuffer * buf, const guint8 * cea608_1,
|
||||||
|
guint cea608_1_len, const guint8 * cea608_2, guint cea608_2_len,
|
||||||
|
const guint8 * cc_data, guint cc_data_len)
|
||||||
|
{
|
||||||
|
guint8 cea608_1_copy[MAX_CEA608_LEN];
|
||||||
|
guint8 cea608_2_copy[MAX_CEA608_LEN];
|
||||||
|
guint8 cc_data_copy[MAX_CDP_PACKET_LEN];
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
if (cea608_1 && cea608_1_len > 0) {
|
||||||
|
guint out_i = 0;
|
||||||
|
for (i = 0; i < cea608_1_len / 2; i++) {
|
||||||
|
if (cea608_1[i] != 0x80 || cea608_1[i + 1] != 0x80) {
|
||||||
|
cea608_1_copy[out_i++] = cea608_1[i];
|
||||||
|
cea608_1_copy[out_i++] = cea608_1[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cea608_1_len = out_i;
|
||||||
|
} else {
|
||||||
|
cea608_1_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cea608_2 && cea608_2_len > 0) {
|
||||||
|
guint out_i = 0;
|
||||||
|
for (i = 0; i < cea608_2_len / 2; i++) {
|
||||||
|
if (cea608_2[i] != 0x80 || cea608_2[i + 1] != 0x80) {
|
||||||
|
cea608_2_copy[out_i++] = cea608_2[i];
|
||||||
|
cea608_2_copy[out_i++] = cea608_2[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cea608_2_len = out_i;
|
||||||
|
} else {
|
||||||
|
cea608_2_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cc_data && cc_data_len > 0) {
|
||||||
|
memcpy (cc_data_copy, cc_data, cc_data_len);
|
||||||
|
cc_data_len = compact_cc_data (cc_data_copy, cc_data_len);
|
||||||
|
} else {
|
||||||
|
cc_data_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
push_internal (buf, cea608_1_copy, cea608_1_len, cea608_2_copy,
|
||||||
|
cea608_2_len, cc_data_copy, cc_data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cc_buffer_push_cc_data (CCBuffer * buf, const guint8 * cc_data,
|
||||||
|
guint cc_data_len)
|
||||||
|
{
|
||||||
|
guint8 cea608_1[MAX_CEA608_LEN];
|
||||||
|
guint8 cea608_2[MAX_CEA608_LEN];
|
||||||
|
guint8 cc_data_copy[MAX_CDP_PACKET_LEN];
|
||||||
|
guint cea608_1_len = MAX_CEA608_LEN;
|
||||||
|
guint cea608_2_len = MAX_CEA608_LEN;
|
||||||
|
int ccp_offset;
|
||||||
|
|
||||||
|
memcpy (cc_data_copy, cc_data, cc_data_len);
|
||||||
|
|
||||||
|
cc_data_len = compact_cc_data (cc_data_copy, cc_data_len);
|
||||||
|
|
||||||
|
ccp_offset = cc_data_extract_cea608 (cc_data_copy, cc_data_len, cea608_1,
|
||||||
|
&cea608_1_len, cea608_2, &cea608_2_len);
|
||||||
|
|
||||||
|
if (ccp_offset < 0) {
|
||||||
|
GST_WARNING_OBJECT (buf, "Failed to extract cea608 from cc_data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
push_internal (buf, cea608_1, cea608_1_len, cea608_2,
|
||||||
|
cea608_2_len, &cc_data_copy[ccp_offset], cc_data_len - ccp_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cc_buffer_get_stored_size (CCBuffer * buf, guint * cea608_1_len,
|
||||||
|
guint * cea608_2_len, guint * cc_data_len)
|
||||||
|
{
|
||||||
|
if (cea608_1_len)
|
||||||
|
*cea608_1_len = buf->cea608_1->len;
|
||||||
|
if (cea608_2_len)
|
||||||
|
*cea608_2_len = buf->cea608_2->len;
|
||||||
|
if (cc_data_len)
|
||||||
|
*cc_data_len = buf->cc_data->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cc_buffer_discard (CCBuffer * buf)
|
||||||
|
{
|
||||||
|
g_array_set_size (buf->cea608_1, 0);
|
||||||
|
g_array_set_size (buf->cea608_2, 0);
|
||||||
|
g_array_set_size (buf->cc_data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void
|
||||||
|
cc_buffer_peek (CCBuffer * buf, guint8 ** cea608_1, guint * cea608_1_len,
|
||||||
|
guint8 ** cea608_2, guint * cea608_2_len, guint8 ** cc_data,
|
||||||
|
guint * cc_data_len)
|
||||||
|
{
|
||||||
|
if (cea608_1_len) {
|
||||||
|
if (cea608_1) {
|
||||||
|
*cea608_1 = (guint8 *) buf->cea608_1->data;
|
||||||
|
}
|
||||||
|
*cea608_1_len = buf->cea608_1->len;
|
||||||
|
}
|
||||||
|
if (cea608_1_len) {
|
||||||
|
if (cea608_2) {
|
||||||
|
*cea608_2 = (guint8 *) buf->cea608_2->data;
|
||||||
|
}
|
||||||
|
*cea608_2_len = buf->cea608_2->len;
|
||||||
|
}
|
||||||
|
if (cc_data_len) {
|
||||||
|
if (cc_data) {
|
||||||
|
*cc_data = (guint8 *) buf->cc_data->data;
|
||||||
|
}
|
||||||
|
*cc_data_len = buf->cc_data->len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
static void
|
||||||
|
cc_buffer_get_out_sizes (CCBuffer * buf, const struct cdp_fps_entry *fps_entry,
|
||||||
|
guint * cea608_1_len, guint * field1_padding, guint * cea608_2_len,
|
||||||
|
guint * field2_padding, guint * cc_data_len)
|
||||||
|
{
|
||||||
|
gint extra_ccp = 0, extra_cea608_1 = 0, extra_cea608_2 = 0;
|
||||||
|
gint write_ccp_size = 0, write_cea608_1_size = 0, write_cea608_2_size = 0;
|
||||||
|
gboolean wrote_first = FALSE;
|
||||||
|
|
||||||
|
if (buf->cc_data->len) {
|
||||||
|
extra_ccp = buf->cc_data->len - 3 * fps_entry->max_ccp_count;
|
||||||
|
extra_ccp = MAX (0, extra_ccp);
|
||||||
|
write_ccp_size = buf->cc_data->len - extra_ccp;
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_cea608_1 = buf->cea608_1->len;
|
||||||
|
extra_cea608_2 = buf->cea608_2->len;
|
||||||
|
*field1_padding = 0;
|
||||||
|
*field2_padding = 0;
|
||||||
|
|
||||||
|
wrote_first = !buf->last_cea608_written_was_field1;
|
||||||
|
/* try to push data into the packets. Anything 'extra' will be
|
||||||
|
* stored for later */
|
||||||
|
while (TRUE) {
|
||||||
|
gint avail_1, avail_2;
|
||||||
|
|
||||||
|
avail_1 = buf->cea608_1->len - extra_cea608_1 + *field1_padding;
|
||||||
|
avail_2 = buf->cea608_2->len - extra_cea608_2 + *field2_padding;
|
||||||
|
if (avail_1 + avail_2 >= 2 * fps_entry->max_cea608_count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (wrote_first) {
|
||||||
|
if (extra_cea608_1 > 0) {
|
||||||
|
extra_cea608_1 -= 2;
|
||||||
|
g_assert_cmpint (extra_cea608_1, >=, 0);
|
||||||
|
write_cea608_1_size += 2;
|
||||||
|
g_assert_cmpint (write_cea608_1_size, <=, buf->cea608_1->len);
|
||||||
|
} else {
|
||||||
|
*field1_padding += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
avail_1 = buf->cea608_1->len - extra_cea608_1 + *field1_padding;
|
||||||
|
avail_2 = buf->cea608_2->len - extra_cea608_2 + *field2_padding;
|
||||||
|
if (avail_1 + avail_2 >= 2 * fps_entry->max_cea608_count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (extra_cea608_2 > 0) {
|
||||||
|
extra_cea608_2 -= 2;
|
||||||
|
g_assert_cmpint (extra_cea608_2, >=, 0);
|
||||||
|
write_cea608_2_size += 2;
|
||||||
|
g_assert_cmpint (write_cea608_2_size, <=, buf->cea608_2->len);
|
||||||
|
} else {
|
||||||
|
/* we need to insert field 2 padding if we don't have data and are
|
||||||
|
* requested to start with field2 */
|
||||||
|
*field2_padding += 2;
|
||||||
|
}
|
||||||
|
wrote_first = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (buf, "allocated sizes ccp:%u, cea608-1:%u (pad:%u), "
|
||||||
|
"cea608-2:%u (pad:%u)", write_ccp_size, write_cea608_1_size,
|
||||||
|
*field1_padding, write_cea608_2_size, *field2_padding);
|
||||||
|
|
||||||
|
*cea608_1_len = write_cea608_1_size;
|
||||||
|
*cea608_2_len = write_cea608_2_size;
|
||||||
|
*cc_data_len = write_ccp_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cc_buffer_take_separated (CCBuffer * buf,
|
||||||
|
const struct cdp_fps_entry *fps_entry, guint8 * cea608_1,
|
||||||
|
guint * cea608_1_len, guint8 * cea608_2, guint * cea608_2_len,
|
||||||
|
guint8 * cc_data, guint * cc_data_len)
|
||||||
|
{
|
||||||
|
guint write_cea608_1_size, write_cea608_2_size, write_ccp_size;
|
||||||
|
guint field1_padding, field2_padding;
|
||||||
|
|
||||||
|
cc_buffer_get_out_sizes (buf, fps_entry, &write_cea608_1_size,
|
||||||
|
&field1_padding, &write_cea608_2_size, &field2_padding, &write_ccp_size);
|
||||||
|
|
||||||
|
if (cea608_1_len) {
|
||||||
|
if (*cea608_1_len < write_cea608_1_size + field1_padding) {
|
||||||
|
GST_WARNING_OBJECT (buf, "output cea608 field 1 buffer (%u) is too "
|
||||||
|
"small to hold output (%u)", *cea608_1_len,
|
||||||
|
write_cea608_1_size + field1_padding);
|
||||||
|
*cea608_1_len = 0;
|
||||||
|
} else if (cea608_1) {
|
||||||
|
memcpy (cea608_1, buf->cea608_1->data, write_cea608_1_size);
|
||||||
|
memset (&cea608_1[write_cea608_1_size], 0x80, field1_padding);
|
||||||
|
*cea608_1_len = write_cea608_1_size + field1_padding;
|
||||||
|
} else {
|
||||||
|
*cea608_1_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cea608_2_len) {
|
||||||
|
if (*cea608_2_len < write_cea608_2_size + field2_padding) {
|
||||||
|
GST_WARNING_OBJECT (buf, "output cea608 field 2 buffer (%u) is too "
|
||||||
|
"small to hold output (%u)", *cea608_2_len, write_cea608_2_size);
|
||||||
|
*cea608_2_len = 0;
|
||||||
|
} else if (cea608_2) {
|
||||||
|
memcpy (cea608_2, buf->cea608_2->data, write_cea608_2_size);
|
||||||
|
memset (&cea608_2[write_cea608_2_size], 0x80, field2_padding);
|
||||||
|
*cea608_2_len = write_cea608_2_size + field2_padding;
|
||||||
|
} else {
|
||||||
|
*cea608_2_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cc_data_len) {
|
||||||
|
if (*cc_data_len < write_ccp_size) {
|
||||||
|
GST_WARNING_OBJECT (buf, "output ccp buffer (%u) is too "
|
||||||
|
"small to hold output (%u)", *cc_data_len, write_ccp_size);
|
||||||
|
*cc_data_len = 0;
|
||||||
|
} else if (cc_data) {
|
||||||
|
memcpy (cc_data, buf->cc_data->data, write_ccp_size);
|
||||||
|
*cc_data_len = write_ccp_size;
|
||||||
|
} else {
|
||||||
|
*cc_data_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_remove_range (buf->cea608_1, 0, write_cea608_1_size);
|
||||||
|
g_array_remove_range (buf->cea608_2, 0, write_cea608_2_size);
|
||||||
|
g_array_remove_range (buf->cc_data, 0, write_ccp_size);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (buf, "bytes currently stored, cea608-1:%u, cea608-2:%u "
|
||||||
|
"ccp:%u", buf->cea608_1->len, buf->cea608_2->len, buf->cc_data->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cc_buffer_take_cc_data (CCBuffer * buf,
|
||||||
|
const struct cdp_fps_entry *fps_entry, guint8 * cc_data,
|
||||||
|
guint * cc_data_len)
|
||||||
|
{
|
||||||
|
guint write_cea608_1_size, write_cea608_2_size, write_ccp_size;
|
||||||
|
guint field1_padding, field2_padding;
|
||||||
|
gboolean wrote_first;
|
||||||
|
|
||||||
|
cc_buffer_get_out_sizes (buf, fps_entry, &write_cea608_1_size,
|
||||||
|
&field1_padding, &write_cea608_2_size, &field2_padding, &write_ccp_size);
|
||||||
|
|
||||||
|
{
|
||||||
|
guint cea608_1_i = 0, cea608_2_i = 0;
|
||||||
|
guint out_i = 0;
|
||||||
|
guint8 *cea608_1 = (guint8 *) buf->cea608_1->data;
|
||||||
|
guint8 *cea608_2 = (guint8 *) buf->cea608_2->data;
|
||||||
|
guint cea608_output_count =
|
||||||
|
write_cea608_1_size + write_cea608_2_size + field1_padding +
|
||||||
|
field2_padding;
|
||||||
|
|
||||||
|
wrote_first = !buf->last_cea608_written_was_field1;
|
||||||
|
while (cea608_1_i + cea608_2_i < cea608_output_count) {
|
||||||
|
if (wrote_first) {
|
||||||
|
if (cea608_1_i < write_cea608_1_size) {
|
||||||
|
cc_data[out_i++] = 0xfc;
|
||||||
|
cc_data[out_i++] = cea608_1[cea608_1_i];
|
||||||
|
cc_data[out_i++] = cea608_1[cea608_1_i + 1];
|
||||||
|
cea608_1_i += 2;
|
||||||
|
buf->last_cea608_written_was_field1 = TRUE;
|
||||||
|
} else if (cea608_1_i < write_cea608_1_size + field1_padding) {
|
||||||
|
cc_data[out_i++] = 0xf8;
|
||||||
|
cc_data[out_i++] = 0x80;
|
||||||
|
cc_data[out_i++] = 0x80;
|
||||||
|
cea608_1_i += 2;
|
||||||
|
buf->last_cea608_written_was_field1 = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cea608_2_i < write_cea608_2_size) {
|
||||||
|
cc_data[out_i++] = 0xfd;
|
||||||
|
cc_data[out_i++] = cea608_2[cea608_2_i];
|
||||||
|
cc_data[out_i++] = cea608_2[cea608_2_i + 1];
|
||||||
|
cea608_2_i += 2;
|
||||||
|
buf->last_cea608_written_was_field1 = FALSE;
|
||||||
|
} else if (cea608_2_i < write_cea608_2_size + field2_padding) {
|
||||||
|
cc_data[out_i++] = 0xf9;
|
||||||
|
cc_data[out_i++] = 0x80;
|
||||||
|
cc_data[out_i++] = 0x80;
|
||||||
|
cea608_2_i += 2;
|
||||||
|
buf->last_cea608_written_was_field1 = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrote_first = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_ccp_size > 0)
|
||||||
|
memcpy (&cc_data[out_i], buf->cc_data->data, write_ccp_size);
|
||||||
|
*cc_data_len = out_i + write_ccp_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_remove_range (buf->cea608_1, 0, write_cea608_1_size);
|
||||||
|
g_array_remove_range (buf->cea608_2, 0, write_cea608_2_size);
|
||||||
|
g_array_remove_range (buf->cc_data, 0, write_ccp_size);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (buf, "bytes currently stored, cea608-1:%u, cea608-2:%u "
|
||||||
|
"ccp:%u", buf->cea608_1->len, buf->cea608_2->len, buf->cc_data->len);
|
||||||
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ typedef enum {
|
||||||
GST_CC_CDP_MODE_CC_SVC_INFO = (1<<2)
|
GST_CC_CDP_MODE_CC_SVC_INFO = (1<<2)
|
||||||
} GstCCCDPMode;
|
} GstCCCDPMode;
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL
|
||||||
guint convert_cea708_cc_data_to_cdp (GstObject * dbg_obj,
|
guint convert_cea708_cc_data_to_cdp (GstObject * dbg_obj,
|
||||||
GstCCCDPMode cdp_mode,
|
GstCCCDPMode cdp_mode,
|
||||||
guint16 cdp_hdr_sequence_cntr,
|
guint16 cdp_hdr_sequence_cntr,
|
||||||
|
@ -60,14 +61,60 @@ guint convert_cea708_cc_data_to_cdp (GstObject * dbg_obj,
|
||||||
const GstVideoTimeCode * tc,
|
const GstVideoTimeCode * tc,
|
||||||
const struct cdp_fps_entry *fps_entry);
|
const struct cdp_fps_entry *fps_entry);
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL
|
||||||
guint convert_cea708_cdp_to_cc_data (GstObject * dbg_obj,
|
guint convert_cea708_cdp_to_cc_data (GstObject * dbg_obj,
|
||||||
const guint8 * cdp,
|
const guint8 * cdp,
|
||||||
guint cdp_len,
|
guint cdp_len,
|
||||||
guint8 *cc_data,
|
guint8 *cc_data,
|
||||||
GstVideoTimeCode * tc,
|
GstVideoTimeCode * tc,
|
||||||
const struct cdp_fps_entry **out_fps_entry);
|
const struct cdp_fps_entry **out_fps_entry);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
gint drop_ccp_from_cc_data (guint8 * cc_data,
|
||||||
|
guint cc_data_len);
|
||||||
|
|
||||||
#define MAX_CDP_PACKET_LEN 256
|
#define MAX_CDP_PACKET_LEN 256
|
||||||
|
#define MAX_CEA608_LEN 32
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE (CCBuffer, cc_buffer, GST, CC_BUFFER, GObject);
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
CCBuffer * cc_buffer_new (void);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
void cc_buffer_get_stored_size (CCBuffer * buf,
|
||||||
|
guint * cea608_1_len,
|
||||||
|
guint * cea608_2_len,
|
||||||
|
guint * cc_data_len);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
void cc_buffer_push_separated (CCBuffer * buf,
|
||||||
|
const guint8 * cea608_1,
|
||||||
|
guint cea608_1_len,
|
||||||
|
const guint8 * cea608_2,
|
||||||
|
guint cea608_2_len,
|
||||||
|
const guint8 * cc_data,
|
||||||
|
guint cc_data_len);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
void cc_buffer_push_cc_data (CCBuffer * buf,
|
||||||
|
const guint8 * cc_data,
|
||||||
|
guint cc_data_len);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
void cc_buffer_take_cc_data (CCBuffer * buf,
|
||||||
|
const struct cdp_fps_entry * fps_entry,
|
||||||
|
guint8 * cc_data,
|
||||||
|
guint * cc_data_len);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
void cc_buffer_take_separated (CCBuffer * buf,
|
||||||
|
const struct cdp_fps_entry * fps_entry,
|
||||||
|
guint8 * cea608_1,
|
||||||
|
guint * cea608_1_len,
|
||||||
|
guint8 * cea608_2,
|
||||||
|
guint * cea608_2_len,
|
||||||
|
guint8 * cc_data,
|
||||||
|
guint * cc_data_len);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
void cc_buffer_discard (CCBuffer * buf);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
void cc_buffer_set_max_buffer_time (CCBuffer * buf,
|
||||||
|
GstClockTime max_time);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,6 +25,8 @@
|
||||||
#include <gst/base/base.h>
|
#include <gst/base/base.h>
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
#include "ccutils.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
#define GST_TYPE_CCCONVERTER \
|
#define GST_TYPE_CCCONVERTER \
|
||||||
(gst_cc_converter_get_type())
|
(gst_cc_converter_get_type())
|
||||||
|
@ -40,9 +42,6 @@ G_BEGIN_DECLS
|
||||||
typedef struct _GstCCConverter GstCCConverter;
|
typedef struct _GstCCConverter GstCCConverter;
|
||||||
typedef struct _GstCCConverterClass GstCCConverterClass;
|
typedef struct _GstCCConverterClass GstCCConverterClass;
|
||||||
|
|
||||||
#define MAX_CDP_PACKET_LEN 256
|
|
||||||
#define MAX_CEA608_LEN 32
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GST_CC_CONVERTER_CDP_MODE_TIME_CODE = (1<<0),
|
GST_CC_CONVERTER_CDP_MODE_TIME_CODE = (1<<0),
|
||||||
GST_CC_CONVERTER_CDP_MODE_CC_DATA = (1<<1),
|
GST_CC_CONVERTER_CDP_MODE_CC_DATA = (1<<1),
|
||||||
|
@ -67,21 +66,13 @@ struct _GstCCConverter
|
||||||
/* for framerate differences, we need to keep previous/next frames in order
|
/* for framerate differences, we need to keep previous/next frames in order
|
||||||
* to split/merge data across multiple input or output buffers. The data is
|
* to split/merge data across multiple input or output buffers. The data is
|
||||||
* stored as cc_data */
|
* stored as cc_data */
|
||||||
guint8 scratch_cea608_1[MAX_CEA608_LEN];
|
CCBuffer *cc_buffer;
|
||||||
guint scratch_cea608_1_len;
|
|
||||||
guint8 scratch_cea608_2[MAX_CEA608_LEN];
|
|
||||||
guint scratch_cea608_2_len;
|
|
||||||
guint8 scratch_ccp[MAX_CDP_PACKET_LEN];
|
|
||||||
guint scratch_ccp_len;
|
|
||||||
|
|
||||||
guint input_frames;
|
guint input_frames;
|
||||||
guint output_frames;
|
guint output_frames;
|
||||||
GstVideoTimeCode current_output_timecode;
|
GstVideoTimeCode current_output_timecode;
|
||||||
/* previous buffer for copying metas onto */
|
/* previous buffer for copying metas onto */
|
||||||
GstBuffer *previous_buffer;
|
GstBuffer *previous_buffer;
|
||||||
|
|
||||||
/* used for tracking which field to write across output buffer boundaries */
|
|
||||||
gboolean last_cea608_written_was_field1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstCCConverterClass
|
struct _GstCCConverterClass
|
||||||
|
|
|
@ -476,12 +476,12 @@ GST_START_TEST (convert_cea708_cdp_cea608_raw)
|
||||||
{ 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x80,
|
{ 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x80,
|
||||||
0xfd, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a
|
0xfd, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a
|
||||||
};
|
};
|
||||||
const guint8 out1[] = { 0x80, 0x80, 0x80, 0x80 };
|
const guint8 out1[] = { 0x80, 0x80, };
|
||||||
const guint8 in2[] =
|
const guint8 in2[] =
|
||||||
{ 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x81,
|
{ 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x81,
|
||||||
0xfd, 0x82, 0x83, 0x74, 0x00, 0x00, 0x8a
|
0xfd, 0x82, 0x83, 0x74, 0x00, 0x00, 0x8a
|
||||||
};
|
};
|
||||||
const guint8 out2[] = { 0x80, 0x81, 0x80, 0x80 };
|
const guint8 out2[] = { 0x80, 0x81, };
|
||||||
check_conversion_tc_passthrough (in1, sizeof (in1), out1, sizeof (out1),
|
check_conversion_tc_passthrough (in1, sizeof (in1), out1, sizeof (out1),
|
||||||
"closedcaption/x-cea-708,format=(string)cdp,framerate=30/1",
|
"closedcaption/x-cea-708,format=(string)cdp,framerate=30/1",
|
||||||
"closedcaption/x-cea-608,format=(string)raw");
|
"closedcaption/x-cea-608,format=(string)raw");
|
||||||
|
@ -514,7 +514,7 @@ GST_START_TEST (convert_cea708_cdp_cea708_cc_data)
|
||||||
};
|
};
|
||||||
const guint8 out[] = { 0xf8, 0x80, 0x80, 0xf9, 0x80, 0x80 };
|
const guint8 out[] = { 0xf8, 0x80, 0x80, 0xf9, 0x80, 0x80 };
|
||||||
check_conversion_tc_passthrough (in, sizeof (in), out, sizeof (out),
|
check_conversion_tc_passthrough (in, sizeof (in), out, sizeof (out),
|
||||||
"closedcaption/x-cea-708,format=(string)cdp",
|
"closedcaption/x-cea-708,format=(string)cdp,framerate=30/1",
|
||||||
"closedcaption/x-cea-708,format=(string)cc_data");
|
"closedcaption/x-cea-708,format=(string)cc_data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,17 +524,23 @@ GST_START_TEST (convert_cea708_cdp_cea708_cc_data_too_big)
|
||||||
{
|
{
|
||||||
/* tests that too large input is truncated */
|
/* tests that too large input is truncated */
|
||||||
const guint8 in[] =
|
const guint8 in[] =
|
||||||
{ 0x96, 0x69, 0x2e, 0x8f, 0x43, 0x00, 0x00, 0x72, 0xeb, 0xfc, 0x80, 0x80,
|
{ 0x96, 0x69, 0x4c, 0x8f, 0x43, 0x00, 0x00, 0x72, 0xf5, 0xfc, 0x80, 0x80,
|
||||||
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a,
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
|
0x74, 0x00, 0x00, 0x8a,
|
||||||
};
|
};
|
||||||
const guint8 out[] = { 0xf8, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
const guint8 out[] = { 0xf8, 0x80, 0x80, 0xf9, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
|
0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x80,
|
||||||
|
0xfe, 0x80, 0x80
|
||||||
};
|
};
|
||||||
check_conversion_tc_passthrough (in, sizeof (in), out, sizeof (out),
|
check_conversion_tc_passthrough (in, sizeof (in), out, sizeof (out),
|
||||||
"closedcaption/x-cea-708,format=(string)cdp",
|
"closedcaption/x-cea-708,format=(string)cdp,framerate=30/1",
|
||||||
"closedcaption/x-cea-708,format=(string)cc_data");
|
"closedcaption/x-cea-708,format=(string)cc_data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,12 +1137,12 @@ GST_START_TEST (convert_cea708_cc_data_cea708_cdp_field1_overflow)
|
||||||
gsize in_data_offset;
|
gsize in_data_offset;
|
||||||
/* take frames sequentially from the input */
|
/* take frames sequentially from the input */
|
||||||
gsize in_idx = i / 2;
|
gsize in_idx = i / 2;
|
||||||
/* take the first 16 input frames, then skip the next 16 frames and take
|
/* take the first 12 input frames, then skip the next 12 frames and take
|
||||||
* the next 16 frames etc.
|
* the next 12 frames etc.
|
||||||
* 32 is the byte size of the internal cea608 field buffers that we are
|
* 24 is the byte size of the internal cea608 field buffers that we are
|
||||||
* overflowing but every second buffer will have cea608 field 1 in it.
|
* overflowing but every second buffer will have cea608 field 1 in it.
|
||||||
* 16 frames is 32 bytes stored and is enough to cause overflow */
|
* 12 frames is 24 bytes stored and is enough to cause overflow */
|
||||||
in_idx = (in_idx / 16) * 32 + in_idx % 16;
|
in_idx = (in_idx / 6) * 12 + in_idx % 6;
|
||||||
in_data_offset = in_idx * 3;
|
in_data_offset = in_idx * 3;
|
||||||
|
|
||||||
out_data[i * 43 + 9] = in_data[in_data_offset + 0];
|
out_data[i * 43 + 9] = in_data[in_data_offset + 0];
|
||||||
|
|
Loading…
Reference in a new issue