From d644cda79b2e2419a294942761c1090d3d66be27 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 6 Feb 2014 12:09:01 -0300 Subject: [PATCH] qtmux: add support for the TX3G atoms Adds functions for creating and setting values related to the tx3g atom for raw text subtitle support. QTFF spec has information on those atoms https://bugzilla.gnome.org/show_bug.cgi?id=581295 --- gst/isomp4/atoms.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++ gst/isomp4/atoms.h | 35 ++++++++- gst/isomp4/fourcc.h | 1 + 3 files changed, 241 insertions(+), 2 deletions(-) diff --git a/gst/isomp4/atoms.c b/gst/isomp4/atoms.c index 8ce375c..4b65e52 100644 --- a/gst/isomp4/atoms.c +++ b/gst/isomp4/atoms.c @@ -487,6 +487,38 @@ sample_entry_mp4v_new (AtomsContext * context) } static void +sample_entry_tx3g_init (SampleTableEntryTX3G * tx3g) +{ + atom_sample_entry_init (&tx3g->se, FOURCC_tx3g); + + tx3g->display_flags = 0; + tx3g->font_id = 1; /* must be 1 as there is a single font */ + tx3g->font_face = 0; + tx3g->foreground_color_rgba = 0xFFFFFFFF; /* white, opaque */ + + /* can't set this now */ + tx3g->default_text_box = 0; + tx3g->font_size = 0; +} + +static void +sample_entry_tx3g_free (SampleTableEntryTX3G * tx3g) +{ + atom_sample_entry_free (&tx3g->se); + g_free (tx3g); +} + +static SampleTableEntryTX3G * +sample_entry_tx3g_new (void) +{ + SampleTableEntryTX3G *tx3g = g_new0 (SampleTableEntryTX3G, 1); + + sample_entry_tx3g_init (tx3g); + return tx3g; +} + + +static void atom_stsd_init (AtomSTSD * stsd) { guint8 flags[3] = { 0, 0, 0 }; @@ -516,6 +548,9 @@ atom_stsd_remove_entries (AtomSTSD * stsd) case VIDEO: sample_entry_mp4v_free ((SampleTableEntryMP4V *) se); break; + case SUBTITLE: + sample_entry_tx3g_free ((SampleTableEntryTX3G *) se); + break; default: /* best possible cleanup */ atom_sample_entry_free (se); @@ -1795,6 +1830,48 @@ sample_entry_mp4v_copy_data (SampleTableEntryMP4V * mp4v, guint8 ** buffer, return *offset - original_offset; } +static guint64 +sample_entry_tx3g_copy_data (SampleTableEntryTX3G * tx3g, guint8 ** buffer, + guint64 * size, guint64 * offset) +{ + guint64 original_offset = *offset; + + if (!atom_sample_entry_copy_data (&tx3g->se, buffer, size, offset)) { + return 0; + } + + prop_copy_uint32 (tx3g->display_flags, buffer, size, offset); + + /* reserved */ + prop_copy_uint8 (1, buffer, size, offset); + prop_copy_uint8 (-1, buffer, size, offset); + prop_copy_uint32 (0, buffer, size, offset); + + prop_copy_uint64 (tx3g->default_text_box, buffer, size, offset); + + /* reserved */ + prop_copy_uint32 (0, buffer, size, offset); + + prop_copy_uint16 (tx3g->font_id, buffer, size, offset); + prop_copy_uint8 (tx3g->font_face, buffer, size, offset); + prop_copy_uint8 (tx3g->font_size, buffer, size, offset); + prop_copy_uint32 (tx3g->foreground_color_rgba, buffer, size, offset); + + /* it must have a fonttable atom */ + { + Atom atom; + + atom_header_set (&atom, FOURCC_ftab, 18, 0); + atom_copy_data (&atom, buffer, size, offset); + prop_copy_uint16 (1, buffer, size, offset); /* Count must be 1 */ + prop_copy_uint16 (1, buffer, size, offset); /* Font id: 1 */ + prop_copy_size_string ((guint8 *) "Serif", 5, buffer, size, offset); + } + + atom_write_size (buffer, size, offset, original_offset); + return *offset - original_offset; +} + guint64 atom_stsz_copy_data (AtomSTSZ * stsz, guint8 ** buffer, guint64 * size, guint64 * offset) @@ -1985,6 +2062,11 @@ atom_stsd_copy_data (AtomSTSD * stsd, guint8 ** buffer, guint64 * size, walker->data, buffer, size, offset)) { return 0; } + } else if (se->kind == SUBTITLE) { + if (!sample_entry_tx3g_copy_data ((SampleTableEntryTX3G *) + walker->data, buffer, size, offset)) { + return 0; + } } else { if (!atom_hint_sample_entry_copy_data ( (AtomHintSampleEntry *) walker->data, buffer, size, offset)) { @@ -2826,6 +2908,40 @@ atom_trak_update_bitrates (AtomTRAK * trak, guint32 avg_bitrate, } } +void +atom_trak_tx3g_update_dimension (AtomTRAK * trak, guint32 width, guint32 height) +{ + AtomSTSD *stsd; + GList *iter; + SampleTableEntryTX3G *tx3g = NULL; + + stsd = &trak->mdia.minf.stbl.stsd; + for (iter = stsd->entries; iter && tx3g == NULL; iter = g_list_next (iter)) { + SampleTableEntry *entry = iter->data; + + switch (entry->kind) { + case SUBTITLE:{ + tx3g = (SampleTableEntryTX3G *) entry; + break; + } + default: + break; + } + } + + /* Currently we never set the vertical placement flag, so we don't + * check for it to set the dimensions differently as the spec says. + * Always do it for the not set case */ + if (tx3g) { + tx3g->font_size = 0.05 * height; + + height = 0.15 * height; + trak->tkhd.width = width << 16; + trak->tkhd.height = height << 16; + tx3g->default_text_box = width | (height << 16); + } +} + /* * Meta tags functions */ @@ -3068,6 +3184,12 @@ atom_minf_set_video (AtomMINF * minf, AtomsContext * context) } static void +atom_minf_set_subtitle (AtomMINF * minf) +{ + atom_minf_clear_handlers (minf); +} + +static void atom_hdlr_set_type (AtomHDLR * hdlr, AtomsContext * context, guint32 comp_type, guint32 hdlr_type) { @@ -3104,6 +3226,15 @@ atom_mdia_set_hdlr_type_video (AtomMDIA * mdia, AtomsContext * context) } static void +atom_mdia_set_hdlr_type_subtitle (AtomMDIA * mdia, AtomsContext * context) +{ + atom_hdlr_set_type (&mdia->hdlr, context, FOURCC_mhlr, FOURCC_sbtl); + + /* Just follows the pattern from video and audio above */ + atom_hdlr_set_name (&mdia->hdlr, "SubtitleHandler"); +} + +static void atom_mdia_set_audio (AtomMDIA * mdia, AtomsContext * context) { atom_mdia_set_hdlr_type_audio (mdia, context); @@ -3118,6 +3249,13 @@ atom_mdia_set_video (AtomMDIA * mdia, AtomsContext * context) } static void +atom_mdia_set_subtitle (AtomMDIA * mdia, AtomsContext * context) +{ + atom_mdia_set_hdlr_type_subtitle (mdia, context); + atom_minf_set_subtitle (&mdia->minf); +} + +static void atom_tkhd_set_audio (AtomTKHD * tkhd) { tkhd->volume = 0x0100; @@ -3136,6 +3274,18 @@ atom_tkhd_set_video (AtomTKHD * tkhd, AtomsContext * context, guint32 width, } static void +atom_tkhd_set_subtitle (AtomTKHD * tkhd, AtomsContext * context, guint32 width, + guint32 height) +{ + tkhd->volume = 0; + + /* qt and ISO base media do not contradict, and examples agree */ + tkhd->width = width; + tkhd->height = height; +} + + +static void atom_edts_add_entry (AtomEDTS * edts, EditListEntry * entry) { edts->elst.entries = g_slist_append (edts->elst.entries, entry); @@ -3205,6 +3355,23 @@ atom_trak_add_video_entry (AtomTRAK * trak, AtomsContext * context, return mp4v; } +static SampleTableEntryTX3G * +atom_trak_add_subtitle_entry (AtomTRAK * trak, AtomsContext * context, + guint32 type) +{ + SampleTableEntryTX3G *tx3g = sample_entry_tx3g_new (); + AtomSTSD *stsd = &trak->mdia.minf.stbl.stsd; + + tx3g->se.header.type = type; + tx3g->se.kind = SUBTITLE; + tx3g->se.data_reference_index = 1; + + stsd->entries = g_list_prepend (stsd->entries, tx3g); + stsd->n_entries++; + return tx3g; +} + + static void atom_trak_set_constant_size_samples (AtomTRAK * trak, guint32 sample_size) { @@ -3227,6 +3394,13 @@ atom_trak_set_video (AtomTRAK * trak, AtomsContext * context, guint32 width, } static void +atom_trak_set_subtitle (AtomTRAK * trak, AtomsContext * context) +{ + atom_tkhd_set_subtitle (&trak->tkhd, context, 0, 0); + atom_mdia_set_subtitle (&trak->mdia, context); +} + +static void atom_trak_set_audio_commons (AtomTRAK * trak, AtomsContext * context, guint32 rate) { @@ -3244,6 +3418,13 @@ atom_trak_set_video_commons (AtomTRAK * trak, AtomsContext * context, trak->tkhd.height = height << 16; } +static void +atom_trak_set_subtitle_commons (AtomTRAK * trak, AtomsContext * context) +{ + atom_trak_set_subtitle (trak, context); + trak->mdia.mdhd.time_info.timescale = 1000; +} + void atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context, AudioSampleEntry * entry, guint32 scale, AtomInfo * ext, gint sample_size) @@ -3348,6 +3529,32 @@ atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context, } } +void +subtitle_sample_entry_init (SubtitleSampleEntry * entry) +{ + entry->font_size = 0; + entry->font_face = 0; + entry->foreground_color_rgba = 0xFFFFFFFF; /* all white, opaque */ +} + +void +atom_trak_set_subtitle_type (AtomTRAK * trak, AtomsContext * context, + SubtitleSampleEntry * entry) +{ + SampleTableEntryTX3G *tx3g; + + atom_trak_set_subtitle_commons (trak, context); + atom_stsd_remove_entries (&trak->mdia.minf.stbl.stsd); + tx3g = atom_trak_add_subtitle_entry (trak, context, entry->fourcc); + + tx3g->font_face = entry->font_face; + tx3g->font_size = entry->font_size; + tx3g->foreground_color_rgba = entry->foreground_color_rgba; + + trak->is_video = FALSE; + trak->is_h264 = FALSE; +} + static void atom_mfhd_init (AtomMFHD * mfhd, guint32 sequence_number) { diff --git a/gst/isomp4/atoms.h b/gst/isomp4/atoms.h index 887b531..b497074 100644 --- a/gst/isomp4/atoms.h +++ b/gst/isomp4/atoms.h @@ -340,7 +340,8 @@ typedef enum _SampleEntryKind { UNKNOWN, AUDIO, - VIDEO + VIDEO, + SUBTITLE, } SampleEntryKind; typedef struct _SampleTableEntry @@ -350,7 +351,7 @@ typedef struct _SampleTableEntry guint8 reserved[6]; guint16 data_reference_index; - /* sort of entry */ + /* type of entry */ SampleEntryKind kind; } SampleTableEntry; @@ -421,6 +422,19 @@ typedef struct _SampleTableEntryMP4S AtomESDS es; } SampleTableEntryMP4S; +typedef struct _SampleTableEntryTX3G +{ + SampleTableEntry se; + + guint32 display_flags; + guint64 default_text_box; + guint16 font_id; + guint8 font_face; /* bold=0x1, italic=0x2, underline=0x4 */ + guint8 font_size; /* should always be 0.05 multiplied by the video track header height */ + guint32 foreground_color_rgba; + +} SampleTableEntryTX3G; + typedef struct _AtomSTSD { AtomFull header; @@ -899,6 +913,17 @@ typedef struct GstBuffer *codec_data; } AudioSampleEntry; +typedef struct +{ + guint32 fourcc; + + guint8 font_face; /* bold=0x1, italic=0x2, underline=0x4 */ + guint8 font_size; + guint32 foreground_color_rgba; +} SubtitleSampleEntry; + +void subtitle_sample_entry_init (SubtitleSampleEntry * entry); + void atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context, AudioSampleEntry * entry, guint32 scale, AtomInfo * ext, gint sample_size); @@ -907,9 +932,15 @@ void atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context, VisualSampleEntry * entry, guint32 rate, GList * ext_atoms_list); +void atom_trak_set_subtitle_type (AtomTRAK * trak, AtomsContext * context, + SubtitleSampleEntry * entry); + void atom_trak_update_bitrates (AtomTRAK * trak, guint32 avg_bitrate, guint32 max_bitrate); +void atom_trak_tx3g_update_dimension (AtomTRAK * trak, guint32 width, + guint32 height); + AtomInfo * build_codec_data_extension (guint32 fourcc, const GstBuffer * codec_data); AtomInfo * build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * codec_data, guint32 avg_bitrate, guint32 max_bitrate); diff --git a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h index ebbcdab..64322a7 100644 --- a/gst/isomp4/fourcc.h +++ b/gst/isomp4/fourcc.h @@ -120,6 +120,7 @@ G_BEGIN_DECLS #define FOURCC_free GST_MAKE_FOURCC('f','r','e','e') #define FOURCC_frma GST_MAKE_FOURCC('f','r','m','a') #define FOURCC_ftyp GST_MAKE_FOURCC('f','t','y','p') +#define FOURCC_ftab GST_MAKE_FOURCC('f','t','a','b') #define FOURCC_gama GST_MAKE_FOURCC('g','a','m','a') #define FOURCC_glbl GST_MAKE_FOURCC('g','l','b','l') #define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d') -- 2.7.4