* Creates a new AtomsContext for the given flavor.
*/
AtomsContext *
-atoms_context_new (AtomsTreeFlavor flavor)
+atoms_context_new (AtomsTreeFlavor flavor, gboolean force_create_timecode_trak)
{
AtomsContext *context = g_new0 (AtomsContext, 1);
context->flavor = flavor;
+ context->force_create_timecode_trak = force_create_timecode_trak;
return context;
}
}
static void
+atom_nmhd_init (AtomNMHD * nmhd)
+{
+ atom_header_set (&nmhd->header, FOURCC_nmhd, 0, 0);
+ nmhd->flags = 0;
+}
+
+static void
+atom_nmhd_clear (AtomNMHD * nmhd)
+{
+ atom_clear (&nmhd->header);
+}
+
+static AtomNMHD *
+atom_nmhd_new (void)
+{
+ AtomNMHD *nmhd = g_new0 (AtomNMHD, 1);
+ atom_nmhd_init (nmhd);
+ return nmhd;
+}
+
+static void
+atom_nmhd_free (AtomNMHD * nmhd)
+{
+ atom_nmhd_clear (nmhd);
+ g_free (nmhd);
+}
+
+static void
atom_sample_entry_init (SampleTableEntry * se, guint32 type)
{
atom_header_set (&se->header, type, 0, 0);
atom_gmhd_free (minf->gmhd);
minf->gmhd = NULL;
}
+ if (minf->nmhd) {
+ atom_nmhd_free (minf->nmhd);
+ minf->nmhd = NULL;
+ }
}
static void
return original_offset - *offset;
}
+static guint64
+atom_nmhd_copy_data (AtomNMHD * nmhd, guint8 ** buffer, guint64 * size,
+ guint64 * offset)
+{
+ guint64 original_offset = *offset;
+
+ if (!atom_copy_data (&nmhd->header, buffer, size, offset)) {
+ return 0;
+ }
+ prop_copy_uint32 (nmhd->flags, buffer, size, offset);
+
+ atom_write_size (buffer, size, offset, original_offset);
+ return original_offset - *offset;
+}
+
static gboolean
atom_url_same_file_flag (AtomURL * url)
{
if (!atom_gmhd_copy_data (minf->gmhd, buffer, size, offset)) {
return 0;
}
+ } else if (minf->nmhd) {
+ if (!atom_nmhd_copy_data (minf->nmhd, buffer, size, offset)) {
+ return 0;
+ }
}
if (minf->hdlr) {
guint32 trak_timescale, GstVideoTimeCode * tc)
{
SampleTableEntryTMCD *ste;
- AtomGMHD *gmhd = trak->mdia.minf.gmhd;
- if (context->flavor != ATOMS_TREE_FLAVOR_MOV) {
+ if (context->flavor != ATOMS_TREE_FLAVOR_MOV &&
+ !context->force_create_timecode_trak) {
return NULL;
}
- ste = atom_trak_add_timecode_entry (trak, context, trak_timescale, tc);
-
- gmhd = atom_gmhd_new ();
- gmhd->gmin.graphics_mode = 0x0040;
- gmhd->gmin.opcolor[0] = 0x8000;
- gmhd->gmin.opcolor[1] = 0x8000;
- gmhd->gmin.opcolor[2] = 0x8000;
- gmhd->tmcd = atom_tmcd_new ();
- gmhd->tmcd->tcmi.text_size = 12;
- gmhd->tmcd->tcmi.font_name = g_strdup ("Chicago"); /* Pascal string */
- trak->mdia.minf.gmhd = gmhd;
+ if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
+ AtomGMHD *gmhd = trak->mdia.minf.gmhd;
+
+ gmhd = atom_gmhd_new ();
+ gmhd->gmin.graphics_mode = 0x0040;
+ gmhd->gmin.opcolor[0] = 0x8000;
+ gmhd->gmin.opcolor[1] = 0x8000;
+ gmhd->gmin.opcolor[2] = 0x8000;
+ gmhd->tmcd = atom_tmcd_new ();
+ gmhd->tmcd->tcmi.text_size = 12;
+ gmhd->tmcd->tcmi.font_name = g_strdup ("Chicago"); /* Pascal string */
+
+ trak->mdia.minf.gmhd = gmhd;
+ } else if (context->force_create_timecode_trak) {
+ AtomNMHD *nmhd = trak->mdia.minf.nmhd;
+ /* MOV files use GMHD, other files use NMHD */
+
+ nmhd = atom_nmhd_new ();
+ trak->mdia.minf.nmhd = nmhd;
+ } else {
+ return NULL;
+ }
+ ste = atom_trak_add_timecode_entry (trak, context, trak_timescale, tc);
trak->is_video = FALSE;
trak->is_h264 = FALSE;
typedef struct _AtomsContext
{
AtomsTreeFlavor flavor;
+ gboolean force_create_timecode_trak;
} AtomsContext;
-AtomsContext* atoms_context_new (AtomsTreeFlavor flavor);
+AtomsContext* atoms_context_new (AtomsTreeFlavor flavor, gboolean force_create_timecode_trak);
void atoms_context_free (AtomsContext *context);
#define METADATA_DATA_FLAG 0x0
} AtomGMHD;
+typedef struct _AtomNMHD
+{
+ Atom header;
+ guint32 flags;
+} AtomNMHD;
+
typedef struct _AtomURL
{
AtomFull header;
AtomSMHD *smhd;
AtomHMHD *hmhd;
AtomGMHD *gmhd;
+ AtomNMHD *nmhd;
AtomHDLR *hdlr;
AtomDINF dinf;
#define FOURCC_name GST_MAKE_FOURCC('n','a','m','e')
#define FOURCC_nclc GST_MAKE_FOURCC('n','c','l','c')
#define FOURCC_nclx GST_MAKE_FOURCC('n','c','l','x')
+#define FOURCC_nmhd GST_MAKE_FOURCC('n','m','h','d')
#define FOURCC_opus GST_MAKE_FOURCC('O','p','u','s')
#define FOURCC_dops GST_MAKE_FOURCC('d','O','p','s')
#define FOURCC_pasp GST_MAKE_FOURCC('p','a','s','p')
PROP_INTERLEAVE_TIME,
PROP_MAX_RAW_AUDIO_DRIFT,
PROP_START_GAP_THRESHOLD,
+ PROP_FORCE_CREATE_TIMECODE_TRAK,
};
/* some spare for header size as well */
#define DEFAULT_INTERLEAVE_TIME 250*GST_MSECOND
#define DEFAULT_MAX_RAW_AUDIO_DRIFT 40 * GST_MSECOND
#define DEFAULT_START_GAP_THRESHOLD 0
+#define DEFAULT_FORCE_CREATE_TIMECODE_TRAK FALSE
static void gst_qt_mux_finalize (GObject * object);
"Threshold for creating an edit list for gaps at the start in nanoseconds",
0, G_MAXUINT64, DEFAULT_START_GAP_THRESHOLD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class,
+ PROP_FORCE_CREATE_TIMECODE_TRAK,
+ g_param_spec_boolean ("force-create-timecode-trak",
+ "Force Create Timecode Trak",
+ "Create a timecode trak even in unsupported flavors",
+ DEFAULT_FORCE_CREATE_TIMECODE_TRAK,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_qt_mux_request_new_pad);
qtmux->interleave_time = DEFAULT_INTERLEAVE_TIME;
qtmux->max_raw_audio_drift = DEFAULT_MAX_RAW_AUDIO_DRIFT;
qtmux->start_gap_threshold = DEFAULT_START_GAP_THRESHOLD;
+ qtmux->force_create_timecode_trak = DEFAULT_FORCE_CREATE_TIMECODE_TRAK;
/* always need this */
qtmux->context =
- atoms_context_new (gst_qt_mux_map_format_to_flavor (qtmux_klass->format));
+ atoms_context_new (gst_qt_mux_map_format_to_flavor (qtmux_klass->format),
+ qtmux->force_create_timecode_trak);
/* internals to initial state */
gst_qt_mux_reset (qtmux, TRUE);
}
GST_OBJECT_UNLOCK (qtmux);
- if (qtmux_klass->format == GST_QT_MUX_FORMAT_QT) {
+ if (qtmux_klass->format == GST_QT_MUX_FORMAT_QT ||
+ qtmux->force_create_timecode_trak) {
/* For the first sample check/update timecode as needed. We do that before
* all actual samples as the code in gst_qt_mux_add_buffer() does it with
* initial buffer directly, not with last_buf */
guint64 offset = qtpad->tc_pos;
GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
- if (qtmux_klass->format != GST_QT_MUX_FORMAT_QT)
+ if (qtmux_klass->format != GST_QT_MUX_FORMAT_QT &&
+ !qtmux->force_create_timecode_trak)
return GST_FLOW_OK;
g_assert (qtpad->tc_pos != -1);
if (!pad->trak->is_video)
return ret;
- if (qtmux_klass->format != GST_QT_MUX_FORMAT_QT)
+ if (qtmux_klass->format != GST_QT_MUX_FORMAT_QT &&
+ !qtmux->force_create_timecode_trak)
return ret;
if (buf == NULL || (pad->tc_trak != NULL && pad->tc_pos == -1))
case PROP_START_GAP_THRESHOLD:
g_value_set_uint64 (value, qtmux->start_gap_threshold);
break;
+ case PROP_FORCE_CREATE_TIMECODE_TRAK:
+ g_value_set_boolean (value, qtmux->force_create_timecode_trak);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_START_GAP_THRESHOLD:
qtmux->start_gap_threshold = g_value_get_uint64 (value);
break;
+ case PROP_FORCE_CREATE_TIMECODE_TRAK:
+ qtmux->force_create_timecode_trak = g_value_get_boolean (value);
+ qtmux->context->force_create_timecode_trak =
+ qtmux->force_create_timecode_trak;
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
GstClockTime start_gap_threshold;
+ gboolean force_create_timecode_trak;
+
/* for request pad naming */
guint video_pads, audio_pads, subtitle_pads, caption_pads;
};