mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-24 16:18:16 +00:00
qtdemux: Detect and expose CEA 608/708 Closed Caption tracks
https://bugzilla.gnome.org/show_bug.cgi?id=606643
This commit is contained in:
parent
8270cbacb4
commit
2869edeea2
3 changed files with 156 additions and 2 deletions
|
@ -95,6 +95,12 @@ G_BEGIN_DECLS
|
||||||
#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1')
|
#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1')
|
||||||
#define FOURCC_avc3 GST_MAKE_FOURCC('a','v','c','3')
|
#define FOURCC_avc3 GST_MAKE_FOURCC('a','v','c','3')
|
||||||
#define FOURCC_avcC GST_MAKE_FOURCC('a','v','c','C')
|
#define FOURCC_avcC GST_MAKE_FOURCC('a','v','c','C')
|
||||||
|
#define FOURCC_c608 GST_MAKE_FOURCC('c','6','0','8')
|
||||||
|
#define FOURCC_c708 GST_MAKE_FOURCC('c','7','0','8')
|
||||||
|
#define FOURCC_ccdp GST_MAKE_FOURCC('c','c','d','p')
|
||||||
|
#define FOURCC_cdat GST_MAKE_FOURCC('c','d','a','t')
|
||||||
|
#define FOURCC_cdt2 GST_MAKE_FOURCC('c','d','t','2')
|
||||||
|
#define FOURCC_clcp GST_MAKE_FOURCC('c','l','c','p')
|
||||||
#define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p')
|
#define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p')
|
||||||
#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v')
|
#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v')
|
||||||
#define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d')
|
#define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d')
|
||||||
|
|
|
@ -5370,6 +5370,120 @@ gst_qtdemux_align_buffer (GstQTDemux * demux,
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static guint8 *
|
||||||
|
convert_to_ccdata (const guint8 * ccpair, guint8 ccpair_size, guint field,
|
||||||
|
gsize * res)
|
||||||
|
{
|
||||||
|
guint8 *storage;
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
/* We are converting from pairs to triplets */
|
||||||
|
*res = ccpair_size / 2 * 3;
|
||||||
|
storage = g_malloc (*res);
|
||||||
|
for (i = 0; i * 2 < ccpair_size; i += 1) {
|
||||||
|
if (field == 1)
|
||||||
|
storage[i * 3] = 0xfc;
|
||||||
|
else
|
||||||
|
storage[i * 3] = 0xfd;
|
||||||
|
storage[i * 3 + 1] = ccpair[i * 2];
|
||||||
|
storage[i * 3 + 2] = ccpair[i * 2 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint8 *
|
||||||
|
extract_cc_from_data (QtDemuxStream * stream, const guint8 * data, gsize size,
|
||||||
|
gsize * cclen)
|
||||||
|
{
|
||||||
|
guint8 *res = NULL;
|
||||||
|
guint32 atom_length, fourcc;
|
||||||
|
QtDemuxStreamStsdEntry *stsd_entry;
|
||||||
|
|
||||||
|
GST_MEMDUMP ("caption atom", data, size);
|
||||||
|
|
||||||
|
/* There might be multiple atoms */
|
||||||
|
|
||||||
|
*cclen = 0;
|
||||||
|
if (size < 8)
|
||||||
|
goto invalid_cdat;
|
||||||
|
atom_length = QT_UINT32 (data);
|
||||||
|
fourcc = QT_FOURCC (data + 4);
|
||||||
|
if (G_UNLIKELY (atom_length > size || atom_length == 8))
|
||||||
|
goto invalid_cdat;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (stream->pad, "here");
|
||||||
|
|
||||||
|
/* Check if we have somethig compatible */
|
||||||
|
stsd_entry = CUR_STREAM (stream);
|
||||||
|
switch (stsd_entry->fourcc) {
|
||||||
|
case FOURCC_c608:{
|
||||||
|
guint8 *cdat = NULL, *cdt2 = NULL;
|
||||||
|
gsize cdat_size = 0, cdt2_size = 0;
|
||||||
|
/* Should be cdat or cdt2 */
|
||||||
|
if (fourcc != FOURCC_cdat && fourcc != FOURCC_cdt2) {
|
||||||
|
GST_WARNING_OBJECT (stream->pad,
|
||||||
|
"Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA608",
|
||||||
|
GST_FOURCC_ARGS (fourcc));
|
||||||
|
goto invalid_cdat;
|
||||||
|
}
|
||||||
|
/* Convert to cc_data triplet */
|
||||||
|
if (fourcc == FOURCC_cdat)
|
||||||
|
cdat = convert_to_ccdata (data + 8, atom_length - 8, 1, &cdat_size);
|
||||||
|
else
|
||||||
|
cdt2 = convert_to_ccdata (data + 8, atom_length - 8, 2, &cdt2_size);
|
||||||
|
GST_DEBUG_OBJECT (stream->pad, "size:%" G_GSIZE_FORMAT " atom_length:%u",
|
||||||
|
size, atom_length);
|
||||||
|
/* Check for another atom ? */
|
||||||
|
if (size > atom_length + 8) {
|
||||||
|
guint32 new_atom_length = QT_UINT32 (data + atom_length);
|
||||||
|
if (size <= atom_length + new_atom_length) {
|
||||||
|
fourcc = QT_FOURCC (data + atom_length + 4);
|
||||||
|
if (fourcc == FOURCC_cdat)
|
||||||
|
cdat =
|
||||||
|
convert_to_ccdata (data + atom_length + 8, new_atom_length - 8,
|
||||||
|
1, &cdat_size);
|
||||||
|
else
|
||||||
|
cdt2 =
|
||||||
|
convert_to_ccdata (data + atom_length + 8, new_atom_length - 8,
|
||||||
|
2, &cdt2_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*cclen = cdat_size + cdt2_size;
|
||||||
|
res = g_malloc (*cclen);
|
||||||
|
if (cdat_size)
|
||||||
|
memcpy (res, cdat, cdat_size);
|
||||||
|
if (cdt2_size)
|
||||||
|
memcpy (res + cdat_size, cdt2, cdt2_size);
|
||||||
|
g_free (cdat);
|
||||||
|
g_free (cdt2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FOURCC_c708:
|
||||||
|
if (fourcc != FOURCC_ccdp) {
|
||||||
|
GST_WARNING_OBJECT (stream->pad,
|
||||||
|
"Unknown data atom (%" GST_FOURCC_FORMAT ") for CEA708",
|
||||||
|
GST_FOURCC_ARGS (fourcc));
|
||||||
|
goto invalid_cdat;
|
||||||
|
}
|
||||||
|
*cclen = atom_length - 8;
|
||||||
|
res = g_memdup (data + 8, *cclen);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Keep this here in case other closed caption formats are added */
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_MEMDUMP ("Output", res, *cclen);
|
||||||
|
return res;
|
||||||
|
|
||||||
|
/* Errors */
|
||||||
|
invalid_cdat:
|
||||||
|
GST_WARNING ("[cdat] atom is too small or invalid");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* the input buffer metadata must be writable,
|
/* the input buffer metadata must be writable,
|
||||||
* but time/duration etc not yet set and need not be preserved */
|
* but time/duration etc not yet set and need not be preserved */
|
||||||
static GstBuffer *
|
static GstBuffer *
|
||||||
|
@ -5390,7 +5504,7 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
|
|
||||||
if (G_UNLIKELY (stream->subtype != FOURCC_text
|
if (G_UNLIKELY (stream->subtype != FOURCC_text
|
||||||
&& stream->subtype != FOURCC_sbtl &&
|
&& stream->subtype != FOURCC_sbtl &&
|
||||||
stream->subtype != FOURCC_subp)) {
|
stream->subtype != FOURCC_subp && stream->subtype != FOURCC_clcp)) {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5408,6 +5522,23 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stream->subtype == FOURCC_clcp) {
|
||||||
|
guint8 *cc;
|
||||||
|
gsize cclen = 0;
|
||||||
|
/* For closed caption, we need to extract the information from the
|
||||||
|
* [cdat],[cdt2] or [ccdp] atom */
|
||||||
|
cc = extract_cc_from_data (stream, map.data, map.size, &cclen);
|
||||||
|
gst_buffer_unmap (buf, &map);
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
if (cc) {
|
||||||
|
buf = _gst_buffer_new_wrapped (cc, cclen, g_free);
|
||||||
|
} else {
|
||||||
|
/* Conversion failed or there's nothing */
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
nsize = GST_READ_UINT16_BE (map.data);
|
nsize = GST_READ_UINT16_BE (map.data);
|
||||||
nsize = MIN (nsize, map.size - 2);
|
nsize = MIN (nsize, map.size - 2);
|
||||||
|
|
||||||
|
@ -11557,7 +11688,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
|
||||||
}
|
}
|
||||||
entry->sampled = TRUE;
|
entry->sampled = TRUE;
|
||||||
} else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
|
} else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text
|
||||||
|| stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt) {
|
|| stream->subtype == FOURCC_sbtl || stream->subtype == FOURCC_subt
|
||||||
|
|| stream->subtype == FOURCC_clcp) {
|
||||||
|
|
||||||
entry->sampled = TRUE;
|
entry->sampled = TRUE;
|
||||||
entry->sparse = TRUE;
|
entry->sparse = TRUE;
|
||||||
|
@ -14548,6 +14680,21 @@ qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
_codec ("XML subtitles");
|
_codec ("XML subtitles");
|
||||||
caps = gst_caps_new_empty_simple ("application/ttml+xml");
|
caps = gst_caps_new_empty_simple ("application/ttml+xml");
|
||||||
break;
|
break;
|
||||||
|
case FOURCC_c608:
|
||||||
|
_codec ("CEA 608 Closed Caption");
|
||||||
|
caps =
|
||||||
|
gst_caps_new_simple ("closedcaption/x-cea-608", "format",
|
||||||
|
G_TYPE_STRING, "cc_data", NULL);
|
||||||
|
stream->need_process = TRUE;
|
||||||
|
break;
|
||||||
|
case FOURCC_c708:
|
||||||
|
_codec ("CEA 708 Closed Caption");
|
||||||
|
caps =
|
||||||
|
gst_caps_new_simple ("closedcaption/x-cea-708", "format",
|
||||||
|
G_TYPE_STRING, "cdp", NULL);
|
||||||
|
stream->need_process = TRUE;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
caps = _get_unknown_codec_name ("text", fourcc);
|
caps = _get_unknown_codec_name ("text", fourcc);
|
||||||
|
|
|
@ -213,6 +213,7 @@ static const QtNodeType qt_node_types[] = {
|
||||||
{FOURCC_pssh, "protection system specific header", 0},
|
{FOURCC_pssh, "protection system specific header", 0},
|
||||||
{FOURCC_tenc, "track encryption", 0},
|
{FOURCC_tenc, "track encryption", 0},
|
||||||
{FOURCC_stpp, "XML subtitle sample entry", 0},
|
{FOURCC_stpp, "XML subtitle sample entry", 0},
|
||||||
|
{FOURCC_clcp, "Closed Caption", 0},
|
||||||
{0, "unknown", 0,},
|
{0, "unknown", 0,},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue