#include <gst/video/video.h>
#include <string.h>
+#include "ccutils.h"
#include "gstccconverter.h"
GST_DEBUG_CATEGORY_STATIC (gst_cc_converter_debug);
gst_cc_converter_cdp_mode_get_type (void)
{
static const GFlagsValue values[] = {
- {GST_CC_CONVERTER_CDP_MODE_TIME_CODE,
+ {GST_CC_CDP_MODE_TIME_CODE,
"Store time code information in CDP packets", "time-code"},
- {GST_CC_CONVERTER_CDP_MODE_CC_DATA, "Store CC data in CDP packets",
+ {GST_CC_CDP_MODE_CC_DATA, "Store CC data in CDP packets",
"cc-data"},
- {GST_CC_CONVERTER_CDP_MODE_CC_SVC_INFO,
+ {GST_CC_CDP_MODE_CC_SVC_INFO,
"Store CC service information in CDP packets", "cc-svc-info"},
{0, NULL, NULL}
};
}
}
-struct cdp_fps_entry
-{
- guint8 fps_idx;
- guint fps_n, fps_d;
- guint max_cc_count;
- guint max_ccp_count;
- guint max_cea608_count;
-};
-
-static const struct cdp_fps_entry cdp_fps_table[] = {
- {0x1f, 24000, 1001, 25, 22, 3 /* FIXME: alternating max cea608 count! */ },
- {0x2f, 24, 1, 25, 22, 2},
- {0x3f, 25, 1, 24, 22, 2},
- {0x4f, 30000, 1001, 20, 18, 2},
- {0x5f, 30, 1, 20, 18, 2},
- {0x6f, 50, 1, 12, 11, 1},
- {0x7f, 60000, 1001, 10, 9, 1},
- {0x8f, 60, 1, 10, 9, 1},
-};
-static const struct cdp_fps_entry null_fps_entry = { 0, 0, 0, 0 };
-
-static const struct cdp_fps_entry *
-cdp_fps_entry_from_id (guint8 id)
-{
- int i;
- for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
- if (cdp_fps_table[i].fps_idx == id)
- return &cdp_fps_table[i];
- }
- return &null_fps_entry;
-}
-
-static const struct cdp_fps_entry *
-cdp_fps_entry_from_fps (guint fps_n, guint fps_d)
-{
- int i;
- for (i = 0; i < G_N_ELEMENTS (cdp_fps_table); i++) {
- if (cdp_fps_table[i].fps_n == fps_n && cdp_fps_table[i].fps_d == fps_d)
- return &cdp_fps_table[i];
- }
- return &null_fps_entry;
-}
-
static void
get_framerate_output_scale (GstCCConverter * self,
const struct cdp_fps_entry *in_fps_entry, gint * scale_n, gint * scale_d)
return out_len;
}
+#define CC_DATA_EXTRACT_TOO_MANY_FIELD1 -2
+#define CC_DATA_EXTRACT_TOO_MANY_FIELD2 -3
+
static gint
cc_data_extract_cea608 (guint8 * cc_data, guint cc_data_len,
guint8 * cea608_field1, guint * 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 -1;
+ return CC_DATA_EXTRACT_TOO_MANY_FIELD1;
}
if (byte1 != 0x80 || byte2 != 0x80) {
if (*cea608_field2_len + 2 > field_2_len) {
GST_WARNING ("Too many cea608 input bytes %u for field 2",
*cea608_field2_len + 2);
- return -1;
+ return CC_DATA_EXTRACT_TOO_MANY_FIELD2;
}
if (byte1 != 0x80 || byte2 != 0x80) {
cea608_field2[(*cea608_field2_len)++] = byte1;
/* This is slightly looser than checking for the exact framerate as the cdp
* spec allow for 0.1% difference between framerates to be considered equal */
- if (in_fps_entry->max_cc_count == out_fps_entry->max_cc_count) {
- if (tc && tc->config.fps_n != 0)
- interpolate_time_code_with_framerate (self, tc, out_fps_entry->fps_n,
- out_fps_entry->fps_d, 1, 1, &self->current_output_timecode);
-
- self->scratch_ccp_len = 0;
- self->scratch_cea608_1_len = 0;
- self->scratch_cea608_2_len = 0;
- self->input_frames = 0;
- self->output_frames = 0;
- } else {
+ {
int input_frame_n, input_frame_d, output_frame_n, output_frame_d;
- int output_time_cmp, scale_n, scale_d, rate_cmp;
+ int output_time_cmp, scale_n, scale_d;
/* TODO: handle input discont */
- /* compute the relative frame count for each */
- if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
- self->input_frames, 1, &input_frame_n, &input_frame_d))
- /* we should never overflow */
- g_assert_not_reached ();
+ if (self->in_fps_n == 0) {
+ input_frame_n = self->input_frames;
+ input_frame_d = 1;
+ } else {
+ /* compute the relative frame count for each */
+ if (!gst_util_fraction_multiply (self->in_fps_d, self->in_fps_n,
+ self->input_frames, 1, &input_frame_n, &input_frame_d))
+ /* we should never overflow */
+ g_assert_not_reached ();
+ }
- if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
- self->output_frames, 1, &output_frame_n, &output_frame_d))
- /* we should never overflow */
- g_assert_not_reached ();
+ if (self->in_fps_n == 0) {
+ output_frame_n = self->output_frames;
+ output_frame_d = 1;
+ } else {
+ if (!gst_util_fraction_multiply (self->out_fps_d, self->out_fps_n,
+ self->output_frames, 1, &output_frame_n, &output_frame_d))
+ /* we should never overflow */
+ g_assert_not_reached ();
+ }
output_time_cmp = gst_util_fraction_compare (input_frame_n, input_frame_d,
output_frame_n, output_frame_d);
/* compute the relative rates of the two framerates */
get_framerate_output_scale (self, in_fps_entry, &scale_n, &scale_d);
- rate_cmp = gst_util_fraction_compare (scale_n, scale_d, 1, 1);
-
- GST_TRACE_OBJECT (self, "performing framerate conversion at scale %d/%d "
+ GST_TRACE_OBJECT (self, "performing conversion at scale %d/%d "
"of cc data of with sizes, ccp:%u, cea608-1:%u, cea608-2:%u", scale_n,
scale_d, VAL_OR_0 (ccp_data_len), VAL_OR_0 (cea608_1_len),
VAL_OR_0 (cea608_2_len));
- if (rate_cmp == 0) {
- /* we are not scaling. Should never happen with current conditions
- * above */
- g_assert_not_reached ();
- } else if (output_time_cmp < 0) {
+ if (output_time_cmp < 0) {
/* we can't generate an output yet */
guint cd_len = ccp_data_len ? *ccp_data_len : 0;
guint c1_len = cea608_1_len ? *cea608_1_len : 0;
if (cea608_2_len)
*cea608_2_len = 0;
return FALSE;
- } else if (rate_cmp != 0) {
+ } else {
/* we are changing the framerate and may overflow the max output packet
* size. Split them where necessary. */
gint extra_ccp = 0, extra_cea608_1 = 0, extra_cea608_2 = 0;
self->scratch_cea608_1_len = 0;
self->scratch_cea608_2_len = 0;
}
- } else {
- g_assert_not_reached ();
}
if (tc && tc->config.fps_n != 0)
return TRUE;
}
-/* Converts raw CEA708 cc_data and an optional timecode into CDP */
static guint
convert_cea708_cc_data_cea708_cdp_internal (GstCCConverter * self,
const guint8 * cc_data, guint cc_data_len, guint8 * cdp, guint cdp_len,
const GstVideoTimeCode * tc, const struct cdp_fps_entry *fps_entry)
{
- GstByteWriter bw;
- guint8 flags, checksum;
- guint i, len;
-
- GST_DEBUG_OBJECT (self, "writing out cdp packet from cc_data with length %u",
- cc_data_len);
-
- gst_byte_writer_init_with_data (&bw, cdp, cdp_len, FALSE);
- gst_byte_writer_put_uint16_be_unchecked (&bw, 0x9669);
- /* Write a length of 0 for now */
- gst_byte_writer_put_uint8_unchecked (&bw, 0);
-
- gst_byte_writer_put_uint8_unchecked (&bw, fps_entry->fps_idx);
-
- if (cc_data_len / 3 > fps_entry->max_cc_count) {
- GST_WARNING_OBJECT (self, "Too many cc_data triplets for framerate: %u. "
- "Truncating to %u", cc_data_len / 3, fps_entry->max_cc_count);
- cc_data_len = 3 * fps_entry->max_cc_count;
- }
-
- /* caption_service_active */
- flags = 0x02;
-
- /* ccdata_present */
- if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA))
- flags |= 0x40;
-
- /* time_code_present */
- if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
- && tc->config.fps_n > 0)
- flags |= 0x80;
-
- /* reserved */
- flags |= 0x01;
-
- gst_byte_writer_put_uint8_unchecked (&bw, flags);
-
- gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
-
- if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_TIME_CODE) && tc
- && tc->config.fps_n > 0) {
- guint8 u8;
-
- gst_byte_writer_put_uint8_unchecked (&bw, 0x71);
- /* reserved 11 - 2 bits */
- u8 = 0xc0;
- /* tens of hours - 2 bits */
- u8 |= ((tc->hours / 10) & 0x3) << 4;
- /* units of hours - 4 bits */
- u8 |= (tc->hours % 10) & 0xf;
- gst_byte_writer_put_uint8_unchecked (&bw, u8);
-
- /* reserved 1 - 1 bit */
- u8 = 0x80;
- /* tens of minutes - 3 bits */
- u8 |= ((tc->minutes / 10) & 0x7) << 4;
- /* units of minutes - 4 bits */
- u8 |= (tc->minutes % 10) & 0xf;
- gst_byte_writer_put_uint8_unchecked (&bw, u8);
-
- /* field flag - 1 bit */
- u8 = tc->field_count < 2 ? 0x00 : 0x80;
- /* tens of seconds - 3 bits */
- u8 |= ((tc->seconds / 10) & 0x7) << 4;
- /* units of seconds - 4 bits */
- u8 |= (tc->seconds % 10) & 0xf;
- gst_byte_writer_put_uint8_unchecked (&bw, u8);
-
- /* drop frame flag - 1 bit */
- u8 = (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) ? 0x80 :
- 0x00;
- /* reserved0 - 1 bit */
- /* tens of frames - 2 bits */
- u8 |= ((tc->frames / 10) & 0x3) << 4;
- /* units of frames 4 bits */
- u8 |= (tc->frames % 10) & 0xf;
- gst_byte_writer_put_uint8_unchecked (&bw, u8);
- }
-
- if ((self->cdp_mode & GST_CC_CONVERTER_CDP_MODE_CC_DATA)) {
- gst_byte_writer_put_uint8_unchecked (&bw, 0x72);
- gst_byte_writer_put_uint8_unchecked (&bw, 0xe0 | fps_entry->max_cc_count);
- gst_byte_writer_put_data_unchecked (&bw, cc_data, cc_data_len);
- while (fps_entry->max_cc_count > cc_data_len / 3) {
- gst_byte_writer_put_uint8_unchecked (&bw, 0xfa);
- gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
- gst_byte_writer_put_uint8_unchecked (&bw, 0x00);
- cc_data_len += 3;
- }
- }
+ guint ret;
- gst_byte_writer_put_uint8_unchecked (&bw, 0x74);
- gst_byte_writer_put_uint16_be_unchecked (&bw, self->cdp_hdr_sequence_cntr);
+ ret = convert_cea708_cc_data_to_cdp (GST_OBJECT (self),
+ (GstCCCDPMode) self->cdp_mode, self->cdp_hdr_sequence_cntr, cc_data,
+ cc_data_len, cdp, cdp_len, tc, fps_entry);
self->cdp_hdr_sequence_cntr++;
- /* We calculate the checksum afterwards */
- gst_byte_writer_put_uint8_unchecked (&bw, 0);
-
- len = gst_byte_writer_get_pos (&bw);
- gst_byte_writer_set_pos (&bw, 2);
- gst_byte_writer_put_uint8_unchecked (&bw, len);
-
- checksum = 0;
- for (i = 0; i < len; i++) {
- checksum += cdp[i];
- }
- checksum &= 0xff;
- checksum = 256 - checksum;
- cdp[len - 1] = checksum;
- return len;
+ return ret;
}
/* Converts CDP into raw CEA708 cc_data */
return 0;
}
- drop_frame = ! !(u8 & 0x80);
+ drop_frame = !(!(u8 & 0x80));
frames = ((u8 >> 4) & 0x3) * 10 + (u8 & 0xf);
gst_video_time_code_init (tc, fps_entry->fps_n, fps_entry->fps_d, NULL,
guint new_cea608_1_len = 0, new_cea608_2_len = 0;
guint8 *new_cea608_1 = cea608_1, *new_cea608_2 = cea608_2;
- if (cea608_1_len) {
- new_cea608_1_len = cea608_1_in_size - *cea608_1_len;
- new_cea608_1 = &cea608_1[*cea608_1_len];
- }
- if (cea608_2_len) {
- new_cea608_2_len = cea608_2_in_size - *cea608_2_len;
- new_cea608_2 = &cea608_2[*cea608_2_len];
- }
-
cc_data_len = compact_cc_data (cc_data, cc_data_len);
if (cc_data_len / 3 > in_fps_entry->max_cc_count) {
cc_data_len = 3 * in_fps_entry->max_cc_count;
}
- ccp_offset = cc_data_extract_cea608 (cc_data, cc_data_len, new_cea608_1,
- &new_cea608_1_len, new_cea608_2, &new_cea608_2_len);
- if (ccp_offset < 0) {
- GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data");
- goto fail;
+ while (TRUE) {
+ if (cea608_1_len) {
+ new_cea608_1_len = cea608_1_in_size - *cea608_1_len;
+ new_cea608_1 = &cea608_1[*cea608_1_len];
+ }
+ if (cea608_2_len) {
+ new_cea608_2_len = cea608_2_in_size - *cea608_2_len;
+ new_cea608_2 = &cea608_2[*cea608_2_len];
+ }
+
+ ccp_offset = cc_data_extract_cea608 (cc_data, cc_data_len, new_cea608_1,
+ &new_cea608_1_len, new_cea608_2, &new_cea608_2_len);
+
+ if (ccp_offset == CC_DATA_EXTRACT_TOO_MANY_FIELD1 && cea608_1_len) {
+ GST_WARNING_OBJECT (self, "cea608 field 1 overflow, dropping all "
+ "previously stored field 1 data and trying again");
+ *cea608_1_len = 0;
+ } else if (ccp_offset == CC_DATA_EXTRACT_TOO_MANY_FIELD2 && cea608_2_len) {
+ GST_WARNING_OBJECT (self, "cea608 field 2 overflow, dropping all "
+ "previously stored field 2 data and trying again");
+ *cea608_2_len = 0;
+ } else if (ccp_offset < 0) {
+ GST_WARNING_OBJECT (self, "Failed to extract cea608 from cc_data");
+ goto fail;
+ } else {
+ /* success */
+ break;
+ }
}
if ((new_cea608_1_len + new_cea608_2_len) / 2 >