GST_DEBUG_CATEGORY (qtdemux_debug);
#define GST_CAT_DEFAULT qtdemux_debug
-typedef struct _QtDemuxSegment QtDemuxSegment;
-typedef struct _QtDemuxSample QtDemuxSample;
-
typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
-
-struct _QtDemuxSample
-{
- guint32 size;
- gint32 pts_offset; /* Add this value to timestamp to get the pts */
- guint64 offset;
- guint64 timestamp; /* DTS In mov time */
- guint32 duration; /* In mov time */
- gboolean keyframe; /* TRUE when this packet is a keyframe */
-};
+typedef struct _QtDemuxAavdEncryptionInfo QtDemuxAavdEncryptionInfo;
+ #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+ typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata;
+
+ struct _QtDemuxSphericalMetadata
+ {
+ gboolean is_spherical;
+ gboolean is_stitched;
+ char *stitching_software;
+ char *projection_type;
+ char *stereo_mode;
+ int source_count;
+ int init_view_heading;
+ int init_view_pitch;
+ int init_view_roll;
+ int timestamp;
+ int full_pano_width_pixels;
+ int full_pano_height_pixels;
+ int cropped_area_image_width;
+ int cropped_area_image_height;
+ int cropped_area_left;
+ int cropped_area_top;
+ QTDEMUX_AMBISONIC_TYPE ambisonic_type;
+ QTDEMUX_AMBISONIC_FORMAT ambisonic_format;
+ QTDEMUX_AMBISONIC_ORDER ambisonic_order;
+ };
+
+ #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
/* Macros for converting to/from timescale */
#define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
#define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
}
}
-static void
-qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
- GstTagList * xmptaglist)
-{
- /* Strip out bogus fields */
- if (xmptaglist) {
- if (gst_tag_list_get_scope (taglist) == GST_TAG_SCOPE_GLOBAL) {
- gst_tag_list_remove_tag (xmptaglist, GST_TAG_VIDEO_CODEC);
- gst_tag_list_remove_tag (xmptaglist, GST_TAG_AUDIO_CODEC);
- } else {
- gst_tag_list_remove_tag (xmptaglist, GST_TAG_CONTAINER_FORMAT);
- }
-
- GST_DEBUG_OBJECT (qtdemux, "Found XMP tags %" GST_PTR_FORMAT, xmptaglist);
-
- /* prioritize native tags using _KEEP mode */
- gst_tag_list_insert (taglist, xmptaglist, GST_TAG_MERGE_KEEP);
- gst_tag_list_unref (xmptaglist);
- }
-}
-
+ #ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+ static void
+ _get_int_value_from_xml_string (GstQTDemux * qtdemux,
+ const char *xml_str, const char *param_name, int *value)
+ {
+ char *value_start, *value_end, *endptr;
+ const short value_length_max = 12;
+ char init_view_ret[12];
+ int value_length = 0;
+ int i = 0;
+
+ value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
+
+ if (!value_start) {
+ GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
+ param_name);
+ return;
+ }
+
+ value_start += strlen (param_name);
+ while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+ value_start++;
+
+ value_end = strchr (value_start, '<');
+ if (!value_end) {
+ GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
+ return;
+ }
+
+ value_length = value_end - value_start;
+ while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
+ || (value_start[value_length - 1] == '\t')))
+ value_length--;
+
+ if (value_start[i] == '+' || value_start[i] == '-')
+ i++;
+ while (i < value_length) {
+ if (value_start[i] < '0' || value_start[i] > '9') {
+ GST_ERROR_OBJECT (qtdemux,
+ "error: incorrect value, integer was expected\n");
+ return;
+ }
+ i++;
+ }
+
+ if (value_length >= value_length_max || value_length < 1) {
+ GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
+ return;
+ }
+
+ strncpy (init_view_ret, value_start, value_length_max);
+ init_view_ret[value_length] = '\0';
+
+ *value = strtol (init_view_ret, &endptr, 10);
+ if (endptr == init_view_ret) {
+ GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
+ return;
+ }
+
+ return;
+ }
+
+ static void
+ _get_string_value_from_xml_string (GstQTDemux * qtdemux,
+ const char *xml_str, const char *param_name, char **value)
+ {
+ char *value_start, *value_end;
+ const short value_length_max = 256;
+ int value_length = 0;
+
+ value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
+
+ if (!value_start) {
+ GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
+ param_name);
+ return;
+ }
+
+ value_start += strlen (param_name);
+ while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+ value_start++;
+
+ value_end = strchr (value_start, '<');
+ if (!value_end) {
+ GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
+ return;
+ }
+
+ value_length = value_end - value_start;
+ while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
+ || (value_start[value_length - 1] == '\t')))
+ value_length--;
+
+ if (value_length >= value_length_max || value_length < 1) {
+ GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
+ return;
+ }
+
+ *value = strndup(value_start, value_length);
+
+ return;
+ }
+
+ static void
+ _get_bool_value_from_xml_string (GstQTDemux * qtdemux,
+ const char *xml_str, const char *param_name, gboolean * value)
+ {
+ char *value_start, *value_end;
+ int value_length = 0;
+
+ value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
+
+ if (!value_start) {
+ GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
+ param_name);
+ return;
+ }
+
+ value_start += strlen (param_name);
+ while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+ value_start++;
+
+ value_end = strchr (value_start, '<');
+ if (!value_end) {
+ GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
+ return;
+ }
+
+ value_length = value_end - value_start;
+ while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
+ || (value_start[value_length - 1] == '\t')))
+ value_length--;
+
+ if (value_length < 1) {
+ GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
+ return;
+ }
+
+ *value = g_strstr_len(value_start, value_length, "true") ? TRUE : FALSE;
+
+ return;
+ }
+
+ static void
+ _parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
+ {
+ const char is_spherical_str[] = "<GSpherical:Spherical>";
+ const char is_stitched_str[] = "<GSpherical:Stitched>";
+ const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
+ const char projection_type_str[] = "<GSpherical:ProjectionType>";
+ const char stereo_mode_str[] = "<GSpherical:StereoMode>";
+ const char source_count_str[] = "<GSpherical:SourceCount>";
+ const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
+ const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
+ const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
+ const char timestamp_str[] = "<GSpherical:Timestamp>";
+ const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
+ const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
+ const char cropped_area_image_width_str[] =
+ "<GSpherical:CroppedAreaImageWidthPixels>";
+ const char cropped_area_image_height_str[] =
+ "<GSpherical:CroppedAreaImageHeightPixels>";
+ const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
+ const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
+
+ QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
+
+ _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
+ (gboolean *) & spherical_metadata->is_spherical);
+ _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
+ (gboolean *) & spherical_metadata->is_stitched);
+
+ if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
+ _get_string_value_from_xml_string (qtdemux, xmlStr,
+ stitching_software_str, &spherical_metadata->stitching_software);
+ _get_string_value_from_xml_string (qtdemux, xmlStr,
+ projection_type_str, &spherical_metadata->projection_type);
+ _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
+ &spherical_metadata->stereo_mode);
+ _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
+ &spherical_metadata->source_count);
+ _get_int_value_from_xml_string (qtdemux, xmlStr,
+ init_view_heading_str, &spherical_metadata->init_view_heading);
+ _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
+ &spherical_metadata->init_view_pitch);
+ _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
+ &spherical_metadata->init_view_roll);
+ _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
+ &spherical_metadata->timestamp);
+ _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
+ &spherical_metadata->full_pano_width_pixels);
+ _get_int_value_from_xml_string (qtdemux, xmlStr,
+ full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
+ _get_int_value_from_xml_string (qtdemux, xmlStr,
+ cropped_area_image_width_str,
+ &spherical_metadata->cropped_area_image_width);
+ _get_int_value_from_xml_string (qtdemux, xmlStr,
+ cropped_area_image_height_str,
+ &spherical_metadata->cropped_area_image_height);
+ _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
+ &spherical_metadata->cropped_area_left);
+ _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
+ &spherical_metadata->cropped_area_top);
+ }
+
+ return;
+ }
+
+ static void
+ gst_tag_register_spherical_tags (void) {
+ gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-spherical"),
+ _("Flag indicating if the video is a spherical video"),
+ NULL);
+ gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-stitched"),
+ _("Flag indicating if the video is stitched"),
+ NULL);
+ gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("tag-stitching-software"),
+ _("Software used to stitch the spherical video"),
+ NULL);
+ gst_tag_register ("projection_type", GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("tag-projection-type"),
+ _("Projection type used in the video frames"),
+ NULL);
+ gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
+ G_TYPE_STRING,
+ _("tag-stereo-mode"),
+ _("Description of stereoscopic 3D layout"),
+ NULL);
+ gst_tag_register ("source_count", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-source-count"),
+ _("Number of cameras used to create the spherical video"),
+ NULL);
+ gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-init-view-heading"),
+ _("The heading angle of the initial view in degrees"),
+ NULL);
+ gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-init-view-pitch"),
+ _("The pitch angle of the initial view in degrees"),
+ NULL);
+ gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-init-view-roll"),
+ _("The roll angle of the initial view in degrees"),
+ NULL);
+ gst_tag_register ("timestamp", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-timestamp"),
+ _("Epoch timestamp of when the first frame in the video was recorded"),
+ NULL);
+ gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-full-pano-width"),
+ _("Width of the encoded video frame in pixels"),
+ NULL);
+ gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-full-pano-height"),
+ _("Height of the encoded video frame in pixels"),
+ NULL);
+ gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-cropped-area-image-width"),
+ _("Width of the video frame to display (e.g. cropping)"),
+ NULL);
+ gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-cropped-area-image-height"),
+ _("Height of the video frame to display (e.g. cropping)"),
+ NULL);
+ gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-cropped-area-left"),
+ _("Column where the left edge of the image was cropped from the"
+ " full sized panorama"),
+ NULL);
+ gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-cropped-area-top"),
+ _("Row where the top edge of the image was cropped from the"
+ " full sized panorama"),
+ NULL);
+ gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-ambisonic-type"),
+ _("Specifies the type of ambisonic audio represented"),
+ NULL);
+ gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-ambisonic-format"),
+ _("Specifies the ambisonic audio format"),
+ NULL);
+ gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
+ G_TYPE_INT,
+ _("tag-ambisonic-order"),
+ _("Specifies the ambisonic audio channel order"),
+ NULL);
+
+ return;
+ }
+
+ static void
+ _send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
+ {
+ GstTagList *taglist;
+ QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
+
+ GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
+ spherical_metadata->is_spherical);
+ GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
+ spherical_metadata->is_stitched);
+ GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
+ spherical_metadata->stitching_software);
+ GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
+ spherical_metadata->projection_type);
+ GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
+ spherical_metadata->stereo_mode);
+ GST_DEBUG_OBJECT (qtdemux, "source_count %d",
+ spherical_metadata->source_count);
+ GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
+ spherical_metadata->init_view_heading);
+ GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
+ spherical_metadata->init_view_pitch);
+ GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
+ spherical_metadata->init_view_roll);
+ GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
+ GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
+ spherical_metadata->full_pano_width_pixels);
+ GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
+ spherical_metadata->full_pano_height_pixels);
+ GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
+ spherical_metadata->cropped_area_image_width);
+ GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
+ spherical_metadata->cropped_area_image_height);
+ GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
+ spherical_metadata->cropped_area_left);
+ GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
+ spherical_metadata->cropped_area_top);
+ GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
+ spherical_metadata->ambisonic_type);
+ GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
+ spherical_metadata->ambisonic_order);
+ GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
+ spherical_metadata->ambisonic_format);
+
+ taglist = gst_tag_list_new_empty ();
+ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+ "is_spherical", spherical_metadata->is_spherical,
+ "is_stitched", spherical_metadata->is_stitched,
+ "source_count", spherical_metadata->source_count,
+ "init_view_heading", spherical_metadata->init_view_heading,
+ "init_view_pitch", spherical_metadata->init_view_pitch,
+ "init_view_roll", spherical_metadata->init_view_roll,
+ "timestamp", spherical_metadata->timestamp,
+ "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
+ "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
+ "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
+ "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
+ "cropped_area_left", spherical_metadata->cropped_area_left,
+ "cropped_area_top", spherical_metadata->cropped_area_top,
+ "ambisonic_type", spherical_metadata->ambisonic_type,
+ "ambisonic_format", spherical_metadata->ambisonic_format,
+ "ambisonic_order", spherical_metadata->ambisonic_order,
+ NULL);
+
+ if (spherical_metadata->stitching_software)
+ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+ "stitching_software", spherical_metadata->stitching_software,
+ NULL);
+ if (spherical_metadata->projection_type)
+ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+ "projection_type", spherical_metadata->projection_type,
+ NULL);
+ if (spherical_metadata->stereo_mode)
+ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+ "stereo_mode", spherical_metadata->stereo_mode,
+ NULL);
+
+ gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
+ gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
+ gst_tag_list_copy (taglist)));
+
+ gst_tag_list_unref(taglist);
+
+ return;
+ }
+
+ static void
+ qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
+ {
+ guint offset = 0;
+
+ guint8 version = 0;
+ guint8 ambisonic_type = 0;
+ guint32 ambisonic_order = 0;
+ guint8 ambisonic_channel_ordering = 0;
+ guint8 ambisonic_normalization = 0;
+ guint32 num_channels = 0;
+ guint32 channel_map[49] = { 0 }; /* Up to 6th order */
+
+ int i;
+
+ GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
+
+ qtdemux->header_size += length;
+ offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
+
+ if (length <= offset + 16) {
+ GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
+ return;
+ }
+
+ version = QT_UINT8 (buffer + offset);
+ ambisonic_type = QT_UINT8 (buffer + offset + 1);
+ ambisonic_order = QT_UINT32 (buffer + offset + 2);
+ ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
+ ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
+ num_channels = QT_UINT32 (buffer + offset + 8);
+ for (i = 0; i < num_channels; ++i)
+ channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
+
+ GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
+ GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
+ GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
+ GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
+ ambisonic_channel_ordering);
+ GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
+ ambisonic_normalization);
+ GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
+ for (i = 0; i < num_channels; ++i)
+ GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
+
+ if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
+ if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
+ qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
+
+ if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
+ if (num_channels == 4) {
+ qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
+
+ if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
+ && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
+ && (channel_map[0] == 0) && (channel_map[1] == 1)
+ && (channel_map[2] == 2) && (channel_map[3] == 3))
+ qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
+
+ if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
+ && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
+ && (channel_map[0] == 0) && (channel_map[1] == 3)
+ && (channel_map[2] == 1) && (channel_map[3] == 2))
+ qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
+ }
+ }
+ }
+
+ return;
+ }
+ #endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
static void
-qtdemux_update_default_sample_encryption_settings (GstQTDemux * qtdemux,
- QtDemuxCencSampleSetInfo * info, guint32 is_encrypted, guint8 iv_size,
- const guint8 * kid)
+qtdemux_update_default_sample_cenc_settings (GstQTDemux * qtdemux,
+ QtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
+ guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
+ guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
+ const guint8 * constant_iv)
{
GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
gst_buffer_fill (kid_buf, 0, kid, 16);
qtdemux_prepare_streams (GstQTDemux * qtdemux)
{
GstFlowReturn ret = GST_FLOW_OK;
+ #ifdef TIZEN_FEATURE_QTDEMUX_DURATION
+ guint64 tkhd_max_duration = 0;
+ #endif
gint i;
- GST_DEBUG_OBJECT (qtdemux, "prepare streams");
+ GST_DEBUG_OBJECT (qtdemux, "prepare %u streams", QTDEMUX_N_STREAMS (qtdemux));
for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
/* parse the initial sample for use in setting the frame rate cap */
while (sample_num == 0 && sample_num < stream->n_samples) {
- if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
- break;
- ++sample_num;
- }
- }
-
-#ifdef TIZEN_FEATURE_QTDEMUX_DURATION
- if (!qtdemux->fragmented && (qtdemux->duration > tkhd_max_duration)) {
- GST_INFO_OBJECT (qtdemux,
- "Update duration: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT,
- qtdemux->duration, tkhd_max_duration);
- qtdemux->duration = tkhd_max_duration;
- }
-#endif
-
- return ret;
-}
-
-static gboolean
-_stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
-{
- return g_strcmp0 (stream->stream_id, stream_id) == 0;
-}
-
-static gboolean
-qtdemux_is_streams_update (GstQTDemux * qtdemux)
-{
- gint i;
-
- /* Different length, updated */
- if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
- return TRUE;
-
- /* streams in list are sorted in track-id order */
- for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
- /* Different stream-id, updated */
- if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
- QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
- QtDemuxStream * oldstream, QtDemuxStream * newstream)
-{
- /* Connect old stream's srcpad to new stream */
- newstream->pad = oldstream->pad;
- oldstream->pad = NULL;
-
- /* unset new_stream to prevent stream-start event */
- newstream->new_stream = FALSE;
-
- return gst_qtdemux_configure_stream (qtdemux, newstream);
-}
-
-/* g_ptr_array_find_with_equal_func is available since 2.54,
- * replacement until we can depend unconditionally on the real one in GLib */
-#if !GLIB_CHECK_VERSION(2,54,0)
-#define g_ptr_array_find_with_equal_func qtdemux_ptr_array_find_with_equal_func
-static gboolean
-qtdemux_ptr_array_find_with_equal_func (GPtrArray * haystack,
- gconstpointer needle, GEqualFunc equal_func, guint * index_)
-{
- guint i;
-
- g_return_val_if_fail (haystack != NULL, FALSE);
-
- if (equal_func == NULL)
- equal_func = g_direct_equal;
-
- for (i = 0; i < haystack->len; i++) {
- if (equal_func (g_ptr_array_index (haystack, i), needle)) {
- if (index_ != NULL)
- *index_ = i;
- return TRUE;
- }
- }
-
- return FALSE;
-}
-#endif
-
-static gboolean
-qtdemux_update_streams (GstQTDemux * qtdemux)
-{
- gint i;
- g_assert (qtdemux->streams_aware);
-
- /* At below, figure out which stream in active_streams has identical stream-id
- * with that of in old_streams. If there is matching stream-id,
- * corresponding newstream will not be exposed again,
- * but demux will reuse srcpad of matched old stream
- *
- * active_streams : newly created streams from the latest moov
- * old_streams : existing streams (belong to previous moov)
- */
-
- for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
- QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
- QtDemuxStream *oldstream = NULL;
- guint target;
-
- GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
- stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
-
- if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
- stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
- oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
-
- /* null pad stream cannot be reused */
- if (oldstream->pad == NULL)
- oldstream = NULL;
- }
-
- if (oldstream) {
- GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
-
- if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
- return FALSE;
-
- /* we don't need to preserve order of old streams */
- g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
- } else {
- GstTagList *list;
-
- /* now we have all info and can expose */
- list = stream->stream_tags;
- stream->stream_tags = NULL;
- if (!gst_qtdemux_add_stream (qtdemux, stream, list))
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/* Must be called with expose lock */
-static GstFlowReturn
-qtdemux_expose_streams (GstQTDemux * qtdemux)
-{
- gint i;
-
- GST_DEBUG_OBJECT (qtdemux, "exposing streams");
-
- if (!qtdemux_is_streams_update (qtdemux)) {
- GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
- for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
- QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
- QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
- if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
- return GST_FLOW_ERROR;
- }
-
- g_ptr_array_set_size (qtdemux->old_streams, 0);
- qtdemux->need_segment = TRUE;
-
- return GST_FLOW_OK;
- }
-
- if (qtdemux->streams_aware) {
- if (!qtdemux_update_streams (qtdemux))
- return GST_FLOW_ERROR;
- } else {
- for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
- QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
- GstTagList *list;
-
- /* now we have all info and can expose */
- list = stream->stream_tags;
- stream->stream_tags = NULL;
- if (!gst_qtdemux_add_stream (qtdemux, stream, list))
- return GST_FLOW_ERROR;
-
- }
- }
-
- gst_qtdemux_guess_bitrate (qtdemux);
-
- gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
-
- /* If we have still old_streams, it's no more used stream */
- for (i = 0; i < qtdemux->old_streams->len; i++) {
- QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
-
- if (stream->pad) {
- GstEvent *event;
-
- event = gst_event_new_eos ();
- if (qtdemux->segment_seqnum)
- gst_event_set_seqnum (event, qtdemux->segment_seqnum);
-
- gst_pad_push_event (stream->pad, event);
- }
- }
-
- g_ptr_array_set_size (qtdemux->old_streams, 0);
-
- /* check if we should post a redirect in case there is a single trak
- * and it is a redirecting trak */
- if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
- QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
- GstMessage *m;
-
- GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
- "an external content");
- m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
- gst_structure_new ("redirect",
- "new-location", G_TYPE_STRING,
- QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
- gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
- qtdemux->posted_redirect = TRUE;
- }
-
- g_ptr_array_foreach (qtdemux->active_streams,
- (GFunc) qtdemux_do_allocation, qtdemux);
-
- qtdemux->need_segment = TRUE;
-
- qtdemux->exposed = TRUE;
- return GST_FLOW_OK;
-}
-
-/* check if major or compatible brand is 3GP */
-static inline gboolean
-qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
-{
- if (major) {
- return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
- FOURCC_3g__);
- } else if (qtdemux->comp_brands != NULL) {
- GstMapInfo map;
- guint8 *data;
- gsize size;
- gboolean res = FALSE;
-
- gst_buffer_map (qtdemux->comp_brands, &map, GST_MAP_READ);
- data = map.data;
- size = map.size;
- while (size >= 4) {
- res = res || ((QT_FOURCC (data) & GST_MAKE_FOURCC (255, 255, 0, 0)) ==
- FOURCC_3g__);
- data += 4;
- size -= 4;
- }
- gst_buffer_unmap (qtdemux->comp_brands, &map);
- return res;
- } else {
- return FALSE;
- }
-}
-
-/* check if tag is a spec'ed 3GP tag keyword storing a string */
-static inline gboolean
-qtdemux_is_string_tag_3gp (GstQTDemux * qtdemux, guint32 fourcc)
-{
- return fourcc == FOURCC_cprt || fourcc == FOURCC_gnre || fourcc == FOURCC_titl
- || fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
- || fourcc == FOURCC_albm;
-}
-
-static void
-qtdemux_tag_add_location (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
-{
- const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
- int offset;
- char *name;
- gchar *data;
- gdouble longitude, latitude, altitude;
- gint len;
-
- len = QT_UINT32 (node->data);
- if (len <= 14)
- goto short_read;
-
- data = node->data;
- offset = 14;
-
- /* TODO: language code skipped */
-
- name = gst_tag_freeform_string_to_utf8 (data + offset, -1, env_vars);
-
- if (!name) {
- /* do not alarm in trivial case, but bail out otherwise */
- if (*(data + offset) != 0) {
- GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8, "
- "giving up", tag);
- }
- } else {
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_GEO_LOCATION_NAME, name, NULL);
- offset += strlen (name);
- g_free (name);
- }
-
- if (len < offset + 2 + 4 + 4 + 4)
- goto short_read;
-
- /* +1 +1 = skip null-terminator and location role byte */
- offset += 1 + 1;
- /* table in spec says unsigned, semantics say negative has meaning ... */
- longitude = QT_SFP32 (data + offset);
-
- offset += 4;
- latitude = QT_SFP32 (data + offset);
-
- offset += 4;
- altitude = QT_SFP32 (data + offset);
-
- /* one invalid means all are invalid */
- if (longitude >= -180.0 && longitude <= 180.0 &&
- latitude >= -90.0 && latitude <= 90.0) {
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_GEO_LOCATION_LATITUDE, latitude,
- GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
- GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
- }
-
- /* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
-
- return;
-
- /* ERRORS */
-short_read:
- {
- GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP location");
- return;
- }
-}
-
-
-static void
-qtdemux_tag_add_year (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
-{
- guint16 y;
- GDate *date;
- gint len;
-
- len = QT_UINT32 (node->data);
- if (len < 14)
- return;
-
- y = QT_UINT16 ((guint8 *) node->data + 12);
- if (y == 0) {
- GST_DEBUG_OBJECT (qtdemux, "year: %u is not a valid year", y);
- return;
- }
- GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
-
- date = g_date_new_dmy (1, 1, y);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
- g_date_free (date);
-}
-
-static void
-qtdemux_tag_add_classification (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
-{
- int offset;
- char *tag_str = NULL;
- guint8 *entity;
- guint16 table;
- gint len;
-
- len = QT_UINT32 (node->data);
- if (len <= 20)
- goto short_read;
-
- offset = 12;
- entity = (guint8 *) node->data + offset;
- if (entity[0] == 0 || entity[1] == 0 || entity[2] == 0 || entity[3] == 0) {
- GST_DEBUG_OBJECT (qtdemux,
- "classification info: %c%c%c%c invalid classification entity",
- entity[0], entity[1], entity[2], entity[3]);
- return;
- }
-
- offset += 4;
- table = QT_UINT16 ((guint8 *) node->data + offset);
-
- /* Language code skipped */
-
- offset += 4;
-
- /* Tag format: "XXXX://Y[YYYY]/classification info string"
- * XXXX: classification entity, fixed length 4 chars.
- * Y[YYYY]: classification table, max 5 chars.
- */
- tag_str = g_strdup_printf ("----://%u/%s",
- table, (char *) node->data + offset);
-
- /* memcpy To be sure we're preserving byte order */
- memcpy (tag_str, entity, 4);
- GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
-
- gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, tag, tag_str, NULL);
-
- g_free (tag_str);
-
- return;
-
- /* ERRORS */
-short_read:
- {
- GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP classification");
- return;
- }
-}
-
-static gboolean
-qtdemux_tag_add_str_full (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
-{
- const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
- GNode *data;
- char *s;
- int len;
- guint32 type;
- int offset;
- gboolean ret = TRUE;
- const gchar *charset = NULL;
-
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
- if (data) {
- len = QT_UINT32 (data->data);
- type = QT_UINT32 ((guint8 *) data->data + 8);
- if (type == 0x00000001 && len > 16) {
- s = gst_tag_freeform_string_to_utf8 ((char *) data->data + 16, len - 16,
- env_vars);
- if (s) {
- GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
- g_free (s);
- } else {
- GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
- }
- }
- } else {
- len = QT_UINT32 (node->data);
- type = QT_UINT32 ((guint8 *) node->data + 4);
- if ((type >> 24) == 0xa9 && len > 8 + 4) {
- gint str_len;
- gint lang_code;
-
- /* Type starts with the (C) symbol, so the next data is a list
- * of (string size(16), language code(16), string) */
-
- str_len = QT_UINT16 ((guint8 *) node->data + 8);
- lang_code = QT_UINT16 ((guint8 *) node->data + 10);
-
- /* the string + fourcc + size + 2 16bit fields,
- * means that there are more tags in this atom */
- if (len > str_len + 8 + 4) {
- /* TODO how to represent the same tag in different languages? */
- GST_WARNING_OBJECT (qtdemux, "Ignoring metadata entry with multiple "
- "text alternatives, reading only first one");
- }
-
- offset = 12;
- len = MIN (len, str_len + 8 + 4); /* remove trailing strings that we don't use */
- GST_DEBUG_OBJECT (qtdemux, "found international text tag");
-
- if (lang_code < 0x800) { /* MAC encoded string */
- charset = "mac";
- }
- } else if (len > 14 && qtdemux_is_string_tag_3gp (qtdemux,
- QT_FOURCC ((guint8 *) node->data + 4))) {
- guint32 type = QT_UINT32 ((guint8 *) node->data + 8);
-
- /* we go for 3GP style encoding if major brands claims so,
- * or if no hope for data be ok UTF-8, and compatible 3GP brand present */
- if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
- (qtdemux_is_brand_3gp (qtdemux, FALSE) &&
- ((type & 0x00FFFFFF) == 0x0) && (type >> 24 <= 0xF))) {
- offset = 14;
- /* 16-bit Language code is ignored here as well */
- GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
- } else {
- goto normal;
- }
- } else {
- normal:
- offset = 8;
- GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
- ret = FALSE; /* may have to fallback */
- }
- if (charset) {
- GError *err = NULL;
-
- s = g_convert ((gchar *) node->data + offset, len - offset, "utf8",
- charset, NULL, NULL, &err);
- if (err) {
- GST_DEBUG_OBJECT (qtdemux, "Failed to convert string from charset %s:"
- " %s(%d): %s", charset, g_quark_to_string (err->domain), err->code,
- err->message);
- g_error_free (err);
- }
- } else {
- s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
- len - offset, env_vars);
- }
- if (s) {
- GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (s));
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
- g_free (s);
- ret = TRUE;
- } else {
- GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
- }
- }
- return ret;
-}
-
-static void
-qtdemux_tag_add_str (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
-{
- qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node);
-}
-
-static void
-qtdemux_tag_add_keywords (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
-{
- const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
- guint8 *data;
- char *s, *t, *k = NULL;
- int len;
- int offset;
- int count;
-
- /* first try normal string tag if major brand not 3GP */
- if (!qtdemux_is_brand_3gp (qtdemux, TRUE)) {
- if (!qtdemux_tag_add_str_full (qtdemux, taglist, tag, dummy, node)) {
- /* hm, that did not work, maybe 3gpp storage in non-3gpp major brand;
- * let's try it 3gpp way after minor safety check */
- data = node->data;
- if (QT_UINT32 (data) < 15 || !qtdemux_is_brand_3gp (qtdemux, FALSE))
- return;
- } else
- return;
- }
-
- GST_DEBUG_OBJECT (qtdemux, "found 3gpp keyword tag");
-
- data = node->data;
-
- len = QT_UINT32 (data);
- if (len < 15)
- goto short_read;
-
- count = QT_UINT8 (data + 14);
- offset = 15;
- for (; count; count--) {
- gint slen;
-
- if (offset + 1 > len)
- goto short_read;
- slen = QT_UINT8 (data + offset);
- offset += 1;
- if (offset + slen > len)
- goto short_read;
- s = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
- slen, env_vars);
- if (s) {
- GST_DEBUG_OBJECT (qtdemux, "adding keyword %s", GST_STR_NULL (s));
- if (k) {
- t = g_strjoin (",", k, s, NULL);
- g_free (s);
- g_free (k);
- k = t;
- } else {
- k = s;
- }
- } else {
- GST_DEBUG_OBJECT (qtdemux, "failed to convert keyword to UTF-8");
- }
- offset += slen;
- }
-
-done:
- if (k) {
- GST_DEBUG_OBJECT (qtdemux, "adding tag %s", GST_STR_NULL (k));
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, k, NULL);
- }
- g_free (k);
-
- return;
-
- /* ERRORS */
-short_read:
- {
- GST_DEBUG_OBJECT (qtdemux, "short read parsing 3GP keywords");
- goto done;
- }
-}
-
-static void
-qtdemux_tag_add_num (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag1, const char *tag2, GNode * node)
-{
- GNode *data;
- int len;
- int type;
- int n1, n2;
-
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
- if (data) {
- len = QT_UINT32 (data->data);
- type = QT_UINT32 ((guint8 *) data->data + 8);
- if (type == 0x00000000 && len >= 22) {
- n1 = QT_UINT16 ((guint8 *) data->data + 18);
- n2 = QT_UINT16 ((guint8 *) data->data + 20);
- if (n1 > 0) {
- GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag1, n1);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, n1, NULL);
- }
- if (n2 > 0) {
- GST_DEBUG_OBJECT (qtdemux, "adding tag %s=%d", tag2, n2);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag2, n2, NULL);
- }
- }
- }
-}
-
-static void
-qtdemux_tag_add_tmpo (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag1, const char *dummy, GNode * node)
-{
- GNode *data;
- int len;
- int type;
- int n1;
-
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
- if (data) {
- len = QT_UINT32 (data->data);
- type = QT_UINT32 ((guint8 *) data->data + 8);
- GST_DEBUG_OBJECT (qtdemux, "have tempo tag, type=%d,len=%d", type, len);
- /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
- if ((type == 0x00000015 || type == 0x0000000f) && len >= 18) {
- n1 = QT_UINT16 ((guint8 *) data->data + 16);
- if (n1) {
- /* do not add bpm=0 */
- GST_DEBUG_OBJECT (qtdemux, "adding tag %d", n1);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, (gdouble) n1,
- NULL);
- }
- }
- }
-}
-
-static void
-qtdemux_tag_add_uint32 (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag1, const char *dummy, GNode * node)
-{
- GNode *data;
- int len;
- int type;
- guint32 num;
-
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
- if (data) {
- len = QT_UINT32 (data->data);
- type = QT_UINT32 ((guint8 *) data->data + 8);
- GST_DEBUG_OBJECT (qtdemux, "have %s tag, type=%d,len=%d", tag1, type, len);
- /* some files wrongly have a type 0x0f=15, but it should be 0x15 */
- if ((type == 0x00000015 || type == 0x0000000f) && len >= 20) {
- num = QT_UINT32 ((guint8 *) data->data + 16);
- if (num) {
- /* do not add num=0 */
- GST_DEBUG_OBJECT (qtdemux, "adding tag %d", num);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, num, NULL);
- }
- }
- }
-}
-
-static void
-qtdemux_tag_add_covr (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag1, const char *dummy, GNode * node)
-{
- GNode *data;
- int len;
- int type;
- GstSample *sample;
-
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
- if (data) {
- len = QT_UINT32 (data->data);
- type = QT_UINT32 ((guint8 *) data->data + 8);
- GST_DEBUG_OBJECT (qtdemux, "have covr tag, type=%d,len=%d", type, len);
- if ((type == 0x0000000d || type == 0x0000000e) && len > 16) {
- GstTagImageType image_type;
-
- if (gst_tag_list_get_tag_size (taglist, GST_TAG_IMAGE) == 0)
- image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
- else
- image_type = GST_TAG_IMAGE_TYPE_NONE;
-
- if ((sample =
- gst_tag_image_data_to_image_sample ((guint8 *) data->data + 16,
- len - 16, image_type))) {
- GST_DEBUG_OBJECT (qtdemux, "adding tag size %d", len - 16);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag1, sample, NULL);
- gst_sample_unref (sample);
- }
+ if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
+ break;
+ ++sample_num;
}
}
+
++#ifdef TIZEN_FEATURE_QTDEMUX_DURATION
++ if (!qtdemux->fragmented && (qtdemux->duration > tkhd_max_duration)) {
++ GST_INFO_OBJECT (qtdemux,
++ "Update duration: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT,
++ qtdemux->duration, tkhd_max_duration);
++ qtdemux->duration = tkhd_max_duration;
++ }
++#endif
++
+ return ret;
}
-static void
-qtdemux_tag_add_date (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
+static gboolean
+_stream_equal_func (const QtDemuxStream * stream, const gchar * stream_id)
{
- GNode *data;
- GstDateTime *datetime = NULL;
- char *s;
- int len;
- int type;
-
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
- if (data) {
- len = QT_UINT32 (data->data);
- type = QT_UINT32 ((guint8 *) data->data + 8);
- if (type == 0x00000001 && len > 16) {
- guint y, m = 1, d = 1;
- gint ret;
-
- s = g_strndup ((char *) data->data + 16, len - 16);
- GST_DEBUG_OBJECT (qtdemux, "adding date '%s'", s);
- datetime = gst_date_time_new_from_iso8601_string (s);
- if (datetime != NULL) {
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
- datetime, NULL);
- gst_date_time_unref (datetime);
- }
-
- ret = sscanf (s, "%u-%u-%u", &y, &m, &d);
- if (ret >= 1 && y > 1500 && y < 3000) {
- GDate *date;
-
- date = g_date_new_dmy (d, m, y);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, date, NULL);
- g_date_free (date);
- } else {
- GST_DEBUG_OBJECT (qtdemux, "could not parse date string '%s'", s);
- }
- g_free (s);
- }
- }
+ return g_strcmp0 (stream->stream_id, stream_id) == 0;
}
-static void
-qtdemux_tag_add_gnre (GstQTDemux * qtdemux, GstTagList * taglist,
- const char *tag, const char *dummy, GNode * node)
+static gboolean
+qtdemux_is_streams_update (GstQTDemux * qtdemux)
{
- GNode *data;
+ gint i;
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
+ /* Different length, updated */
+ if (QTDEMUX_N_STREAMS (qtdemux) != qtdemux->old_streams->len)
+ return TRUE;
- /* re-route to normal string tag if major brand says so
- * or no data atom and compatible brand suggests so */
- if (qtdemux_is_brand_3gp (qtdemux, TRUE) ||
- (qtdemux_is_brand_3gp (qtdemux, FALSE) && !data)) {
- qtdemux_tag_add_str (qtdemux, taglist, tag, dummy, node);
- return;
+ /* streams in list are sorted in track-id order */
+ for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
+ /* Different stream-id, updated */
+ if (g_strcmp0 (QTDEMUX_NTH_STREAM (qtdemux, i)->stream_id,
+ QTDEMUX_NTH_OLD_STREAM (qtdemux, i)->stream_id))
+ return TRUE;
}
- if (data) {
- guint len, type, n;
-
- len = QT_UINT32 (data->data);
- type = QT_UINT32 ((guint8 *) data->data + 8);
- if (type == 0x00000000 && len >= 18) {
- n = QT_UINT16 ((guint8 *) data->data + 16);
- if (n > 0) {
- const gchar *genre;
-
- genre = gst_tag_id3_genre_get (n - 1);
- if (genre != NULL) {
- GST_DEBUG_OBJECT (qtdemux, "adding %d [%s]", n, genre);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, genre, NULL);
- }
- }
- }
- }
+ return FALSE;
}
-static void
-qtdemux_add_double_tag_from_str (GstQTDemux * demux, GstTagList * taglist,
- const gchar * tag, guint8 * data, guint32 datasize)
+static gboolean
+qtdemux_reuse_and_configure_stream (GstQTDemux * qtdemux,
+ QtDemuxStream * oldstream, QtDemuxStream * newstream)
{
- gdouble value;
- gchar *datacopy;
+ /* Connect old stream's srcpad to new stream */
+ newstream->pad = oldstream->pad;
+ oldstream->pad = NULL;
- /* make a copy to have \0 at the end */
- datacopy = g_strndup ((gchar *) data, datasize);
+ /* unset new_stream to prevent stream-start event, unless we are EOS in which
+ * case we need to force one through */
+ newstream->new_stream = GST_PAD_IS_EOS (newstream->pad);
- /* convert the str to double */
- if (sscanf (datacopy, "%lf", &value) == 1) {
- GST_DEBUG_OBJECT (demux, "adding tag: %s [%s]", tag, datacopy);
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, value, NULL);
- } else {
- GST_WARNING_OBJECT (demux, "Failed to parse double from string: %s",
- datacopy);
- }
- g_free (datacopy);
+ return gst_qtdemux_configure_stream (qtdemux, newstream);
}
+static gboolean
+qtdemux_update_streams (GstQTDemux * qtdemux)
+{
+ gint i;
+ g_assert (qtdemux->streams_aware);
-static void
-qtdemux_tag_add_revdns (GstQTDemux * demux, GstTagList * taglist,
- const char *tag, const char *tag_bis, GNode * node)
-{
- GNode *mean;
- GNode *name;
- GNode *data;
- guint32 meansize;
- guint32 namesize;
- guint32 datatype;
- guint32 datasize;
- const gchar *meanstr;
- const gchar *namestr;
-
- /* checking the whole ---- atom size for consistency */
- if (QT_UINT32 (node->data) <= 4 + 12 + 12 + 16) {
- GST_WARNING_OBJECT (demux, "Tag ---- atom is too small, ignoring");
- return;
- }
-
- mean = qtdemux_tree_get_child_by_type (node, FOURCC_mean);
- if (!mean) {
- GST_WARNING_OBJECT (demux, "No 'mean' atom found");
- return;
- }
-
- meansize = QT_UINT32 (mean->data);
- if (meansize <= 12) {
- GST_WARNING_OBJECT (demux, "Small mean atom, ignoring the whole tag");
- return;
- }
- meanstr = ((gchar *) mean->data) + 12;
- meansize -= 12;
+ /* At below, figure out which stream in active_streams has identical stream-id
+ * with that of in old_streams. If there is matching stream-id,
+ * corresponding newstream will not be exposed again,
+ * but demux will reuse srcpad of matched old stream
+ *
+ * active_streams : newly created streams from the latest moov
+ * old_streams : existing streams (belong to previous moov)
+ */
- name = qtdemux_tree_get_child_by_type (node, FOURCC_name);
- if (!name) {
- GST_WARNING_OBJECT (demux, "'name' atom not found, ignoring tag");
- return;
- }
+ for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
+ QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
+ QtDemuxStream *oldstream = NULL;
+ guint target;
- namesize = QT_UINT32 (name->data);
- if (namesize <= 12) {
- GST_WARNING_OBJECT (demux, "'name' atom is too small, ignoring tag");
- return;
- }
- namestr = ((gchar *) name->data) + 12;
- namesize -= 12;
+ GST_DEBUG_OBJECT (qtdemux, "track-id %u, fourcc %" GST_FOURCC_FORMAT,
+ stream->track_id, GST_FOURCC_ARGS (CUR_STREAM (stream)->fourcc));
- /*
- * Data atom is:
- * uint32 - size
- * uint32 - name
- * uint8 - version
- * uint24 - data type
- * uint32 - all 0
- * rest - the data
- */
- data = qtdemux_tree_get_child_by_type (node, FOURCC_data);
- if (!data) {
- GST_WARNING_OBJECT (demux, "No data atom in this tag");
- return;
- }
- datasize = QT_UINT32 (data->data);
- if (datasize <= 16) {
- GST_WARNING_OBJECT (demux, "Data atom too small");
- return;
- }
- datatype = QT_UINT32 (((gchar *) data->data) + 8) & 0xFFFFFF;
+ if (g_ptr_array_find_with_equal_func (qtdemux->old_streams,
+ stream->stream_id, (GEqualFunc) _stream_equal_func, &target)) {
+ oldstream = QTDEMUX_NTH_OLD_STREAM (qtdemux, target);
- if ((strncmp (meanstr, "com.apple.iTunes", meansize) == 0) ||
- (strncmp (meanstr, "org.hydrogenaudio.replaygain", meansize) == 0)) {
- static const struct
- {
- const gchar name[28];
- const gchar tag[28];
- } tags[] = {
- {
- "replaygain_track_gain", GST_TAG_TRACK_GAIN}, {
- "replaygain_track_peak", GST_TAG_TRACK_PEAK}, {
- "replaygain_album_gain", GST_TAG_ALBUM_GAIN}, {
- "replaygain_album_peak", GST_TAG_ALBUM_PEAK}, {
- "MusicBrainz Track Id", GST_TAG_MUSICBRAINZ_TRACKID}, {
- "MusicBrainz Artist Id", GST_TAG_MUSICBRAINZ_ARTISTID}, {
- "MusicBrainz Album Id", GST_TAG_MUSICBRAINZ_ALBUMID}, {
- "MusicBrainz Album Artist Id", GST_TAG_MUSICBRAINZ_ALBUMARTISTID}
- };
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
- if (!g_ascii_strncasecmp (tags[i].name, namestr, namesize)) {
- switch (gst_tag_get_type (tags[i].tag)) {
- case G_TYPE_DOUBLE:
- qtdemux_add_double_tag_from_str (demux, taglist, tags[i].tag,
- ((guint8 *) data->data) + 16, datasize - 16);
- break;
- case G_TYPE_STRING:
- qtdemux_tag_add_str (demux, taglist, tags[i].tag, NULL, node);
- break;
- default:
- /* not reached */
- break;
- }
- break;
- }
+ /* null pad stream cannot be reused */
+ if (oldstream->pad == NULL)
+ oldstream = NULL;
}
- if (i == G_N_ELEMENTS (tags))
- goto unknown_tag;
- } else {
- goto unknown_tag;
- }
-
- return;
-/* errors */
-unknown_tag:
-#ifndef GST_DISABLE_GST_DEBUG
- {
- gchar *namestr_dbg;
- gchar *meanstr_dbg;
+ if (oldstream) {
+ GST_DEBUG_OBJECT (qtdemux, "Reuse track-id %d", oldstream->track_id);
- meanstr_dbg = g_strndup (meanstr, meansize);
- namestr_dbg = g_strndup (namestr, namesize);
+ if (!qtdemux_reuse_and_configure_stream (qtdemux, oldstream, stream))
+ return FALSE;
- GST_WARNING_OBJECT (demux, "This tag %s:%s type:%u is not mapped, "
- "file a bug at bugzilla.gnome.org", meanstr_dbg, namestr_dbg, datatype);
+ /* we don't need to preserve order of old streams */
+ g_ptr_array_remove_fast (qtdemux->old_streams, oldstream);
+ } else {
+ GstTagList *list;
- g_free (namestr_dbg);
- g_free (meanstr_dbg);
+ /* now we have all info and can expose */
+ list = stream->stream_tags;
+ stream->stream_tags = NULL;
+ if (!gst_qtdemux_add_stream (qtdemux, stream, list))
+ return FALSE;
+ }
}
-#endif
- return;
+
+ return TRUE;
}
-static void
-qtdemux_tag_add_id32 (GstQTDemux * demux, GstTagList * taglist, const char *tag,
- const char *tag_bis, GNode * node)
+/* Must be called with expose lock */
+static GstFlowReturn
+qtdemux_expose_streams (GstQTDemux * qtdemux)
{
- guint8 *data;
- GstBuffer *buf;
- guint len;
- GstTagList *id32_taglist = NULL;
-
- GST_LOG_OBJECT (demux, "parsing ID32");
+ gint i;
- data = node->data;
- len = GST_READ_UINT32_BE (data);
+ GST_DEBUG_OBJECT (qtdemux, "exposing streams");
- /* need at least full box and language tag */
- if (len < 12 + 2)
- return;
+ if (!qtdemux_is_streams_update (qtdemux)) {
+ GST_DEBUG_OBJECT (qtdemux, "Reuse all streams");
+ for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
+ QtDemuxStream *new_stream = QTDEMUX_NTH_STREAM (qtdemux, i);
+ QtDemuxStream *old_stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
+ if (!qtdemux_reuse_and_configure_stream (qtdemux, old_stream, new_stream))
+ return GST_FLOW_ERROR;
+ }
- buf = gst_buffer_new_allocate (NULL, len - 14, NULL);
- gst_buffer_fill (buf, 0, data + 14, len - 14);
+ g_ptr_array_set_size (qtdemux->old_streams, 0);
+ qtdemux->need_segment = TRUE;
- id32_taglist = gst_tag_list_from_id3v2_tag (buf);
- if (id32_taglist) {
- GST_LOG_OBJECT (demux, "parsing ok");
- gst_tag_list_insert (taglist, id32_taglist, GST_TAG_MERGE_KEEP);
- gst_tag_list_unref (id32_taglist);
- } else {
- GST_LOG_OBJECT (demux, "parsing failed");
+ return GST_FLOW_OK;
}
- gst_buffer_unref (buf);
-}
-
-typedef void (*GstQTDemuxAddTagFunc) (GstQTDemux * demux, GstTagList * taglist,
- const char *tag, const char *tag_bis, GNode * node);
-
-/* unmapped tags
-FOURCC_pcst -> if media is a podcast -> bool
-FOURCC_cpil -> if media is part of a compilation -> bool
-FOURCC_pgap -> if media is part of a gapless context -> bool
-FOURCC_tven -> the tv episode id e.g. S01E23 -> str
-*/
-
-static const struct
-{
- guint32 fourcc;
- const gchar *gst_tag;
- const gchar *gst_tag_bis;
- const GstQTDemuxAddTagFunc func;
-} add_funcs[] = {
- {
- FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
- FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
- FOURCC__grp, GST_TAG_GROUPING, NULL, qtdemux_tag_add_str}, {
- FOURCC__wrt, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
- FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
- FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, qtdemux_tag_add_str}, {
- FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
- FOURCC_auth, GST_TAG_COMPOSER, NULL, qtdemux_tag_add_str}, {
- FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
- FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
- FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
- FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
- FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
- FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
- FOURCC_desc, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
- FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
- FOURCC__lyr, GST_TAG_LYRICS, NULL, qtdemux_tag_add_str}, {
- FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
- FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
- FOURCC__too, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
- FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
- FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
- FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
- qtdemux_tag_add_num}, {
- FOURCC_disc, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
- qtdemux_tag_add_num}, {
- FOURCC__gen, GST_TAG_GENRE, NULL, qtdemux_tag_add_str}, {
- FOURCC_gnre, GST_TAG_GENRE, NULL, qtdemux_tag_add_gnre}, {
- FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, qtdemux_tag_add_tmpo}, {
- FOURCC_covr, GST_TAG_IMAGE, NULL, qtdemux_tag_add_covr}, {
- FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, qtdemux_tag_add_str}, {
- FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, qtdemux_tag_add_str}, {
- FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
- FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, qtdemux_tag_add_str}, {
- FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, qtdemux_tag_add_str}, {
- FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, qtdemux_tag_add_str}, {
- FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, qtdemux_tag_add_str}, {
- FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, qtdemux_tag_add_uint32}, {
- FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, qtdemux_tag_add_uint32}, {
- FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_keywords}, {
- FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
- FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
- FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
- FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
- qtdemux_tag_add_classification}, {
- FOURCC__mak, GST_TAG_DEVICE_MANUFACTURER, NULL, qtdemux_tag_add_str}, {
- FOURCC__mod, GST_TAG_DEVICE_MODEL, NULL, qtdemux_tag_add_str}, {
- FOURCC__swr, GST_TAG_APPLICATION_NAME, NULL, qtdemux_tag_add_str}, {
-
- /* This is a special case, some tags are stored in this
- * 'reverse dns naming', according to:
- * http://atomicparsley.sourceforge.net/mpeg-4files.html and
- * bug #614471
- */
- FOURCC_____, "", NULL, qtdemux_tag_add_revdns}, {
- /* see http://www.mp4ra.org/specs.html for ID32 in meta box */
- FOURCC_ID32, "", NULL, qtdemux_tag_add_id32}
-};
-
-struct _GstQtDemuxTagList
-{
- GstQTDemux *demux;
- GstTagList *taglist;
-};
-typedef struct _GstQtDemuxTagList GstQtDemuxTagList;
+ if (qtdemux->streams_aware) {
+ if (!qtdemux_update_streams (qtdemux))
+ return GST_FLOW_ERROR;
+ } else {
+ for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
+ QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
+ GstTagList *list;
-static void
-qtdemux_tag_add_blob (GNode * node, GstQtDemuxTagList * qtdemuxtaglist)
-{
- gint len;
- guint8 *data;
- GstBuffer *buf;
- gchar *media_type;
- const gchar *style;
- GstSample *sample;
- GstStructure *s;
- guint i;
- guint8 ndata[4];
- GstQTDemux *demux = qtdemuxtaglist->demux;
- GstTagList *taglist = qtdemuxtaglist->taglist;
-
- data = node->data;
- len = QT_UINT32 (data);
- buf = gst_buffer_new_and_alloc (len);
- gst_buffer_fill (buf, 0, data, len);
-
- /* heuristic to determine style of tag */
- if (QT_FOURCC (data + 4) == FOURCC_____ ||
- (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
- style = "itunes";
- else if (demux->major_brand == FOURCC_qt__)
- style = "quicktime";
- /* fall back to assuming iso/3gp tag style */
- else
- style = "iso";
+ /* now we have all info and can expose */
+ list = stream->stream_tags;
+ stream->stream_tags = NULL;
+ if (!gst_qtdemux_add_stream (qtdemux, stream, list))
+ return GST_FLOW_ERROR;
- /* santize the name for the caps. */
- for (i = 0; i < 4; i++) {
- guint8 d = data[4 + i];
- if (g_ascii_isalnum (d))
- ndata[i] = g_ascii_tolower (d);
- else
- ndata[i] = '_';
+ }
}
- media_type = g_strdup_printf ("application/x-gst-qt-%c%c%c%c-tag",
- ndata[0], ndata[1], ndata[2], ndata[3]);
- GST_DEBUG_OBJECT (demux, "media type %s", media_type);
-
- s = gst_structure_new (media_type, "style", G_TYPE_STRING, style, NULL);
- sample = gst_sample_new (buf, NULL, NULL, s);
- gst_buffer_unref (buf);
- g_free (media_type);
-
- GST_DEBUG_OBJECT (demux, "adding private tag; size %d, info %" GST_PTR_FORMAT,
- len, s);
+ gst_qtdemux_guess_bitrate (qtdemux);
- gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
- GST_QT_DEMUX_PRIVATE_TAG, sample, NULL);
+ gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
- gst_sample_unref (sample);
-}
+ /* If we have still old_streams, it's no more used stream */
+ for (i = 0; i < qtdemux->old_streams->len; i++) {
+ QtDemuxStream *stream = QTDEMUX_NTH_OLD_STREAM (qtdemux, i);
-static void
-qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist, GNode * udta)
-{
- GNode *meta;
- GNode *ilst;
- GNode *xmp_;
- GNode *node;
- gint i;
- GstQtDemuxTagList demuxtaglist;
+ if (stream->pad) {
+ GstEvent *event;
- demuxtaglist.demux = qtdemux;
- demuxtaglist.taglist = taglist;
+ event = gst_event_new_eos ();
+ if (qtdemux->segment_seqnum)
+ gst_event_set_seqnum (event, qtdemux->segment_seqnum);
- meta = qtdemux_tree_get_child_by_type (udta, FOURCC_meta);
- if (meta != NULL) {
- ilst = qtdemux_tree_get_child_by_type (meta, FOURCC_ilst);
- if (ilst == NULL) {
- GST_LOG_OBJECT (qtdemux, "no ilst");
- return;
+ gst_pad_push_event (stream->pad, event);
}
- } else {
- ilst = udta;
- GST_LOG_OBJECT (qtdemux, "no meta so using udta itself");
}
- i = 0;
- while (i < G_N_ELEMENTS (add_funcs)) {
- node = qtdemux_tree_get_child_by_type (ilst, add_funcs[i].fourcc);
- if (node) {
- gint len;
+ g_ptr_array_set_size (qtdemux->old_streams, 0);
- len = QT_UINT32 (node->data);
- if (len < 12) {
- GST_DEBUG_OBJECT (qtdemux, "too small tag atom %" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (add_funcs[i].fourcc));
- } else {
- add_funcs[i].func (qtdemux, taglist, add_funcs[i].gst_tag,
- add_funcs[i].gst_tag_bis, node);
- }
- g_node_destroy (node);
- } else {
- i++;
- }
- }
+ /* check if we should post a redirect in case there is a single trak
+ * and it is a redirecting trak */
+ if (QTDEMUX_N_STREAMS (qtdemux) == 1 &&
+ QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri != NULL) {
+ GstMessage *m;
- /* parsed nodes have been removed, pass along remainder as blob */
- g_node_children_foreach (ilst, G_TRAVERSE_ALL,
- (GNodeForeachFunc) qtdemux_tag_add_blob, &demuxtaglist);
+ GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
+ "an external content");
+ m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
+ gst_structure_new ("redirect",
+ "new-location", G_TYPE_STRING,
+ QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri, NULL));
+ gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
+ g_free (qtdemux->redirect_location);
+ qtdemux->redirect_location =
+ g_strdup (QTDEMUX_NTH_STREAM (qtdemux, 0)->redirect_uri);
+ }
- /* parse up XMP_ node if existing */
- xmp_ = qtdemux_tree_get_child_by_type (udta, FOURCC_XMP_);
- if (xmp_ != NULL) {
- GstBuffer *buf;
- GstTagList *xmptaglist;
+ g_ptr_array_foreach (qtdemux->active_streams,
+ (GFunc) qtdemux_do_allocation, qtdemux);
- buf = _gst_buffer_new_wrapped (((guint8 *) xmp_->data) + 8,
- QT_UINT32 ((guint8 *) xmp_->data) - 8, NULL);
- xmptaglist = gst_tag_list_from_xmp_buffer (buf);
- gst_buffer_unref (buf);
+ qtdemux->need_segment = TRUE;
- qtdemux_handle_xmp_taglist (qtdemux, taglist, xmptaglist);
- } else {
- GST_DEBUG_OBJECT (qtdemux, "No XMP_ node found");
- }
+ qtdemux->exposed = TRUE;
+ return GST_FLOW_OK;
}
typedef struct