From d5a5dc29c79765d7bce7d1cd06cb14450d6ec5de Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Thu, 8 Mar 2018 14:35:28 +0100 Subject: qtmux: WIP CC support --- gst/isomp4/gstqtmux.c | 139 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 121 insertions(+), 18 deletions(-) (limited to 'gst/isomp4/gstqtmux.c') diff --git a/gst/isomp4/gstqtmux.c b/gst/isomp4/gstqtmux.c index 9c2609c09..81eee0d9b 100644 --- a/gst/isomp4/gstqtmux.c +++ b/gst/isomp4/gstqtmux.c @@ -133,6 +133,7 @@ #include #include #include +#include #include #include @@ -873,34 +874,128 @@ gst_qt_mux_prepare_jpc_buffer (GstQTPad * qtpad, GstBuffer * buf, return newbuf; } +static gsize +extract_608_field_from_cc_data (const guint8 * ccdata, gsize ccdata_size, + guint field, guint8 ** res) +{ + guint8 *storage; + gsize storage_size = 128; + gsize i, res_size = 0; + + storage = g_malloc0 (storage_size); + + /* Iterate over the ccdata and put the corresponding tuples for the given field + * in the storage */ + for (i = 0; i < ccdata_size; i += 3) { + if ((field == 1 && ccdata[i * 3] == 0xfc) || + (field == 2 && ccdata[i * 3] == 0xfd)) { + GST_DEBUG ("Storing matching cc for field %d : 0x%02x 0x%02x", field, + ccdata[i * 3 + 1], ccdata[i * 3 + 2]); + if (res_size >= storage_size) { + storage_size += 128; + storage = g_realloc (storage, storage_size); + } + storage[res_size] = ccdata[i * 3 + 1]; + storage[res_size + 1] = ccdata[i * 3 + 2]; + res_size += 2; + } + } + + if (res_size == 0) { + g_free (storage); + *res = NULL; + return 0; + } + + *res = storage; + return res_size; +} + + static GstBuffer * -gst_qt_mux_prepare_cdat_buffer (GstQTPad * qtpad, GstBuffer * buf, +gst_qt_mux_prepare_caption_buffer (GstQTPad * qtpad, GstBuffer * buf, GstQTMux * qtmux) { - GstBuffer *newbuf; + GstBuffer *newbuf = NULL; GstMapInfo map, inmap; gsize size; - GST_LOG_OBJECT (qtmux, "Preparing cdat buffer"); - if (buf == NULL) return NULL; size = gst_buffer_get_size (buf); - GST_LOG_OBJECT (qtmux, "Preparing buffer of size %" G_GSIZE_FORMAT, size + 8); - newbuf = gst_buffer_new_and_alloc (size + 8); + gst_buffer_map (buf, &inmap, GST_MAP_READ); - /* Let's copy over all metadata and not the memory */ - gst_buffer_copy_into (newbuf, buf, GST_BUFFER_COPY_METADATA, 0, size); + GST_LOG_OBJECT (qtmux, + "Preparing caption buffer %" GST_FOURCC_FORMAT " size:%" G_GSIZE_FORMAT, + GST_FOURCC_ARGS (qtpad->fourcc), size); - gst_buffer_map (newbuf, &map, GST_MAP_WRITE); - gst_buffer_map (buf, &inmap, GST_MAP_READ); + switch (qtpad->fourcc) { + case FOURCC_c608: + { + guint8 *cdat, *cdt2; + gsize cdat_size, cdt2_size, total_size = 0; + gsize write_offs = 0; + + cdat_size = extract_608_field_from_cc_data (map.data, map.size, 1, &cdat); + cdt2_size = extract_608_field_from_cc_data (map.data, map.size, 2, &cdt2); + + if (cdat_size) + total_size += cdat_size + 8; + if (cdt2_size) + total_size += cdt2_size + 8; + if (total_size == 0) { + GST_DEBUG_OBJECT (qtmux, "No 608 data ?"); + /* FIXME : We might want to *always* store something, even if + * it's "empty" CC (i.e. 0x80 0x80) */ + break; + } - GST_WRITE_UINT32_BE (map.data, map.size); - GST_WRITE_UINT32_LE (map.data + 4, FOURCC_cdat); - memcpy (map.data + 8, inmap.data, inmap.size); + newbuf = gst_buffer_new_and_alloc (total_size); + /* Let's copy over all metadata and not the memory */ + gst_buffer_copy_into (newbuf, buf, GST_BUFFER_COPY_METADATA, 0, size); + + gst_buffer_map (newbuf, &map, GST_MAP_WRITE); + if (cdat_size) { + GST_WRITE_UINT32_BE (map.data, cdat_size + 8); + GST_WRITE_UINT32_LE (map.data + 4, FOURCC_cdat); + memcpy (map.data + 8, cdat, cdat_size); + write_offs = cdat_size + 8; + g_free (cdat); + } + if (cdt2_size) { + GST_WRITE_UINT32_BE (map.data + write_offs, cdt2_size + 8); + GST_WRITE_UINT32_LE (map.data + write_offs + 4, FOURCC_cdt2); + memcpy (map.data + write_offs + 8, cdt2, cdt2_size); + g_free (cdt2); + } + gst_buffer_unmap (newbuf, &map); + break; + } + break; + case FOURCC_c708: + { + /* Take the whole CDP */ + newbuf = gst_buffer_new_and_alloc (size + 8); + + /* Let's copy over all metadata and not the memory */ + gst_buffer_copy_into (newbuf, buf, GST_BUFFER_COPY_METADATA, 0, size); + + gst_buffer_map (newbuf, &map, GST_MAP_WRITE); + + GST_WRITE_UINT32_BE (map.data, map.size + 8); + GST_WRITE_UINT32_LE (map.data + 4, FOURCC_ccdp); + memcpy (map.data + 8, inmap.data, inmap.size); + + gst_buffer_unmap (newbuf, &map); + break; + } + default: + /* theoretically this should never happen, but let's keep this here in case */ + GST_WARNING_OBJECT (qtmux, "Unknown caption format"); + break; + } - gst_buffer_unmap (newbuf, &map); gst_buffer_unmap (buf, &inmap); gst_buffer_unref (buf); @@ -5829,6 +5924,7 @@ gst_qt_mux_caption_sink_set_caps (GstQTPad * qtpad, GstCaps * caps) GstQTMux *qtmux = GST_QT_MUX_CAST (gst_pad_get_parent (pad)); GstStructure *structure; guint32 fourcc_entry; + guint32 timescale; if (qtpad->fourcc) return gst_qt_mux_can_renegotiate (qtmux, pad, caps); @@ -5840,12 +5936,12 @@ gst_qt_mux_caption_sink_set_caps (GstQTPad * qtpad, GstCaps * caps) qtpad->is_out_of_order = FALSE; qtpad->sync = FALSE; qtpad->sparse = TRUE; - /* Closed caption data is within a cdat atom */ - qtpad->prepare_buf_func = gst_qt_mux_prepare_cdat_buffer; + /* Closed caption data are within atoms */ + qtpad->prepare_buf_func = gst_qt_mux_prepare_caption_buffer; structure = gst_caps_get_structure (caps, 0); - + /* We know we only handle 608,format=cc_data and 708,format=cdp */ if (gst_structure_has_name (structure, "closedcaption/x-cea-608")) { fourcc_entry = FOURCC_c608; } else if (gst_structure_has_name (structure, "closedcaption/x-cea-708")) { @@ -5853,10 +5949,17 @@ gst_qt_mux_caption_sink_set_caps (GstQTPad * qtpad, GstCaps * caps) } else goto refuse_caps; + /* FIXME: Get the timescale from the video track ? */ + timescale = gst_qt_mux_pad_get_timescale (GST_QT_MUX_PAD_CAST (pad)); + if (!timescale && qtmux->trak_timescale) + timescale = qtmux->trak_timescale; + else if (!timescale) + timescale = 30000; + qtpad->fourcc = fourcc_entry; qtpad->trak_ste = (SampleTableEntry *) atom_trak_set_caption_type (qtpad->trak, - qtmux->context, fourcc_entry); + qtmux->context, timescale, fourcc_entry); gst_object_unref (qtmux); return TRUE; -- cgit v1.2.3