static GstFlowReturn gst_wavpack_parse_chain (GstPad * pad, GstBuffer * buf) { GstWavpackParse *wvparse = GST_WAVPACK_PARSE (GST_PAD_PARENT (pad)); GstFlowReturn ret = GST_FLOW_OK; WavpackHeader wph; const guint8 *tmp_buf; if (!wvparse->adapter) { wvparse->adapter = gst_adapter_new (); } if (GST_BUFFER_IS_DISCONT (buf)) { gst_adapter_clear (wvparse->adapter); wvparse->discont = TRUE; } gst_adapter_push (wvparse->adapter, buf); if (gst_adapter_available (wvparse->adapter) < sizeof (WavpackHeader)) return ret; if (!gst_wavpack_parse_resync_adapter (wvparse->adapter)) return ret; tmp_buf = gst_adapter_peek (wvparse->adapter, sizeof (WavpackHeader)); gst_wavpack_read_header (&wph, (guint8 *) tmp_buf); while (gst_adapter_available (wvparse->adapter) >= wph.ckSize + 4 * 1 + 4) { GstBuffer *outbuf = gst_adapter_take_buffer (wvparse->adapter, wph.ckSize + 4 * 1 + 4); if (!outbuf) return GST_FLOW_ERROR; if (wvparse->srcpad == NULL) { if (!gst_wavpack_parse_create_src_pad (wvparse, outbuf, &wph)) { GST_ERROR_OBJECT (wvparse, "Failed to create src pad"); ret = GST_FLOW_ERROR; break; } } ret = gst_wavpack_parse_push_buffer (wvparse, outbuf, &wph); if (ret != GST_FLOW_OK) break; if (gst_adapter_available (wvparse->adapter) >= sizeof (WavpackHeader)) { tmp_buf = gst_adapter_peek (wvparse->adapter, sizeof (WavpackHeader)); if (!gst_wavpack_parse_resync_adapter (wvparse->adapter)) break; gst_wavpack_read_header (&wph, (guint8 *) tmp_buf); } } return ret; }
/* inspired by the original one in wavpack */ gboolean gst_wavpack_read_metadata (GstWavpackMetadata * wpmd, guint8 * header_data, guint8 ** p_data) { WavpackHeader hdr; guint8 *end; gst_wavpack_read_header (&hdr, header_data); end = header_data + hdr.ckSize + 8; if (end - *p_data < 2) return FALSE; wpmd->id = GST_READ_UINT8 (*p_data); wpmd->byte_length = 2 * (guint) GST_READ_UINT8 (*p_data + 1); *p_data += 2; if ((wpmd->id & ID_LARGE) == ID_LARGE) { guint extra; wpmd->id &= ~ID_LARGE; if (end - *p_data < 2) return FALSE; extra = GST_READ_UINT16_LE (*p_data); wpmd->byte_length += (extra << 9); *p_data += 2; } if ((wpmd->id & ID_ODD_SIZE) == ID_ODD_SIZE) { wpmd->id &= ~ID_ODD_SIZE; --wpmd->byte_length; } if (wpmd->byte_length > 0) { if (end - *p_data < wpmd->byte_length + (wpmd->byte_length & 1)) { wpmd->data = NULL; return FALSE; } wpmd->data = *p_data; *p_data += wpmd->byte_length + (wpmd->byte_length & 1); } else { wpmd->data = NULL; } return TRUE; }
static int gst_wavpack_enc_push_block (void *id, void *data, int32_t count) { GstWavpackEncWriteID *wid = (GstWavpackEncWriteID *) id; GstWavpackEnc *enc = GST_WAVPACK_ENC (wid->wavpack_enc); GstFlowReturn *flow; GstBuffer *buffer; GstPad *pad; guchar *block = (guchar *) data; pad = (wid->correction) ? enc->wvcsrcpad : enc->srcpad; flow = (wid->correction) ? &enc->wvcsrcpad_last_return : &enc-> srcpad_last_return; *flow = gst_pad_alloc_buffer_and_set_caps (pad, GST_BUFFER_OFFSET_NONE, count, GST_PAD_CAPS (pad), &buffer); if (*flow != GST_FLOW_OK) { GST_WARNING_OBJECT (enc, "flow on %s:%s = %s", GST_DEBUG_PAD_NAME (pad), gst_flow_get_name (*flow)); return FALSE; } g_memmove (GST_BUFFER_DATA (buffer), block, count); if (count > sizeof (WavpackHeader) && memcmp (block, "wvpk", 4) == 0) { /* if it's a Wavpack block set buffer timestamp and duration, etc */ WavpackHeader wph; GST_LOG_OBJECT (enc, "got %d bytes of encoded wavpack %sdata", count, (wid->correction) ? "correction " : ""); gst_wavpack_read_header (&wph, block); /* Only set when pushing the first buffer again, in that case * we don't want to delay the buffer or push newsegment events */ if (!wid->passthrough) { /* Only push complete blocks */ if (enc->pending_buffer == NULL) { enc->pending_buffer = buffer; enc->pending_offset = wph.block_index; } else if (enc->pending_offset == wph.block_index) { enc->pending_buffer = gst_buffer_join (enc->pending_buffer, buffer); } else { GST_ERROR ("Got incomplete block, dropping"); gst_buffer_unref (enc->pending_buffer); enc->pending_buffer = buffer; enc->pending_offset = wph.block_index; } if (!(wph.flags & FINAL_BLOCK)) return TRUE; buffer = enc->pending_buffer; enc->pending_buffer = NULL; enc->pending_offset = 0; /* if it's the first wavpack block, send a NEW_SEGMENT event */ if (wph.block_index == 0) { gst_pad_push_event (pad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, GST_BUFFER_OFFSET_NONE, 0)); /* save header for later reference, so we can re-send it later on * EOS with fixed up values for total sample count etc. */ if (enc->first_block == NULL && !wid->correction) { enc->first_block = g_memdup (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer)); enc->first_block_size = GST_BUFFER_SIZE (buffer); } } } /* set buffer timestamp, duration, offset, offset_end from * the wavpack header */ GST_BUFFER_TIMESTAMP (buffer) = enc->timestamp_offset + gst_util_uint64_scale_int (GST_SECOND, wph.block_index, enc->samplerate); GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale_int (GST_SECOND, wph.block_samples, enc->samplerate); GST_BUFFER_OFFSET (buffer) = wph.block_index; GST_BUFFER_OFFSET_END (buffer) = wph.block_index + wph.block_samples; } else { /* if it's something else set no timestamp and duration on the buffer */ GST_DEBUG_OBJECT (enc, "got %d bytes of unknown data", count); GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; } /* push the buffer and forward errors */ GST_DEBUG_OBJECT (enc, "pushing buffer with %d bytes", GST_BUFFER_SIZE (buffer)); *flow = gst_pad_push (pad, buffer); if (*flow != GST_FLOW_OK) { GST_WARNING_OBJECT (enc, "flow on %s:%s = %s", GST_DEBUG_PAD_NAME (pad), gst_flow_get_name (*flow)); return FALSE; } return TRUE; }
static GstFlowReturn gst_wavpack_dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf) { GstWavpackDec *dec; GstBuffer *outbuf = NULL; GstFlowReturn ret = GST_FLOW_OK; WavpackHeader wph; int32_t decoded, unpacked_size; gboolean format_changed; gint width, depth, i, j, max; gint32 *dec_data = NULL; guint8 *out_data; GstMapInfo map, omap; dec = GST_WAVPACK_DEC (bdec); g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); gst_buffer_map (buf, &map, GST_MAP_READ); /* check input, we only accept framed input with complete chunks */ if (map.size < sizeof (WavpackHeader)) goto input_not_framed; if (!gst_wavpack_read_header (&wph, map.data)) goto invalid_header; if (map.size < wph.ckSize + 4 * 1 + 4) goto input_not_framed; if (!(wph.flags & INITIAL_BLOCK)) goto input_not_framed; dec->wv_id.buffer = map.data; dec->wv_id.length = map.size; dec->wv_id.position = 0; /* create a new wavpack context if there is none yet but if there * was already one (i.e. caps were set on the srcpad) check whether * the new one has the same caps */ if (!dec->context) { gchar error_msg[80]; dec->context = WavpackOpenFileInputEx (dec->stream_reader, &dec->wv_id, NULL, error_msg, OPEN_STREAMING, 0); /* expect this to work */ if (!dec->context) { GST_WARNING_OBJECT (dec, "Couldn't decode buffer: %s", error_msg); goto context_failed; } } g_assert (dec->context != NULL); format_changed = (dec->sample_rate != WavpackGetSampleRate (dec->context)) || (dec->channels != WavpackGetNumChannels (dec->context)) || (dec->depth != WavpackGetBytesPerSample (dec->context) * 8) || (dec->channel_mask != WavpackGetChannelMask (dec->context)); if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (dec)) || format_changed) { gint channel_mask; dec->sample_rate = WavpackGetSampleRate (dec->context); dec->channels = WavpackGetNumChannels (dec->context); dec->depth = WavpackGetBytesPerSample (dec->context) * 8; channel_mask = WavpackGetChannelMask (dec->context); if (channel_mask == 0) channel_mask = gst_wavpack_get_default_channel_mask (dec->channels); dec->channel_mask = channel_mask; gst_wavpack_dec_negotiate (dec); /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something * is decoded or after the format has changed */ gst_wavpack_dec_post_tags (dec); } /* alloc output buffer */ dec_data = g_malloc (4 * wph.block_samples * dec->channels); /* decode */ decoded = WavpackUnpackSamples (dec->context, dec_data, wph.block_samples); if (decoded != wph.block_samples) goto decode_error; unpacked_size = (dec->width / 8) * wph.block_samples * dec->channels; outbuf = gst_buffer_new_and_alloc (unpacked_size); /* legacy; pass along offset, whatever that might entail */ GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buf); gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); out_data = omap.data; width = dec->width; depth = dec->depth; max = dec->channels * wph.block_samples; if (width == 8) { gint8 *outbuffer = (gint8 *) out_data; gint *reorder_map = dec->channel_reorder_map; for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint8) (dec_data[i + reorder_map[j]]); } } else if (width == 16) { gint16 *outbuffer = (gint16 *) out_data; gint *reorder_map = dec->channel_reorder_map; for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint16) (dec_data[i + reorder_map[j]]); } } else if (dec->width == 32) { gint32 *outbuffer = (gint32 *) out_data; gint *reorder_map = dec->channel_reorder_map; if (width != depth) { for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint32) (dec_data[i + reorder_map[j]] << (width - depth)); } } else { for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint32) (dec_data[i + reorder_map[j]]); } } } else { g_assert_not_reached (); } gst_buffer_unmap (outbuf, &omap); gst_buffer_unmap (buf, &map); buf = NULL; g_free (dec_data); ret = gst_audio_decoder_finish_frame (bdec, outbuf, 1); out: if (buf) gst_buffer_unmap (buf, &map); if (G_UNLIKELY (ret != GST_FLOW_OK)) { GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (ret)); } return ret; /* ERRORS */ input_not_framed: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Expected framed input")); ret = GST_FLOW_ERROR; goto out; } invalid_header: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Invalid wavpack header")); ret = GST_FLOW_ERROR; goto out; } context_failed: { GST_AUDIO_DECODER_ERROR (bdec, 1, LIBRARY, INIT, (NULL), ("error creating Wavpack context"), ret); goto out; } decode_error: { const gchar *reason = "unknown"; if (dec->context) { reason = WavpackGetErrorMessage (dec->context); } else { reason = "couldn't create decoder context"; } GST_AUDIO_DECODER_ERROR (bdec, 1, STREAM, DECODE, (NULL), ("decoding error: %s", reason), ret); g_free (dec_data); if (ret == GST_FLOW_OK) gst_audio_decoder_finish_frame (bdec, NULL, 1); goto out; } }
static GstFlowReturn gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) { GstWavpackDec *dec; GstBuffer *outbuf; GstFlowReturn ret = GST_FLOW_OK; WavpackHeader wph; int32_t decoded, unpacked_size; gboolean format_changed; dec = GST_WAVPACK_DEC (GST_PAD_PARENT (pad)); /* check input, we only accept framed input with complete chunks */ if (GST_BUFFER_SIZE (buf) < sizeof (WavpackHeader)) goto input_not_framed; if (!gst_wavpack_read_header (&wph, GST_BUFFER_DATA (buf))) goto invalid_header; if (GST_BUFFER_SIZE (buf) < wph.ckSize + 4 * 1 + 4) goto input_not_framed; if (!(wph.flags & INITIAL_BLOCK)) goto input_not_framed; dec->wv_id.buffer = GST_BUFFER_DATA (buf); dec->wv_id.length = GST_BUFFER_SIZE (buf); dec->wv_id.position = 0; /* create a new wavpack context if there is none yet but if there * was already one (i.e. caps were set on the srcpad) check whether * the new one has the same caps */ if (!dec->context) { gchar error_msg[80]; dec->context = WavpackOpenFileInputEx (dec->stream_reader, &dec->wv_id, NULL, error_msg, OPEN_STREAMING, 0); if (!dec->context) { GST_WARNING ("Couldn't decode buffer: %s", error_msg); dec->error_count++; if (dec->error_count <= WAVPACK_DEC_MAX_ERRORS) { goto out; /* just return OK for now */ } else { goto decode_error; } } } g_assert (dec->context != NULL); dec->error_count = 0; format_changed = (dec->sample_rate != WavpackGetSampleRate (dec->context)) || (dec->channels != WavpackGetNumChannels (dec->context)) || (dec->depth != WavpackGetBitsPerSample (dec->context)) || #ifdef WAVPACK_OLD_API (dec->channel_mask != dec->context->config.channel_mask); #else (dec->channel_mask != WavpackGetChannelMask (dec->context)); #endif if (!GST_PAD_CAPS (dec->srcpad) || format_changed) { GstCaps *caps; gint channel_mask; dec->sample_rate = WavpackGetSampleRate (dec->context); dec->channels = WavpackGetNumChannels (dec->context); dec->depth = WavpackGetBitsPerSample (dec->context); caps = gst_caps_new_simple ("audio/x-raw-int", "rate", G_TYPE_INT, dec->sample_rate, "channels", G_TYPE_INT, dec->channels, "depth", G_TYPE_INT, dec->depth, "width", G_TYPE_INT, 32, "endianness", G_TYPE_INT, G_BYTE_ORDER, "signed", G_TYPE_BOOLEAN, TRUE, NULL); #ifdef WAVPACK_OLD_API channel_mask = dec->context->config.channel_mask; #else channel_mask = WavpackGetChannelMask (dec->context); #endif if (channel_mask == 0) channel_mask = gst_wavpack_get_default_channel_mask (dec->channels); dec->channel_mask = channel_mask; /* Only set the channel layout for more than two channels * otherwise things break unfortunately */ if (channel_mask != 0 && dec->channels > 2) if (!gst_wavpack_set_channel_layout (caps, channel_mask)) GST_WARNING_OBJECT (dec, "Failed to set channel layout"); GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps); /* should always succeed */ gst_pad_set_caps (dec->srcpad, caps); gst_caps_unref (caps); /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something * is decoded or after the format has changed */ gst_wavpack_dec_post_tags (dec); } /* alloc output buffer */ unpacked_size = 4 * wph.block_samples * dec->channels; ret = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET (buf), unpacked_size, GST_PAD_CAPS (dec->srcpad), &outbuf); if (ret != GST_FLOW_OK) goto out; gst_buffer_copy_metadata (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS); /* If we got a DISCONT buffer forward the flag. Nothing else * has to be done as libwavpack doesn't store state between * Wavpack blocks */ if (GST_BUFFER_IS_DISCONT (buf) || dec->next_block_index != wph.block_index) GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); dec->next_block_index = wph.block_index + wph.block_samples; /* decode */ decoded = WavpackUnpackSamples (dec->context, (int32_t *) GST_BUFFER_DATA (outbuf), wph.block_samples); if (decoded != wph.block_samples) goto decode_error; if ((outbuf = gst_audio_buffer_clip (outbuf, &dec->segment, dec->sample_rate, 4 * dec->channels))) { GST_LOG_OBJECT (dec, "pushing buffer with time %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf))); ret = gst_pad_push (dec->srcpad, outbuf); } out: if (G_UNLIKELY (ret != GST_FLOW_OK)) { GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (ret)); } gst_buffer_unref (buf); return ret; /* ERRORS */ input_not_framed: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Expected framed input")); gst_buffer_unref (buf); return GST_FLOW_ERROR; } invalid_header: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Invalid wavpack header")); gst_buffer_unref (buf); return GST_FLOW_ERROR; } decode_error: { const gchar *reason = "unknown"; if (dec->context) { #ifdef WAVPACK_OLD_API reason = dec->context->error_message; #else reason = WavpackGetErrorMessage (dec->context); #endif } else { reason = "couldn't create decoder context"; } GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Failed to decode wavpack stream: %s", reason)); gst_buffer_unref (outbuf); gst_buffer_unref (buf); return GST_FLOW_ERROR; } }
/* returns TRUE on success, with byte_offset set to the offset of the * wavpack chunk containing the sample requested. start_sample will be * set to the first sample in the chunk starting at byte_offset. * Scanning from the last known header offset to the wanted position * when seeking forward isn't very clever, but seems fast enough in * practice and has the nice side effect of populating our index * table */ static gboolean gst_wavpack_parse_scan_to_find_sample (GstWavpackParse * parse, gint64 sample, gint64 * byte_offset, gint64 * start_sample) { GstWavpackParseIndexEntry *entry; GstFlowReturn ret; gint64 off = 0; /* first, check if we have to scan at all */ entry = gst_wavpack_parse_index_get_entry_from_sample (parse, sample); if (entry) { *byte_offset = entry->byte_offset; *start_sample = entry->sample_offset; GST_LOG_OBJECT (parse, "Found index entry: sample %" G_GINT64_FORMAT " @ offset %" G_GINT64_FORMAT, entry->sample_offset, entry->byte_offset); return TRUE; } GST_LOG_OBJECT (parse, "No matching entry in index, scanning file ..."); /* if we have an index, we can start scanning from the last known offset * in there, after all we know our wanted sample is not in the index */ if (parse->entries) { GstWavpackParseIndexEntry *entry; entry = gst_wavpack_parse_index_get_last_entry (parse); off = entry->byte_offset; } /* now scan forward until we find the chunk we're looking for or hit EOS */ do { WavpackHeader header; GstBuffer *buf; buf = gst_wavpack_parse_pull_buffer (parse, off, sizeof (WavpackHeader), &ret); if (buf == NULL) break; gst_wavpack_read_header (&header, GST_BUFFER_DATA (buf)); gst_buffer_unref (buf); if (header.flags & INITIAL_BLOCK) gst_wavpack_parse_index_append_entry (parse, off, header.block_index, header.block_samples); else continue; if (header.block_index <= sample && sample < (header.block_index + header.block_samples)) { *byte_offset = off; *start_sample = header.block_index; return TRUE; } off += header.ckSize + 8; } while (1); GST_DEBUG_OBJECT (parse, "scan failed: %s (off=0x%08" G_GINT64_MODIFIER "x)", gst_flow_get_name (ret), off); return FALSE; }
static GstFlowReturn gst_wavpack_parse_resync_loop (GstWavpackParse * parse, WavpackHeader * header) { GstFlowReturn flow_ret = GST_FLOW_UNEXPECTED; GstBuffer *buf = NULL; /* loop until we have a frame header or reach the end of the stream */ while (1) { guint8 *data, *marker; guint len, size; if (buf) { gst_buffer_unref (buf); buf = NULL; } if (parse->upstream_length == 0 || parse->upstream_length <= parse->current_offset) { parse->upstream_length = gst_wavpack_parse_get_upstream_length (parse); if (parse->upstream_length == 0 || parse->upstream_length <= parse->current_offset) { break; } } len = MIN (parse->upstream_length - parse->current_offset, 2048); GST_LOG_OBJECT (parse, "offset: %" G_GINT64_FORMAT, parse->current_offset); buf = gst_wavpack_parse_pull_buffer (parse, parse->current_offset, len, &flow_ret); /* whatever the problem is, there's nothing more for us to do for now */ if (flow_ret != GST_FLOW_OK) break; data = GST_BUFFER_DATA (buf); size = GST_BUFFER_SIZE (buf); /* not enough data for a header? */ if (size < sizeof (WavpackHeader)) break; /* got a header right where we are at now? */ if (gst_wavpack_read_header (header, data)) break; /* nope, let's see if we can find one */ marker = gst_wavpack_parse_find_marker (data + 1, size - 1); if (marker) { parse->current_offset += marker - data; /* do one more loop iteration to make sure we pull enough * data for a full header, we'll bail out then */ } else { parse->current_offset += len - 4; } } if (buf) gst_buffer_unref (buf); return flow_ret; }