540e2a7b2fb97ab6a0f93552761e9297e41d6ecf
[platform/upstream/gst-plugins-good.git] / gst / avi / gstavimux.c
1 /* Gnome-Streamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "gstavimux.h"
26
27
28
29 /* elementfactory information */
30 static GstElementDetails 
31 gst_avimux_details = 
32 {
33   ".avi mux",
34   "Mux/Video",
35   "Encodes audio and video into an avi stream",
36   VERSION,
37   "Wim Taymans <wim.taymans@chello.be>",
38   "(C) 2000",
39 };
40
41 /* AviMux signals and args */
42 enum {
43   /* FILL ME */
44   LAST_SIGNAL
45 };
46
47 enum {
48   ARG_0,
49   /* FILL ME */
50 };
51
52 GST_PADTEMPLATE_FACTORY (src_factory,
53   "src",
54   GST_PAD_SRC,
55   GST_PAD_ALWAYS,
56   GST_CAPS_NEW (
57     "sink_video",
58     "video/avi",
59     NULL
60   )
61 )
62     
63 GST_PADTEMPLATE_FACTORY (video_sink_factory,
64   "video_%02d",
65   GST_PAD_SINK,
66   GST_PAD_REQUEST,
67   GST_CAPS_NEW (
68     "sink_video",
69     "video/avi",
70       "format",   GST_PROPS_STRING ("strf_vids")
71   )
72 )
73     
74 GST_PADTEMPLATE_FACTORY (audio_sink_factory,
75   "audio_%02d",
76   GST_PAD_SINK,
77   GST_PAD_REQUEST,
78   GST_CAPS_NEW (
79     "sink_audio",
80     "video/avi",
81       "format",   GST_PROPS_STRING ("strf_auds")
82   )
83 )
84     
85
86 static void     gst_avimux_class_init           (GstAviMuxClass *klass);
87 static void     gst_avimux_init                 (GstAviMux *avimux);
88
89 static void     gst_avimux_chain                (GstPad *pad, GstBuffer *buf);
90 static GstPad*  gst_avimux_request_new_pad      (GstElement *element, GstPadTemplate *templ, const gchar *name);
91         
92 static void     gst_avimux_set_property         (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
93 static void     gst_avimux_get_property         (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
94
95
96 static GstElementClass *parent_class = NULL;
97 //static guint gst_avimux_signals[LAST_SIGNAL] = { 0 };
98
99 GType
100 gst_avimux_get_type (void) 
101 {
102   static GType avimux_type = 0;
103
104   if (!avimux_type) {
105     static const GTypeInfo avimux_info = {
106       sizeof(GstAviMuxClass),      
107       NULL,
108       NULL,
109       (GClassInitFunc)gst_avimux_class_init,
110       NULL,
111       NULL,
112       sizeof(GstAviMux),
113       0,
114       (GInstanceInitFunc)gst_avimux_init,
115     };
116     avimux_type = g_type_register_static(GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
117   }
118   return avimux_type;
119 }
120
121 static void
122 gst_avimux_class_init (GstAviMuxClass *klass) 
123 {
124   GObjectClass *gobject_class;
125   GstElementClass *gstelement_class;
126
127   gobject_class = (GObjectClass*)klass;
128   gstelement_class = (GstElementClass*)klass;
129
130   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
131
132   gobject_class->set_property = gst_avimux_set_property;
133   gobject_class->get_property = gst_avimux_get_property;
134
135   gstelement_class->request_new_pad = gst_avimux_request_new_pad;
136 }
137
138 static void 
139 gst_avimux_init (GstAviMux *avimux) 
140 {
141   avimux->srcpad = gst_pad_new_from_template (
142                   GST_PADTEMPLATE_GET (src_factory), "src");
143   gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
144
145   avimux->state = GST_AVIMUX_INITIAL;
146   avimux->riff = NULL;
147   avimux->num_audio_pads = 0;
148   avimux->num_video_pads = 0;
149   avimux->next_time = 0;
150
151   avimux->riff = gst_riff_encoder_new (GST_RIFF_RIFF_AVI);
152   avimux->aviheader = g_malloc0 (sizeof (gst_riff_avih));
153 }
154
155 static GstPadConnectReturn
156 gst_avimux_sinkconnect (GstPad *pad, GstCaps *caps)
157 {
158   GstAviMux *avimux;
159   const gchar* format = gst_caps_get_string (caps, "format");
160   gint padnum = GPOINTER_TO_INT (gst_pad_get_element_private (pad));
161
162   avimux = GST_AVIMUX (gst_pad_get_parent (pad));
163
164   GST_DEBUG (0, "avimux: sinkconnect triggered on %s (%d), %s\n", gst_pad_get_name (pad), 
165                   padnum, format);
166
167   if (!strncmp (format, "strf_vids", 9)) {
168     gst_riff_strf_vids *strf_vids = g_malloc(sizeof(gst_riff_strf_vids));
169
170     strf_vids->size        = sizeof(gst_riff_strf_vids);
171     strf_vids->width       = gst_caps_get_int (caps, "width");
172     strf_vids->height      = gst_caps_get_int (caps, "height");;
173     strf_vids->planes      = gst_caps_get_int (caps, "planes");;
174     strf_vids->bit_cnt     = gst_caps_get_int (caps, "bit_cnt");;
175     strf_vids->compression = gst_caps_get_fourcc_int (caps, "compression");;
176     strf_vids->image_size  = gst_caps_get_int (caps, "image_size");;
177     strf_vids->xpels_meter = gst_caps_get_int (caps, "xpels_meter");;
178     strf_vids->ypels_meter = gst_caps_get_int (caps, "ypels_meter");;
179     strf_vids->num_colors  = gst_caps_get_int (caps, "num_colors");;
180     strf_vids->imp_colors  = gst_caps_get_int (caps, "imp_colors");;
181
182     avimux->video_header[padnum] = strf_vids;
183   }
184   else if (!strncmp (format, "strf_auds", 9)) {
185
186   }
187   return GST_PAD_CONNECT_OK;
188 }
189
190 static GstPad*
191 gst_avimux_request_new_pad (GstElement     *element,
192                             GstPadTemplate *templ,
193                             const gchar    *req_name)
194 {
195   GstAviMux *avimux;
196   gchar *name = NULL;
197   GstPad *newpad;
198   
199   g_return_val_if_fail (templ != NULL, NULL);
200
201   if (templ->direction != GST_PAD_SINK) {
202     g_warning ("avimux: request pad that is not a SINK pad\n");
203     return NULL;
204   }
205
206   g_return_val_if_fail (GST_IS_AVIMUX (element), NULL);
207
208   avimux = GST_AVIMUX (element);
209
210   if (templ == GST_PADTEMPLATE_GET (audio_sink_factory)) {
211     name = g_strdup_printf ("audio_%02d", avimux->num_audio_pads);
212     newpad = gst_pad_new_from_template (templ, name);
213     gst_pad_set_element_private (newpad, GINT_TO_POINTER (avimux->num_audio_pads));
214
215     avimux->audio_pad[avimux->num_audio_pads] = newpad;
216     avimux->num_audio_pads++;
217   }
218   else if (templ == GST_PADTEMPLATE_GET (video_sink_factory)) {
219     name = g_strdup_printf ("video_%02d", avimux->num_video_pads);
220     newpad = gst_pad_new_from_template (templ, name);
221     gst_pad_set_element_private (newpad, GINT_TO_POINTER (avimux->num_video_pads));
222
223     avimux->video_pad[avimux->num_video_pads] = newpad;
224     avimux->num_video_pads++;
225   }
226   else {
227     g_warning ("avimux: this is not our template!\n");
228     return NULL;
229   }
230
231   gst_pad_set_chain_function (newpad, gst_avimux_chain);
232   gst_pad_set_connect_function (newpad, gst_avimux_sinkconnect);
233   gst_element_add_pad (element, newpad);
234   
235   return newpad;
236 }
237
238 static void
239 gst_avimux_make_header (GstAviMux *avimux)
240 {
241   gint i;
242
243   gst_riff_strh strh;
244
245   avimux->aviheader->us_frame = 40000;
246   avimux->aviheader->streams  = avimux->num_video_pads + avimux->num_audio_pads;
247   avimux->aviheader->width    = -1;
248   avimux->aviheader->height   = -1;
249   gst_riff_encoder_avih(avimux->riff, avimux->aviheader, sizeof(gst_riff_avih));
250
251   memset(&strh, 0, sizeof(gst_riff_strh));
252   strh.scale = 40000;
253
254   gst_riff_encoder_strh(avimux->riff, GST_RIFF_FCC_vids, &strh, sizeof(gst_riff_strh));
255
256   for (i=0; i<avimux->num_video_pads; i++) {
257     gst_riff_encoder_strf(avimux->riff, avimux->video_header[i], sizeof(gst_riff_strf_vids));
258   }
259 }
260
261 static void
262 gst_avimux_chain (GstPad *pad, GstBuffer *buf)
263 {
264   GstAviMux *avimux;
265   guchar *data;
266   gulong size;
267   const gchar *padname;
268   gint channel;
269   GstBuffer *newbuf;
270
271   g_return_if_fail (pad != NULL);
272   g_return_if_fail (GST_IS_PAD (pad));
273   g_return_if_fail (buf != NULL);
274   g_return_if_fail (GST_BUFFER_DATA (buf) != NULL);
275
276   avimux = GST_AVIMUX (gst_pad_get_parent (pad));
277
278   data = (guchar *)GST_BUFFER_DATA(buf);
279   size = GST_BUFFER_SIZE(buf);
280
281   switch(avimux->state) {
282     case GST_AVIMUX_INITIAL:
283       GST_DEBUG (0,"gst_avimux_chain: writing header\n");
284       gst_avimux_make_header(avimux);
285       newbuf = gst_riff_encoder_get_and_reset_buffer(avimux->riff);
286       gst_pad_push(avimux->srcpad, newbuf);
287       avimux->state = GST_AVIMUX_MOVI;
288     case GST_AVIMUX_MOVI:
289       padname = gst_pad_get_name (pad);
290       channel = GPOINTER_TO_INT (gst_pad_get_element_private (pad));
291
292       if (strncmp(padname, "audio_", 6) == 0) {
293         GST_DEBUG (0,"gst_avimux_chain: got audio buffer in from channel %02d %lu\n", channel, size);
294         gst_riff_encoder_chunk(avimux->riff, GST_RIFF_01wb, NULL, size); 
295         newbuf = gst_riff_encoder_get_and_reset_buffer(avimux->riff);
296         gst_pad_push(avimux->srcpad, newbuf);
297       }
298       else if (strncmp(padname, "video_", 6) == 0) {
299         GST_DEBUG (0,"gst_avimux_chain: got video buffer in from channel %02d %lu\n", channel, size);
300         gst_riff_encoder_chunk(avimux->riff, GST_RIFF_00db, NULL, size); 
301         newbuf = gst_riff_encoder_get_and_reset_buffer(avimux->riff);
302         GST_DEBUG (0,"gst_avimux_chain: encoded %u\n", GST_BUFFER_SIZE(newbuf));
303         gst_pad_push(avimux->srcpad, newbuf);
304       }
305       GST_BUFFER_SIZE(buf) = (GST_BUFFER_SIZE(buf)+1)&~1;
306       gst_pad_push(avimux->srcpad, buf);
307       break;
308     default:
309       break;
310   }
311
312   //gst_buffer_unref(buf);
313 }
314
315 static void 
316 gst_avimux_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
317 {
318   GstAviMux *src;
319
320   /* it's not null if we got it, but it might not be ours */
321   g_return_if_fail(GST_IS_AVIMUX(object));
322   src = GST_AVIMUX(object);
323
324   switch(prop_id) {
325     default:
326       break;
327   }
328 }
329
330 static void 
331 gst_avimux_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
332 {
333   GstAviMux *src;
334
335   /* it's not null if we got it, but it might not be ours */
336   g_return_if_fail(GST_IS_AVIMUX(object));
337   src = GST_AVIMUX(object);
338
339   switch(prop_id) {
340     default:
341       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
342       break;
343   }
344 }
345
346 static gboolean
347 plugin_init (GModule *module, GstPlugin *plugin)
348 {
349   GstElementFactory *factory;
350
351   /* this filter needs the riff parser */
352   if (!gst_library_load ("gstriff")) {
353     gst_info ("avimux: could not load support library: 'gstriff'\n");
354     return FALSE;
355   }
356
357   /* create an elementfactory for the avimux element */
358   factory = gst_elementfactory_new ("avimux", GST_TYPE_AVIMUX,
359                                     &gst_avimux_details);
360   g_return_val_if_fail (factory != NULL, FALSE);
361
362   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_factory));
363   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (audio_sink_factory));
364   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (video_sink_factory));
365   
366   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
367
368   return TRUE;
369 }
370
371 GstPluginDesc plugin_desc = {
372   GST_VERSION_MAJOR,
373   GST_VERSION_MINOR,
374   "avimux",
375   plugin_init
376 };
377