summaryrefslogtreecommitdiffstats
path: root/gst/asfmux/gstasfobjects.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/asfmux/gstasfobjects.c')
-rw-r--r--gst/asfmux/gstasfobjects.c813
1 files changed, 813 insertions, 0 deletions
diff --git a/gst/asfmux/gstasfobjects.c b/gst/asfmux/gstasfobjects.c
new file mode 100644
index 00000000..c0b63ae0
--- /dev/null
+++ b/gst/asfmux/gstasfobjects.c
@@ -0,0 +1,813 @@
+/* ASF muxer plugin for GStreamer
+ * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gstasfobjects.h"
+#include <string.h>
+
+/* Guids */
+const Guid guids[] = {
+ /* asf header object */
+ {0x75B22630, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)},
+ /* asf file properties object */
+ {0x8CABDCA1, 0xA947, 0x11CF, G_GUINT64_CONSTANT (0x8EE400C00C205365)},
+ /* asf stream properties object */
+ {0xB7DC0791, 0xA9B7, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)},
+ /* asf audio media */
+ {0xF8699E40, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)},
+ /* asf no error correction */
+ {0x20FB5700, 0x5B55, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)},
+ /* asf audio spread */
+ {0xBFC3CD50, 0x618F, 0x11CF, G_GUINT64_CONSTANT (0x8BB200AA00B4E220)},
+ /* asf header extension object */
+ {0x5FBF03B5, 0xA92E, 0x11CF, G_GUINT64_CONSTANT (0x8EE300C00C205365)},
+ /* asf reserved 1 */
+ {0xABD3D211, 0xA9BA, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)},
+ /* asf data object */
+ {0x75B22636, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)},
+ /* asf extended stream properties object */
+ {0x14E6A5CB, 0xC672, 0x4332, G_GUINT64_CONSTANT (0x8399A96952065B5A)},
+ /* asf video media */
+ {0xBC19EFC0, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)},
+ /* asf simple index object */
+ {0x33000890, 0xE5B1, 0x11CF, G_GUINT64_CONSTANT (0x89F400A0C90349CB)},
+ /* asf content description */
+ {0x75B22633, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)},
+ /* asf extended content description */
+ {0xD2D0A440, 0xE307, 0x11D2, G_GUINT64_CONSTANT (0x97F000A0C95EA850)},
+ /* asf metadata object */
+ {0xC5F8CBEA, 0x5BAF, 0x4877, G_GUINT64_CONSTANT (0x8467AA8C44FA4CCA)},
+ /* asf padding object */
+ {0x1806D474, 0xCADF, 0x4509, G_GUINT64_CONSTANT (0xA4BA9AABCB96AAE8)}
+};
+
+/**
+ * gst_asf_generate_file_id:
+ *
+ * Generates a random GUID
+ *
+ * Returns: The generated GUID
+ */
+Guid
+gst_asf_generate_file_id ()
+{
+ guint32 aux;
+ Guid guid;
+ guid.v1 = g_random_int ();
+ aux = g_random_int ();
+ guid.v2 = (guint16) (aux & 0x0000FFFF);
+ guid.v3 = (guint16) (aux >> 16);
+ guid.v4 = (((guint64) g_random_int ()) << 32) | (guint64) g_random_int ();
+
+ return guid;
+}
+
+/**
+ * gst_byte_reader_get_asf_var_size_field:
+ * @reader: A #GstByteReader
+ * @field_type: an asf field type
+ * @var: pointer to store the result
+ *
+ * Reads the proper data from the #GstByteReader according to the
+ * asf field type and stores it in var
+ *
+ * Returns: True on success, false otherwise
+ */
+gboolean
+gst_byte_reader_get_asf_var_size_field (GstByteReader * reader,
+ guint8 field_type, guint32 * var)
+{
+ guint8 aux8 = 0;
+ guint16 aux16 = 0;
+ guint32 aux32 = 0;
+ gboolean ret;
+
+ switch (field_type) {
+ case ASF_FIELD_TYPE_DWORD:
+ ret = gst_byte_reader_get_uint32_le (reader, &aux32);
+ *var = aux32;
+ break;
+ case ASF_FIELD_TYPE_WORD:
+ ret = gst_byte_reader_get_uint16_le (reader, &aux16);
+ *var = aux16;
+ break;
+ case ASF_FIELD_TYPE_BYTE:
+ ret = gst_byte_reader_get_uint8 (reader, &aux8);
+ *var = aux8;
+ break;
+ case ASF_FIELD_TYPE_NONE:
+ ret = TRUE;
+ *var = 0;
+ break;
+ default:
+ return FALSE;
+ }
+ return ret;
+}
+
+/**
+ * gst_asf_read_var_size_field:
+ * @data: pointer to the data to be read
+ * @field_type: the asf field type pointed by data
+ *
+ * Reads and returns the value read from the data, according to the
+ * field type given
+ *
+ * Returns: The value read
+ */
+guint32
+gst_asf_read_var_size_field (guint8 * data, guint8 field_type)
+{
+ switch (field_type) {
+ case ASF_FIELD_TYPE_DWORD:
+ return GST_READ_UINT32_LE (data);
+ case ASF_FIELD_TYPE_WORD:
+ return GST_READ_UINT16_LE (data);
+ case ASF_FIELD_TYPE_BYTE:
+ return data[0];
+ default:
+ return 0;
+ }
+}
+
+/**
+ * gst_asf_get_var_size_field_len:
+ * @field_type: the asf field type
+ *
+ * Returns: the size in bytes of a variable of field_type type
+ */
+guint
+gst_asf_get_var_size_field_len (guint8 field_type)
+{
+ switch (field_type) {
+ case ASF_FIELD_TYPE_DWORD:
+ return 4;
+ case ASF_FIELD_TYPE_WORD:
+ return 2;
+ case ASF_FIELD_TYPE_BYTE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * gst_asf_file_info_new:
+ * Creates a new #GstAsfFileInfo
+ * Returns: the created struct
+ */
+GstAsfFileInfo *
+gst_asf_file_info_new ()
+{
+ return g_new0 (GstAsfFileInfo, 1);
+}
+
+/**
+ * gst_asf_file_info_reset:
+ * @info: the #GstAsfFileInfo to be reset
+ * resets the data of a #GstFileInfo
+ */
+void
+gst_asf_file_info_reset (GstAsfFileInfo * info)
+{
+ info->packet_size = 0;
+ info->packets_count = 0;
+ info->broadcast = FALSE;
+}
+
+/**
+ * gst_asf_file_info_free:
+ * @info: the #GstAsfFileInfo to be freed
+ *
+ * Releases memory associated with this #GstAsfFileInfo
+ */
+void
+gst_asf_file_info_free (GstAsfFileInfo * info)
+{
+ g_free (info);
+}
+
+/**
+ * gst_asf_payload_get_size:
+ * @payload: the payload to get the size from
+ *
+ * Returns: the size of an asf payload of the data represented by this
+ * #AsfPayload
+ */
+guint32
+gst_asf_payload_get_size (AsfPayload * payload)
+{
+ return ASF_MULTIPLE_PAYLOAD_HEADER_SIZE + GST_BUFFER_SIZE (payload->data);
+}
+
+/**
+ * gst_asf_payload_free:
+ * @payload: the #AsfPayload to be freed
+ *
+ * Releases teh memory associated with this payload
+ */
+void
+gst_asf_payload_free (AsfPayload * payload)
+{
+ gst_buffer_unref (payload->data);
+ g_free (payload);
+}
+
+/**
+ * gst_asf_get_current_time:
+ *
+ * Gets system current time in ASF time unit
+ * (100-nanoseconds since Jan, 1st 1601)
+ *
+ * Returns:
+ */
+guint64
+gst_asf_get_current_time ()
+{
+ GTimeVal timeval;
+ guint64 secs;
+ guint64 usecs;
+
+ g_get_current_time (&timeval);
+
+ secs = (guint64) timeval.tv_sec;
+ usecs = (guint64) timeval.tv_usec;
+ return secs * G_GUINT64_CONSTANT (10000000) + usecs * 10
+ + G_GUINT64_CONSTANT (116444628000000000);
+}
+
+/**
+ * gst_asf_match_guid:
+ * @data: pointer to the guid to be tested
+ * @guid: guid to match against data
+ *
+ * Checks if the guid pointed by data is the same
+ * as the guid parameter
+ *
+ * Returns: True if they are the same, false otherwise
+ */
+gboolean
+gst_asf_match_guid (const guint8 * data, const Guid * guid)
+{
+ Guid g;
+ g.v1 = GST_READ_UINT32_LE (data);
+ g.v2 = GST_READ_UINT16_LE (data + 4);
+ g.v3 = GST_READ_UINT16_LE (data + 6);
+ g.v4 = GST_READ_UINT64_BE (data + 8);
+
+ return g.v1 == guid->v1 &&
+ g.v2 == guid->v2 && g.v3 == guid->v3 && g.v4 == guid->v4;
+}
+
+/**
+ * gst_asf_put_i32:
+ * @buf: the memory to write data to
+ * @data: the value to be writen
+ *
+ * Writes a 32 bit signed integer to memory
+ */
+void
+gst_asf_put_i32 (guint8 * buf, gint32 data)
+{
+ *(gint32 *) buf = data;
+}
+
+/**
+ * gst_asf_put_time:
+ * @buf: pointer to the buffer to write the value to
+ * @time: value to be writen
+ *
+ * Writes an asf time value to the buffer
+ */
+void
+gst_asf_put_time (guint8 * buf, guint64 time)
+{
+ GST_WRITE_UINT64_LE (buf, time);
+}
+
+/**
+ * gst_asf_put_guid:
+ * @buf: the buffer to write the guid to
+ * @guid: the guid to be writen
+ *
+ * Writes a GUID to the buffer
+ */
+void
+gst_asf_put_guid (guint8 * buf, Guid guid)
+{
+ guint32 *aux32 = (guint32 *) buf;
+ guint16 *aux16 = (guint16 *) & (buf[4]);
+ guint64 *aux64 = (guint64 *) & (buf[8]);
+ *aux32 = GUINT32_TO_LE (guid.v1);
+ *aux16 = GUINT16_TO_LE (guid.v2);
+ aux16 = (guint16 *) & (buf[6]);
+ *aux16 = GUINT16_TO_LE (guid.v3);
+ *aux64 = GUINT64_TO_BE (guid.v4);
+}
+
+/**
+ * gst_asf_put_payload:
+ * @buf: memory to write the payload to
+ * @payload: #AsfPayload to be writen
+ *
+ * Writes the asf payload to the buffer. The #AsfPayload
+ * packet count is incremented.
+ */
+void
+gst_asf_put_payload (guint8 * buf, AsfPayload * payload)
+{
+ GST_WRITE_UINT8 (buf, payload->stream_number);
+ GST_WRITE_UINT8 (buf + 1, payload->media_obj_num);
+ GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj);
+ GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length);
+ GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size);
+ GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time);
+ GST_WRITE_UINT16_LE (buf + 15, (guint16) GST_BUFFER_SIZE (payload->data));
+ memcpy (buf + 17, GST_BUFFER_DATA (payload->data),
+ GST_BUFFER_SIZE (payload->data));
+
+ payload->packet_count++;
+}
+
+/**
+ * gst_asf_put_subpayload:
+ * @buf: buffer to write the payload to
+ * @payload: the payload to be writen
+ * @size: maximum size in bytes to write
+ *
+ * Serializes part of a payload to a buffer.
+ * The maximum size is checked against the payload length,
+ * the minimum of this size and the payload length is writen
+ * to the buffer and the writen size is returned.
+ *
+ * It also updates the values of the payload to match the remaining
+ * data.
+ * In case there is not enough space to write the headers, nothing is done.
+ *
+ * Returns: The writen size in bytes.
+ */
+guint16
+gst_asf_put_subpayload (guint8 * buf, AsfPayload * payload, guint16 size)
+{
+ guint16 payload_size;
+ GstBuffer *newbuf;
+ if (size <= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE) {
+ return 0; /* do nothing if there is not enough space */
+ }
+ GST_WRITE_UINT8 (buf, payload->stream_number);
+ GST_WRITE_UINT8 (buf + 1, payload->media_obj_num);
+ GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj);
+ GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length);
+ GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size);
+ GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time);
+ size -= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE;
+ payload_size = size < GST_BUFFER_SIZE (payload->data) ?
+ size : GST_BUFFER_SIZE (payload->data);
+ GST_WRITE_UINT16_LE (buf + 15, payload_size);
+ memcpy (buf + 17, GST_BUFFER_DATA (payload->data), payload_size);
+
+ /* updates the payload to the remaining data */
+ payload->offset_in_media_obj += payload_size;
+ newbuf = gst_buffer_create_sub (payload->data, payload_size,
+ GST_BUFFER_SIZE (payload->data) - payload_size);
+ gst_buffer_copy_metadata (payload->data, newbuf, GST_BUFFER_COPY_FLAGS |
+ GST_BUFFER_COPY_CAPS);
+ GST_BUFFER_TIMESTAMP (newbuf) = GST_BUFFER_TIMESTAMP (payload->data);
+ gst_buffer_unref (payload->data);
+ payload->data = newbuf;
+
+ payload->packet_count++;
+
+ return payload_size;
+}
+
+/**
+ * gst_asf_match_and_peek_obj_size:
+ * @data: data to be peeked at
+ * @guid: pointer to a guid
+ *
+ * Compares the first bytes of data against the guid parameter and
+ * if they match gets the object size (that are right after the guid in
+ * asf objects).
+ *
+ * In case the guids don't match, 0 is returned.
+ * If the guid is NULL the match is assumed to be true.
+ *
+ * Returns: The size of the object in case the guid matches, 0 otherwise
+ */
+guint64
+gst_asf_match_and_peek_obj_size (const guint8 * data, const Guid * guid)
+{
+ g_assert (data);
+ if (guid && !gst_asf_match_guid (data, guid)) {
+ /* this is not the expected object */
+ return 0;
+ }
+ /* return the object size */
+ return GST_READ_UINT64_LE (data + ASF_GUID_SIZE);
+}
+
+/**
+ * gst_asf_parse_mult_payload:
+ * @reader: a #GstByteReader ready to read the multiple payload data
+ * @has_keyframe: pointer to return the result
+ *
+ * Parses a multiple payload section of an asf data packet
+ * to see if any of the paylaods has a a keyframe
+ *
+ * Notice that the #GstByteReader might not be positioned after
+ * this section on this function return. Because this section
+ * is the last one in an asf packet and the remaining data
+ * is probably uninteresting to the application.
+ *
+ * Returns: true on success, false if some error occurrs
+ */
+static gboolean
+gst_asf_parse_mult_payload (GstByteReader * reader, gboolean * has_keyframe)
+{
+ guint payloads;
+ guint8 payload_len_type;
+ guint8 rep_data_len;
+ guint32 payload_len;
+ guint8 stream_num;
+ guint8 aux;
+ guint i;
+
+ if (!gst_byte_reader_get_uint8 (reader, &aux))
+ return FALSE;
+
+ payloads = (aux & 0x3F);
+ payload_len_type = (aux & 0xC0) >> 6;
+
+ *has_keyframe = FALSE;
+ for (i = 0; i < payloads; i++) {
+ GST_LOG ("Parsing payload %u/%u", i + 1, payloads);
+ if (!gst_byte_reader_get_uint8 (reader, &stream_num))
+ goto error;
+ if ((stream_num & 0x80) != 0) {
+ GST_LOG ("Keyframe found, stoping parse of payloads");
+ *has_keyframe = TRUE;
+ return TRUE;
+ }
+ /* skip to replicated data length */
+ if (!gst_byte_reader_skip (reader, 5))
+ goto error;
+ if (!gst_byte_reader_get_uint8 (reader, &rep_data_len))
+ goto error;
+ if (!gst_byte_reader_skip (reader, rep_data_len))
+ goto error;
+ if (!gst_byte_reader_get_asf_var_size_field (reader, payload_len_type,
+ &payload_len))
+ goto error;
+ if (!gst_byte_reader_skip (reader, payload_len))
+ goto error;
+ }
+
+ /* we do not skip the rest of the payload bytes as
+ this is the last data to be parsed on the buffer */
+ return TRUE;
+error:
+ GST_WARNING ("Error while parsing payloads");
+ return FALSE;
+}
+
+/**
+ * gst_asf_parse_single_payload:
+ * @reader: a #GstByteReader ready to read the multiple payload data
+ * @has_keyframe: pointer to return the result
+ *
+ * Parses a single payload section of an asf data packet
+ * to see if any of the paylaods has a a keyframe
+ *
+ * Notice that the #GstByteReader might not be positioned after
+ * this section on this function return. Because this section
+ * is the last one in an asf packet and the remaining data
+ * is probably uninteresting to the application.
+ *
+ * Returns: true on success, false if some error occurrs
+ */
+static gboolean
+gst_asf_parse_single_payload (GstByteReader * reader, gboolean * has_keyframe)
+{
+ guint8 stream_num;
+ if (!gst_byte_reader_get_uint8 (reader, &stream_num))
+ return GST_FLOW_ERROR;
+ *has_keyframe = (stream_num & 0x80) != 0;
+
+ /* we do not skip the rest of the payload bytes as
+ this is the last data to be parsed on the buffer */
+ return TRUE;
+}
+
+gboolean
+gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet,
+ gboolean trust_delta_flag)
+{
+ GstByteReader *reader;
+ gboolean ret = TRUE;
+ guint8 first;
+ guint8 err_length = 0; /* length of the error fields */
+ guint8 aux;
+ guint8 packet_len_type;
+ guint8 padding_len_type;
+ guint8 seq_len_type;
+ guint8 rep_data_len_type;
+ guint8 mo_number_len_type;
+ guint8 mo_offset_type;
+ gboolean mult_payloads;
+ guint32 packet_len;
+ guint32 padd_len;
+ guint32 send_time;
+ guint16 duration;
+ gboolean has_keyframe;
+
+ reader = gst_byte_reader_new_from_buffer (buffer);
+
+ GST_LOG ("Starting packet parsing, size: %u", GST_BUFFER_SIZE (buffer));
+ if (!gst_byte_reader_get_uint8 (reader, &first))
+ goto error;
+
+ if (first & 0x80) { /* error correction present */
+ guint8 err_cor_len;
+ err_length += 1;
+ GST_DEBUG ("Packet contains error correction");
+ if (first & 0x60) {
+ GST_ERROR ("Error correction data length should be "
+ "set to 0 and is reserved for future use.");
+ return FALSE;
+ }
+ err_cor_len = (first & 0x0F);
+ err_length += err_cor_len;
+ GST_DEBUG ("Error correction data length: %d", (gint) err_cor_len);
+ if (!gst_byte_reader_skip (reader, err_cor_len))
+ goto error;
+
+ /* put payload parsing info first byte in aux var */
+ if (!gst_byte_reader_get_uint8 (reader, &aux))
+ goto error;
+ } else {
+ aux = first;
+ }
+ mult_payloads = (aux & 0x1) != 0;
+
+ packet_len_type = (aux >> 5) & 0x3;
+ padding_len_type = (aux >> 3) & 0x3;
+ seq_len_type = (aux >> 1) & 0x3;
+ GST_LOG ("Field sizes: packet length type: %u "
+ ", padding length type: %u, sequence length type: %u",
+ gst_asf_get_var_size_field_len (packet_len_type),
+ gst_asf_get_var_size_field_len (padding_len_type),
+ gst_asf_get_var_size_field_len (seq_len_type));
+
+ if (mult_payloads) {
+ GST_DEBUG ("Packet contains multiple payloads");
+ }
+
+ if (!gst_byte_reader_get_uint8 (reader, &aux))
+ goto error;
+ rep_data_len_type = aux & 0x3;
+ mo_offset_type = (aux >> 2) & 0x3;
+ mo_number_len_type = (aux >> 4) & 0x3;
+
+ /* gets the fields lengths */
+ GST_LOG ("Getting packet and padding length");
+ if (!gst_byte_reader_get_asf_var_size_field (reader,
+ packet_len_type, &packet_len))
+ goto error;
+ if (!gst_byte_reader_skip (reader,
+ gst_asf_get_var_size_field_len (seq_len_type)))
+ goto error;
+ if (!gst_byte_reader_get_asf_var_size_field (reader,
+ padding_len_type, &padd_len))
+ goto error;
+
+ if (packet_len_type != ASF_FIELD_TYPE_NONE &&
+ packet_len != GST_BUFFER_SIZE (buffer)) {
+ GST_WARNING ("ASF packets should be aligned with buffers");
+ ret = FALSE;
+ goto end;
+ }
+
+ GST_LOG ("Getting send time and duration");
+ if (!gst_byte_reader_get_uint32_le (reader, &send_time))
+ goto error;
+ if (!gst_byte_reader_get_uint16_le (reader, &duration))
+ goto error;
+
+ has_keyframe = FALSE;
+ GST_LOG ("Checking for keyframes");
+ if (trust_delta_flag) {
+ has_keyframe = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
+ } else {
+ if (mult_payloads) {
+ ret = gst_asf_parse_mult_payload (reader, &has_keyframe);
+ } else {
+ ret = gst_asf_parse_single_payload (reader, &has_keyframe);
+ }
+ }
+
+ if (!ret) {
+ GST_WARNING ("Failed to parse payloads");
+ goto end;
+ }
+ GST_DEBUG ("Received packet of length %" G_GUINT32_FORMAT
+ ", padding %" G_GUINT32_FORMAT ", send time %" G_GUINT32_FORMAT
+ ", duration %" G_GUINT16_FORMAT " and %s keyframe(s)",
+ packet_len, padd_len, send_time, duration,
+ (has_keyframe) ? "with" : "without");
+
+ packet->packet_size = packet_len;
+ packet->padding = padd_len;
+ packet->send_time = send_time;
+ packet->duration = duration;
+ packet->has_keyframe = has_keyframe;
+ packet->multiple_payloads = mult_payloads ? TRUE : FALSE;
+ packet->padd_field_type = padding_len_type;
+ packet->packet_field_type = packet_len_type;
+ packet->seq_field_type = seq_len_type;
+ packet->err_cor_len = err_length;
+
+ gst_byte_reader_free (reader);
+ return ret;
+
+error:
+ ret = FALSE;
+ GST_WARNING ("Error while parsing data packet");
+end:
+ gst_byte_reader_free (reader);
+ return ret;
+}
+
+static gboolean
+gst_asf_parse_file_properties_obj (GstByteReader * reader,
+ GstAsfFileInfo * asfinfo)
+{
+ guint32 min_ps;
+ guint32 max_ps;
+ guint64 packets;
+ guint32 flags;
+ GST_DEBUG ("ASF: Parsing file properties object");
+
+ /* skip until data packets count */
+ if (!gst_byte_reader_skip (reader, 32))
+ return FALSE;
+ if (!gst_byte_reader_get_uint64_le (reader, &packets))
+ return FALSE;
+ asfinfo->packets_count = packets;
+ GST_DEBUG ("ASF: packets count %" G_GUINT64_FORMAT, packets);
+
+ /* skip until flags */
+ if (!gst_byte_reader_skip (reader, 24))
+ return FALSE;
+
+ if (!gst_byte_reader_get_uint32_le (reader, &flags))
+ return GST_FLOW_ERROR;
+ asfinfo->broadcast = (flags & 0x1) == 1;
+ GST_DEBUG ("ASF: broadcast flag: %s", asfinfo->broadcast ? "true" : "false");
+ if (!gst_byte_reader_get_uint32_le (reader, &min_ps))
+ return GST_FLOW_ERROR;
+ if (!gst_byte_reader_get_uint32_le (reader, &max_ps))
+ return GST_FLOW_ERROR;
+
+ if (min_ps != max_ps) {
+ GST_WARNING ("Mininum and maximum packet size differ "
+ "%" G_GUINT32_FORMAT " and %" G_GUINT32_FORMAT ", "
+ "ASF spec states they should be the same", min_ps, max_ps);
+ return FALSE;
+ }
+
+ GST_DEBUG ("ASF: Packet size: %" G_GUINT32_FORMAT, min_ps);
+ asfinfo->packet_size = min_ps;
+ if (!gst_byte_reader_skip (reader, 4))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gst_asf_parse_headers (GstBuffer * buffer, GstAsfFileInfo * file_info)
+{
+ gboolean ret = TRUE;
+ guint32 header_objects;
+ guint32 i;
+ GstByteReader *reader;
+ guint64 object_size;
+
+ object_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer),
+ &(guids[ASF_HEADER_OBJECT_INDEX]));
+ if (object_size == 0) {
+ GST_WARNING ("ASF: Cannot parse, header guid not found at the beginning "
+ " of data");
+ return FALSE;
+ }
+
+ reader = gst_byte_reader_new_from_buffer (buffer);
+
+ if (!gst_byte_reader_skip (reader, ASF_GUID_OBJSIZE_SIZE))
+ goto error;
+ if (!gst_byte_reader_get_uint32_le (reader, &header_objects))
+ goto error;
+ GST_DEBUG ("ASF: Header has %" G_GUINT32_FORMAT " child"
+ " objects", header_objects);
+ /* skip reserved bytes */
+ if (!gst_byte_reader_skip (reader, 2))
+ goto error;
+
+ /* iterate through childs of header object */
+ for (i = 0; i < header_objects; i++) {
+ const guint8 *guid = NULL;
+ guint64 obj_size;
+ if (!gst_byte_reader_get_data (reader, ASF_GUID_SIZE, &guid))
+ goto error;
+ if (!gst_byte_reader_get_uint64_le (reader, &obj_size))
+ goto error;
+
+ if (gst_asf_match_guid (guid, &guids[ASF_FILE_PROPERTIES_OBJECT_INDEX])) {
+ ret = gst_asf_parse_file_properties_obj (reader, file_info);
+ } else {
+ /* we don't know/care about this object */
+ if (!gst_byte_reader_skip (reader, obj_size - ASF_GUID_OBJSIZE_SIZE))
+ goto error;
+ }
+
+ if (!ret)
+ goto end;
+ }
+ goto end;
+
+error:
+ ret = FALSE;
+ GST_WARNING ("ASF: Error while parsing headers");
+end:
+ gst_byte_reader_free (reader);
+ return ret;
+}
+
+#define MAP_GST_TO_ASF_TAG(tag, gst, asf) \
+ if (strcmp (tag, gst) == 0) \
+ return asf
+
+/**
+ * gst_asf_get_asf_tag:
+ * @gsttag: a gstreamer tag
+ *
+ * Maps gstreamer tags to asf tags
+ *
+ * Returns: The tag corresponding name in asf files or NULL if it is not mapped
+ */
+const gchar *
+gst_asf_get_asf_tag (const gchar * gsttag)
+{
+ g_return_val_if_fail (gsttag != NULL, NULL);
+
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE, ASF_TAG_TITLE);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE_SORTNAME, ASF_TAG_TITLE_SORTNAME);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST, ASF_TAG_ARTIST);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST_SORTNAME, ASF_TAG_ARTIST_SORTNAME);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM, ASF_TAG_ALBUM_TITLE);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM_SORTNAME,
+ ASF_TAG_ALBUM_TITLE_SORTNAME);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_GENRE, ASF_TAG_GENRE);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COPYRIGHT, ASF_TAG_COPYRIGHT);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMPOSER, ASF_TAG_COMPOSER);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMMENT, ASF_TAG_COMMENT);
+ MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TRACK_NUMBER, ASF_TAG_TRACK_NUMBER);
+
+ return NULL;
+}
+
+guint
+gst_asf_get_tag_field_type (GValue * value)
+{
+ if (G_VALUE_HOLDS_STRING (value))
+ return ASF_TAG_TYPE_UNICODE_STR;
+ if (G_VALUE_HOLDS_UINT (value))
+ return ASF_TAG_TYPE_DWORD;
+
+ return -1;
+}
+
+gboolean
+gst_asf_tag_present_in_content_description (const gchar * tag)
+{
+ return strcmp (tag, GST_TAG_TITLE) == 0 ||
+ strcmp (tag, GST_TAG_ARTIST) == 0 ||
+ strcmp (tag, GST_TAG_COPYRIGHT) == 0 ||
+ strcmp (tag, GST_TAG_DESCRIPTION) == 0;
+ /* FIXME we have no tag for rating */
+}