Move files from gst-plugins-bad into the "subprojects/gst-plugins-bad/" subdir
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst / asfmux / gstasfmux.c
1 /* ASF muxer plugin for GStreamer
2  * Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /* based on:
21  * - avimux (by Ronald Bultje and Mark Nauwelaerts)
22  * - qtmux (by Thiago Santos and Mark Nauwelaerts)
23  */
24
25 /**
26  * SECTION:element-asfmux
27  * @title: asfmux
28  *
29  * Muxes media into an ASF file/stream.
30  *
31  * Pad names are either video_xx or audio_xx, where 'xx' is the
32  * stream number of the stream that goes through that pad. Stream numbers
33  * are assigned sequentially, starting from 1.
34  *
35  * ## Example launch lines
36  *
37  * (write everything in one line, without the backslash characters)
38  * |[
39  * gst-launch-1.0 videotestsrc num-buffers=250 \
40  * ! "video/x-raw,format=(string)I420,framerate=(fraction)25/1" ! avenc_wmv2 \
41  * ! asfmux name=mux ! filesink location=test.asf \
42  * audiotestsrc num-buffers=440 ! audioconvert \
43  * ! "audio/x-raw,rate=44100" ! avenc_wmav2 ! mux.
44  * ]| This creates an ASF file containing an WMV video stream
45  * with a test picture and WMA audio stream of a test sound.
46  *
47  * ## Live streaming
48  * asfmux and rtpasfpay are capable of generating a live asf stream.
49  * asfmux has to set its 'streamable' property to true, because in this
50  * mode it won't try to seek back to the start of the file to replace
51  * some fields that couldn't be known at the file start. In this mode,
52  * it won't also send indexes at the end of the data packets (the actual
53  * media content)
54  * the following pipelines are an example of this usage.
55  * (write everything in one line, without the backslash characters)
56  * Server (sender)
57  * |[
58  * gst-launch-1.0 -ve videotestsrc ! avenc_wmv2 ! asfmux name=mux streamable=true \
59  * ! rtpasfpay ! udpsink host=127.0.0.1 port=3333 \
60  * audiotestsrc ! avenc_wmav2 ! mux.
61  * ]|
62  * Client (receiver)
63  * |[
64  * gst-launch-1.0 udpsrc port=3333 ! "caps_from_rtpasfpay_at_sender" \
65  * ! rtpasfdepay ! decodebin name=d ! queue \
66  * ! videoconvert ! autovideosink \
67  * d. ! queue ! audioconvert ! autoaudiosink
68  * ]|
69  *
70  */
71
72 #ifdef HAVE_CONFIG_H
73 #include "config.h"
74 #endif
75
76 #include <string.h>
77 #include <stdio.h>
78 #include <gst/gst-i18n-plugin.h>
79 #include "gstasfmux.h"
80
81 #define DEFAULT_SIMPLE_INDEX_TIME_INTERVAL G_GUINT64_CONSTANT (10000000)
82 #define MAX_PAYLOADS_IN_A_PACKET 63
83
84 GST_DEBUG_CATEGORY_STATIC (asfmux_debug);
85 #define GST_CAT_DEFAULT asfmux_debug
86
87 enum
88 {
89   PROP_0,
90   PROP_PACKET_SIZE,
91   PROP_PREROLL,
92   PROP_MERGE_STREAM_TAGS,
93   PROP_PADDING,
94   PROP_STREAMABLE
95 };
96
97 /* Stores a tag list for the available/known tags
98  * in an ASF file
99  * Also stores the sizes those entries would use in a
100  * content description object and extended content
101  * description object
102  */
103 typedef struct
104 {
105   GstTagList *tags;
106   guint64 cont_desc_size;
107   guint64 ext_cont_desc_size;
108 } GstAsfTags;
109
110 /* Helper struct to be used as user data
111  * in gst_tag_foreach function for writing
112  * each tag for the metadata objects
113  *
114  * stream_num is used only for stream dependent tags
115  */
116 typedef struct
117 {
118   GstAsfMux *asfmux;
119   guint8 *buf;
120   guint16 count;
121   guint64 size;
122   guint16 stream_num;
123 } GstAsfExtContDescData;
124
125 typedef GstAsfExtContDescData GstAsfMetadataObjData;
126
127 #define DEFAULT_PACKET_SIZE 4800
128 #define DEFAULT_PREROLL 5000
129 #define DEFAULT_MERGE_STREAM_TAGS TRUE
130 #define DEFAULT_PADDING 0
131 #define DEFAULT_STREAMABLE FALSE
132
133 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
134     GST_PAD_SRC,
135     GST_PAD_ALWAYS,
136     GST_STATIC_CAPS ("video/x-ms-asf, " "parsed = (boolean) true")
137     );
138
139 static GstStaticPadTemplate video_sink_factory =
140 GST_STATIC_PAD_TEMPLATE ("video_%u",
141     GST_PAD_SINK,
142     GST_PAD_REQUEST,
143     GST_STATIC_CAPS ("video/x-wmv, wmvversion = (int) [1,3]"));
144
145 static GstStaticPadTemplate audio_sink_factory =
146     GST_STATIC_PAD_TEMPLATE ("audio_%u",
147     GST_PAD_SINK,
148     GST_PAD_REQUEST,
149     GST_STATIC_CAPS ("audio/x-wma, wmaversion = (int) [1,3]; "
150         "audio/mpeg, layer = (int) 3, mpegversion = (int) 1, "
151         "channels = (int) [1,2], rate = (int) [8000,96000]"));
152
153 static gboolean gst_asf_mux_audio_set_caps (GstPad * pad, GstCaps * caps);
154 static gboolean gst_asf_mux_video_set_caps (GstPad * pad, GstCaps * caps);
155
156 static GstPad *gst_asf_mux_request_new_pad (GstElement * element,
157     GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
158 static void gst_asf_mux_set_property (GObject * object,
159     guint prop_id, const GValue * value, GParamSpec * pspec);
160 static void gst_asf_mux_get_property (GObject * object,
161     guint prop_id, GValue * value, GParamSpec * pspec);
162 static GstStateChangeReturn gst_asf_mux_change_state (GstElement * element,
163     GstStateChange transition);
164
165 static gboolean gst_asf_mux_sink_event (GstCollectPads * pads,
166     GstCollectData * cdata, GstEvent * event, GstAsfMux * asfmux);
167
168 static void gst_asf_mux_pad_reset (GstAsfPad * data);
169 static GstFlowReturn gst_asf_mux_collected (GstCollectPads * collect,
170     gpointer data);
171
172 static GstElementClass *parent_class = NULL;
173
174 G_DEFINE_TYPE_WITH_CODE (GstAsfMux, gst_asf_mux, GST_TYPE_ELEMENT,
175     G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
176 GST_ELEMENT_REGISTER_DEFINE (asfmux, "asfmux",
177     GST_RANK_PRIMARY, GST_TYPE_ASF_MUX);
178
179 static void
180 gst_asf_mux_reset (GstAsfMux * asfmux)
181 {
182   asfmux->state = GST_ASF_MUX_STATE_NONE;
183   asfmux->stream_number = 0;
184   asfmux->data_object_size = 0;
185   asfmux->data_object_position = 0;
186   asfmux->file_properties_object_position = 0;
187   asfmux->total_data_packets = 0;
188   asfmux->file_size = 0;
189   asfmux->packet_size = 0;
190   asfmux->first_ts = GST_CLOCK_TIME_NONE;
191
192   if (asfmux->payloads) {
193     GSList *walk;
194     for (walk = asfmux->payloads; walk; walk = g_slist_next (walk)) {
195       gst_asf_payload_free ((AsfPayload *) walk->data);
196       walk->data = NULL;
197     }
198     g_slist_free (asfmux->payloads);
199   }
200   asfmux->payloads = NULL;
201   asfmux->payload_data_size = 0;
202
203   asfmux->file_id.v1 = 0;
204   asfmux->file_id.v2 = 0;
205   asfmux->file_id.v3 = 0;
206   asfmux->file_id.v4 = 0;
207
208   gst_tag_setter_reset_tags (GST_TAG_SETTER (asfmux));
209 }
210
211 static void
212 gst_asf_mux_finalize (GObject * object)
213 {
214   GstAsfMux *asfmux;
215
216   asfmux = GST_ASF_MUX (object);
217
218   gst_asf_mux_reset (asfmux);
219   gst_object_unref (asfmux->collect);
220
221   G_OBJECT_CLASS (parent_class)->finalize (object);
222 }
223
224 static void
225 gst_asf_mux_class_init (GstAsfMuxClass * klass)
226 {
227   GObjectClass *gobject_class;
228   GstElementClass *gstelement_class;
229
230   gobject_class = (GObjectClass *) klass;
231   gstelement_class = (GstElementClass *) klass;
232
233   parent_class = g_type_class_peek_parent (klass);
234
235   gobject_class->get_property = gst_asf_mux_get_property;
236   gobject_class->set_property = gst_asf_mux_set_property;
237   gobject_class->finalize = gst_asf_mux_finalize;
238
239   g_object_class_install_property (gobject_class, PROP_PACKET_SIZE,
240       g_param_spec_uint ("packet-size", "Packet size",
241           "The ASF packets size (bytes)",
242           ASF_MULTIPLE_PAYLOAD_HEADER_SIZE + 1, G_MAXUINT32,
243           DEFAULT_PACKET_SIZE,
244           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
245   g_object_class_install_property (gobject_class, PROP_PREROLL,
246       g_param_spec_uint64 ("preroll", "Preroll",
247           "The preroll time (milliseconds)",
248           0, G_MAXUINT64,
249           DEFAULT_PREROLL,
250           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
251   g_object_class_install_property (gobject_class, PROP_MERGE_STREAM_TAGS,
252       g_param_spec_boolean ("merge-stream-tags", "Merge Stream Tags",
253           "If the stream metadata (received as events in the sink) should be "
254           "merged to the main file metadata.",
255           DEFAULT_MERGE_STREAM_TAGS,
256           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
257   g_object_class_install_property (gobject_class, PROP_PADDING,
258       g_param_spec_uint64 ("padding", "Padding",
259           "Size of the padding object to be added to the end of the header. "
260           "If this less than 24 (the smaller size of an ASF object), "
261           "no padding is added.",
262           0, G_MAXUINT64,
263           DEFAULT_PADDING,
264           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
265   g_object_class_install_property (gobject_class, PROP_STREAMABLE,
266       g_param_spec_boolean ("streamable", "Streamable",
267           "If set to true, the output should be as if it is to be streamed "
268           "and hence no indexes written or duration written.",
269           DEFAULT_STREAMABLE,
270           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
271
272   gstelement_class->request_new_pad =
273       GST_DEBUG_FUNCPTR (gst_asf_mux_request_new_pad);
274   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_asf_mux_change_state);
275
276   gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
277   gst_element_class_add_static_pad_template (gstelement_class,
278       &audio_sink_factory);
279   gst_element_class_add_static_pad_template (gstelement_class,
280       &video_sink_factory);
281
282   gst_element_class_set_static_metadata (gstelement_class, "ASF muxer",
283       "Codec/Muxer",
284       "Muxes audio and video into an ASF stream",
285       "Thiago Santos <thiagoss@embedded.ufcg.edu.br>");
286
287   GST_DEBUG_CATEGORY_INIT (asfmux_debug, "asfmux", 0, "Muxer for ASF streams");
288 }
289
290 static void
291 gst_asf_mux_init (GstAsfMux * asfmux)
292 {
293   asfmux->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
294   gst_pad_use_fixed_caps (asfmux->srcpad);
295   gst_element_add_pad (GST_ELEMENT (asfmux), asfmux->srcpad);
296
297   asfmux->collect = gst_collect_pads_new ();
298   gst_collect_pads_set_function (asfmux->collect,
299       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_asf_mux_collected),
300       asfmux);
301   gst_collect_pads_set_event_function (asfmux->collect,
302       (GstCollectPadsEventFunction) GST_DEBUG_FUNCPTR (gst_asf_mux_sink_event),
303       asfmux);
304
305   asfmux->payloads = NULL;
306   asfmux->prop_packet_size = DEFAULT_PACKET_SIZE;
307   asfmux->prop_preroll = DEFAULT_PREROLL;
308   asfmux->prop_merge_stream_tags = DEFAULT_MERGE_STREAM_TAGS;
309   asfmux->prop_padding = DEFAULT_PADDING;
310   asfmux->prop_streamable = DEFAULT_STREAMABLE;
311   gst_asf_mux_reset (asfmux);
312 }
313
314 static gboolean
315 gst_asf_mux_sink_event (GstCollectPads * pads, GstCollectData * cdata,
316     GstEvent * event, GstAsfMux * asfmux)
317 {
318   GstAsfPad *asfpad = (GstAsfPad *) cdata;
319   gboolean ret = TRUE;
320
321   switch (GST_EVENT_TYPE (event)) {
322     case GST_EVENT_CAPS:
323     {
324       GstCaps *caps;
325
326       gst_event_parse_caps (event, &caps);
327       if (asfpad->is_audio)
328         ret = gst_asf_mux_audio_set_caps (cdata->pad, caps);
329       else
330         ret = gst_asf_mux_video_set_caps (cdata->pad, caps);
331       gst_event_unref (event);
332       event = NULL;
333       break;
334     }
335     case GST_EVENT_TAG:{
336       GST_DEBUG_OBJECT (asfmux, "received tag event");
337       /* we discard tag events that come after we started
338        * writing the headers, because tags are to be in
339        * the headers
340        */
341       if (asfmux->state == GST_ASF_MUX_STATE_NONE) {
342         GstTagList *list = NULL;
343         gst_event_parse_tag (event, &list);
344         if (asfmux->merge_stream_tags) {
345           GstTagSetter *setter = GST_TAG_SETTER (asfmux);
346           const GstTagMergeMode mode =
347               gst_tag_setter_get_tag_merge_mode (setter);
348           gst_tag_setter_merge_tags (setter, list, mode);
349         } else {
350           if (asfpad->taglist == NULL) {
351             asfpad->taglist = gst_tag_list_new_empty ();
352           }
353           gst_tag_list_insert (asfpad->taglist, list, GST_TAG_MERGE_REPLACE);
354         }
355       }
356       break;
357     }
358     default:
359       break;
360   }
361
362   if (event != NULL)
363     return gst_collect_pads_event_default (pads, cdata, event, FALSE);
364
365   return ret;
366 }
367
368 /**
369  * gst_asf_mux_push_buffer:
370  * @asfmux: #GstAsfMux that should push the buffer
371  * @buf: #GstBuffer to be pushed
372  *
373  * Pushes a buffer downstream and adds its size to the total file size
374  *
375  * Returns: the result of #gst_pad_push on the buffer
376  */
377 static GstFlowReturn
378 gst_asf_mux_push_buffer (GstAsfMux * asfmux, GstBuffer * buf, gsize bufsize)
379 {
380   GstFlowReturn ret;
381
382   ret = gst_pad_push (asfmux->srcpad, buf);
383
384   if (ret == GST_FLOW_OK)
385     asfmux->file_size += bufsize;
386
387   return ret;
388 }
389
390 /**
391  * content_description_calc_size_for_tag:
392  * @taglist: the #GstTagList that contains the tag
393  * @tag: the tag's name
394  * @user_data: a #GstAsfTags struct for putting the results
395  *
396  * Function that has the #GstTagForEach signature and
397  * is used to calculate the size in bytes for each tag
398  * that can be contained in asf's content description object
399  * and extended content description object. This size is added
400  * to the total size for each of that objects in the #GstAsfTags
401  * struct passed in the user_data pointer.
402  */
403 static void
404 content_description_calc_size_for_tag (const GstTagList * taglist,
405     const gchar * tag, gpointer user_data)
406 {
407   const gchar *asftag = gst_asf_get_asf_tag (tag);
408   GValue value = { 0 };
409   guint type;
410   GstAsfTags *asftags = (GstAsfTags *) user_data;
411   guint content_size;
412
413   if (asftag == NULL)
414     return;
415
416   if (!gst_tag_list_copy_value (&value, taglist, tag)) {
417     return;
418   }
419   type = gst_asf_get_tag_field_type (&value);
420   switch (type) {
421     case ASF_TAG_TYPE_UNICODE_STR:
422     {
423       const gchar *text;
424
425       text = g_value_get_string (&value);
426       /* +1 -> because of the \0 at the end
427        * 2* -> because we have uft8, and asf demands utf16
428        */
429       content_size = 2 * (1 + g_utf8_strlen (text, -1));
430
431       if (gst_asf_tag_present_in_content_description (tag)) {
432         asftags->cont_desc_size += content_size;
433       }
434     }
435       break;
436     case ASF_TAG_TYPE_DWORD:
437       content_size = 4;
438       break;
439     default:
440       GST_WARNING ("Unhandled asf tag field type %u for tag %s", type, tag);
441       g_value_reset (&value);
442       return;
443   }
444   if (asftag) {
445     /* size of the tag content in utf16 +
446      * size of the tag name +
447      * 3 uint16 (size of the tag name string,
448      * size of the tag content string and
449      * type of content
450      */
451     asftags->ext_cont_desc_size += content_size +
452         (g_utf8_strlen (asftag, -1) + 1) * 2 + 6;
453   }
454   gst_tag_list_add_value (asftags->tags, GST_TAG_MERGE_REPLACE, tag, &value);
455   g_value_reset (&value);
456 }
457
458 /* FIXME
459  * it is awful to keep track of the size here
460  * and get the same tags in the writing function */
461 /**
462  * gst_asf_mux_get_content_description_tags:
463  * @asfmux: #GstAsfMux to have its tags processed
464  * @asftags: #GstAsfTags to hold the results
465  *
466  * Inspects the tags received by the GstTagSetter interface
467  * or possibly by sink tag events and calculates the total
468  * size needed for the default and extended content description objects.
469  * This results and a copy of the #GstTagList
470  * are stored in the #GstAsfTags. We store a copy so that
471  * the sizes estimated here maintain the same until they are
472  * written to the asf file.
473  */
474 static void
475 gst_asf_mux_get_content_description_tags (GstAsfMux * asfmux,
476     GstAsfTags * asftags)
477 {
478   const GstTagList *tags;
479
480   tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (asfmux));
481   if (tags && !gst_tag_list_is_empty (tags)) {
482     if (asftags->tags != NULL) {
483       gst_tag_list_unref (asftags->tags);
484     }
485     asftags->tags = gst_tag_list_new_empty ();
486     asftags->cont_desc_size = 0;
487     asftags->ext_cont_desc_size = 0;
488
489     GST_DEBUG_OBJECT (asfmux, "Processing tags");
490     gst_tag_list_foreach (tags, content_description_calc_size_for_tag, asftags);
491   } else {
492     GST_DEBUG_OBJECT (asfmux, "No tags received");
493   }
494
495   if (asftags->cont_desc_size > 0) {
496     asftags->cont_desc_size += ASF_CONTENT_DESCRIPTION_OBJECT_SIZE;
497   }
498   if (asftags->ext_cont_desc_size > 0) {
499     asftags->ext_cont_desc_size += ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE;
500   }
501 }
502
503 /**
504  * add_metadata_tag_size:
505  * @taglist: #GstTagList
506  * @tag: tag name
507  * @user_data: pointer to a guint to store the result
508  *
509  * GstTagForeachFunc implementation that accounts the size of
510  * each tag in the taglist and adds them to the guint pointed
511  * by the user_data
512  */
513 static void
514 add_metadata_tag_size (const GstTagList * taglist, const gchar * tag,
515     gpointer user_data)
516 {
517   const gchar *asftag = gst_asf_get_asf_tag (tag);
518   GValue value = { 0 };
519   guint type;
520   guint content_size;
521   guint *total_size = (guint *) user_data;
522
523   if (asftag == NULL)
524     return;
525
526   if (!gst_tag_list_copy_value (&value, taglist, tag)) {
527     return;
528   }
529   type = gst_asf_get_tag_field_type (&value);
530   switch (type) {
531     case ASF_TAG_TYPE_UNICODE_STR:
532     {
533       const gchar *text;
534
535       text = g_value_get_string (&value);
536       /* +1 -> because of the \0 at the end
537        * 2* -> because we have uft8, and asf demands utf16
538        */
539       content_size = 2 * (1 + g_utf8_strlen (text, -1));
540     }
541       break;
542     case ASF_TAG_TYPE_DWORD:
543       content_size = 4;
544       break;
545     default:
546       GST_WARNING ("Unhandled asf tag field type %u for tag %s", type, tag);
547       g_value_reset (&value);
548       return;
549   }
550   /* size of reserved (2) +
551    * size of stream number (2) +
552    * size of the tag content in utf16 +
553    * size of the tag name +
554    * 2 uint16 (size of the tag name string and type of content) +
555    * 1 uint32 (size of the data)
556    */
557   *total_size +=
558       4 + content_size + (g_utf8_strlen (asftag, -1) + 1) * 2 + 4 + 4;
559   g_value_reset (&value);
560 }
561
562 /**
563  * gst_asf_mux_get_metadata_object_size:
564  * @asfmux: #GstAsfMux
565  * @asfpad: pad for which the metadata object size should be calculated
566  *
567  * Calculates the size of the metadata object for the tags of the stream
568  * handled by the asfpad in the parameter
569  *
570  * Returns: The size calculated
571  */
572 static guint
573 gst_asf_mux_get_metadata_object_size (GstAsfMux * asfmux, GstAsfPad * asfpad)
574 {
575   guint size = ASF_METADATA_OBJECT_SIZE;
576   if (asfpad->taglist == NULL || gst_tag_list_is_empty (asfpad->taglist))
577     return 0;
578
579   gst_tag_list_foreach (asfpad->taglist, add_metadata_tag_size, &size);
580   return size;
581 }
582
583 /**
584  * gst_asf_mux_get_headers_size:
585  * @asfmux: #GstAsfMux
586  *
587  * Calculates the size of the headers of the asf stream
588  * to be generated by this #GstAsfMux.
589  * Its used for determining the size of the buffer to allocate
590  * to exactly fit the headers in.
591  * Padding and metadata objects sizes are not included.
592  *
593  * Returns: the calculated size
594  */
595 static guint
596 gst_asf_mux_get_headers_size (GstAsfMux * asfmux)
597 {
598   GSList *walk;
599   gint stream_num = 0;
600   guint size = ASF_HEADER_OBJECT_SIZE +
601       ASF_FILE_PROPERTIES_OBJECT_SIZE + ASF_HEADER_EXTENSION_OBJECT_SIZE;
602
603   /* per stream data */
604   for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) {
605     GstAsfPad *asfpad = (GstAsfPad *) walk->data;
606
607     if (asfpad->is_audio)
608       size += ASF_AUDIO_SPECIFIC_DATA_SIZE;
609     else
610       size += ASF_VIDEO_SPECIFIC_DATA_SIZE;
611
612     if (asfpad->codec_data)
613       size += gst_buffer_get_size (asfpad->codec_data);
614
615     stream_num++;
616   }
617   size += stream_num * (ASF_STREAM_PROPERTIES_OBJECT_SIZE +
618       ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE);
619
620   return size;
621 }
622
623 /**
624  * gst_asf_mux_write_header_object:
625  * @asfmux:
626  * @buf: pointer to the data pointer
627  * @size: size of the header object
628  * @child_objects: number of children objects inside the main header object
629  *
630  * Writes the main asf header object start. The buffer pointer
631  * is incremented to the next writing position.
632  */
633 static void
634 gst_asf_mux_write_header_object (GstAsfMux * asfmux, guint8 ** buf,
635     guint64 size, guint32 child_objects)
636 {
637   gst_asf_put_guid (*buf, guids[ASF_HEADER_OBJECT_INDEX]);
638   GST_WRITE_UINT64_LE (*buf + 16, size);        /* object size */
639   GST_WRITE_UINT32_LE (*buf + 24, child_objects);       /* # of child objects */
640   GST_WRITE_UINT8 (*buf + 28, 0x01);    /* reserved */
641   GST_WRITE_UINT8 (*buf + 29, 0x02);    /* reserved */
642   *buf += ASF_HEADER_OBJECT_SIZE;
643 }
644
645 /**
646  * gst_asf_mux_write_file_properties:
647  * @asfmux:
648  * @buf: pointer to the data pointer
649  *
650  * Writes the file properties object to the buffer. The buffer pointer
651  * is incremented to the next writing position.
652  */
653 static void
654 gst_asf_mux_write_file_properties (GstAsfMux * asfmux, guint8 ** buf)
655 {
656   gst_asf_put_guid (*buf, guids[ASF_FILE_PROPERTIES_OBJECT_INDEX]);
657   GST_WRITE_UINT64_LE (*buf + 16, ASF_FILE_PROPERTIES_OBJECT_SIZE);     /* object size */
658   gst_asf_put_guid (*buf + 24, asfmux->file_id);
659   GST_WRITE_UINT64_LE (*buf + 40, 0);   /* file size - needs update */
660   gst_asf_put_time (*buf + 48, gst_asf_get_current_time ());    /* creation time */
661   GST_WRITE_UINT64_LE (*buf + 56, 0);   /* data packets - needs update */
662   GST_WRITE_UINT64_LE (*buf + 64, 0);   /* play duration - needs update */
663   GST_WRITE_UINT64_LE (*buf + 72, 0);   /* send duration - needs update */
664   GST_WRITE_UINT64_LE (*buf + 80, asfmux->preroll);     /* preroll */
665   GST_WRITE_UINT32_LE (*buf + 88, 0x1); /* flags - broadcast on */
666   GST_WRITE_UINT32_LE (*buf + 92, asfmux->packet_size); /* minimum data packet size */
667   GST_WRITE_UINT32_LE (*buf + 96, asfmux->packet_size); /* maximum data packet size */
668   GST_WRITE_UINT32_LE (*buf + 100, 0);  /* maximum bitrate TODO */
669
670   *buf += ASF_FILE_PROPERTIES_OBJECT_SIZE;
671 }
672
673 /**
674  * gst_asf_mux_write_stream_properties:
675  * @asfmux:
676  * @buf: pointer to the data pointer
677  * @asfpad: Pad that handles the stream
678  *
679  * Writes the stream properties object in the buffer
680  * for the stream handled by the #GstAsfPad passed.
681  * The pointer is incremented to the next writing position
682  */
683 static void
684 gst_asf_mux_write_stream_properties (GstAsfMux * asfmux, guint8 ** buf,
685     GstAsfPad * asfpad)
686 {
687   guint32 codec_data_length = 0;
688   guint32 media_specific_data_length = 0;
689   guint16 flags = 0;
690
691   /* codec specific data length */
692   if (asfpad->codec_data)
693     codec_data_length = gst_buffer_get_size (asfpad->codec_data);
694   if (asfpad->is_audio)
695     media_specific_data_length = ASF_AUDIO_SPECIFIC_DATA_SIZE;
696   else
697     media_specific_data_length = ASF_VIDEO_SPECIFIC_DATA_SIZE;
698
699   GST_DEBUG_OBJECT (asfmux, "Stream %" G_GUINT16_FORMAT " codec data length: %"
700       G_GUINT32_FORMAT ", media specific data length: %" G_GUINT32_FORMAT,
701       (guint16) asfpad->stream_number, codec_data_length,
702       media_specific_data_length);
703
704   gst_asf_put_guid (*buf, guids[ASF_STREAM_PROPERTIES_OBJECT_INDEX]);
705   GST_WRITE_UINT64_LE (*buf + 16, ASF_STREAM_PROPERTIES_OBJECT_SIZE + codec_data_length + media_specific_data_length);  /* object size */
706
707   /* stream type */
708   if (asfpad->is_audio)
709     gst_asf_put_guid (*buf + 24, guids[ASF_AUDIO_MEDIA_INDEX]);
710   else
711     gst_asf_put_guid (*buf + 24, guids[ASF_VIDEO_MEDIA_INDEX]);
712   /* error correction */
713   gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]);
714   GST_WRITE_UINT64_LE (*buf + 56, 0);   /* time offset */
715
716   GST_WRITE_UINT32_LE (*buf + 64, codec_data_length + media_specific_data_length);      /* type specific data length */
717   GST_WRITE_UINT32_LE (*buf + 68, 0);   /* error correction data length */
718
719   flags = (asfpad->stream_number & 0x7F);
720   GST_WRITE_UINT16_LE (*buf + 72, flags);
721   GST_WRITE_UINT32_LE (*buf + 74, 0);   /* reserved */
722
723   *buf += ASF_STREAM_PROPERTIES_OBJECT_SIZE;
724   /* audio specific data */
725   if (asfpad->is_audio) {
726     GstAsfAudioPad *audiopad = (GstAsfAudioPad *) asfpad;
727     GST_WRITE_UINT16_LE (*buf, audiopad->audioinfo.format);
728     GST_WRITE_UINT16_LE (*buf + 2, audiopad->audioinfo.channels);
729     GST_WRITE_UINT32_LE (*buf + 4, audiopad->audioinfo.rate);
730     GST_WRITE_UINT32_LE (*buf + 8, audiopad->audioinfo.av_bps);
731     GST_WRITE_UINT16_LE (*buf + 12, audiopad->audioinfo.blockalign);
732     GST_WRITE_UINT16_LE (*buf + 14, audiopad->audioinfo.bits_per_sample);
733     GST_WRITE_UINT16_LE (*buf + 16, codec_data_length);
734
735     GST_DEBUG_OBJECT (asfmux,
736         "wave formatex values: codec_id=%" G_GUINT16_FORMAT ", channels=%"
737         G_GUINT16_FORMAT ", rate=%" G_GUINT32_FORMAT ", bytes_per_sec=%"
738         G_GUINT32_FORMAT ", block_alignment=%" G_GUINT16_FORMAT
739         ", bits_per_sample=%" G_GUINT16_FORMAT ", codec_data_length=%u",
740         audiopad->audioinfo.format, audiopad->audioinfo.channels,
741         audiopad->audioinfo.rate, audiopad->audioinfo.av_bps,
742         audiopad->audioinfo.blockalign, audiopad->audioinfo.bits_per_sample,
743         codec_data_length);
744
745
746     *buf += ASF_AUDIO_SPECIFIC_DATA_SIZE;
747   } else {
748     GstAsfVideoPad *videopad = (GstAsfVideoPad *) asfpad;
749     GST_WRITE_UINT32_LE (*buf, (guint32) videopad->vidinfo.width);
750     GST_WRITE_UINT32_LE (*buf + 4, (guint32) videopad->vidinfo.height);
751     GST_WRITE_UINT8 (*buf + 8, 2);
752
753     /* the BITMAPINFOHEADER size + codec_data size */
754     GST_WRITE_UINT16_LE (*buf + 9,
755         ASF_VIDEO_SPECIFIC_DATA_SIZE + codec_data_length - 11);
756
757     /* BITMAPINFOHEADER */
758     GST_WRITE_UINT32_LE (*buf + 11,
759         ASF_VIDEO_SPECIFIC_DATA_SIZE + codec_data_length - 11);
760     gst_asf_put_i32 (*buf + 15, videopad->vidinfo.width);
761     gst_asf_put_i32 (*buf + 19, videopad->vidinfo.height);
762     GST_WRITE_UINT16_LE (*buf + 23, 1); /* reserved */
763     GST_WRITE_UINT16_LE (*buf + 25, videopad->vidinfo.bit_cnt);
764     GST_WRITE_UINT32_LE (*buf + 27, videopad->vidinfo.compression);
765     GST_WRITE_UINT32_LE (*buf + 31, videopad->vidinfo.width *
766         videopad->vidinfo.height * videopad->vidinfo.bit_cnt);
767     GST_WRITE_UINT32_LE (*buf + 35, videopad->vidinfo.xpels_meter);
768     GST_WRITE_UINT32_LE (*buf + 39, videopad->vidinfo.ypels_meter);
769     GST_WRITE_UINT32_LE (*buf + 43, videopad->vidinfo.num_colors);
770     GST_WRITE_UINT32_LE (*buf + 47, videopad->vidinfo.imp_colors);
771
772     *buf += ASF_VIDEO_SPECIFIC_DATA_SIZE;
773   }
774
775   if (codec_data_length > 0)
776     gst_buffer_extract (asfpad->codec_data, 0, *buf, codec_data_length);
777
778   *buf += codec_data_length;
779 }
780
781 /**
782  * gst_asf_mux_write_header_extension:
783  * @asfmux:
784  * @buf: pointer to the buffer pointer
785  * @extension_size: size of the extensions
786  *
787  * Writes the header of the header extension object. The buffer pointer
788  * is incremented to the next writing position (the  header extension object
789  * childs should be written from that point)
790  */
791 static void
792 gst_asf_mux_write_header_extension (GstAsfMux * asfmux, guint8 ** buf,
793     guint64 extension_size)
794 {
795   gst_asf_put_guid (*buf, guids[ASF_HEADER_EXTENSION_OBJECT_INDEX]);
796   GST_WRITE_UINT64_LE (*buf + 16, ASF_HEADER_EXTENSION_OBJECT_SIZE + extension_size);   /* object size */
797   gst_asf_put_guid (*buf + 24, guids[ASF_RESERVED_1_INDEX]);    /* reserved */
798   GST_WRITE_UINT16_LE (*buf + 40, 6);   /* reserved */
799   GST_WRITE_UINT32_LE (*buf + 42, extension_size);      /* header extension data size */
800   *buf += ASF_HEADER_EXTENSION_OBJECT_SIZE;
801 }
802
803 /**
804  * gst_asf_mux_write_extended_stream_properties:
805  * @asfmux:
806  * @buf: pointer to the buffer pointer
807  * @asfpad: Pad that handles the stream of the properties to be written
808  *
809  * Writes the extended stream properties object (that is part of the
810  * header extension objects) for the stream handled by asfpad
811  */
812 static void
813 gst_asf_mux_write_extended_stream_properties (GstAsfMux * asfmux, guint8 ** buf,
814     GstAsfPad * asfpad)
815 {
816   gst_asf_put_guid (*buf, guids[ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_INDEX]);
817   GST_WRITE_UINT64_LE (*buf + 16, ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE);
818   GST_WRITE_UINT64_LE (*buf + 24, 0);   /* start time */
819   GST_WRITE_UINT64_LE (*buf + 32, 0);   /* end time */
820   GST_WRITE_UINT32_LE (*buf + 40, asfpad->bitrate);     /* bitrate */
821   GST_WRITE_UINT32_LE (*buf + 44, 0);   /* buffer size */
822   GST_WRITE_UINT32_LE (*buf + 48, 0);   /* initial buffer fullness */
823   GST_WRITE_UINT32_LE (*buf + 52, asfpad->bitrate);     /* alternate data bitrate */
824   GST_WRITE_UINT32_LE (*buf + 56, 0);   /* alternate buffer size */
825   GST_WRITE_UINT32_LE (*buf + 60, 0);   /* alternate initial buffer fullness */
826   GST_WRITE_UINT32_LE (*buf + 64, 0);   /* maximum object size */
827
828   /* flags */
829   if (asfpad->is_audio) {
830     /* TODO check if audio is seekable */
831     GST_WRITE_UINT32_LE (*buf + 68, 0x0);
832   } else {
833     /* video has indexes, so it is seekable unless we are streaming */
834     if (asfmux->prop_streamable)
835       GST_WRITE_UINT32_LE (*buf + 68, 0x0);
836     else
837       GST_WRITE_UINT32_LE (*buf + 68, 0x2);
838   }
839
840   GST_WRITE_UINT16_LE (*buf + 72, asfpad->stream_number);
841   GST_WRITE_UINT16_LE (*buf + 74, 0);   /* language index */
842   GST_WRITE_UINT64_LE (*buf + 76, 0);   /* avg time per frame */
843   GST_WRITE_UINT16_LE (*buf + 84, 0);   /* stream name count */
844   GST_WRITE_UINT16_LE (*buf + 86, 0);   /* payload extension count */
845
846   *buf += ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE;
847 }
848
849 /**
850  * gst_asf_mux_write_string_with_size:
851  * @asfmux:
852  * @size_buf: pointer to the memory position to write the size of the string
853  * @str_buf: pointer to the memory position to write the string
854  * @str: the string to be written (in UTF-8)
855  * @use32: if the string size should be written with 32 bits (if true)
856  * or with 16 (if false)
857  *
858  * Writes a string with its size as it is needed in many asf objects.
859  * The size is written to size_buf as a WORD field if use32 is false, and
860  * as a DWORD if use32 is true. The string is written to str_buf in UTF16-LE.
861  * The string should be passed in UTF-8.
862  *
863  * The string size in UTF16-LE is returned.
864  */
865 static guint64
866 gst_asf_mux_write_string_with_size (GstAsfMux * asfmux,
867     guint8 * size_buf, guint8 * str_buf, const gchar * str, gboolean use32)
868 {
869   GError *error = NULL;
870   gsize str_size = 0;
871   gchar *str_utf16 = NULL;
872
873   GST_LOG_OBJECT (asfmux, "Writing extended content description string: "
874       "%s", str);
875
876   /*
877    * Covert the string to utf16
878    * Also force the last bytes to null terminated,
879    * tags were with extra weird characters without it.
880    */
881   str_utf16 = g_convert (str, -1, "UTF-16LE", "UTF-8", NULL, &str_size, &error);
882
883   /* sum up the null terminating char */
884   str_size += 2;
885
886   if (use32)
887     GST_WRITE_UINT32_LE (size_buf, str_size);
888   else
889     GST_WRITE_UINT16_LE (size_buf, str_size);
890   if (error) {
891     GST_WARNING_OBJECT (asfmux, "Error converting string "
892         "to UTF-16: %s - %s", str, error->message);
893     g_error_free (error);
894     memset (str_buf, 0, str_size);
895   } else {
896     /* HACK: g_convert seems to add only a single byte null char to
897      * the end of the stream, we force the second one */
898     memcpy (str_buf, str_utf16, str_size - 1);
899     str_buf[str_size - 1] = 0;
900   }
901   g_free (str_utf16);
902   return str_size;
903 }
904
905 /**
906  * gst_asf_mux_write_content_description_entry:
907  * @asfmux:
908  * @tags:
909  * @tagname:
910  * @size_buf:
911  * @data_buf:
912  *
913  * Checks if a string tag with tagname exists in the taglist. If it
914  * exists it is written as an UTF-16LE to data_buf and its size in bytes
915  * is written to size_buf. It is used for writing content description
916  * object fields.
917  *
918  * Returns: the size of the string
919  */
920 static guint16
921 gst_asf_mux_write_content_description_entry (GstAsfMux * asfmux,
922     const GstTagList * tags, const gchar * tagname,
923     guint8 * size_buf, guint8 * data_buf)
924 {
925   gchar *text = NULL;
926   guint16 text_size = 0;
927   if (gst_tag_list_get_string (tags, tagname, &text)) {
928     text_size = gst_asf_mux_write_string_with_size (asfmux, size_buf,
929         data_buf, text, FALSE);
930     g_free (text);
931   } else {
932     GST_WRITE_UINT16_LE (size_buf, 0);
933   }
934   return text_size;
935 }
936
937 static guint64
938 gst_asf_mux_write_ext_content_description_dword_entry (GstAsfMux * asfmux,
939     guint8 * buf, const gchar * asf_tag, const guint32 value)
940 {
941   guint64 tag_size;
942   GST_DEBUG_OBJECT (asfmux, "Writing extended content description tag: "
943       "%s (%u)", asf_tag, value);
944
945   tag_size = gst_asf_mux_write_string_with_size (asfmux, buf, buf + 2,
946       asf_tag, FALSE);
947   buf += tag_size + 2;
948   GST_WRITE_UINT16_LE (buf, ASF_TAG_TYPE_DWORD);
949   GST_WRITE_UINT16_LE (buf + 2, 4);
950   GST_WRITE_UINT32_LE (buf + 4, value);
951
952   /* tagsize -> string size
953    * 2 -> string size field size
954    * 4 -> dword entry
955    * 4 -> type of entry + entry size
956    */
957   return tag_size + 2 + 4 + 4;
958 }
959
960 static guint64
961 gst_asf_mux_write_ext_content_description_string_entry (GstAsfMux * asfmux,
962     guint8 * buf, const gchar * asf_tag, const gchar * text)
963 {
964   guint64 tag_size = 0;
965   guint64 text_size = 0;
966
967   GST_DEBUG_OBJECT (asfmux, "Writing extended content description tag: "
968       "%s (%s)", asf_tag, text);
969
970   tag_size = gst_asf_mux_write_string_with_size (asfmux,
971       buf, buf + 2, asf_tag, FALSE);
972   GST_WRITE_UINT16_LE (buf + tag_size + 2, ASF_TAG_TYPE_UNICODE_STR);
973   buf += tag_size + 2 + 2;
974   text_size = gst_asf_mux_write_string_with_size (asfmux,
975       buf, buf + 2, text, FALSE);
976
977   /* the size of the strings in utf16-le plus the 3 WORD fields */
978   return tag_size + text_size + 6;
979 }
980
981 static void
982 gst_asf_mux_write_content_description (GstAsfMux * asfmux, guint8 ** buf,
983     const GstTagList * tags)
984 {
985   guint8 *values = (*buf) + ASF_CONTENT_DESCRIPTION_OBJECT_SIZE;
986   guint64 size = 0;
987
988   GST_DEBUG_OBJECT (asfmux, "Writing content description object");
989
990   gst_asf_put_guid (*buf, guids[ASF_CONTENT_DESCRIPTION_INDEX]);
991
992   values += gst_asf_mux_write_content_description_entry (asfmux, tags,
993       GST_TAG_TITLE, *buf + 24, values);
994   values += gst_asf_mux_write_content_description_entry (asfmux, tags,
995       GST_TAG_ARTIST, *buf + 26, values);
996   values += gst_asf_mux_write_content_description_entry (asfmux, tags,
997       GST_TAG_COPYRIGHT, *buf + 28, values);
998   values += gst_asf_mux_write_content_description_entry (asfmux, tags,
999       GST_TAG_DESCRIPTION, *buf + 30, values);
1000
1001   /* rating is currently not present in gstreamer tags, so we put 0 */
1002   GST_WRITE_UINT16_LE (*buf + 32, 0);
1003
1004   size += values - *buf;
1005   GST_WRITE_UINT64_LE (*buf + 16, size);
1006   *buf += size;
1007 }
1008
1009 static void
1010 write_ext_content_description_tag (const GstTagList * taglist,
1011     const gchar * tag, gpointer user_data)
1012 {
1013   const gchar *asftag = gst_asf_get_asf_tag (tag);
1014   GValue value = { 0 };
1015   guint type;
1016   GstAsfExtContDescData *data = (GstAsfExtContDescData *) user_data;
1017
1018   if (asftag == NULL)
1019     return;
1020
1021   if (!gst_tag_list_copy_value (&value, taglist, tag)) {
1022     return;
1023   }
1024
1025   type = gst_asf_get_tag_field_type (&value);
1026   switch (type) {
1027     case ASF_TAG_TYPE_UNICODE_STR:
1028     {
1029       const gchar *text;
1030       text = g_value_get_string (&value);
1031       data->size +=
1032           gst_asf_mux_write_ext_content_description_string_entry (data->asfmux,
1033           data->buf + data->size, asftag, text);
1034     }
1035       break;
1036     case ASF_TAG_TYPE_DWORD:
1037     {
1038       guint num = g_value_get_uint (&value);
1039       data->size +=
1040           gst_asf_mux_write_ext_content_description_dword_entry (data->asfmux,
1041           data->buf + data->size, asftag, num);
1042     }
1043       break;
1044     default:
1045       GST_WARNING_OBJECT (data->asfmux,
1046           "Unhandled asf tag field type %u for tag %s", type, tag);
1047       g_value_reset (&value);
1048       return;
1049   }
1050   data->count++;
1051   g_value_reset (&value);
1052 }
1053
1054 static void
1055 gst_asf_mux_write_ext_content_description (GstAsfMux * asfmux, guint8 ** buf,
1056     GstTagList * tags)
1057 {
1058   GstAsfExtContDescData extContDesc;
1059   extContDesc.asfmux = asfmux;
1060   extContDesc.buf = *buf;
1061   extContDesc.count = 0;
1062   extContDesc.size = ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE;
1063
1064   GST_DEBUG_OBJECT (asfmux, "Writing extended content description object");
1065   gst_asf_put_guid (*buf, guids[ASF_EXT_CONTENT_DESCRIPTION_INDEX]);
1066
1067   gst_tag_list_foreach (tags, write_ext_content_description_tag, &extContDesc);
1068
1069   GST_WRITE_UINT64_LE (*buf + 16, extContDesc.size);
1070   GST_WRITE_UINT16_LE (*buf + 24, extContDesc.count);
1071
1072   *buf += extContDesc.size;
1073 }
1074
1075 static void
1076 write_metadata_tag (const GstTagList * taglist, const gchar * tag,
1077     gpointer user_data)
1078 {
1079   const gchar *asftag = gst_asf_get_asf_tag (tag);
1080   GValue value = { 0 };
1081   guint type;
1082   GstAsfMetadataObjData *data = (GstAsfMetadataObjData *) user_data;
1083   guint16 tag_size;
1084   guint32 content_size;
1085
1086   if (asftag == NULL)
1087     return;
1088
1089   if (!gst_tag_list_copy_value (&value, taglist, tag)) {
1090     return;
1091   }
1092
1093   type = gst_asf_get_tag_field_type (&value);
1094   switch (type) {
1095     case ASF_TAG_TYPE_UNICODE_STR:
1096     {
1097       const gchar *text;
1098       text = g_value_get_string (&value);
1099       GST_WRITE_UINT16_LE (data->buf + data->size, 0);
1100       GST_WRITE_UINT16_LE (data->buf + data->size + 2, data->stream_num);
1101       data->size += 4;
1102
1103       tag_size = gst_asf_mux_write_string_with_size (data->asfmux,
1104           data->buf + data->size, data->buf + data->size + 8, asftag, FALSE);
1105       data->size += 2;
1106
1107       GST_WRITE_UINT16_LE (data->buf + data->size, type);
1108       data->size += 2;
1109
1110       content_size = gst_asf_mux_write_string_with_size (data->asfmux,
1111           data->buf + data->size, data->buf + data->size + tag_size + 4, text,
1112           TRUE);
1113       data->size += tag_size + content_size + 4;
1114     }
1115       break;
1116     case ASF_TAG_TYPE_DWORD:
1117     {
1118       guint num = g_value_get_uint (&value);
1119       GST_WRITE_UINT16_LE (data->buf + data->size, 0);
1120       GST_WRITE_UINT16_LE (data->buf + data->size + 2, data->stream_num);
1121       data->size += 4;
1122
1123       tag_size = gst_asf_mux_write_string_with_size (data->asfmux,
1124           data->buf + data->size, data->buf + data->size + 8, asftag, FALSE);
1125       data->size += 2;
1126
1127       GST_WRITE_UINT16_LE (data->buf + data->size, type);
1128       data->size += 2;
1129       /* dword length */
1130       GST_WRITE_UINT32_LE (data->buf + data->size, 4);
1131       data->size += 4 + tag_size;
1132
1133       GST_WRITE_UINT32_LE (data->buf + data->size, num);
1134       data->size += 4;
1135     }
1136       break;
1137     default:
1138       GST_WARNING_OBJECT (data->asfmux,
1139           "Unhandled asf tag field type %u for tag %s", type, tag);
1140       g_value_reset (&value);
1141       return;
1142   }
1143
1144   data->count++;
1145   g_value_reset (&value);
1146 }
1147
1148 static void
1149 gst_asf_mux_write_metadata_object (GstAsfMux * asfmux, guint8 ** buf,
1150     GstAsfPad * asfpad)
1151 {
1152   GstAsfMetadataObjData metaObjData;
1153   metaObjData.asfmux = asfmux;
1154   metaObjData.buf = *buf;
1155   metaObjData.count = 0;
1156   metaObjData.size = ASF_METADATA_OBJECT_SIZE;
1157   metaObjData.stream_num = asfpad->stream_number;
1158
1159   if (asfpad->taglist == NULL || gst_tag_list_is_empty (asfpad->taglist))
1160     return;
1161
1162   GST_DEBUG_OBJECT (asfmux, "Writing metadata object");
1163   gst_asf_put_guid (*buf, guids[ASF_METADATA_OBJECT_INDEX]);
1164
1165   gst_tag_list_foreach (asfpad->taglist, write_metadata_tag, &metaObjData);
1166
1167   GST_WRITE_UINT64_LE (*buf + 16, metaObjData.size);
1168   GST_WRITE_UINT16_LE (*buf + 24, metaObjData.count);
1169
1170   *buf += metaObjData.size;
1171 }
1172
1173 static void
1174 gst_asf_mux_write_padding_object (GstAsfMux * asfmux, guint8 ** buf,
1175     guint64 padding)
1176 {
1177   if (padding < ASF_PADDING_OBJECT_SIZE) {
1178     return;
1179   }
1180
1181   GST_DEBUG_OBJECT (asfmux, "Writing padding object of size %" G_GUINT64_FORMAT,
1182       padding);
1183   gst_asf_put_guid (*buf, guids[ASF_PADDING_OBJECT_INDEX]);
1184   GST_WRITE_UINT64_LE (*buf + 16, padding);
1185   memset (*buf + 24, 0, padding - ASF_PADDING_OBJECT_SIZE);
1186   *buf += padding;
1187 }
1188
1189 static void
1190 gst_asf_mux_write_data_object (GstAsfMux * asfmux, guint8 ** buf)
1191 {
1192   gst_asf_put_guid (*buf, guids[ASF_DATA_OBJECT_INDEX]);
1193
1194   /* Data object size. This is always >= ASF_DATA_OBJECT_SIZE. The standard
1195    * specifically accepts the value 0 in live streams, but WMP is not accepting
1196    * this while streaming using WMSP, so we default to minimum size also for
1197    * live streams. Otherwise this field must be updated later on when we know
1198    * the complete stream size.
1199    */
1200   GST_WRITE_UINT64_LE (*buf + 16, ASF_DATA_OBJECT_SIZE);
1201
1202   gst_asf_put_guid (*buf + 24, asfmux->file_id);
1203   GST_WRITE_UINT64_LE (*buf + 40, 0);   /* total data packets */
1204   GST_WRITE_UINT16_LE (*buf + 48, 0x0101);      /* reserved */
1205   *buf += ASF_DATA_OBJECT_SIZE;
1206 }
1207
1208 static void
1209 gst_asf_mux_put_buffer_in_streamheader (GValue * streamheader,
1210     GstBuffer * buffer)
1211 {
1212   GValue value = { 0 };
1213   GstBuffer *buf;
1214
1215   g_value_init (&value, GST_TYPE_BUFFER);
1216   buf = gst_buffer_copy (buffer);
1217   gst_value_set_buffer (&value, buf);
1218   gst_buffer_unref (buf);
1219   gst_value_array_append_value (streamheader, &value);
1220   g_value_unset (&value);
1221 }
1222
1223 static guint
1224 gst_asf_mux_find_payload_parsing_info_size (GstAsfMux * asfmux)
1225 {
1226   /* Minimum payload parsing information size is 8 bytes */
1227   guint size = 8;
1228
1229   if (asfmux->prop_packet_size > 65535)
1230     size += 4;
1231   else
1232     size += 2;
1233
1234   if (asfmux->prop_padding > 65535)
1235     size += 4;
1236   else
1237     size += 2;
1238
1239   return size;
1240 }
1241
1242 /**
1243  * gst_asf_mux_start_file:
1244  * @asfmux: #GstAsfMux
1245  *
1246  * Starts the asf file/stream by creating and pushing
1247  * the headers downstream.
1248  */
1249 static GstFlowReturn
1250 gst_asf_mux_start_file (GstAsfMux * asfmux)
1251 {
1252   GstBuffer *buf = NULL;
1253   guint8 *bufdata = NULL;
1254   GSList *walk;
1255   guint stream_num = g_slist_length (asfmux->collect->data);
1256   guint metadata_obj_size = 0;
1257   GstAsfTags *asftags;
1258   GValue streamheader = { 0 };
1259   GstCaps *caps;
1260   GstStructure *structure;
1261   guint64 padding = asfmux->prop_padding;
1262   GstSegment segment;
1263   GstMapInfo map;
1264   gsize bufsize;
1265   gchar s_id[32];
1266
1267   if (padding < ASF_PADDING_OBJECT_SIZE)
1268     padding = 0;
1269
1270   /* if not streaming, check if downstream is seekable */
1271   if (!asfmux->prop_streamable) {
1272     gboolean seekable;
1273     GstQuery *query;
1274
1275     query = gst_query_new_seeking (GST_FORMAT_BYTES);
1276     if (gst_pad_peer_query (asfmux->srcpad, query)) {
1277       gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
1278       GST_INFO_OBJECT (asfmux, "downstream is %sseekable",
1279           seekable ? "" : "not ");
1280     } else {
1281       /* assume seeking is not supported if query not handled downstream */
1282       GST_WARNING_OBJECT (asfmux, "downstream did not handle seeking query");
1283       seekable = FALSE;
1284     }
1285     if (!seekable) {
1286       asfmux->prop_streamable = TRUE;
1287       g_object_notify (G_OBJECT (asfmux), "streamable");
1288       GST_WARNING_OBJECT (asfmux, "downstream is not seekable, but "
1289           "streamable=false. Will ignore that and create streamable output "
1290           "instead");
1291     }
1292     gst_query_unref (query);
1293   }
1294
1295   /* from this point we started writing the headers */
1296   GST_INFO_OBJECT (asfmux, "Writing headers");
1297   asfmux->state = GST_ASF_MUX_STATE_HEADERS;
1298
1299   /* stream-start (FIXME: create id based on input ids) */
1300   g_snprintf (s_id, sizeof (s_id), "asfmux-%08x", g_random_int ());
1301   gst_pad_push_event (asfmux->srcpad, gst_event_new_stream_start (s_id));
1302
1303   caps = gst_pad_get_pad_template_caps (asfmux->srcpad);
1304   gst_pad_set_caps (asfmux->srcpad, caps);
1305   gst_caps_unref (caps);
1306
1307   /* send a BYTE format segment if we're going to seek to fix up the headers
1308    * later, otherwise send a TIME segment */
1309   if (asfmux->prop_streamable)
1310     gst_segment_init (&segment, GST_FORMAT_TIME);
1311   else
1312     gst_segment_init (&segment, GST_FORMAT_BYTES);
1313   gst_pad_push_event (asfmux->srcpad, gst_event_new_segment (&segment));
1314
1315   gst_asf_generate_file_id (&asfmux->file_id);
1316
1317   /* Get the metadata for content description object.
1318    * We store our own taglist because it might get changed from now
1319    * to the time we actually add its contents to the file, changing
1320    * the size of the data we already calculated here.
1321    */
1322   asftags = g_new0 (GstAsfTags, 1);
1323   gst_asf_mux_get_content_description_tags (asfmux, asftags);
1324
1325   /* get the total metadata objects size */
1326   for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) {
1327     metadata_obj_size += gst_asf_mux_get_metadata_object_size (asfmux,
1328         (GstAsfPad *) walk->data);
1329   }
1330
1331   /* alloc a buffer for all header objects */
1332   buf = gst_buffer_new_and_alloc (gst_asf_mux_get_headers_size (asfmux) +
1333       asftags->cont_desc_size +
1334       asftags->ext_cont_desc_size +
1335       metadata_obj_size + padding + ASF_DATA_OBJECT_SIZE);
1336
1337   gst_buffer_map (buf, &map, GST_MAP_WRITE);
1338   bufdata = map.data;
1339   bufsize = map.size;
1340
1341   gst_asf_mux_write_header_object (asfmux, &bufdata, map.size -
1342       ASF_DATA_OBJECT_SIZE, 2 + stream_num);
1343
1344   /* get the position of the file properties object for
1345    * updating it in gst_asf_mux_stop_file */
1346   asfmux->file_properties_object_position = bufdata - map.data;
1347   gst_asf_mux_write_file_properties (asfmux, &bufdata);
1348
1349   for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) {
1350     gst_asf_mux_write_stream_properties (asfmux, &bufdata,
1351         (GstAsfPad *) walk->data);
1352   }
1353
1354   if (asftags->cont_desc_size) {
1355     gst_asf_mux_write_content_description (asfmux, &bufdata, asftags->tags);
1356   }
1357   if (asftags->ext_cont_desc_size) {
1358     gst_asf_mux_write_ext_content_description (asfmux, &bufdata, asftags->tags);
1359   }
1360
1361   if (asftags->tags)
1362     gst_tag_list_unref (asftags->tags);
1363   g_free (asftags);
1364
1365   /* writing header extension objects */
1366   gst_asf_mux_write_header_extension (asfmux, &bufdata, stream_num *
1367       ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE + metadata_obj_size);
1368   for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) {
1369     gst_asf_mux_write_extended_stream_properties (asfmux, &bufdata,
1370         (GstAsfPad *) walk->data);
1371   }
1372   for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) {
1373     gst_asf_mux_write_metadata_object (asfmux, &bufdata,
1374         (GstAsfPad *) walk->data);
1375   }
1376
1377   gst_asf_mux_write_padding_object (asfmux, &bufdata, padding);
1378
1379   /* store data object position for later updating some fields */
1380   asfmux->data_object_position = bufdata - map.data;
1381   gst_asf_mux_write_data_object (asfmux, &bufdata);
1382
1383   /* set streamheader in source pad if 'streamable' */
1384   if (asfmux->prop_streamable) {
1385     g_value_init (&streamheader, GST_TYPE_ARRAY);
1386     gst_asf_mux_put_buffer_in_streamheader (&streamheader, buf);
1387
1388     caps = gst_pad_get_current_caps (asfmux->srcpad);
1389     caps = gst_caps_make_writable (caps);
1390     structure = gst_caps_get_structure (caps, 0);
1391     gst_structure_set_value (structure, "streamheader", &streamheader);
1392     gst_pad_set_caps (asfmux->srcpad, caps);
1393     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
1394     g_value_unset (&streamheader);
1395     gst_caps_unref (caps);
1396   }
1397
1398   g_assert (bufdata - map.data == map.size);
1399   gst_buffer_unmap (buf, &map);
1400   return gst_asf_mux_push_buffer (asfmux, buf, bufsize);
1401 }
1402
1403 /**
1404  * gst_asf_mux_add_simple_index_entry:
1405  * @asfmux:
1406  * @videopad:
1407  *
1408  * Adds a new entry to the simple index of the stream handler by videopad.
1409  * This functions doesn't check if the time elapsed
1410  * is larger than the established time interval between entries. The caller
1411  * is responsible for verifying this.
1412  */
1413 static void
1414 gst_asf_mux_add_simple_index_entry (GstAsfMux * asfmux,
1415     GstAsfVideoPad * videopad)
1416 {
1417   SimpleIndexEntry *entry = NULL;
1418   GST_DEBUG_OBJECT (asfmux, "Adding new simple index entry "
1419       "packet number: %" G_GUINT32_FORMAT ", "
1420       "packet count: %" G_GUINT16_FORMAT,
1421       videopad->last_keyframe_packet, videopad->last_keyframe_packet_count);
1422   entry = g_malloc0 (sizeof (SimpleIndexEntry));
1423   entry->packet_number = videopad->last_keyframe_packet;
1424   entry->packet_count = videopad->last_keyframe_packet_count;
1425   if (entry->packet_count > videopad->max_keyframe_packet_count)
1426     videopad->max_keyframe_packet_count = entry->packet_count;
1427   videopad->simple_index = g_slist_append (videopad->simple_index, entry);
1428 }
1429
1430 /**
1431  * gst_asf_mux_send_packet:
1432  * @asfmux:
1433  * @buf: The asf data packet
1434  *
1435  * Pushes an asf data packet downstream. The total number
1436  * of packets and bytes of the stream are incremented.
1437  *
1438  * Returns: the result of pushing the buffer downstream
1439  */
1440 static GstFlowReturn
1441 gst_asf_mux_send_packet (GstAsfMux * asfmux, GstBuffer * buf, gsize bufsize)
1442 {
1443   g_assert (bufsize == asfmux->packet_size);
1444   asfmux->total_data_packets++;
1445   GST_LOG_OBJECT (asfmux,
1446       "Pushing a packet of size %" G_GSIZE_FORMAT " and timestamp %"
1447       G_GUINT64_FORMAT, bufsize, GST_BUFFER_TIMESTAMP (buf));
1448   GST_LOG_OBJECT (asfmux, "Total data packets: %" G_GUINT64_FORMAT,
1449       asfmux->total_data_packets);
1450   return gst_asf_mux_push_buffer (asfmux, buf, bufsize);
1451 }
1452
1453 /**
1454  * gst_asf_mux_flush_payloads:
1455  * @asfmux: #GstAsfMux to flush the payloads from
1456  *
1457  * Fills an asf packet with asfmux queued payloads and
1458  * pushes it downstream.
1459  *
1460  * Returns: The result of pushing the packet
1461  */
1462 static GstFlowReturn
1463 gst_asf_mux_flush_payloads (GstAsfMux * asfmux)
1464 {
1465   GstBuffer *buf;
1466   guint8 payloads_count = 0;    /* we only use 6 bits, max is 63 */
1467   guint i;
1468   GstClockTime send_ts = GST_CLOCK_TIME_NONE;
1469   guint64 size_left;
1470   guint8 *data;
1471   gsize size;
1472   GSList *walk;
1473   GstAsfPad *pad;
1474   gboolean has_keyframe;
1475   AsfPayload *payload;
1476   guint32 payload_size;
1477   guint offset;
1478   GstMapInfo map;
1479
1480   if (asfmux->payloads == NULL)
1481     return GST_FLOW_OK;         /* nothing to send is ok */
1482
1483   GST_LOG_OBJECT (asfmux, "Flushing payloads");
1484
1485   buf = gst_buffer_new_and_alloc (asfmux->packet_size);
1486   gst_buffer_map (buf, &map, GST_MAP_WRITE);
1487   memset (map.data, 0, asfmux->packet_size);
1488
1489   /* 1 for the multiple payload flags */
1490   data = map.data + asfmux->payload_parsing_info_size + 1;
1491   size_left = asfmux->packet_size - asfmux->payload_parsing_info_size - 1;
1492
1493   has_keyframe = FALSE;
1494   walk = asfmux->payloads;
1495   while (walk && payloads_count < MAX_PAYLOADS_IN_A_PACKET) {
1496     payload = (AsfPayload *) walk->data;
1497     pad = (GstAsfPad *) payload->pad;
1498     payload_size = gst_asf_payload_get_size (payload);
1499     if (size_left < payload_size) {
1500       break;                    /* next payload doesn't fit fully */
1501     }
1502
1503     if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (send_ts))) {
1504       send_ts = GST_BUFFER_TIMESTAMP (payload->data);
1505     }
1506
1507     /* adding new simple index entry (if needed) */
1508     if (!pad->is_audio
1509         && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (payload->data))) {
1510       GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad;
1511       if (videopad->has_keyframe) {
1512         for (; videopad->next_index_time <=
1513             ASF_MILI_TO_100NANO (payload->presentation_time);
1514             videopad->next_index_time += videopad->time_interval) {
1515           gst_asf_mux_add_simple_index_entry (asfmux, videopad);
1516         }
1517       }
1518     }
1519
1520     /* serialize our payload */
1521     GST_DEBUG_OBJECT (asfmux, "Serializing payload into packet");
1522     GST_DEBUG_OBJECT (asfmux, "stream number: %d", pad->stream_number & 0x7F);
1523     GST_DEBUG_OBJECT (asfmux, "media object number: %d",
1524         (gint) payload->media_obj_num);
1525     GST_DEBUG_OBJECT (asfmux, "offset into media object: %" G_GUINT32_FORMAT,
1526         payload->offset_in_media_obj);
1527     GST_DEBUG_OBJECT (asfmux, "media object size: %" G_GUINT32_FORMAT,
1528         payload->media_object_size);
1529     GST_DEBUG_OBJECT (asfmux, "replicated data length: %d",
1530         (gint) payload->replicated_data_length);
1531     GST_DEBUG_OBJECT (asfmux, "payload size: %" G_GSIZE_FORMAT,
1532         gst_buffer_get_size (payload->data));
1533     GST_DEBUG_OBJECT (asfmux, "presentation time: %" G_GUINT32_FORMAT " (%"
1534         GST_TIME_FORMAT ")", payload->presentation_time,
1535         GST_TIME_ARGS (payload->presentation_time * GST_MSECOND));
1536     GST_DEBUG_OBJECT (asfmux, "keyframe: %s",
1537         (payload->stream_number & 0x80 ? "yes" : "no"));
1538     GST_DEBUG_OBJECT (asfmux, "buffer timestamp: %" GST_TIME_FORMAT,
1539         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (payload->data)));
1540     GST_DEBUG_OBJECT (asfmux, "buffer duration %" GST_TIME_FORMAT,
1541         GST_TIME_ARGS (GST_BUFFER_DURATION (payload->data)));
1542
1543     gst_asf_put_payload (data, payload);
1544     if (!payload->has_packet_info) {
1545       payload->has_packet_info = TRUE;
1546       payload->packet_number = asfmux->total_data_packets;
1547     }
1548     GST_DEBUG_OBJECT (asfmux, "packet number: %" G_GUINT32_FORMAT,
1549         payload->packet_number);
1550
1551     if (ASF_PAYLOAD_IS_KEYFRAME (payload)) {
1552       has_keyframe = TRUE;
1553       if (!pad->is_audio) {
1554         GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad;
1555         videopad->last_keyframe_packet = payload->packet_number;
1556         videopad->last_keyframe_packet_count = payload->packet_count;
1557         videopad->has_keyframe = TRUE;
1558       }
1559     }
1560
1561     /* update our variables */
1562     data += payload_size;
1563     size_left -= payload_size;
1564     payloads_count++;
1565     walk = g_slist_next (walk);
1566   }
1567
1568   /* remove flushed payloads */
1569   GST_LOG_OBJECT (asfmux, "Freeing already used payloads");
1570   for (i = 0; i < payloads_count; i++) {
1571     GSList *aux = g_slist_nth (asfmux->payloads, 0);
1572     AsfPayload *payload;
1573     g_assert (aux);
1574     payload = (AsfPayload *) aux->data;
1575     asfmux->payloads = g_slist_remove (asfmux->payloads, payload);
1576     asfmux->payload_data_size -=
1577         (gst_buffer_get_size (payload->data) +
1578         ASF_MULTIPLE_PAYLOAD_HEADER_SIZE);
1579     gst_asf_payload_free (payload);
1580   }
1581
1582   /* check if we can add part of the next payload */
1583   if (asfmux->payloads && size_left > ASF_MULTIPLE_PAYLOAD_HEADER_SIZE) {
1584     AsfPayload *payload =
1585         (AsfPayload *) g_slist_nth (asfmux->payloads, 0)->data;
1586     guint16 bytes_writen;
1587     GST_DEBUG_OBJECT (asfmux, "Adding part of a payload to a packet");
1588
1589     if (ASF_PAYLOAD_IS_KEYFRAME (payload))
1590       has_keyframe = TRUE;
1591
1592     if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (send_ts))) {
1593       send_ts = GST_BUFFER_TIMESTAMP (payload->data);
1594     }
1595
1596     bytes_writen = gst_asf_put_subpayload (data, payload, size_left);
1597     if (!payload->has_packet_info) {
1598       payload->has_packet_info = TRUE;
1599       payload->packet_number = asfmux->total_data_packets;
1600     }
1601     asfmux->payload_data_size -= bytes_writen;
1602     size_left -= (bytes_writen + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE);
1603     payloads_count++;
1604   }
1605
1606   GST_LOG_OBJECT (asfmux, "Payload data size: %" G_GUINT32_FORMAT,
1607       asfmux->payload_data_size);
1608
1609   /* fill payload parsing info */
1610   data = map.data;
1611   size = map.size;
1612
1613   /* flags */
1614   GST_WRITE_UINT8 (data, (0x0 << 7) |   /* no error correction */
1615       (ASF_FIELD_TYPE_DWORD << 5) |     /* packet length type */
1616       (ASF_FIELD_TYPE_DWORD << 3) |     /* padding length type */
1617       (ASF_FIELD_TYPE_NONE << 1) |      /* sequence type type */
1618       0x1);                     /* multiple payloads */
1619   offset = 1;
1620
1621   /* property flags - according to the spec, this should not change */
1622   GST_WRITE_UINT8 (data + offset, (ASF_FIELD_TYPE_BYTE << 6) |  /* stream number length type */
1623       (ASF_FIELD_TYPE_BYTE << 4) |      /* media obj number length type */
1624       (ASF_FIELD_TYPE_DWORD << 2) |     /* offset info media object length type */
1625       (ASF_FIELD_TYPE_BYTE));   /* replicated data length type */
1626   offset++;
1627
1628   /* Due to a limitation in WMP while streaming through WMSP we reduce the
1629    * packet & padding size to 16bit if they are <= 65535 bytes
1630    */
1631   if (asfmux->packet_size > 65535) {
1632     GST_WRITE_UINT32_LE (data + offset, asfmux->packet_size - size_left);
1633     offset += 4;
1634   } else {
1635     *data &= ~(ASF_FIELD_TYPE_MASK << 5);
1636     *data |= ASF_FIELD_TYPE_WORD << 5;
1637     GST_WRITE_UINT16_LE (data + offset, asfmux->packet_size - size_left);
1638     offset += 2;
1639   }
1640   if (asfmux->prop_padding > 65535) {
1641     GST_WRITE_UINT32_LE (data + offset, size_left);
1642     offset += 4;
1643   } else {
1644     *data &= ~(ASF_FIELD_TYPE_MASK << 3);
1645     *data |= ASF_FIELD_TYPE_WORD << 3;
1646     GST_WRITE_UINT16_LE (data + offset, size_left);
1647     offset += 2;
1648   }
1649
1650   /* packet send time */
1651   if (GST_CLOCK_TIME_IS_VALID (send_ts)) {
1652     GST_WRITE_UINT32_LE (data + offset, (send_ts / GST_MSECOND));
1653     GST_BUFFER_TIMESTAMP (buf) = send_ts;
1654   }
1655   offset += 4;
1656
1657   /* packet duration */
1658   GST_WRITE_UINT16_LE (data + offset, 0);       /* FIXME send duration needs to be estimated */
1659   offset += 2;
1660
1661   /* multiple payloads flags */
1662   GST_WRITE_UINT8 (data + offset, 0x2 << 6 | payloads_count);
1663   gst_buffer_unmap (buf, &map);
1664
1665   if (payloads_count == 0) {
1666     GST_WARNING_OBJECT (asfmux, "Sending packet without any payload");
1667   }
1668   asfmux->data_object_size += size;
1669
1670   if (!has_keyframe)
1671     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1672
1673   return gst_asf_mux_send_packet (asfmux, buf, size);
1674 }
1675
1676 /**
1677  * stream_number_compare:
1678  * @a: a #GstAsfPad
1679  * @b: another #GstAsfPad
1680  *
1681  * Utility function to compare #GstAsfPad by their stream numbers
1682  *
1683  * Returns: The difference between their stream numbers
1684  */
1685 static gint
1686 stream_number_compare (gconstpointer a, gconstpointer b)
1687 {
1688   GstAsfPad *pad_a = (GstAsfPad *) a;
1689   GstAsfPad *pad_b = (GstAsfPad *) b;
1690   return pad_b->stream_number - pad_a->stream_number;
1691 }
1692
1693 static GstFlowReturn
1694 gst_asf_mux_push_simple_index (GstAsfMux * asfmux, GstAsfVideoPad * pad)
1695 {
1696   guint64 object_size = ASF_SIMPLE_INDEX_OBJECT_SIZE +
1697       g_slist_length (pad->simple_index) * ASF_SIMPLE_INDEX_ENTRY_SIZE;
1698   GstBuffer *buf;
1699   GSList *walk;
1700   guint8 *data;
1701   guint32 entries_count = g_slist_length (pad->simple_index);
1702   GstMapInfo map;
1703   gsize bufsize;
1704
1705   buf = gst_buffer_new_and_alloc (object_size);
1706   bufsize = object_size;
1707
1708   gst_buffer_map (buf, &map, GST_MAP_WRITE);
1709   data = map.data;
1710
1711   gst_asf_put_guid (data, guids[ASF_SIMPLE_INDEX_OBJECT_INDEX]);
1712   GST_WRITE_UINT64_LE (data + 16, object_size);
1713   gst_asf_put_guid (data + 24, asfmux->file_id);
1714   GST_WRITE_UINT64_LE (data + 40, pad->time_interval);
1715   GST_WRITE_UINT32_LE (data + 48, pad->max_keyframe_packet_count);
1716   GST_WRITE_UINT32_LE (data + 52, entries_count);
1717   data += ASF_SIMPLE_INDEX_OBJECT_SIZE;
1718
1719   GST_DEBUG_OBJECT (asfmux,
1720       "Simple index object values - size:%" G_GUINT64_FORMAT ", time interval:%"
1721       G_GUINT64_FORMAT ", max packet count:%" G_GUINT32_FORMAT ", entries:%"
1722       G_GUINT32_FORMAT, object_size, pad->time_interval,
1723       pad->max_keyframe_packet_count, entries_count);
1724
1725   for (walk = pad->simple_index; walk; walk = g_slist_next (walk)) {
1726     SimpleIndexEntry *entry = (SimpleIndexEntry *) walk->data;
1727     GST_DEBUG_OBJECT (asfmux, "Simple index entry: packet_number:%"
1728         G_GUINT32_FORMAT " packet_count:%" G_GUINT16_FORMAT,
1729         entry->packet_number, entry->packet_count);
1730     GST_WRITE_UINT32_LE (data, entry->packet_number);
1731     GST_WRITE_UINT16_LE (data + 4, entry->packet_count);
1732     data += ASF_SIMPLE_INDEX_ENTRY_SIZE;
1733   }
1734
1735   GST_DEBUG_OBJECT (asfmux, "Pushing the simple index");
1736   g_assert (data - map.data == object_size);
1737   gst_buffer_unmap (buf, &map);
1738   return gst_asf_mux_push_buffer (asfmux, buf, bufsize);
1739 }
1740
1741 static GstFlowReturn
1742 gst_asf_mux_write_indexes (GstAsfMux * asfmux)
1743 {
1744   GSList *ordered_pads;
1745   GSList *walker;
1746   GstFlowReturn ret = GST_FLOW_OK;
1747
1748   /* write simple indexes for video medias */
1749   ordered_pads =
1750       g_slist_sort (g_slist_copy (asfmux->collect->data),
1751       (GCompareFunc) stream_number_compare);
1752   for (walker = ordered_pads; walker; walker = g_slist_next (walker)) {
1753     GstAsfPad *pad = (GstAsfPad *) walker->data;
1754     if (!pad->is_audio) {
1755       ret = gst_asf_mux_push_simple_index (asfmux, (GstAsfVideoPad *) pad);
1756       if (ret != GST_FLOW_OK) {
1757         GST_ERROR_OBJECT (asfmux, "Failed to write simple index for stream %"
1758             G_GUINT16_FORMAT, (guint16) pad->stream_number);
1759         goto cleanup_and_return;
1760       }
1761     }
1762   }
1763 cleanup_and_return:
1764   g_slist_free (ordered_pads);
1765   return ret;
1766 }
1767
1768 /**
1769  * gst_asf_mux_stop_file:
1770  * @asfmux: #GstAsfMux
1771  *
1772  * Finalizes the asf stream by pushing the indexes after
1773  * the data object. Also seeks back to the header positions
1774  * to rewrite some fields such as the total number of bytes
1775  * of the file, or any other that couldn't be predicted/known
1776  * back on the header generation.
1777  *
1778  * Returns: GST_FLOW_OK on success
1779  */
1780 static GstFlowReturn
1781 gst_asf_mux_stop_file (GstAsfMux * asfmux)
1782 {
1783   GstEvent *event;
1784   GstBuffer *buf;
1785   GstFlowReturn ret = GST_FLOW_OK;
1786   GSList *walk;
1787   GstClockTime play_duration = 0;
1788   guint32 bitrate = 0;
1789   GstSegment segment;
1790   GstMapInfo map;
1791   guint8 *data;
1792
1793   /* write indexes */
1794   ret = gst_asf_mux_write_indexes (asfmux);
1795   if (ret != GST_FLOW_OK) {
1796     GST_ERROR_OBJECT (asfmux, "Failed to write indexes");
1797     return ret;
1798   }
1799
1800   /* find max stream duration and bitrate */
1801   for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) {
1802     GstAsfPad *pad = (GstAsfPad *) walk->data;
1803     bitrate += pad->bitrate;
1804     if (pad->play_duration > play_duration)
1805       play_duration = pad->play_duration;
1806   }
1807
1808   /* going back to file properties object to fill in
1809    * values we didn't know back then */
1810   GST_DEBUG_OBJECT (asfmux,
1811       "Sending new segment to file properties object position");
1812   gst_segment_init (&segment, GST_FORMAT_BYTES);
1813   segment.start = segment.position =
1814       asfmux->file_properties_object_position + 40;
1815   event = gst_event_new_segment (&segment);
1816   if (!gst_pad_push_event (asfmux->srcpad, event)) {
1817     GST_ERROR_OBJECT (asfmux, "Failed to update file properties object");
1818     return GST_FLOW_ERROR;
1819   }
1820   /* All file properties fields except the first 40 bytes */
1821   buf = gst_buffer_new_and_alloc (ASF_FILE_PROPERTIES_OBJECT_SIZE - 40);
1822   gst_buffer_map (buf, &map, GST_MAP_WRITE);
1823   data = map.data;
1824
1825   GST_WRITE_UINT64_LE (data, asfmux->file_size);
1826   gst_asf_put_time (data + 8, gst_asf_get_current_time ());
1827   GST_WRITE_UINT64_LE (data + 16, asfmux->total_data_packets);
1828   GST_WRITE_UINT64_LE (data + 24, (play_duration / 100) +
1829       ASF_MILI_TO_100NANO (asfmux->preroll));
1830   GST_WRITE_UINT64_LE (data + 32, (play_duration / 100));       /* TODO send duration */
1831
1832   /* if play duration is smaller then preroll, player might have problems */
1833   if (asfmux->preroll > play_duration / GST_MSECOND) {
1834     GST_ELEMENT_WARNING (asfmux, STREAM, MUX, (_("Generated file has a larger"
1835                 " preroll time than its streams duration")),
1836         ("Preroll time larger than streams duration, "
1837             "try setting a smaller preroll value next time"));
1838   }
1839   GST_WRITE_UINT64_LE (data + 40, asfmux->preroll);
1840   GST_WRITE_UINT32_LE (data + 48, 0x2); /* flags - seekable */
1841   GST_WRITE_UINT32_LE (data + 52, asfmux->packet_size);
1842   GST_WRITE_UINT32_LE (data + 56, asfmux->packet_size);
1843   /* FIXME - we want the max instantaneous bitrate, for vbr streams, we can't
1844    * get it this way, this would be the average, right? */
1845   GST_WRITE_UINT32_LE (data + 60, bitrate);     /* max bitrate */
1846   gst_buffer_unmap (buf, &map);
1847
1848   /* we don't use gst_asf_mux_push_buffer because we are overwriting
1849    * already sent data */
1850   ret = gst_pad_push (asfmux->srcpad, buf);
1851   if (ret != GST_FLOW_OK) {
1852     GST_ERROR_OBJECT (asfmux, "Failed to update file properties object");
1853     return ret;
1854   }
1855
1856   GST_DEBUG_OBJECT (asfmux, "Seeking back to data object");
1857
1858   /* seek back to the data object */
1859   segment.start = segment.position = asfmux->data_object_position + 16;
1860   event = gst_event_new_segment (&segment);
1861   if (!gst_pad_push_event (asfmux->srcpad, event)) {
1862     GST_ERROR_OBJECT (asfmux, "Seek to update data object failed");
1863     return GST_FLOW_ERROR;
1864   }
1865
1866   buf = gst_buffer_new_and_alloc (32);  /* qword+guid+qword */
1867   gst_buffer_map (buf, &map, GST_MAP_WRITE);
1868   data = map.data;
1869   GST_WRITE_UINT64_LE (data, asfmux->data_object_size + ASF_DATA_OBJECT_SIZE);
1870   gst_asf_put_guid (data + 8, asfmux->file_id);
1871   GST_WRITE_UINT64_LE (data + 24, asfmux->total_data_packets);
1872   gst_buffer_unmap (buf, &map);
1873
1874   return gst_pad_push (asfmux->srcpad, buf);
1875 }
1876
1877 /**
1878  * gst_asf_mux_process_buffer:
1879  * @asfmux:
1880  * @pad: stream of the buffer
1881  * @buf: The buffer to be processed
1882  *
1883  * Processes the buffer by parsing it and
1884  * queueing it up as an asf payload for later
1885  * being added and pushed inside an asf packet.
1886  *
1887  * Returns: a #GstFlowReturn
1888  */
1889 static GstFlowReturn
1890 gst_asf_mux_process_buffer (GstAsfMux * asfmux, GstAsfPad * pad,
1891     GstBuffer * buf)
1892 {
1893   guint8 keyframe;
1894   AsfPayload *payload;
1895
1896   payload = g_malloc0 (sizeof (AsfPayload));
1897   payload->pad = (GstCollectData *) pad;
1898   payload->data = buf;
1899
1900   GST_LOG_OBJECT (asfmux, "Processing payload data for stream number %u",
1901       pad->stream_number);
1902
1903   /* stream number */
1904   if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
1905     keyframe = 0;
1906   } else {
1907     keyframe = 0x1 << 7;
1908   }
1909   payload->stream_number = keyframe | pad->stream_number;
1910
1911   payload->media_obj_num = pad->media_object_number;
1912   payload->offset_in_media_obj = 0;
1913   payload->replicated_data_length = 8;
1914
1915   /* replicated data - 1) media object size */
1916   payload->media_object_size = gst_buffer_get_size (buf);
1917   /* replicated data - 2) presentation time */
1918   if (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf))) {
1919     GST_ERROR_OBJECT (asfmux, "Received buffer without timestamp");
1920     gst_asf_payload_free (payload);
1921     return GST_FLOW_ERROR;
1922   }
1923
1924   g_assert (GST_CLOCK_TIME_IS_VALID (asfmux->first_ts));
1925   g_assert (GST_CLOCK_TIME_IS_VALID (pad->first_ts));
1926
1927   payload->presentation_time = asfmux->preroll +
1928       ((GST_BUFFER_TIMESTAMP (buf) - asfmux->first_ts) / GST_MSECOND);
1929
1930   /* update counting values */
1931   pad->media_object_number = (pad->media_object_number + 1) % 256;
1932   if (GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) {
1933     pad->play_duration += GST_BUFFER_DURATION (buf);
1934   } else {
1935     GST_WARNING_OBJECT (asfmux, "Received buffer without duration, it will not "
1936         "be accounted in the total file time");
1937   }
1938
1939   asfmux->payloads = g_slist_append (asfmux->payloads, payload);
1940   asfmux->payload_data_size +=
1941       gst_buffer_get_size (buf) + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE;
1942   GST_LOG_OBJECT (asfmux, "Payload data size: %" G_GUINT32_FORMAT,
1943       asfmux->payload_data_size);
1944
1945   while (asfmux->payload_data_size + asfmux->payload_parsing_info_size >=
1946       asfmux->packet_size) {
1947     GstFlowReturn ret = gst_asf_mux_flush_payloads (asfmux);
1948     if (ret != GST_FLOW_OK)
1949       return ret;
1950   }
1951
1952   return GST_FLOW_OK;
1953 }
1954
1955 static GstFlowReturn
1956 gst_asf_mux_collected (GstCollectPads * collect, gpointer data)
1957 {
1958   GstAsfMux *asfmux = GST_ASF_MUX_CAST (data);
1959   GstFlowReturn ret = GST_FLOW_OK;
1960   GstAsfPad *best_pad = NULL;
1961   GstClockTime best_time = GST_CLOCK_TIME_NONE;
1962   GstBuffer *buf = NULL;
1963   GSList *walk;
1964
1965   if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_NONE)) {
1966     ret = gst_asf_mux_start_file (asfmux);
1967     if (ret != GST_FLOW_OK) {
1968       GST_WARNING_OBJECT (asfmux, "Failed to send headers");
1969       return ret;
1970     } else {
1971       asfmux->state = GST_ASF_MUX_STATE_DATA;
1972     }
1973   }
1974
1975   if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_EOS))
1976     return GST_FLOW_EOS;
1977
1978   /* select the earliest buffer */
1979   walk = asfmux->collect->data;
1980   while (walk) {
1981     GstAsfPad *pad;
1982     GstCollectData *data;
1983     GstClockTime time;
1984
1985     data = (GstCollectData *) walk->data;
1986     pad = (GstAsfPad *) data;
1987
1988     walk = g_slist_next (walk);
1989
1990     buf = gst_collect_pads_peek (collect, data);
1991     if (buf == NULL) {
1992       GST_LOG_OBJECT (asfmux, "Pad %s has no buffers",
1993           GST_PAD_NAME (pad->collect.pad));
1994       continue;
1995     }
1996     time = GST_BUFFER_TIMESTAMP (buf);
1997
1998     /* check the ts for getting the first time */
1999     if (!GST_CLOCK_TIME_IS_VALID (pad->first_ts) &&
2000         GST_CLOCK_TIME_IS_VALID (time)) {
2001       GST_DEBUG_OBJECT (asfmux,
2002           "First ts for stream number %u: %" GST_TIME_FORMAT,
2003           pad->stream_number, GST_TIME_ARGS (time));
2004       pad->first_ts = time;
2005       if (!GST_CLOCK_TIME_IS_VALID (asfmux->first_ts) ||
2006           time < asfmux->first_ts) {
2007         GST_DEBUG_OBJECT (asfmux, "New first ts for file %" GST_TIME_FORMAT,
2008             GST_TIME_ARGS (time));
2009         asfmux->first_ts = time;
2010       }
2011     }
2012
2013     gst_buffer_unref (buf);
2014
2015     if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time) ||
2016         (GST_CLOCK_TIME_IS_VALID (best_time) && time < best_time)) {
2017       best_pad = pad;
2018       best_time = time;
2019     }
2020   }
2021
2022   if (best_pad != NULL) {
2023     /* we have data */
2024     GST_LOG_OBJECT (asfmux, "selected pad %s with time %" GST_TIME_FORMAT,
2025         GST_PAD_NAME (best_pad->collect.pad), GST_TIME_ARGS (best_time));
2026     buf = gst_collect_pads_pop (collect, &best_pad->collect);
2027     ret = gst_asf_mux_process_buffer (asfmux, best_pad, buf);
2028   } else {
2029     /* no data, let's finish it up */
2030     while (asfmux->payloads) {
2031       ret = gst_asf_mux_flush_payloads (asfmux);
2032       if (ret != GST_FLOW_OK) {
2033         return ret;
2034       }
2035     }
2036     g_assert (asfmux->payloads == NULL);
2037     g_assert (asfmux->payload_data_size == 0);
2038     /* in not on 'streamable' mode we need to push indexes
2039      * and update headers */
2040     if (!asfmux->prop_streamable) {
2041       ret = gst_asf_mux_stop_file (asfmux);
2042     }
2043     if (ret == GST_FLOW_OK) {
2044       gst_pad_push_event (asfmux->srcpad, gst_event_new_eos ());
2045       ret = GST_FLOW_EOS;
2046     }
2047     asfmux->state = GST_ASF_MUX_STATE_EOS;
2048   }
2049
2050   return ret;
2051 }
2052
2053 static void
2054 gst_asf_mux_pad_reset (GstAsfPad * pad)
2055 {
2056   pad->stream_number = 0;
2057   pad->media_object_number = 0;
2058   pad->play_duration = (GstClockTime) 0;
2059   pad->bitrate = 0;
2060   if (pad->codec_data)
2061     gst_buffer_unref (pad->codec_data);
2062   pad->codec_data = NULL;
2063   if (pad->taglist)
2064     gst_tag_list_unref (pad->taglist);
2065   pad->taglist = NULL;
2066
2067   pad->first_ts = GST_CLOCK_TIME_NONE;
2068
2069   if (pad->is_audio) {
2070     GstAsfAudioPad *audiopad = (GstAsfAudioPad *) pad;
2071     audiopad->audioinfo.rate = 0;
2072     audiopad->audioinfo.channels = 0;
2073     audiopad->audioinfo.format = 0;
2074     audiopad->audioinfo.av_bps = 0;
2075     audiopad->audioinfo.blockalign = 0;
2076     audiopad->audioinfo.bits_per_sample = 0;
2077   } else {
2078     GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad;
2079     videopad->vidinfo.size = 0;
2080     videopad->vidinfo.width = 0;
2081     videopad->vidinfo.height = 0;
2082     videopad->vidinfo.planes = 1;
2083     videopad->vidinfo.bit_cnt = 0;
2084     videopad->vidinfo.compression = 0;
2085     videopad->vidinfo.image_size = 0;
2086     videopad->vidinfo.xpels_meter = 0;
2087     videopad->vidinfo.ypels_meter = 0;
2088     videopad->vidinfo.num_colors = 0;
2089     videopad->vidinfo.imp_colors = 0;
2090
2091     videopad->last_keyframe_packet = 0;
2092     videopad->has_keyframe = FALSE;
2093     videopad->last_keyframe_packet_count = 0;
2094     videopad->max_keyframe_packet_count = 0;
2095     videopad->next_index_time = 0;
2096     videopad->time_interval = DEFAULT_SIMPLE_INDEX_TIME_INTERVAL;
2097     if (videopad->simple_index) {
2098       GSList *walk;
2099       for (walk = videopad->simple_index; walk; walk = g_slist_next (walk)) {
2100         g_free (walk->data);
2101         walk->data = NULL;
2102       }
2103       g_slist_free (videopad->simple_index);
2104     }
2105     videopad->simple_index = NULL;
2106   }
2107 }
2108
2109 static gboolean
2110 gst_asf_mux_audio_set_caps (GstPad * pad, GstCaps * caps)
2111 {
2112   GstAsfMux *asfmux;
2113   GstAsfAudioPad *audiopad;
2114   GstStructure *structure;
2115   const gchar *caps_name;
2116   gint channels, rate;
2117   gchar *aux;
2118   const GValue *codec_data;
2119
2120   asfmux = GST_ASF_MUX (gst_pad_get_parent (pad));
2121
2122   audiopad = (GstAsfAudioPad *) gst_pad_get_element_private (pad);
2123   g_assert (audiopad);
2124
2125   aux = gst_caps_to_string (caps);
2126   GST_DEBUG_OBJECT (asfmux, "%s:%s, caps=%s", GST_DEBUG_PAD_NAME (pad), aux);
2127   g_free (aux);
2128
2129   structure = gst_caps_get_structure (caps, 0);
2130   caps_name = gst_structure_get_name (structure);
2131
2132   if (!gst_structure_get_int (structure, "channels", &channels) ||
2133       !gst_structure_get_int (structure, "rate", &rate))
2134     goto refuse_caps;
2135
2136   audiopad->audioinfo.channels = (guint16) channels;
2137   audiopad->audioinfo.rate = (guint32) rate;
2138
2139   /* taken from avimux
2140    * codec initialization data, if any
2141    */
2142   codec_data = gst_structure_get_value (structure, "codec_data");
2143   if (codec_data) {
2144     audiopad->pad.codec_data = gst_value_get_buffer (codec_data);
2145     gst_buffer_ref (audiopad->pad.codec_data);
2146   }
2147
2148   if (strcmp (caps_name, "audio/x-wma") == 0) {
2149     gint version;
2150     gint block_align = 0;
2151     gint bitrate = 0;
2152
2153     if (!gst_structure_get_int (structure, "wmaversion", &version)) {
2154       goto refuse_caps;
2155     }
2156
2157     if (gst_structure_get_int (structure, "block_align", &block_align)) {
2158       audiopad->audioinfo.blockalign = (guint16) block_align;
2159     }
2160     if (gst_structure_get_int (structure, "bitrate", &bitrate)) {
2161       audiopad->pad.bitrate = (guint32) bitrate;
2162       audiopad->audioinfo.av_bps = bitrate / 8;
2163     }
2164
2165     if (version == 1) {
2166       audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV1;
2167     } else if (version == 2) {
2168       audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV2;
2169     } else if (version == 3) {
2170       audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV3;
2171     } else {
2172       goto refuse_caps;
2173     }
2174   } else if (strcmp (caps_name, "audio/mpeg") == 0) {
2175     gint version;
2176     gint layer;
2177
2178     if (!gst_structure_get_int (structure, "mpegversion", &version) ||
2179         !gst_structure_get_int (structure, "layer", &layer)) {
2180       goto refuse_caps;
2181     }
2182     if (version != 1 || layer != 3) {
2183       goto refuse_caps;
2184     }
2185
2186     audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
2187   } else {
2188     goto refuse_caps;
2189   }
2190
2191   gst_object_unref (asfmux);
2192   return TRUE;
2193
2194 refuse_caps:
2195   GST_WARNING_OBJECT (asfmux, "pad %s refused caps %" GST_PTR_FORMAT,
2196       GST_PAD_NAME (pad), caps);
2197   gst_object_unref (asfmux);
2198   return FALSE;
2199 }
2200
2201 /* TODO Read pixel aspect ratio */
2202 static gboolean
2203 gst_asf_mux_video_set_caps (GstPad * pad, GstCaps * caps)
2204 {
2205   GstAsfMux *asfmux;
2206   GstAsfVideoPad *videopad;
2207   GstStructure *structure;
2208   const gchar *caps_name;
2209   gint width, height;
2210   gchar *aux;
2211   const GValue *codec_data;
2212
2213   asfmux = GST_ASF_MUX (gst_pad_get_parent (pad));
2214
2215   videopad = (GstAsfVideoPad *) gst_pad_get_element_private (pad);
2216   g_assert (videopad);
2217
2218   aux = gst_caps_to_string (caps);
2219   GST_DEBUG_OBJECT (asfmux, "%s:%s, caps=%s", GST_DEBUG_PAD_NAME (pad), aux);
2220   g_free (aux);
2221
2222   structure = gst_caps_get_structure (caps, 0);
2223   caps_name = gst_structure_get_name (structure);
2224
2225   if (!gst_structure_get_int (structure, "width", &width) ||
2226       !gst_structure_get_int (structure, "height", &height))
2227     goto refuse_caps;
2228
2229   videopad->vidinfo.width = (gint32) width;
2230   videopad->vidinfo.height = (gint32) height;
2231
2232   /* taken from avimux
2233    * codec initialization data, if any
2234    */
2235   codec_data = gst_structure_get_value (structure, "codec_data");
2236   if (codec_data) {
2237     videopad->pad.codec_data = gst_value_get_buffer (codec_data);
2238     gst_buffer_ref (videopad->pad.codec_data);
2239   }
2240
2241   if (strcmp (caps_name, "video/x-wmv") == 0) {
2242     gint wmvversion;
2243     const gchar *fstr;
2244
2245     videopad->vidinfo.bit_cnt = 24;
2246
2247     /* in case we have a format, we use it */
2248     fstr = gst_structure_get_string (structure, "format");
2249     if (fstr && strlen (fstr) == 4) {
2250       videopad->vidinfo.compression = GST_STR_FOURCC (fstr);
2251     } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
2252       if (wmvversion == 2) {
2253         videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
2254       } else if (wmvversion == 1) {
2255         videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
2256       } else if (wmvversion == 3) {
2257         videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
2258       }
2259     } else
2260       goto refuse_caps;
2261   } else {
2262     goto refuse_caps;
2263   }
2264
2265   gst_object_unref (asfmux);
2266   return TRUE;
2267
2268 refuse_caps:
2269   GST_WARNING_OBJECT (asfmux, "pad %s refused caps %" GST_PTR_FORMAT,
2270       GST_PAD_NAME (pad), caps);
2271   gst_object_unref (asfmux);
2272   return FALSE;
2273 }
2274
2275 static GstPad *
2276 gst_asf_mux_request_new_pad (GstElement * element,
2277     GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2278 {
2279   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2280   GstAsfMux *asfmux = GST_ASF_MUX_CAST (element);
2281   GstPad *newpad;
2282   GstAsfPad *collect_pad;
2283   gboolean is_audio;
2284   guint collect_size = 0;
2285   gchar *name = NULL;
2286   const gchar *pad_name = NULL;
2287   guint pad_id;
2288
2289   GST_DEBUG_OBJECT (asfmux, "Requested pad: %s", GST_STR_NULL (req_name));
2290
2291   if (asfmux->state != GST_ASF_MUX_STATE_NONE) {
2292     GST_WARNING_OBJECT (asfmux, "Not providing request pad after element is at "
2293         "paused/playing state.");
2294     return NULL;
2295   }
2296
2297   if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2298     /* don't mix named and unnamed pads, if the pad already exists we fail when
2299      * trying to add it */
2300     if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2301       pad_name = req_name;
2302     } else {
2303       name = g_strdup_printf ("audio_%u", asfmux->stream_number + 1);
2304       pad_name = name;
2305     }
2306     GST_DEBUG_OBJECT (asfmux, "Adding new pad %s", name);
2307     newpad = gst_pad_new_from_template (templ, pad_name);
2308     is_audio = TRUE;
2309   } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2310     /* don't mix named and unnamed pads, if the pad already exists we fail when
2311      * trying to add it */
2312     if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2313       pad_name = req_name;
2314     } else {
2315       name = g_strdup_printf ("video_%u", asfmux->stream_number + 1);
2316       pad_name = name;
2317     }
2318     GST_DEBUG_OBJECT (asfmux, "Adding new pad %s", name);
2319     newpad = gst_pad_new_from_template (templ, name);
2320     is_audio = FALSE;
2321   } else {
2322     GST_WARNING_OBJECT (asfmux, "This is not our template!");
2323     return NULL;
2324   }
2325
2326   g_free (name);
2327
2328   /* add pad to collections */
2329   if (is_audio) {
2330     collect_size = sizeof (GstAsfAudioPad);
2331   } else {
2332     collect_size = sizeof (GstAsfVideoPad);
2333   }
2334   collect_pad = (GstAsfPad *)
2335       gst_collect_pads_add_pad (asfmux->collect, newpad, collect_size,
2336       (GstCollectDataDestroyNotify) (gst_asf_mux_pad_reset), TRUE);
2337
2338   /* set up pad */
2339   collect_pad->is_audio = is_audio;
2340   if (!is_audio)
2341     ((GstAsfVideoPad *) collect_pad)->simple_index = NULL;
2342   collect_pad->taglist = NULL;
2343   gst_asf_mux_pad_reset (collect_pad);
2344
2345   /* set pad stream number */
2346   asfmux->stream_number += 1;
2347   collect_pad->stream_number = asfmux->stream_number;
2348
2349   gst_pad_set_active (newpad, TRUE);
2350   gst_element_add_pad (element, newpad);
2351
2352   return newpad;
2353 }
2354
2355 static void
2356 gst_asf_mux_get_property (GObject * object,
2357     guint prop_id, GValue * value, GParamSpec * pspec)
2358 {
2359   GstAsfMux *asfmux;
2360
2361   asfmux = GST_ASF_MUX (object);
2362   switch (prop_id) {
2363     case PROP_PACKET_SIZE:
2364       g_value_set_uint (value, asfmux->prop_packet_size);
2365       break;
2366     case PROP_PREROLL:
2367       g_value_set_uint64 (value, asfmux->prop_preroll);
2368       break;
2369     case PROP_MERGE_STREAM_TAGS:
2370       g_value_set_boolean (value, asfmux->prop_merge_stream_tags);
2371       break;
2372     case PROP_PADDING:
2373       g_value_set_uint64 (value, asfmux->prop_padding);
2374       break;
2375     case PROP_STREAMABLE:
2376       g_value_set_boolean (value, asfmux->prop_streamable);
2377       break;
2378     default:
2379       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2380       break;
2381   }
2382 }
2383
2384 static void
2385 gst_asf_mux_set_property (GObject * object,
2386     guint prop_id, const GValue * value, GParamSpec * pspec)
2387 {
2388   GstAsfMux *asfmux;
2389
2390   asfmux = GST_ASF_MUX (object);
2391   switch (prop_id) {
2392     case PROP_PACKET_SIZE:
2393       asfmux->prop_packet_size = g_value_get_uint (value);
2394       break;
2395     case PROP_PREROLL:
2396       asfmux->prop_preroll = g_value_get_uint64 (value);
2397       break;
2398     case PROP_MERGE_STREAM_TAGS:
2399       asfmux->prop_merge_stream_tags = g_value_get_boolean (value);
2400       break;
2401     case PROP_PADDING:
2402       asfmux->prop_padding = g_value_get_uint64 (value);
2403       break;
2404     case PROP_STREAMABLE:
2405       asfmux->prop_streamable = g_value_get_boolean (value);
2406       break;
2407     default:
2408       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2409       break;
2410   }
2411 }
2412
2413 static GstStateChangeReturn
2414 gst_asf_mux_change_state (GstElement * element, GstStateChange transition)
2415 {
2416   GstAsfMux *asfmux;
2417   GstStateChangeReturn ret;
2418
2419   asfmux = GST_ASF_MUX (element);
2420
2421   switch (transition) {
2422     case GST_STATE_CHANGE_READY_TO_PAUSED:
2423       /* TODO - check if it is possible to mux 2 files without going
2424        * through here */
2425       asfmux->payload_parsing_info_size =
2426           gst_asf_mux_find_payload_parsing_info_size (asfmux);
2427       asfmux->packet_size = asfmux->prop_packet_size;
2428       asfmux->preroll = asfmux->prop_preroll;
2429       asfmux->merge_stream_tags = asfmux->prop_merge_stream_tags;
2430       gst_collect_pads_start (asfmux->collect);
2431       break;
2432     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2433       break;
2434     case GST_STATE_CHANGE_PAUSED_TO_READY:
2435       gst_collect_pads_stop (asfmux->collect);
2436       asfmux->state = GST_ASF_MUX_STATE_NONE;
2437       break;
2438     default:
2439       break;
2440   }
2441
2442   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2443   if (ret == GST_STATE_CHANGE_FAILURE)
2444     goto done;
2445
2446   switch (transition) {
2447     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2448       break;
2449     case GST_STATE_CHANGE_PAUSED_TO_READY:
2450       break;
2451     case GST_STATE_CHANGE_READY_TO_NULL:
2452       break;
2453     default:
2454       break;
2455   }
2456
2457 done:
2458   return ret;
2459 }