| /* 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., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, 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 |
| */ |
| void |
| gst_asf_generate_file_id (Guid * guid) |
| { |
| guint32 aux; |
| |
| 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 (); |
| } |
| |
| /** |
| * 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 (void) |
| { |
| 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_get_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 (void) |
| { |
| 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) |
| { |
| GST_WRITE_UINT32_LE (buf, (guint32) 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_get_size (payload->data)); |
| gst_buffer_extract (payload->data, 0, buf + 17, |
| gst_buffer_get_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_get_size (payload->data) ? |
| size : gst_buffer_get_size (payload->data); |
| GST_WRITE_UINT16_LE (buf + 15, payload_size); |
| gst_buffer_extract (payload->data, 0, buf + 17, payload_size); |
| |
| /* updates the payload to the remaining data */ |
| payload->offset_in_media_obj += payload_size; |
| newbuf = gst_buffer_copy_region (payload->data, GST_BUFFER_COPY_ALL, |
| payload_size, gst_buffer_get_size (payload->data) - payload_size); |
| 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_match_and_peek_obj_size_buf: |
| * @buf: buffer to be peeked at |
| * @guid: pointer to a guid |
| * |
| * Compares the first bytes of buf 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_buf (GstBuffer * buf, const Guid * guid) |
| { |
| GstMapInfo map; |
| guint64 res; |
| |
| gst_buffer_map (buf, &map, GST_MAP_READ); |
| res = gst_asf_match_and_peek_obj_size (map.data, guid); |
| gst_buffer_unmap (buf, &map); |
| |
| return res; |
| } |
| |
| /** |
| * 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 = 0; |
| guint32 payload_len; |
| guint8 stream_num = 0; |
| guint8 aux = 0; |
| 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 = 0; |
| 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, guint packet_size) |
| { |
| gboolean ret; |
| GstMapInfo map; |
| |
| gst_buffer_map (buffer, &map, GST_MAP_READ); |
| ret = gst_asf_parse_packet_from_data (map.data, map.size, buffer, packet, |
| trust_delta_flag, packet_size); |
| gst_buffer_unmap (buffer, &map); |
| |
| return ret; |
| } |
| |
| gboolean |
| gst_asf_parse_packet_from_data (guint8 * data, gsize size, GstBuffer * buffer, |
| GstAsfPacketInfo * packet, gboolean trust_delta_flag, guint packet_size) |
| { |
| /* Might be useful in future: |
| guint8 rep_data_len_type; |
| guint8 mo_number_len_type; |
| guint8 mo_offset_type; |
| */ |
| GstByteReader *reader; |
| gboolean ret = TRUE; |
| guint8 first = 0; |
| guint8 err_length = 0; /* length of the error fields */ |
| guint8 aux = 0; |
| guint8 packet_len_type; |
| guint8 padding_len_type; |
| guint8 seq_len_type; |
| gboolean mult_payloads; |
| guint32 packet_len; |
| guint32 padd_len; |
| guint32 send_time = 0; |
| guint16 duration = 0; |
| gboolean has_keyframe; |
| |
| if (packet_size != 0 && size != packet_size) { |
| GST_WARNING ("ASF packets should be aligned with buffers"); |
| return FALSE; |
| } |
| |
| reader = gst_byte_reader_new (data, size); |
| |
| GST_LOG ("Starting packet parsing, size: %" G_GSIZE_FORMAT, size); |
| 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."); |
| goto error; |
| } |
| 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; |
| |
| /* some packet size validation */ |
| if (packet_size != 0 && packet_len_type != ASF_FIELD_TYPE_NONE) { |
| if (padding_len_type != ASF_FIELD_TYPE_NONE && |
| packet_len + padd_len != packet_size) { |
| GST_WARNING ("Packet size (payload=%u + padding=%u) doesn't " |
| "match expected size %u", packet_len, padd_len, packet_size); |
| ret = FALSE; |
| } |
| |
| /* Be forgiving if packet_len has the full packet size |
| * as the spec isn't really clear on its meaning. |
| * |
| * I had been taking it as the full packet size (fixed) |
| * until bug #607555, that convinced me that it is more likely |
| * the actual payloaded data size. |
| */ |
| if (packet_len == packet_size) { |
| GST_DEBUG ("This packet's length field represents the full " |
| "packet and not the payloaded data length"); |
| ret = TRUE; |
| } |
| |
| if (!ret) |
| 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 = 0; |
| guint32 max_ps = 0; |
| guint64 packets = 0; |
| guint32 flags = 0; |
| 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) |
| { |
| GstMapInfo map; |
| gboolean ret; |
| |
| gst_buffer_map (buffer, &map, GST_MAP_READ); |
| ret = gst_asf_parse_headers_from_data (map.data, map.size, file_info); |
| gst_buffer_unmap (buffer, &map); |
| |
| return ret; |
| } |
| |
| gboolean |
| gst_asf_parse_headers_from_data (guint8 * data, guint size, |
| GstAsfFileInfo * file_info) |
| { |
| gboolean ret = TRUE; |
| guint32 header_objects = 0; |
| guint32 i; |
| GstByteReader *reader; |
| guint64 object_size; |
| |
| object_size = gst_asf_match_and_peek_obj_size (data, |
| &(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 (data, size); |
| |
| 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 = 0; |
| 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 */ |
| } |