• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
4  * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  * Copyright (C) <2011-2012> Vincent Penquerc'h <vincent.penquerch@collabora.co.uk>
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., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:element-opusparse
25  * @title: opusparse
26  * @see_also: opusenc, opusdec
27  *
28  * This element parses OPUS packets.
29  *
30  * ## Example pipelines
31  * |[
32  * gst-launch-1.0 -v filesrc location=opusdata ! opusparse ! opusdec ! audioconvert ! audioresample ! alsasink
33  * ]| Decode and plays an unmuxed Opus file.
34  *
35  */
36 
37 #ifdef HAVE_CONFIG_H
38 #  include "config.h"
39 #endif
40 
41 #include <string.h>
42 #include <opus.h>
43 #include "gstopusheader.h"
44 #include "gstopusparse.h"
45 
46 #include <gst/audio/audio.h>
47 #include <gst/pbutils/pbutils.h>
48 #include <gst/tag/tag.h>
49 
50 GST_DEBUG_CATEGORY_STATIC (opusparse_debug);
51 #define GST_CAT_DEFAULT opusparse_debug
52 
53 #define MAX_PAYLOAD_BYTES 1500
54 
55 static GstStaticPadTemplate opus_parse_src_factory =
56 GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS ("audio/x-opus, framed = (boolean) true")
60     );
61 
62 static GstStaticPadTemplate opus_parse_sink_factory =
63 GST_STATIC_PAD_TEMPLATE ("sink",
64     GST_PAD_SINK,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS ("audio/x-opus")
67     );
68 
69 
70 
71 static gboolean gst_opus_parse_start (GstBaseParse * parse);
72 static gboolean gst_opus_parse_stop (GstBaseParse * parse);
73 static GstFlowReturn gst_opus_parse_handle_frame (GstBaseParse * base,
74     GstBaseParseFrame * frame, gint * skip);
75 static GstFlowReturn gst_opus_parse_parse_frame (GstBaseParse * base,
76     GstBaseParseFrame * frame);
77 static gboolean opusparse_element_init (GstPlugin * plugin);
78 
79 G_DEFINE_TYPE (GstOpusParse, gst_opus_parse, GST_TYPE_BASE_PARSE);
80 GST_ELEMENT_REGISTER_DEFINE_CUSTOM (opusparse, opusparse_element_init);
81 
82 static void
gst_opus_parse_class_init(GstOpusParseClass * klass)83 gst_opus_parse_class_init (GstOpusParseClass * klass)
84 {
85   GstBaseParseClass *bpclass;
86   GstElementClass *element_class;
87 
88   bpclass = (GstBaseParseClass *) klass;
89   element_class = (GstElementClass *) klass;
90 
91   bpclass->start = GST_DEBUG_FUNCPTR (gst_opus_parse_start);
92   bpclass->stop = GST_DEBUG_FUNCPTR (gst_opus_parse_stop);
93   bpclass->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_parse_handle_frame);
94 
95   gst_element_class_add_static_pad_template (element_class,
96       &opus_parse_src_factory);
97   gst_element_class_add_static_pad_template (element_class,
98       &opus_parse_sink_factory);
99   gst_element_class_set_static_metadata (element_class, "Opus audio parser",
100       "Codec/Parser/Audio", "parses opus audio streams",
101       "Vincent Penquerc'h <vincent.penquerch@collabora.co.uk>");
102 
103   GST_DEBUG_CATEGORY_INIT (opusparse_debug, "opusparse", 0,
104       "opus parsing element");
105 }
106 
107 static void
gst_opus_parse_init(GstOpusParse * parse)108 gst_opus_parse_init (GstOpusParse * parse)
109 {
110   parse->header_sent = FALSE;
111   parse->got_headers = FALSE;
112   parse->pre_skip = 0;
113 }
114 
115 static gboolean
gst_opus_parse_start(GstBaseParse * base)116 gst_opus_parse_start (GstBaseParse * base)
117 {
118   GstOpusParse *parse = GST_OPUS_PARSE (base);
119 
120   parse->header_sent = FALSE;
121   parse->got_headers = FALSE;
122   parse->pre_skip = 0;
123   parse->next_ts = 0;
124 
125   return TRUE;
126 }
127 
128 static gboolean
gst_opus_parse_stop(GstBaseParse * base)129 gst_opus_parse_stop (GstBaseParse * base)
130 {
131   GstOpusParse *parse = GST_OPUS_PARSE (base);
132 
133   parse->header_sent = FALSE;
134   parse->got_headers = FALSE;
135   parse->pre_skip = 0;
136 
137   return TRUE;
138 }
139 
140 static GstFlowReturn
gst_opus_parse_handle_frame(GstBaseParse * base,GstBaseParseFrame * frame,gint * skip)141 gst_opus_parse_handle_frame (GstBaseParse * base,
142     GstBaseParseFrame * frame, gint * skip)
143 {
144   GstOpusParse *parse;
145   guint8 *data;
146   gsize size;
147   guint32 packet_size;
148   int ret = FALSE;
149   const unsigned char *frames[48];
150   unsigned char toc;
151   short frame_sizes[48];
152   int payload_offset;
153   int packet_offset = 0;
154   gboolean is_header, is_idheader, is_commentheader;
155   GstMapInfo map;
156 
157   parse = GST_OPUS_PARSE (base);
158 
159   *skip = -1;
160 
161   gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
162   data = map.data;
163   size = map.size;
164   GST_DEBUG_OBJECT (parse,
165       "Checking for frame, %" G_GSIZE_FORMAT " bytes in buffer", size);
166 
167   /* check for headers */
168   is_idheader = gst_opus_header_is_id_header (frame->buffer);
169   is_commentheader = gst_opus_header_is_comment_header (frame->buffer);
170   is_header = is_idheader || is_commentheader;
171 
172   if (!is_header) {
173     int nframes;
174 
175     /* Next, check if there's an Opus packet there */
176     nframes =
177         opus_packet_parse (data, size, &toc, frames, frame_sizes,
178         &payload_offset);
179 
180     if (nframes < 0) {
181       /* Then, check for the test vector framing */
182       GST_DEBUG_OBJECT (parse,
183           "No Opus packet found, trying test vector framing");
184       if (size < 4) {
185         GST_DEBUG_OBJECT (parse, "Too small");
186         goto beach;
187       }
188       packet_size = GST_READ_UINT32_BE (data);
189       GST_DEBUG_OBJECT (parse, "Packet size: %u bytes", packet_size);
190       if (packet_size > MAX_PAYLOAD_BYTES) {
191         GST_DEBUG_OBJECT (parse, "Too large");
192         goto beach;
193       }
194       if (packet_size > size - 4) {
195         GST_DEBUG_OBJECT (parse, "Truncated");
196         goto beach;
197       }
198       nframes =
199           opus_packet_parse (data + 8, packet_size, &toc, frames, frame_sizes,
200           &payload_offset);
201       if (nframes < 0) {
202         GST_DEBUG_OBJECT (parse, "No test vector framing either");
203         goto beach;
204       }
205 
206       packet_offset = 8;
207 
208       /* for ad hoc framing, heed the framing, so we eat any padding */
209       payload_offset = packet_size;
210     } else {
211       /* Add up all the frame sizes found */
212       int f;
213       for (f = 0; f < nframes; ++f)
214         payload_offset += frame_sizes[f];
215     }
216   }
217 
218   if (is_header) {
219     *skip = 0;
220   } else {
221     *skip = packet_offset;
222     size = payload_offset;
223   }
224 
225   GST_DEBUG_OBJECT (parse,
226       "Got Opus packet at offset %d, %" G_GSIZE_FORMAT " bytes", *skip, size);
227   ret = TRUE;
228 
229 beach:
230   gst_buffer_unmap (frame->buffer, &map);
231 
232   /* convert old style result to new one */
233   if (!ret) {
234     if (*skip < 0)
235       *skip = 1;
236     return GST_FLOW_OK;
237   }
238 
239   /* always skip first if needed */
240   if (*skip > 0)
241     return GST_FLOW_OK;
242 
243   /* normalize again */
244   if (*skip < 0)
245     *skip = 0;
246 
247   /* not enough */
248   if (size > map.size)
249     return GST_FLOW_OK;
250 
251   /* FIXME some day ... should not mess with buffer itself */
252   if (!parse->got_headers) {
253     gst_buffer_replace (&frame->buffer,
254         gst_buffer_copy_region (frame->buffer, GST_BUFFER_COPY_ALL, 0, size));
255     gst_buffer_unref (frame->buffer);
256   }
257 
258   ret = gst_opus_parse_parse_frame (base, frame);
259 
260   if (ret == GST_BASE_PARSE_FLOW_DROPPED) {
261     frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
262     ret = GST_FLOW_OK;
263   }
264   if (ret == GST_FLOW_OK)
265     ret = gst_base_parse_finish_frame (base, frame, size);
266 
267   return ret;
268 }
269 
270 /* Adapted copy of the one in gstoggstream.c... */
271 static guint64
packet_duration_opus(const guint8 * data,size_t len)272 packet_duration_opus (const guint8 * data, size_t len)
273 {
274   static const guint64 durations[32] = {
275     10000, 20000, 40000, 60000, /* Silk NB */
276     10000, 20000, 40000, 60000, /* Silk MB */
277     10000, 20000, 40000, 60000, /* Silk WB */
278     10000, 20000,               /* Hybrid SWB */
279     10000, 20000,               /* Hybrid FB */
280     2500, 5000, 10000, 20000,   /* CELT NB */
281     2500, 5000, 10000, 20000,   /* CELT NB */
282     2500, 5000, 10000, 20000,   /* CELT NB */
283     2500, 5000, 10000, 20000,   /* CELT NB */
284   };
285 
286   gint64 duration;
287   gint64 frame_duration;
288   gint nframes = 0;
289   guint8 toc;
290 
291   if (len < 1)
292     return 0;
293 
294   toc = data[0];
295 
296   frame_duration = durations[toc >> 3] * 1000;
297   switch (toc & 3) {
298     case 0:
299       nframes = 1;
300       break;
301     case 1:
302       nframes = 2;
303       break;
304     case 2:
305       nframes = 2;
306       break;
307     case 3:
308       if (len < 2) {
309         GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
310         return 0;
311       }
312       nframes = data[1] & 63;
313       break;
314   }
315 
316   duration = nframes * frame_duration;
317   if (duration > 120 * GST_MSECOND) {
318     GST_WARNING ("Opus packet duration > 120 ms, invalid");
319     return 0;
320   }
321   GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
322       frame_duration / 1000000.f, nframes, duration / 1000000.f);
323   return duration;
324 }
325 
326 static GstFlowReturn
gst_opus_parse_parse_frame(GstBaseParse * base,GstBaseParseFrame * frame)327 gst_opus_parse_parse_frame (GstBaseParse * base, GstBaseParseFrame * frame)
328 {
329   guint64 duration;
330   GstOpusParse *parse;
331   gboolean is_idheader, is_commentheader;
332   GstMapInfo map;
333   GstAudioClippingMeta *cmeta =
334       gst_buffer_get_audio_clipping_meta (frame->buffer);
335 
336   parse = GST_OPUS_PARSE (base);
337 
338   g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
339 
340   is_idheader = gst_opus_header_is_id_header (frame->buffer);
341   is_commentheader = gst_opus_header_is_comment_header (frame->buffer);
342 
343   if (!parse->got_headers || !parse->header_sent) {
344     GstCaps *caps;
345 
346     /* Opus streams can decode to 1 or 2 channels, so use the header
347        value if we have one, or 2 otherwise */
348     if (is_idheader) {
349       gst_buffer_replace (&parse->id_header, frame->buffer);
350       GST_DEBUG_OBJECT (parse, "Found ID header, keeping");
351       return GST_BASE_PARSE_FLOW_DROPPED;
352     } else if (is_commentheader) {
353       gst_buffer_replace (&parse->comment_header, frame->buffer);
354       GST_DEBUG_OBJECT (parse, "Found comment header, keeping");
355       return GST_BASE_PARSE_FLOW_DROPPED;
356     }
357 
358     parse->got_headers = TRUE;
359 
360     if (cmeta && cmeta->start) {
361       parse->pre_skip += cmeta->start;
362 
363       gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
364       duration = packet_duration_opus (map.data, map.size);
365       gst_buffer_unmap (frame->buffer, &map);
366 
367       /* Queue frame for later once we know all initial padding */
368       if (duration == cmeta->start) {
369         frame->flags |= GST_BASE_PARSE_FRAME_FLAG_QUEUE;
370       }
371     }
372 
373     if (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_QUEUE)) {
374       GstCaps *sink_caps;
375       guint32 sample_rate = 48000;
376       guint8 n_channels, n_streams, n_stereo_streams, channel_mapping_family;
377       guint8 channel_mapping[256];
378       GstBuffer *id_header;
379       guint16 pre_skip = 0;
380       gint16 gain = 0;
381 
382       if (parse->id_header) {
383         gst_buffer_map (parse->id_header, &map, GST_MAP_READWRITE);
384         pre_skip = GST_READ_UINT16_LE (map.data + 10);
385         gain = GST_READ_UINT16_LE (map.data + 16);
386         gst_buffer_unmap (parse->id_header, &map);
387       }
388 
389       sink_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
390       if (!sink_caps
391           || !gst_codec_utils_opus_parse_caps (sink_caps, &sample_rate,
392               &n_channels, &channel_mapping_family, &n_streams,
393               &n_stereo_streams, channel_mapping)) {
394         GST_INFO_OBJECT (parse,
395             "No headers and no caps, blindly setting up canonical stereo");
396         n_channels = 2;
397         n_streams = 1;
398         n_stereo_streams = 1;
399         channel_mapping_family = 0;
400         channel_mapping[0] = 0;
401         channel_mapping[1] = 1;
402       }
403       if (sink_caps)
404         gst_caps_unref (sink_caps);
405 
406       id_header =
407           gst_codec_utils_opus_create_header (sample_rate, n_channels,
408           channel_mapping_family, n_streams, n_stereo_streams,
409           channel_mapping, pre_skip, gain);
410       caps = gst_codec_utils_opus_create_caps_from_header (id_header, NULL);
411       gst_buffer_unref (id_header);
412 
413       gst_buffer_replace (&parse->id_header, NULL);
414       gst_buffer_replace (&parse->comment_header, NULL);
415 
416       gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
417       gst_caps_unref (caps);
418       parse->header_sent = TRUE;
419     }
420   }
421 
422   GST_BUFFER_TIMESTAMP (frame->buffer) = parse->next_ts;
423 
424   gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
425   duration = packet_duration_opus (map.data, map.size);
426   gst_buffer_unmap (frame->buffer, &map);
427   parse->next_ts += duration;
428 
429   GST_BUFFER_DURATION (frame->buffer) = duration;
430   GST_BUFFER_OFFSET_END (frame->buffer) =
431       gst_util_uint64_scale (parse->next_ts, 48000, GST_SECOND);
432   GST_BUFFER_OFFSET (frame->buffer) = parse->next_ts;
433 
434   return GST_FLOW_OK;
435 }
436 
437 static gboolean
opusparse_element_init(GstPlugin * plugin)438 opusparse_element_init (GstPlugin * plugin)
439 {
440   if (!gst_element_register (plugin, "opusparse", GST_RANK_NONE,
441           GST_TYPE_OPUS_PARSE))
442     return FALSE;
443 
444   gst_tag_register_musicbrainz_tags ();
445   return TRUE;
446 }
447