/* GStreamer * * Copyright (C) 2008 Sebastian Dröge . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstflacparse.h" #include #include #include #include #include GST_DEBUG_CATEGORY_STATIC (flacparse_debug); #define GST_CAT_DEFAULT flacparse_debug static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-flac, framed = (boolean) true, " "channels = (int) [ 1, 8 ], " "rate = (int) [ 1, 655350 ]") ); static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-flac, framed = (boolean) false") ); static void gst_flac_parse_finalize (GObject * object); static gboolean gst_flac_parse_start (GstBaseParse * parse); static gboolean gst_flac_parse_stop (GstBaseParse * parse); static gboolean gst_flac_parse_check_valid_frame (GstBaseParse * parse, GstBuffer * buffer, guint * framesize, gint * skipsize); static GstFlowReturn gst_flac_parse_parse_frame (GstBaseParse * parse, GstBuffer * buffer); GST_BOILERPLATE (GstFlacParse, gst_flac_parse, GstBaseParse, GST_TYPE_BASE_PARSE); static void gst_flac_parse_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_factory)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_factory)); gst_element_class_set_details_simple (element_class, "FLAC audio parser", "Codec/Parser/Audio", "Parses audio with the FLAC lossless audio codec", "Sebastian Dröge "); GST_DEBUG_CATEGORY_INIT (flacparse_debug, "flacparse", 0, "Flac parser element"); } static void gst_flac_parse_class_init (GstFlacParseClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstBaseParseClass *baseparse_class = GST_BASE_PARSE_CLASS (klass); gobject_class->finalize = gst_flac_parse_finalize; baseparse_class->start = GST_DEBUG_FUNCPTR (gst_flac_parse_start); baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_flac_parse_stop); baseparse_class->check_valid_frame = GST_DEBUG_FUNCPTR (gst_flac_parse_check_valid_frame); baseparse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_flac_parse_parse_frame); } static void gst_flac_parse_init (GstFlacParse * flacparse, GstFlacParseClass * klass) { } static void gst_flac_parse_finalize (GObject * object) { GstFlacParse *flacparse = GST_FLAC_PARSE (object); if (flacparse->tags) { gst_tag_list_free (flacparse->tags); flacparse->tags = NULL; } g_list_foreach (flacparse->headers, (GFunc) gst_mini_object_unref, NULL); g_list_free (flacparse->headers); flacparse->headers = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean gst_flac_parse_start (GstBaseParse * parse) { GstFlacParse *flacparse = GST_FLAC_PARSE (parse); flacparse->state = GST_FLAC_PARSE_STATE_INIT; flacparse->min_blocksize = 0; flacparse->max_blocksize = 0; flacparse->min_framesize = 0; flacparse->max_framesize = 0; flacparse->upstream_length = -1; flacparse->samplerate = 0; flacparse->channels = 0; flacparse->bps = 0; flacparse->total_samples = 0; flacparse->requested_frame_size = 0; flacparse->offset = GST_CLOCK_TIME_NONE; flacparse->blocking_strategy = 0; flacparse->block_size = 0; flacparse->sample_number = 0; /* "fLaC" marker */ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 4); return TRUE; } static gboolean gst_flac_parse_stop (GstBaseParse * parse) { GstFlacParse *flacparse = GST_FLAC_PARSE (parse); if (flacparse->tags) { gst_tag_list_free (flacparse->tags); flacparse->tags = NULL; } g_list_foreach (flacparse->headers, (GFunc) gst_mini_object_unref, NULL); g_list_free (flacparse->headers); flacparse->headers = NULL; return TRUE; } static gint gst_flac_parse_get_frame_size (GstFlacParse * flacparse, GstBuffer * buffer, guint * framesize_ret) { GstBitReader reader = GST_BIT_READER_INIT_FROM_BUFFER (buffer); guint16 samplerate; guint8 tmp; gint i; guint8 channel_assignment = 0; /* Skip 14 bit sync code */ if (!gst_bit_reader_skip (&reader, 14)) goto need_more_data; /* Must be 0 */ if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) goto need_more_data; else if (tmp != 0) goto error; /* 0 == fixed block size, 1 == variable block size */ if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->blocking_strategy, 1)) goto need_more_data; /* block size index, calculation of the real blocksize below */ if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->block_size, 4)) goto need_more_data; else if (flacparse->block_size == 0) goto error; /* sample rate index, calculation of the real samplerate below */ if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 4)) goto need_more_data; else if (samplerate == 0x0f) goto error; /* channel assignment */ if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 4)) { goto need_more_data; } else if (tmp < 8) { if (flacparse->channels && tmp + 1 != flacparse->channels) goto error; else flacparse->channels = tmp + 1; } else if (tmp <= 10 && flacparse->channels != 2) { if (flacparse->channels && 2 != flacparse->channels) goto error; else flacparse->channels = 2; if (tmp == 8) channel_assignment = 1; /* left-side */ else if (tmp == 9) channel_assignment = 2; /* right-side */ else channel_assignment = 3; /* mid-side */ } else if (tmp > 10) { goto error; } /* bits per sample */ if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 3)) { goto need_more_data; } else if (tmp == 0x03 || tmp == 0x07) { goto error; } else if (tmp == 0 && flacparse->bps == 0) { goto need_streaminfo; } else if (tmp == 0x01 && flacparse->bps != 8) { if (flacparse->bps && flacparse->bps != 8) goto error; else flacparse->bps = 8; } else if (tmp == 0x02 && flacparse->bps != 12) { if (flacparse->bps && flacparse->bps != 12) goto error; else flacparse->bps = 12; } else if (tmp == 0x04 && flacparse->bps != 16) { if (flacparse->bps && flacparse->bps != 16) goto error; else flacparse->bps = 16; } else if (tmp == 0x05 && flacparse->bps != 20) { if (flacparse->bps && flacparse->bps != 20) goto error; else flacparse->bps = 20; } else if (tmp == 0x06 && flacparse->bps != 24) { if (flacparse->bps && flacparse->bps != 24) goto error; else flacparse->bps = 24; } /* reserved, must be 0 */ if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) goto need_more_data; else if (tmp != 0) goto error; /* read "utf8" encoded sample/frame number */ { guint len = 0; tmp = 1; while (tmp != 0) { if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) goto need_more_data; else if (tmp == 1) len++; } if (len == 1) goto error; flacparse->sample_number = 0; if (len == 0) { if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 7)) goto need_more_data; flacparse->sample_number = tmp; } else if ((flacparse->blocking_strategy == 0 && len > 6) || (flacparse->blocking_strategy == 1 && len > 7)) { goto error; } else { if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 8 - len - 1)) goto need_more_data; flacparse->sample_number = tmp; len -= 1; while (len > 0) { if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 2)) goto need_more_data; else if (tmp != 0x02) goto error; if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 6)) goto need_more_data; flacparse->sample_number <<= 6; flacparse->sample_number |= tmp; len--; } } } /* calculate real blocksize from the blocksize index */ if (flacparse->block_size == 1) flacparse->block_size = 192; else if (flacparse->block_size <= 5) flacparse->block_size = 576 * (1 << (flacparse->block_size - 2)); else if (flacparse->block_size <= 15) flacparse->block_size = 256 * (1 << (flacparse->block_size - 8)); else if (flacparse->block_size == 6) { if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->block_size, 8)) goto need_more_data; flacparse->block_size++; } else if (flacparse->block_size == 7) { if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->block_size, 16)) goto need_more_data; flacparse->block_size++; } /* calculate the real samplerate from the samplerate index */ if (samplerate == 0 && flacparse->samplerate == 0) { goto need_streaminfo; } else if (samplerate == 1) { if (flacparse->samplerate == 0) flacparse->samplerate = 88200; else if (flacparse->samplerate != 88200) goto error; } else if (samplerate == 2) { if (flacparse->samplerate == 0) flacparse->samplerate = 176400; else if (flacparse->samplerate != 176400) goto error; } else if (samplerate == 3) { if (flacparse->samplerate == 0) flacparse->samplerate = 192000; else if (flacparse->samplerate != 192000) goto error; } else if (samplerate == 4) { if (flacparse->samplerate == 0) flacparse->samplerate = 8000; else if (flacparse->samplerate != 8000) goto error; } else if (samplerate == 5) { if (flacparse->samplerate == 0) flacparse->samplerate = 16000; else if (flacparse->samplerate != 16000) goto error; } else if (samplerate == 6) { if (flacparse->samplerate == 0) flacparse->samplerate = 22050; else if (flacparse->samplerate != 22050) goto error; } else if (samplerate == 7) { if (flacparse->samplerate == 0) flacparse->samplerate = 24000; else if (flacparse->samplerate != 24000) goto error; } else if (samplerate == 8) { if (flacparse->samplerate == 0) flacparse->samplerate = 32000; else if (flacparse->samplerate != 32000) goto error; } else if (samplerate == 9) { if (flacparse->samplerate == 0) flacparse->samplerate = 44100; else if (flacparse->samplerate != 44100) goto error; } else if (samplerate == 10) { if (flacparse->samplerate == 0) flacparse->samplerate = 48000; else if (flacparse->samplerate != 48000) goto error; } else if (samplerate == 11) { if (flacparse->samplerate == 0) flacparse->samplerate = 96000; else if (flacparse->samplerate != 96000) goto error; } else if (samplerate == 12) { if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 8)) goto need_more_data; samplerate *= 1000; if (flacparse->samplerate == 0) flacparse->samplerate = samplerate; else if (flacparse->samplerate != samplerate) goto error; } else if (samplerate == 13) { if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 16)) goto need_more_data; if (flacparse->samplerate == 0) flacparse->samplerate = samplerate; else if (flacparse->samplerate != samplerate) goto error; } else if (samplerate == 14) { if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 16)) goto need_more_data; samplerate *= 10; if (flacparse->samplerate == 0) flacparse->samplerate = samplerate; else if (flacparse->samplerate != samplerate) goto error; } /* skip crc-8 for the header */ if (!gst_bit_reader_skip (&reader, 8)) goto need_more_data; /* parse subframes, one subframe per channel */ for (i = 0; i < flacparse->channels; i++) { guint8 sf_type; guint8 cur_bps; cur_bps = flacparse->bps; /* for mid/side, left/side, right/side the "difference" channel * needs and additional bit */ if (i == 0 && channel_assignment == 2) cur_bps++; else if (i == 1 && (channel_assignment == 1 || channel_assignment == 3)) cur_bps++; /* must be 0 */ if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) goto need_more_data; else if (tmp != 0) goto error; /* sub frame type */ if (!gst_bit_reader_get_bits_uint8 (&reader, &sf_type, 6)) goto need_more_data; else if (((sf_type & 0xfe) == 0x02) || ((sf_type & 0xfc) == 0x04) || ((sf_type & 0xf8) == 0x08 && (sf_type & 0x07) > 4) || ((sf_type & 0xf0) == 0x10)) goto error; /* wasted bits per sample, if 1 the value follows unary coded */ if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) { goto need_more_data; } else if (tmp != 0) { guint wasted = 1; tmp = 0; while (tmp == 0) { if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) goto need_more_data; else wasted++; } cur_bps -= wasted; } /* subframe type: constant */ if (sf_type == 0x00) { if (!gst_bit_reader_skip (&reader, cur_bps)) goto need_more_data; /* subframe type: verbatim */ } else if (sf_type == 0x01) { if (!gst_bit_reader_skip (&reader, cur_bps * flacparse->block_size)) goto need_more_data; /* subframe type: LPC or fixed */ } else { guint8 residual_type; guint order = 0; guint16 partition_order; guint j; /* Skip warm-up samples for fixed subframe and calculate order */ if ((sf_type & 0xf8) == 0x08) { order = sf_type & 0x07; g_assert (order <= 4); if (!gst_bit_reader_skip (&reader, cur_bps * order)) goto need_more_data; /* Skip warm-up samples for LPC subframe, get parameters and calculate order */ } else if ((sf_type & 0xe0) == 0x20) { guint8 prec; order = (sf_type & 0x1f) + 1; /* warm-up samples */ if (!gst_bit_reader_skip (&reader, cur_bps * order)) goto need_more_data; /* LPC coefficient precision */ if (!gst_bit_reader_get_bits_uint8 (&reader, &prec, 4)) goto need_more_data; else if (prec == 0x0f) goto error; prec++; /* LPC coefficient shift */ if (!gst_bit_reader_skip (&reader, 5)) goto need_more_data; /* LPC coefficients */ if (!gst_bit_reader_skip (&reader, order * prec)) goto need_more_data; } else { g_assert_not_reached (); } /* residual type: 0 == rice, 1 == rice2 */ if (!gst_bit_reader_get_bits_uint8 (&reader, &residual_type, 2)) goto need_more_data; if (residual_type & 0x02) goto error; /* partition order */ if (!gst_bit_reader_get_bits_uint16 (&reader, &partition_order, 4)) goto need_more_data; partition_order = 1 << partition_order; /* 2^partition_order partitions */ for (j = 0; j < partition_order; j++) { guint samples; guint8 rice_parameter; /* calculate number of samples for the current partition */ if (partition_order == 1) { samples = flacparse->block_size - order; } else if (j != 0) { samples = flacparse->block_size / partition_order; } else { samples = flacparse->block_size / partition_order - order; } /* rice parameter */ if (!gst_bit_reader_get_bits_uint8 (&reader, &rice_parameter, (residual_type == 0) ? 4 : 5)) goto need_more_data; /* if rice parameter has all bits set the samples follow unencoded with the number of bits * per sample in the following 5 bits */ if ((residual_type == 0 && rice_parameter == 0x0f) || (residual_type == 1 && rice_parameter == 0x1f)) { if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 5)) goto need_more_data; if (!gst_bit_reader_skip (&reader, tmp * samples)) goto need_more_data; } else { guint k; /* read the rice encoded samples */ for (k = 0; k < samples; k++) { tmp = 0; while (tmp == 0) if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) goto need_more_data; if (!gst_bit_reader_skip (&reader, rice_parameter)) goto need_more_data; } } } } } /* zero padding to byte alignment */ gst_bit_reader_skip_to_byte (&reader); /* Skip crc-16 for the complete frame */ if (!gst_bit_reader_skip (&reader, 16)) goto need_more_data; *framesize_ret = gst_bit_reader_get_pos (&reader) / 8; GST_DEBUG_OBJECT (flacparse, "Parsed frame at offset %" G_GUINT64_FORMAT ":\n" "Frame size: %u\n" "Block size: %u\n" "Sample/Frame number: %" G_GUINT64_FORMAT, flacparse->offset, *framesize_ret, flacparse->block_size, flacparse->sample_number); return 0; need_more_data: { gint max; /* not enough, if that was all available, give up on frame */ if (G_UNLIKELY (gst_base_parse_get_drain (GST_BASE_PARSE_CAST (flacparse)))) goto eos; /* otherwise, ask for some more */ max = flacparse->max_framesize; if (!max) max = 1 << 24; flacparse->requested_frame_size = MIN (GST_BUFFER_SIZE (buffer) + 4096, max); if (flacparse->requested_frame_size > GST_BUFFER_SIZE (buffer)) { GST_DEBUG_OBJECT (flacparse, "Requesting %u bytes", flacparse->requested_frame_size); return flacparse->requested_frame_size; } else { GST_DEBUG_OBJECT (flacparse, "Giving up on invalid frame (%d bytes)", GST_BUFFER_SIZE (buffer)); return -1; } } need_streaminfo: { GST_ERROR_OBJECT (flacparse, "Need STREAMINFO"); return -2; } eos: { GST_WARNING_OBJECT (flacparse, "EOS"); return -1; } error: { GST_WARNING_OBJECT (flacparse, "Invalid frame"); return -1; } } static gboolean gst_flac_parse_check_valid_frame (GstBaseParse * parse, GstBuffer * buffer, guint * framesize, gint * skipsize) { GstFlacParse *flacparse = GST_FLAC_PARSE (parse); const guint8 *data = GST_BUFFER_DATA (buffer); if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 4)) return FALSE; if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) { if (memcmp (GST_BUFFER_DATA (buffer), "fLaC", 4) == 0) { GST_DEBUG_OBJECT (flacparse, "fLaC marker found"); *framesize = 4; return TRUE; } else if (data[0] == 0xff && (data[1] >> 2) == 0x3e) { GST_DEBUG_OBJECT (flacparse, "Found headerless FLAC"); /* Minimal size of a frame header */ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 16); flacparse->requested_frame_size = 16; flacparse->state = GST_FLAC_PARSE_STATE_GENERATE_HEADERS; *skipsize = 0; return FALSE; } else { GST_DEBUG_OBJECT (flacparse, "fLaC marker not found"); return FALSE; } } else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) { guint size = 4 + ((data[1] << 16) | (data[2] << 8) | (data[3])); GST_DEBUG_OBJECT (flacparse, "Found metadata block of size %u", size); *framesize = size; return TRUE; } else { if (data[0] == 0xff && (data[1] >> 2) == 0x3e) { gint ret = 0; flacparse->offset = GST_BUFFER_OFFSET (buffer); flacparse->blocking_strategy = 0; flacparse->block_size = 0; flacparse->sample_number = 0; GST_DEBUG_OBJECT (flacparse, "Found sync code"); ret = gst_flac_parse_get_frame_size (flacparse, buffer, framesize); if (ret == 0) { return TRUE; } else if (ret == -1) { return FALSE; } else if (ret == -2) { GST_ELEMENT_ERROR (flacparse, STREAM, FORMAT, (NULL), ("Need STREAMINFO for parsing")); return FALSE; } else if (ret > 0) { *skipsize = 0; gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), ret); flacparse->requested_frame_size = ret; return FALSE; } } else { GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); gint off; off = gst_byte_reader_masked_scan_uint32 (&reader, 0xfffc0000, 0xfff80000, 0, GST_BUFFER_SIZE (buffer)); if (off > 0) { GST_DEBUG_OBJECT (parse, "Possible sync at buffer offset %d", off); *skipsize = off; return FALSE; } else { GST_DEBUG_OBJECT (flacparse, "Sync code not found"); *skipsize = GST_BUFFER_SIZE (buffer) - 3; return FALSE; } } } return FALSE; } static gboolean gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer) { GstBitReader reader = GST_BIT_READER_INIT_FROM_BUFFER (buffer); if (GST_BUFFER_SIZE (buffer) != 4 + 34) { GST_ERROR_OBJECT (flacparse, "Invalid metablock size for STREAMINFO: %u", GST_BUFFER_SIZE (buffer)); return FALSE; } /* Skip metadata block header */ gst_bit_reader_skip (&reader, 32); if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->min_blocksize, 16)) goto error; if (flacparse->min_blocksize < 16) { GST_ERROR_OBJECT (flacparse, "Invalid minimum block size: %u", flacparse->min_blocksize); return FALSE; } if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->max_blocksize, 16)) goto error; if (flacparse->max_blocksize < 16) { GST_ERROR_OBJECT (flacparse, "Invalid maximum block size: %u", flacparse->max_blocksize); return FALSE; } if (!gst_bit_reader_get_bits_uint32 (&reader, &flacparse->min_framesize, 24)) goto error; if (!gst_bit_reader_get_bits_uint32 (&reader, &flacparse->max_framesize, 24)) goto error; if (!gst_bit_reader_get_bits_uint32 (&reader, &flacparse->samplerate, 20)) goto error; if (flacparse->samplerate == 0) { GST_ERROR_OBJECT (flacparse, "Invalid sample rate 0"); return FALSE; } if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->channels, 3)) goto error; flacparse->channels++; if (flacparse->channels > 8) { GST_ERROR_OBJECT (flacparse, "Invalid number of channels %u", flacparse->channels); return FALSE; } if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->bps, 5)) goto error; flacparse->bps++; if (!gst_bit_reader_get_bits_uint64 (&reader, &flacparse->total_samples, 36)) goto error; if (flacparse->total_samples) gst_base_parse_set_duration (GST_BASE_PARSE (flacparse), GST_FORMAT_TIME, GST_FRAMES_TO_CLOCK_TIME (flacparse->total_samples, flacparse->samplerate)); GST_DEBUG_OBJECT (flacparse, "STREAMINFO:\n" "\tmin/max blocksize: %u/%u,\n" "\tmin/max framesize: %u/%u,\n" "\tsamplerate: %u,\n" "\tchannels: %u,\n" "\tbits per sample: %u,\n" "\ttotal samples: %" G_GUINT64_FORMAT, flacparse->min_blocksize, flacparse->max_blocksize, flacparse->min_framesize, flacparse->max_framesize, flacparse->samplerate, flacparse->channels, flacparse->bps, flacparse->total_samples); return TRUE; error: GST_ERROR_OBJECT (flacparse, "Failed to read data"); return FALSE; } static gboolean gst_flac_parse_handle_vorbiscomment (GstFlacParse * flacparse, GstBuffer * buffer) { flacparse->tags = gst_tag_list_from_vorbiscomment_buffer (buffer, GST_BUFFER_DATA (buffer), 4, NULL); if (flacparse->tags == NULL) { GST_ERROR_OBJECT (flacparse, "Invalid vorbiscomment block"); } else if (gst_tag_list_is_empty (flacparse->tags)) { gst_tag_list_free (flacparse->tags); flacparse->tags = NULL; } return TRUE; } static gboolean gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer) { GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); const guint8 *data = GST_BUFFER_DATA (buffer); guint32 img_len, img_type; guint32 img_mimetype_len, img_description_len; if (!gst_byte_reader_get_uint32_be (&reader, &img_type)) goto error; if (!gst_byte_reader_get_uint32_be (&reader, &img_mimetype_len)) goto error; if (!gst_byte_reader_skip (&reader, img_mimetype_len)) goto error; if (!gst_byte_reader_get_uint32_be (&reader, &img_description_len)) goto error; if (!gst_byte_reader_skip (&reader, img_description_len)) goto error; if (!gst_byte_reader_skip (&reader, 4 * 4)) goto error; if (!gst_byte_reader_get_uint32_be (&reader, &img_len)) goto error; if (!flacparse->tags) flacparse->tags = gst_tag_list_new (); gst_tag_list_add_id3_image (flacparse->tags, data + gst_byte_reader_get_pos (&reader), img_len, img_type); if (gst_tag_list_is_empty (flacparse->tags)) { gst_tag_list_free (flacparse->tags); flacparse->tags = NULL; } return TRUE; error: GST_ERROR_OBJECT (flacparse, "Error reading data"); return FALSE; } static void _value_array_append_buffer (GValue * array_val, GstBuffer * buf) { GValue value = { 0, }; g_value_init (&value, GST_TYPE_BUFFER); /* copy buffer to avoid problems with circular refcounts */ buf = gst_buffer_copy (buf); /* again, for good measure */ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_value_array_append_value (array_val, &value); g_value_unset (&value); } static gboolean gst_flac_parse_handle_headers (GstFlacParse * flacparse) { GstBuffer *vorbiscomment = NULL; GstBuffer *streaminfo = NULL; GstBuffer *marker = NULL; GValue array = { 0, }; GstCaps *caps; GList *l; caps = gst_caps_new_simple ("audio/x-flac", "channels", G_TYPE_INT, flacparse->channels, "rate", G_TYPE_INT, flacparse->samplerate, NULL); if (!flacparse->headers) goto push_headers; for (l = flacparse->headers; l; l = l->next) { GstBuffer *header = l->data; const guint8 *data = GST_BUFFER_DATA (header); guint size = GST_BUFFER_SIZE (header); GST_BUFFER_FLAG_SET (header, GST_BUFFER_FLAG_IN_CAPS); if (size == 4 && memcmp (data, "fLaC", 4) == 0) { marker = header; } else if (size > 1 && (data[0] & 0x7f) == 0) { streaminfo = header; } else if (size > 1 && (data[0] & 0x7f) == 4) { vorbiscomment = header; } } if (marker == NULL || streaminfo == NULL || vorbiscomment == NULL) { GST_WARNING_OBJECT (flacparse, "missing header %p %p %p, muxing into container " "formats may be broken", marker, streaminfo, vorbiscomment); goto push_headers; } g_value_init (&array, GST_TYPE_ARRAY); /* add marker including STREAMINFO header */ { GstBuffer *buf; guint16 num; /* minus one for the marker that is merged with streaminfo here */ num = g_list_length (flacparse->headers) - 1; buf = gst_buffer_new_and_alloc (13 + GST_BUFFER_SIZE (streaminfo)); GST_BUFFER_DATA (buf)[0] = 0x7f; memcpy (GST_BUFFER_DATA (buf) + 1, "FLAC", 4); GST_BUFFER_DATA (buf)[5] = 0x01; /* mapping version major */ GST_BUFFER_DATA (buf)[6] = 0x00; /* mapping version minor */ GST_BUFFER_DATA (buf)[7] = (num & 0xFF00) >> 8; GST_BUFFER_DATA (buf)[8] = (num & 0x00FF) >> 0; memcpy (GST_BUFFER_DATA (buf) + 9, "fLaC", 4); memcpy (GST_BUFFER_DATA (buf) + 13, GST_BUFFER_DATA (streaminfo), GST_BUFFER_SIZE (streaminfo)); _value_array_append_buffer (&array, buf); gst_buffer_unref (buf); } /* add VORBISCOMMENT header */ _value_array_append_buffer (&array, vorbiscomment); /* add other headers, if there are any */ for (l = flacparse->headers; l; l = l->next) { if (GST_BUFFER_CAST (l->data) != marker && GST_BUFFER_CAST (l->data) != streaminfo && GST_BUFFER_CAST (l->data) != vorbiscomment) { _value_array_append_buffer (&array, GST_BUFFER_CAST (l->data)); } } gst_structure_set_value (gst_caps_get_structure (caps, 0), "streamheader", &array); g_value_unset (&array); push_headers: gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (GST_BASE_PARSE (flacparse)), caps); gst_caps_unref (caps); /* push header buffers; update caps, so when we push the first buffer the * negotiated caps will change to caps that include the streamheader field */ for (l = flacparse->headers; l != NULL; l = l->next) { GstBuffer *buf = GST_BUFFER (l->data); GstFlowReturn ret; l->data = NULL; gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_PARSE_SRC_PAD (GST_BASE_PARSE (flacparse)))); ret = gst_base_parse_push_buffer (GST_BASE_PARSE (flacparse), buf); if (ret != GST_FLOW_OK) return FALSE; } g_list_free (flacparse->headers); flacparse->headers = NULL; /* Push tags */ if (flacparse->tags) gst_element_found_tags (GST_ELEMENT (flacparse), gst_tag_list_copy (flacparse->tags)); return TRUE; } static gboolean gst_flac_parse_generate_headers (GstFlacParse * flacparse) { GstBuffer *marker, *streaminfo, *vorbiscomment; guint8 *data; marker = gst_buffer_new_and_alloc (4); memcpy (GST_BUFFER_DATA (marker), "fLaC", 4); GST_BUFFER_TIMESTAMP (marker) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (marker) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (marker) = 0; GST_BUFFER_OFFSET_END (marker) = 0; flacparse->headers = g_list_append (flacparse->headers, marker); streaminfo = gst_buffer_new_and_alloc (4 + 34); data = GST_BUFFER_DATA (streaminfo); memset (data, 0, 4 + 34); /* metadata block header */ data[0] = 0x00; /* is_last = 0; type = 0; */ data[1] = 0x00; /* length = 34; */ data[2] = 0x00; data[3] = 0x22; /* streaminfo */ data[4] = (flacparse->block_size >> 8) & 0xff; /* min blocksize = blocksize; */ data[5] = (flacparse->block_size) & 0xff; data[6] = (flacparse->block_size >> 8) & 0xff; /* max blocksize = blocksize; */ data[7] = (flacparse->block_size) & 0xff; data[8] = 0x00; /* min framesize = 0; */ data[9] = 0x00; data[10] = 0x00; data[11] = 0x00; /* max framesize = 0; */ data[12] = 0x00; data[13] = 0x00; data[14] = (flacparse->samplerate >> 12) & 0xff; data[15] = (flacparse->samplerate >> 4) & 0xff; data[16] = (flacparse->samplerate >> 0) & 0xf0; data[16] |= (flacparse->channels - 1) << 1; data[16] |= ((flacparse->bps - 1) >> 4) & 0x01; data[17] = (((flacparse->bps - 1)) & 0x0f) << 4; { gint64 duration; GstFormat fmt = GST_FORMAT_TIME; if (gst_pad_query_peer_duration (GST_BASE_PARSE_SINK_PAD (GST_BASE_PARSE (flacparse)), &fmt, &duration) && fmt == GST_FORMAT_TIME) { duration = GST_CLOCK_TIME_TO_FRAMES (duration, flacparse->samplerate); data[17] |= (duration >> 32) & 0xff; data[18] |= (duration >> 24) & 0xff; data[19] |= (duration >> 16) & 0xff; data[20] |= (duration >> 8) & 0xff; data[21] |= (duration >> 0) & 0xff; } } /* MD5 = 0; */ GST_BUFFER_TIMESTAMP (streaminfo) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (streaminfo) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (streaminfo) = 0; GST_BUFFER_OFFSET_END (streaminfo) = 0; flacparse->headers = g_list_append (flacparse->headers, streaminfo); /* empty vorbiscomment */ { GstTagList *taglist = gst_tag_list_new (); guchar header[4]; guint size; header[0] = 0x84; /* is_last = 1; type = 4; */ vorbiscomment = gst_tag_list_to_vorbiscomment_buffer (taglist, header, sizeof (header), NULL); gst_tag_list_free (taglist); /* Get rid of framing bit */ if (GST_BUFFER_DATA (vorbiscomment)[GST_BUFFER_SIZE (vorbiscomment) - 1] == 1) { GstBuffer *sub; sub = gst_buffer_create_sub (vorbiscomment, 0, GST_BUFFER_SIZE (vorbiscomment) - 1); gst_buffer_unref (vorbiscomment); vorbiscomment = sub; } size = GST_BUFFER_SIZE (vorbiscomment) - 4; GST_BUFFER_DATA (vorbiscomment)[1] = ((size & 0xFF0000) >> 16); GST_BUFFER_DATA (vorbiscomment)[2] = ((size & 0x00FF00) >> 8); GST_BUFFER_DATA (vorbiscomment)[3] = (size & 0x0000FF); GST_BUFFER_TIMESTAMP (vorbiscomment) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (vorbiscomment) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (vorbiscomment) = 0; GST_BUFFER_OFFSET_END (vorbiscomment) = 0; flacparse->headers = g_list_append (flacparse->headers, vorbiscomment); } return TRUE; } static GstFlowReturn gst_flac_parse_parse_frame (GstBaseParse * parse, GstBuffer * buffer) { GstFlacParse *flacparse = GST_FLAC_PARSE (parse); const guint8 *data = GST_BUFFER_DATA (buffer); if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) { GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (buffer) = 0; GST_BUFFER_OFFSET_END (buffer) = 0; /* 32 bits metadata block */ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 4); flacparse->state = GST_FLAC_PARSE_STATE_HEADERS; flacparse->headers = g_list_append (flacparse->headers, gst_buffer_ref (buffer)); return GST_BASE_PARSE_FLOW_DROPPED; } else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) { gboolean is_last = ((data[0] & 0x80) == 0x80); guint type = (data[0] & 0x7F); if (type == 127) { GST_WARNING_OBJECT (flacparse, "Invalid metadata block type"); return GST_BASE_PARSE_FLOW_DROPPED; } GST_DEBUG_OBJECT (flacparse, "Handling metadata block of type %u", type); switch (type) { case 0: /* STREAMINFO */ if (!gst_flac_parse_handle_streaminfo (flacparse, buffer)) return GST_FLOW_ERROR; break; case 3: /* SEEKTABLE */ /* TODO: handle seektables */ break; case 4: /* VORBIS_COMMENT */ if (!gst_flac_parse_handle_vorbiscomment (flacparse, buffer)) return GST_FLOW_ERROR; break; case 6: /* PICTURE */ if (!gst_flac_parse_handle_picture (flacparse, buffer)) return GST_FLOW_ERROR; break; case 1: /* PADDING */ case 2: /* APPLICATION */ case 5: /* CUESHEET */ default: /* RESERVED */ break; } GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (buffer) = 0; GST_BUFFER_OFFSET_END (buffer) = 0; if (is_last) { flacparse->headers = g_list_append (flacparse->headers, gst_buffer_ref (buffer)); if (!gst_flac_parse_handle_headers (flacparse)) return GST_FLOW_ERROR; /* Minimal size of a frame header */ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), MAX (16, flacparse->min_framesize)); flacparse->requested_frame_size = MAX (16, flacparse->min_framesize); flacparse->state = GST_FLAC_PARSE_STATE_DATA; /* DROPPED because we pushed all headers manually already */ return GST_BASE_PARSE_FLOW_DROPPED; } else { flacparse->headers = g_list_append (flacparse->headers, gst_buffer_ref (buffer)); return GST_BASE_PARSE_FLOW_DROPPED; } } else { if (flacparse->offset != GST_BUFFER_OFFSET (buffer)) { gint ret; guint framesize; flacparse->offset = GST_BUFFER_OFFSET (buffer); ret = gst_flac_parse_get_frame_size (flacparse, buffer, &framesize); if (ret != 0) { GST_ERROR_OBJECT (flacparse, "Baseclass didn't provide a complete frame"); return GST_FLOW_ERROR; } } if (flacparse->block_size == 0) { GST_ERROR_OBJECT (flacparse, "Unparsed frame"); return GST_FLOW_ERROR; } if (flacparse->state == GST_FLAC_PARSE_STATE_GENERATE_HEADERS) { if (flacparse->blocking_strategy == 1) { GST_WARNING_OBJECT (flacparse, "Generating headers for variable blocksize streams not supported"); if (!gst_flac_parse_handle_headers (flacparse)) return GST_FLOW_ERROR; } else { GST_DEBUG_OBJECT (flacparse, "Generating headers"); if (!gst_flac_parse_generate_headers (flacparse)) return GST_FLOW_ERROR; if (!gst_flac_parse_handle_headers (flacparse)) return GST_FLOW_ERROR; } flacparse->state = GST_FLAC_PARSE_STATE_DATA; } /* also cater for oggmux metadata */ if (flacparse->blocking_strategy == 0) { GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (flacparse->sample_number, flacparse->block_size * GST_SECOND, flacparse->samplerate); GST_BUFFER_OFFSET_END (buffer) = flacparse->sample_number * flacparse->block_size + flacparse->block_size; } else { GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (flacparse->sample_number, GST_SECOND, flacparse->samplerate); GST_BUFFER_OFFSET_END (buffer) = flacparse->sample_number + flacparse->block_size; } GST_BUFFER_DURATION (buffer) = GST_FRAMES_TO_CLOCK_TIME (flacparse->block_size, flacparse->samplerate); GST_BUFFER_OFFSET (buffer) = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer); /* Minimal size of a frame header */ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), MAX (16, flacparse->min_framesize)); flacparse->requested_frame_size = MAX (16, flacparse->min_framesize); flacparse->offset = -1; flacparse->blocking_strategy = 0; flacparse->block_size = 0; flacparse->sample_number = 0; return GST_FLOW_OK; } }