matroskademux: put streamheaders on vorbis/speex/flac/theora caps to make remuxing...
[platform/upstream/gstreamer.git] / gst / matroska / matroska-ids.c
1 /* GStreamer Matroska muxer/demuxer
2  * (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  * (C) 2006 Tim-Philipp Müller <tim centricular net>
4  *
5  * matroska-ids.c: matroska track context utility functions
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "matroska-ids.h"
28
29 #include <string.h>
30
31 gboolean
32 gst_matroska_track_init_video_context (GstMatroskaTrackContext ** p_context)
33 {
34   GstMatroskaTrackVideoContext *video_context;
35
36   g_assert (p_context != NULL && *p_context != NULL);
37
38   /* already set up? (track info might come before track type) */
39   if ((*p_context)->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
40     GST_LOG ("video context already set up");
41     return TRUE;
42   }
43
44   /* it better not have been set up as some other track type ... */
45   if ((*p_context)->type != 0) {
46     g_return_val_if_reached (FALSE);
47   }
48
49   video_context = g_renew (GstMatroskaTrackVideoContext, *p_context, 1);
50   *p_context = (GstMatroskaTrackContext *) video_context;
51
52   /* defaults */
53   (*p_context)->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
54   video_context->display_width = 0;
55   video_context->display_height = 0;
56   video_context->pixel_width = 0;
57   video_context->pixel_height = 0;
58   video_context->asr_mode = 0;
59   video_context->fourcc = 0;
60   video_context->default_fps = 0.0;
61   video_context->earliest_time = GST_CLOCK_TIME_NONE;
62   return TRUE;
63 }
64
65 gboolean
66 gst_matroska_track_init_audio_context (GstMatroskaTrackContext ** p_context)
67 {
68   GstMatroskaTrackAudioContext *audio_context;
69
70   g_assert (p_context != NULL && *p_context != NULL);
71
72   /* already set up? (track info might come before track type) */
73   if ((*p_context)->type == GST_MATROSKA_TRACK_TYPE_AUDIO)
74     return TRUE;
75
76   /* it better not have been set up as some other track type ... */
77   if ((*p_context)->type != 0) {
78     g_return_val_if_reached (FALSE);
79   }
80
81   audio_context = g_renew (GstMatroskaTrackAudioContext, *p_context, 1);
82   *p_context = (GstMatroskaTrackContext *) audio_context;
83
84   /* defaults */
85   (*p_context)->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
86   audio_context->channels = 1;
87   audio_context->samplerate = 8000;
88   return TRUE;
89 }
90
91 gboolean
92 gst_matroska_track_init_subtitle_context (GstMatroskaTrackContext ** p_context)
93 {
94   GstMatroskaTrackSubtitleContext *subtitle_context;
95
96   g_assert (p_context != NULL && *p_context != NULL);
97
98   /* already set up? (track info might come before track type) */
99   if ((*p_context)->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE)
100     return TRUE;
101
102   /* it better not have been set up as some other track type ... */
103   if ((*p_context)->type != 0) {
104     g_return_val_if_reached (FALSE);
105   }
106
107   subtitle_context = g_renew (GstMatroskaTrackSubtitleContext, *p_context, 1);
108   *p_context = (GstMatroskaTrackContext *) subtitle_context;
109
110   (*p_context)->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
111   subtitle_context->invalid_utf8 = FALSE;
112   subtitle_context->seen_markup_tag = FALSE;
113   return TRUE;
114 }
115
116 void
117 gst_matroska_register_tags (void)
118 {
119   /* TODO: register other custom tags */
120 }
121
122 GstBufferList *
123 gst_matroska_parse_xiph_stream_headers (gpointer codec_data,
124     gsize codec_data_size)
125 {
126   GstBufferList *list = NULL;
127   guint8 *p = codec_data;
128   gint i, offset, num_packets;
129   guint *length, last;
130
131   GST_MEMDUMP ("xiph codec data", codec_data, codec_data_size);
132
133   if (codec_data == NULL || codec_data_size == 0)
134     goto error;
135
136   /* start of the stream and vorbis audio or theora video, need to
137    * send the codec_priv data as first three packets */
138   num_packets = p[0] + 1;
139   GST_DEBUG ("%u stream headers, total length=%" G_GSIZE_FORMAT " bytes",
140       (guint) num_packets, codec_data_size);
141
142   length = g_alloca (num_packets * sizeof (guint));
143   last = 0;
144   offset = 1;
145
146   /* first packets, read length values */
147   for (i = 0; i < num_packets - 1; i++) {
148     length[i] = 0;
149     while (offset < codec_data_size) {
150       length[i] += p[offset];
151       if (p[offset++] != 0xff)
152         break;
153     }
154     last += length[i];
155   }
156   if (offset + last > codec_data_size)
157     goto error;
158
159   /* last packet is the remaining size */
160   length[i] = codec_data_size - offset - last;
161
162   list = gst_buffer_list_new ();
163
164   for (i = 0; i < num_packets; i++) {
165     GstBuffer *hdr;
166
167     GST_DEBUG ("buffer %d: %u bytes", i, (guint) length[i]);
168
169     if (offset + length[i] > codec_data_size)
170       goto error;
171
172     hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]);
173     gst_buffer_list_add (list, hdr);
174
175     offset += length[i];
176   }
177
178   return list;
179
180 /* ERRORS */
181 error:
182   {
183     if (list != NULL)
184       gst_buffer_list_unref (list);
185     return NULL;
186   }
187 }
188
189 GstBufferList *
190 gst_matroska_parse_speex_stream_headers (gpointer codec_data,
191     gsize codec_data_size)
192 {
193   GstBufferList *list = NULL;
194   GstBuffer *hdr;
195   guint8 *pdata = codec_data;
196
197   GST_MEMDUMP ("speex codec data", codec_data, codec_data_size);
198
199   if (codec_data == NULL || codec_data_size < 80) {
200     GST_WARNING ("not enough codec priv data for speex headers");
201     return NULL;
202   }
203
204   if (memcmp (pdata, "Speex   ", 8) != 0) {
205     GST_WARNING ("no Speex marker at start of stream headers");
206     return NULL;
207   }
208
209   list = gst_buffer_list_new ();
210
211   hdr = gst_buffer_new_wrapped (g_memdup (pdata, 80), 80);
212   gst_buffer_list_add (list, hdr);
213
214   if (codec_data_size > 80) {
215     hdr = gst_buffer_new_wrapped (g_memdup (pdata + 80, codec_data_size - 80),
216         codec_data_size - 80);
217     gst_buffer_list_add (list, hdr);
218   }
219
220   return list;
221 }
222
223 GstBufferList *
224 gst_matroska_parse_flac_stream_headers (gpointer codec_data,
225     gsize codec_data_size)
226 {
227   GstBufferList *list = NULL;
228   GstBuffer *hdr;
229   guint8 *pdata = codec_data;
230   guint len, off;
231
232   GST_MEMDUMP ("flac codec data", codec_data, codec_data_size);
233
234   /* need at least 'fLaC' marker + STREAMINFO metadata block */
235   if (codec_data == NULL || codec_data_size < ((4) + (4 + 34))) {
236     GST_WARNING ("not enough codec priv data for flac headers");
237     return NULL;
238   }
239
240   if (memcmp (pdata, "fLaC", 4) != 0) {
241     GST_WARNING ("no flac marker at start of stream headers");
242     return NULL;
243   }
244
245   list = gst_buffer_list_new ();
246
247   hdr = gst_buffer_new_wrapped (g_memdup (pdata, 4), 4);
248   gst_buffer_list_add (list, hdr);
249
250   /* skip fLaC marker */
251   off = 4;
252
253   /* FIXME: check size remaining */
254   while (off < codec_data_size) {
255     len = GST_READ_UINT8 (pdata + off + 1) << 16;
256     len |= GST_READ_UINT8 (pdata + off + 2) << 8;
257     len |= GST_READ_UINT8 (pdata + off + 3);
258
259     GST_DEBUG ("header packet: len=%u bytes, flags=0x%02x", len, pdata[off]);
260
261     /* FIXME: check size remaining */
262     hdr = gst_buffer_new_wrapped (g_memdup (pdata + off, len + 4), len + 4);
263     gst_buffer_list_add (list, hdr);
264
265     off += 4 + len;
266   }
267   return list;
268 }