PROP_RATE_CONTROL,
PROP_CPB_SIZE,
PROP_AUD,
+ PROP_CC,
N_PROPERTIES
};
gboolean use_dct8x8;
gboolean use_trellis;
gboolean aud;
+ gboolean cc;
guint32 mbbrc;
guint32 num_slices;
guint32 cpb_size;
gboolean use_dct8x8;
gboolean use_trellis;
gboolean aud;
+ gboolean cc;
guint32 num_slices;
guint32 packed_headers;
self->use_dct8x8 = self->prop.use_dct8x8;
self->use_trellis = self->prop.use_trellis;
self->aud = self->prop.aud;
+ self->cc = self->prop.cc;
self->num_slices = self->prop.num_slices;
self->gop.idr_period = self->prop.key_int_max;
self->aud = self->aud && self->packed_headers & VA_ENC_PACKED_HEADER_RAW_DATA;
update_property_bool (base, &self->prop.aud, self->aud, PROP_AUD);
+ self->cc = self->cc && self->packed_headers & VA_ENC_PACKED_HEADER_RAW_DATA;
+ update_property_bool (base, &self->prop.cc, self->cc, PROP_CC);
+
max_ref_frames = self->gop.num_ref_frames + 3 /* scratch frames */ ;
if (!gst_va_encoder_open (base->encoder, base->profile,
GST_VIDEO_INFO_FORMAT (&base->input_state->info), base->rt_format,
return TRUE;
}
+static void
+_create_sei_cc_message (GstVideoCaptionMeta * cc_meta,
+ GstH264SEIMessage * sei_msg)
+{
+ guint8 *data;
+ GstH264RegisteredUserData *user_data;
+
+ sei_msg->payloadType = GST_H264_SEI_REGISTERED_USER_DATA;
+
+ user_data = &sei_msg->payload.registered_user_data;
+
+ user_data->country_code = 181;
+ user_data->size = 10 + cc_meta->size;
+
+ data = g_malloc (user_data->size);
+
+ /* 16-bits itu_t_t35_provider_code */
+ data[0] = 0;
+ data[1] = 49;
+ /* 32-bits ATSC_user_identifier */
+ data[2] = 'G';
+ data[3] = 'A';
+ data[4] = '9';
+ data[5] = '4';
+ /* 8-bits ATSC1_data_user_data_type_code */
+ data[6] = 3;
+ /* 8-bits:
+ * 1 bit process_em_data_flag (0)
+ * 1 bit process_cc_data_flag (1)
+ * 1 bit additional_data_flag (0)
+ * 5-bits cc_count
+ */
+ data[7] = ((cc_meta->size / 3) & 0x1f) | 0x40;
+ /* 8 bits em_data, unused */
+ data[8] = 255;
+
+ memcpy (data + 9, cc_meta->data, cc_meta->size);
+
+ /* 8 marker bits */
+ data[user_data->size - 1] = 255;
+
+ user_data->data = data;
+}
+
+static gboolean
+_create_sei_cc_data (GPtrArray * cc_list, guint8 * sei_data, guint * data_size)
+{
+ GArray *msg_list = NULL;
+ GstH264BitWriterResult ret;
+ gint i;
+
+ msg_list = g_array_new (TRUE, TRUE, sizeof (GstH264SEIMessage));
+ g_array_set_clear_func (msg_list, (GDestroyNotify) gst_h264_sei_clear);
+ g_array_set_size (msg_list, cc_list->len);
+
+ for (i = 0; i < cc_list->len; i++) {
+ GstH264SEIMessage *msg = &g_array_index (msg_list, GstH264SEIMessage, i);
+ _create_sei_cc_message (g_ptr_array_index (cc_list, i), msg);
+ }
+
+ ret = gst_h264_bit_writer_sei (msg_list, TRUE, sei_data, data_size);
+
+ g_array_unref (msg_list);
+
+ return (ret == GST_H264_BIT_WRITER_OK);
+}
+
+static void
+_add_sei_cc (GstVaH264Enc * self, GstVideoCodecFrame * gst_frame)
+{
+ GstVaBaseEnc *base = GST_VA_BASE_ENC (self);
+ GstVaH264EncFrame *frame;
+ GPtrArray *cc_list = NULL;
+ GstVideoCaptionMeta *cc_meta;
+ gpointer iter = NULL;
+ guint8 *packed_sei = NULL;
+ guint sei_size = 0;
+
+ frame = _enc_frame (gst_frame);
+
+ /* SEI header size */
+ sei_size = 6;
+ while ((cc_meta = (GstVideoCaptionMeta *)
+ gst_buffer_iterate_meta_filtered (gst_frame->input_buffer, &iter,
+ GST_VIDEO_CAPTION_META_API_TYPE))) {
+ if (cc_meta->caption_type != GST_VIDEO_CAPTION_TYPE_CEA708_RAW)
+ continue;
+
+ if (!cc_list)
+ cc_list = g_ptr_array_new ();
+
+ g_ptr_array_add (cc_list, cc_meta);
+ /* Add enough SEI message size for bitwriter. */
+ sei_size += cc_meta->size + 50;
+ }
+
+ if (!cc_list)
+ goto out;
+
+ packed_sei = g_malloc0 (sei_size);
+
+ if (!_create_sei_cc_data (cc_list, packed_sei, &sei_size)) {
+ GST_WARNING_OBJECT (self, "Failed to write the SEI CC data");
+ goto out;
+ }
+
+ if (!gst_va_encoder_add_packed_header (base->encoder, frame->picture,
+ VAEncPackedHeaderRawData, packed_sei, sei_size * 8, FALSE)) {
+ GST_WARNING_OBJECT (self, "Failed to add SEI CC data");
+ goto out;
+ }
+
+out:
+ g_clear_pointer (&cc_list, g_ptr_array_unref);
+ if (packed_sei)
+ g_free (packed_sei);
+}
+
static gboolean
_encode_one_frame (GstVaH264Enc * self, GstVideoCodecFrame * gst_frame)
{
&& !_add_picture_header (self, frame, &pps))
return FALSE;
+ if (self->cc) {
+ /* CC errors are not fatal */
+ _add_sei_cc (self, gst_frame);
+ }
+
slice_of_mbs = self->mb_width * self->mb_height / self->num_slices;
slice_mod_mbs = self->mb_width * self->mb_height % self->num_slices;
slice_start_mb = 0;
self->prop.use_cabac = TRUE;
self->prop.use_trellis = FALSE;
self->prop.aud = FALSE;
+ self->prop.cc = TRUE;
self->prop.mbbrc = 0;
self->prop.bitrate = 0;
self->prop.target_percentage = 66;
case PROP_AUD:
self->prop.aud = g_value_get_boolean (value);
break;
+ case PROP_CC:
+ self->prop.cc = g_value_get_boolean (value);
+ break;
case PROP_MBBRC:{
/* Macroblock-level rate control.
* 0: use default,
case PROP_AUD:
g_value_set_boolean (value, self->prop.aud);
break;
+ case PROP_CC:
+ g_value_set_boolean (value, self->prop.cc);
+ break;
case PROP_MBBRC:
g_value_set_enum (value, self->prop.mbbrc);
break;
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
/**
+ * GstVaH264Enc:cc-insert:
+ *
+ * Closed Caption Insert mode.
+ * Only CEA-708 RAW format is supported for now.
+ */
+ properties[PROP_CC] = g_param_spec_boolean ("cc-insert",
+ "Insert Closed Captions",
+ "Insert CEA-708 Closed Captions",
+ TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
+
+ /**
* GstVaH264Enc:mbbrc:
*
* Macroblock level bitrate control.