flac: Port to the new raw audio caps

This commit is contained in:
Sebastian Dröge 2012-01-06 09:40:22 +01:00
parent 6c800137d8
commit a22a566c0b
5 changed files with 216 additions and 184 deletions

View file

@ -316,7 +316,7 @@ dnl Make sure you have a space before and after all plugins
GST_PLUGINS_NONPORTED="deinterlace interleave flx goom2k1 \ GST_PLUGINS_NONPORTED="deinterlace interleave flx goom2k1 \
imagefreeze interleave monoscope smpte \ imagefreeze interleave monoscope smpte \
videobox videomixer \ videobox videomixer \
cairo cairo_gobject dv1394 flac gdk_pixbuf libdv libpng \ cairo cairo_gobject dv1394 gdk_pixbuf libdv libpng \
oss oss4 shout2 \ oss oss4 shout2 \
taglib wavpack \ taglib wavpack \
osx_video osx_audio " osx_video osx_audio "

View file

@ -45,12 +45,11 @@
#include "gstflacdec.h" #include "gstflacdec.h"
#include <gst/gst-i18n-plugin.h> #include <gst/gst-i18n-plugin.h>
#include <gst/audio/multichannel.h>
#include <gst/tag/tag.h> #include <gst/tag/tag.h>
/* Taken from http://flac.sourceforge.net/format.html#frame_header */ /* Taken from http://flac.sourceforge.net/format.html#frame_header */
static const GstAudioChannelPosition channel_positions[8][8] = { static const GstAudioChannelPosition channel_positions[8][8] = {
{GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}, {GST_AUDIO_CHANNEL_POSITION_MONO},
{GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, { GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
@ -68,7 +67,7 @@ static const GstAudioChannelPosition channel_positions[8][8] = {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
GST_AUDIO_CHANNEL_POSITION_LFE, GST_AUDIO_CHANNEL_POSITION_LFE1,
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
/* FIXME: 7/8 channel layouts are not defined in the FLAC specs */ /* FIXME: 7/8 channel layouts are not defined in the FLAC specs */
@ -78,14 +77,14 @@ static const GstAudioChannelPosition channel_positions[8][8] = {
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
GST_AUDIO_CHANNEL_POSITION_LFE, GST_AUDIO_CHANNEL_POSITION_LFE1,
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, { GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
GST_AUDIO_CHANNEL_POSITION_LFE, GST_AUDIO_CHANNEL_POSITION_LFE1,
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT} GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}
}; };
@ -120,12 +119,10 @@ G_DEFINE_TYPE (GstFlacDec, gst_flac_dec, GST_TYPE_AUDIO_DECODER);
#define FORMATS "{ S8BE, S16BE, S32BE } " #define FORMATS "{ S8BE, S16BE, S32BE } "
#endif #endif
/* FIXME 0.11: Use width=32 for all depths and let audioconvert
* handle the conversions instead of doing it ourself.
*/
#define GST_FLAC_DEC_SRC_CAPS \ #define GST_FLAC_DEC_SRC_CAPS \
"audio/x-raw, " \ "audio/x-raw, " \
"format = (string) " FORMATS ", " \ "format = (string) " FORMATS ", " \
"layout = (string) interleaved, " \
"rate = (int) [ 1, 655350 ], " \ "rate = (int) [ 1, 655350 ], " \
"channels = (int) [ 1, 8 ]" "channels = (int) [ 1, 8 ]"
@ -193,6 +190,9 @@ gst_flac_dec_start (GstAudioDecoder * audio_dec)
dec->decoder = FLAC__stream_decoder_new (); dec->decoder = FLAC__stream_decoder_new ();
gst_audio_info_init (&dec->info);
dec->depth = 0;
/* no point calculating MD5 since it's never checked here */ /* no point calculating MD5 since it's never checked here */
FLAC__stream_decoder_set_md5_checking (dec->decoder, false); FLAC__stream_decoder_set_md5_checking (dec->decoder, false);
@ -405,10 +405,10 @@ gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, guint8 * data, guint size,
GST_DEBUG_OBJECT (flacdec, "frame number: %" G_GINT64_FORMAT, GST_DEBUG_OBJECT (flacdec, "frame number: %" G_GINT64_FORMAT,
*last_sample_num); *last_sample_num);
if (flacdec->sample_rate > 0 && *last_sample_num != 0) { if (flacdec->info.rate > 0 && *last_sample_num != 0) {
GST_DEBUG_OBJECT (flacdec, "last sample %" G_GINT64_FORMAT " = %" GST_DEBUG_OBJECT (flacdec, "last sample %" G_GINT64_FORMAT " = %"
GST_TIME_FORMAT, *last_sample_num, GST_TIME_FORMAT, *last_sample_num,
GST_TIME_ARGS (*last_sample_num * GST_SECOND / flacdec->sample_rate)); GST_TIME_ARGS (*last_sample_num * GST_SECOND / flacdec->info.rate));
} }
return TRUE; return TRUE;
@ -425,29 +425,42 @@ gst_flac_dec_metadata_cb (const FLAC__StreamDecoder * decoder,
switch (metadata->type) { switch (metadata->type) {
case FLAC__METADATA_TYPE_STREAMINFO:{ case FLAC__METADATA_TYPE_STREAMINFO:{
gint64 samples; gint64 samples;
guint depth; guint depth, width;
samples = metadata->data.stream_info.total_samples; samples = metadata->data.stream_info.total_samples;
flacdec->min_blocksize = metadata->data.stream_info.min_blocksize; flacdec->min_blocksize = metadata->data.stream_info.min_blocksize;
flacdec->max_blocksize = metadata->data.stream_info.max_blocksize; flacdec->max_blocksize = metadata->data.stream_info.max_blocksize;
flacdec->sample_rate = metadata->data.stream_info.sample_rate;
flacdec->depth = depth = metadata->data.stream_info.bits_per_sample; flacdec->depth = depth = metadata->data.stream_info.bits_per_sample;
flacdec->channels = metadata->data.stream_info.channels;
if (depth < 9) if (depth < 9)
flacdec->width = 8; width = 8;
else if (depth < 17) else if (depth < 17)
flacdec->width = 16; width = 16;
else else
flacdec->width = 32; width = 32;
gst_audio_info_set_format (&flacdec->info,
gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, width),
metadata->data.stream_info.sample_rate,
metadata->data.stream_info.channels, NULL);
memcpy (flacdec->info.position,
channel_positions[flacdec->info.channels - 1],
sizeof (GstAudioChannelPosition) * flacdec->info.channels);
gst_audio_channel_positions_to_valid_order (flacdec->info.position,
flacdec->info.channels);
/* Note: we create the inverse reordering map here */
gst_audio_get_channel_reorder_map (flacdec->info.channels,
flacdec->info.position, channel_positions[flacdec->info.channels - 1],
flacdec->channel_reorder_map);
GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u", GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u",
flacdec->min_blocksize, flacdec->max_blocksize); flacdec->min_blocksize, flacdec->max_blocksize);
GST_DEBUG_OBJECT (flacdec, "sample rate: %u, channels: %u", GST_DEBUG_OBJECT (flacdec, "sample rate: %u, channels: %u",
flacdec->sample_rate, flacdec->channels); flacdec->info.rate, flacdec->info.channels);
GST_DEBUG_OBJECT (flacdec, "depth: %u, width: %u", flacdec->depth, GST_DEBUG_OBJECT (flacdec, "depth: %u, width: %u", flacdec->depth,
flacdec->width); flacdec->info.finfo->width);
GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples); GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
break; break;
@ -524,7 +537,7 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
guint j, i; guint j, i;
gpointer data; gpointer data;
gsize size; gsize size;
const gchar *format; gboolean caps_changed;
GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples); GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples);
@ -537,29 +550,20 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
} }
depth = flacdec->depth; depth = flacdec->depth;
if (depth < 9)
depth = 8;
else if (depth < 17)
depth = 16;
else
depth = 32;
} }
switch (depth) { switch (depth) {
case 8: case 8:
width = 8; width = 8;
format = GST_AUDIO_NE (S8);
break; break;
case 12: case 12:
case 16: case 16:
width = 16; width = 16;
format = GST_AUDIO_NE (S16);
break; break;
case 20: case 20:
case 24: case 24:
case 32: case 32:
width = 32; width = 32;
format = GST_AUDIO_NE (S32);
break; break;
default: default:
GST_ERROR_OBJECT (flacdec, "unsupported depth %d", depth); GST_ERROR_OBJECT (flacdec, "unsupported depth %d", depth);
@ -568,8 +572,8 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
} }
if (sample_rate == 0) { if (sample_rate == 0) {
if (flacdec->sample_rate != 0) { if (flacdec->info.rate != 0) {
sample_rate = flacdec->sample_rate; sample_rate = flacdec->info.rate;
} else { } else {
GST_ERROR_OBJECT (flacdec, "unknown sample rate"); GST_ERROR_OBJECT (flacdec, "unknown sample rate");
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
@ -577,27 +581,34 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
} }
} }
if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (flacdec))) { caps_changed = (sample_rate != flacdec->info.rate)
|| (width != flacdec->info.finfo->width)
|| (channels != flacdec->info.channels);
if (caps_changed
|| !gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (flacdec))) {
GstCaps *caps; GstCaps *caps;
GST_DEBUG_OBJECT (flacdec, "Negotiating %d Hz @ %d channels", GST_DEBUG_OBJECT (flacdec, "Negotiating %d Hz @ %d channels", sample_rate,
frame->header.sample_rate, channels); channels);
caps = gst_caps_new_simple ("audio/x-raw", gst_audio_info_set_format (&flacdec->info,
"format", G_TYPE_STRING, format, gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, width),
"rate", G_TYPE_INT, frame->header.sample_rate, sample_rate, channels, NULL);
"channels", G_TYPE_INT, channels, NULL);
if (channels > 2) { memcpy (flacdec->info.position,
GstStructure *s = gst_caps_get_structure (caps, 0); channel_positions[flacdec->info.channels - 1],
sizeof (GstAudioChannelPosition) * flacdec->info.channels);
gst_audio_channel_positions_to_valid_order (flacdec->info.position,
flacdec->info.channels);
/* Note: we create the inverse reordering map here */
gst_audio_get_channel_reorder_map (flacdec->info.channels,
flacdec->info.position, channel_positions[flacdec->info.channels - 1],
flacdec->channel_reorder_map);
gst_audio_set_channel_positions (s, channel_positions[channels - 1]); caps = gst_audio_info_to_caps (&flacdec->info);
}
flacdec->depth = depth; flacdec->depth = depth;
flacdec->width = width;
flacdec->channels = channels;
flacdec->sample_rate = sample_rate;
gst_audio_decoder_set_outcaps (GST_AUDIO_DECODER (flacdec), caps); gst_audio_decoder_set_outcaps (GST_AUDIO_DECODER (flacdec), caps);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -609,26 +620,55 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
data = gst_buffer_map (outbuf, &size, NULL, GST_MAP_WRITE); data = gst_buffer_map (outbuf, &size, NULL, GST_MAP_WRITE);
if (width == 8) { if (width == 8) {
gint8 *outbuffer = (gint8 *) data; gint8 *outbuffer = (gint8 *) data;
gint *reorder_map = flacdec->channel_reorder_map;
for (i = 0; i < samples; i++) { if (width != depth) {
for (j = 0; j < channels; j++) { for (i = 0; i < samples; i++) {
*outbuffer++ = (gint8) buffer[j][i]; for (j = 0; j < channels; j++) {
*outbuffer++ = (gint8) (buffer[reorder_map[j]][i] >> (width - depth));
}
}
} else {
for (i = 0; i < samples; i++) {
for (j = 0; j < channels; j++) {
*outbuffer++ = (gint8) buffer[reorder_map[j]][i];
}
} }
} }
} else if (width == 16) { } else if (width == 16) {
gint16 *outbuffer = (gint16 *) data; gint16 *outbuffer = (gint16 *) data;
gint *reorder_map = flacdec->channel_reorder_map;
for (i = 0; i < samples; i++) { if (width != depth) {
for (j = 0; j < channels; j++) { for (i = 0; i < samples; i++) {
*outbuffer++ = (gint16) buffer[j][i]; for (j = 0; j < channels; j++) {
*outbuffer++ =
(gint16) (buffer[reorder_map[j]][i] >> (width - depth));
}
}
} else {
for (i = 0; i < samples; i++) {
for (j = 0; j < channels; j++) {
*outbuffer++ = (gint16) buffer[reorder_map[j]][i];
}
} }
} }
} else if (width == 32) { } else if (width == 32) {
gint32 *outbuffer = (gint32 *) data; gint32 *outbuffer = (gint32 *) data;
gint *reorder_map = flacdec->channel_reorder_map;
for (i = 0; i < samples; i++) { if (width != depth) {
for (j = 0; j < channels; j++) { for (i = 0; i < samples; i++) {
*outbuffer++ = (gint32) buffer[j][i]; for (j = 0; j < channels; j++) {
*outbuffer++ =
(gint32) (buffer[reorder_map[j]][i] >> (width - depth));
}
}
} else {
for (i = 0; i < samples; i++) {
for (j = 0; j < channels; j++) {
*outbuffer++ = (gint32) buffer[reorder_map[j]][i];
}
} }
} }
} else { } else {

View file

@ -23,6 +23,7 @@
#define __GST_FLAC_DEC_H__ #define __GST_FLAC_DEC_H__
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstaudiodecoder.h> #include <gst/audio/gstaudiodecoder.h>
#include <FLAC/all.h> #include <FLAC/all.h>
@ -50,10 +51,9 @@ struct _GstFlacDec {
GstFlowReturn last_flow; /* to marshal flow return from finis_frame to GstFlowReturn last_flow; /* to marshal flow return from finis_frame to
* handle_frame via flac callbacks */ * handle_frame via flac callbacks */
gint channels; GstAudioInfo info;
gint channel_reorder_map[8];
gint depth; gint depth;
gint width;
gint sample_rate;
/* from the stream info, needed for scanning */ /* from the stream info, needed for scanning */
guint16 min_blocksize; guint16 min_blocksize;

View file

@ -45,13 +45,12 @@
#include <gstflacenc.h> #include <gstflacenc.h>
#include <gst/audio/audio.h> #include <gst/audio/audio.h>
#include <gst/audio/multichannel.h>
#include <gst/tag/tag.h> #include <gst/tag/tag.h>
#include <gst/gsttagsetter.h> #include <gst/gsttagsetter.h>
/* Taken from http://flac.sourceforge.net/format.html#frame_header */ /* Taken from http://flac.sourceforge.net/format.html#frame_header */
static const GstAudioChannelPosition channel_positions[8][8] = { static const GstAudioChannelPosition channel_positions[8][8] = {
{GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}, {GST_AUDIO_CHANNEL_POSITION_MONO},
{GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, { GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
@ -69,7 +68,7 @@ static const GstAudioChannelPosition channel_positions[8][8] = {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
GST_AUDIO_CHANNEL_POSITION_LFE, GST_AUDIO_CHANNEL_POSITION_LFE1,
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
/* FIXME: 7/8 channel layouts are not defined in the FLAC specs */ /* FIXME: 7/8 channel layouts are not defined in the FLAC specs */
@ -79,40 +78,30 @@ static const GstAudioChannelPosition channel_positions[8][8] = {
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
GST_AUDIO_CHANNEL_POSITION_LFE, GST_AUDIO_CHANNEL_POSITION_LFE1,
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, { GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
GST_AUDIO_CHANNEL_POSITION_LFE, GST_AUDIO_CHANNEL_POSITION_LFE1,
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT} GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}
}; };
#define FLAC_SINK_CAPS \ #if G_BYTE_ORDER == G_LITTLE_ENDIAN
"audio/x-raw-int, " \ #define FORMATS "{ S8LE, S16LE, S24LE, S32LE } "
"endianness = (int) BYTE_ORDER, " \ #else
"signed = (boolean) TRUE, " \ #define FORMATS "{ S8BE, S16BE, S24BE, S32BE } "
"width = (int) 8, " \ #endif
"depth = (int) 8, " \
"rate = (int) [ 1, 655350 ], " \ #define FLAC_SINK_CAPS \
"channels = (int) [ 1, 8 ]; " \ "audio/x-raw, " \
"audio/x-raw-int, " \ "format = (string) " FORMATS ", " \
"endianness = (int) BYTE_ORDER, " \ "layout = (string) interleaved, " \
"signed = (boolean) TRUE, " \ "rate = (int) [ 1, 655350 ], " \
"width = (int) 16, " \ "channels = (int) [ 1, 8 ]"
"depth = (int) { 12, 16 }, " \
"rate = (int) [ 1, 655350 ], " \
"channels = (int) [ 1, 8 ]; " \
"audio/x-raw-int, " \
"endianness = (int) BYTE_ORDER, " \
"signed = (boolean) TRUE, " \
"width = (int) 32, " \
"depth = (int) { 20, 24 }, " \
"rate = (int) [ 1, 655350 ], " \
"channels = (int) [ 1, 8 ]"
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC, GST_PAD_SRC,
@ -424,9 +413,6 @@ gst_flac_enc_start (GstAudioEncoder * enc)
flacenc->got_headers = FALSE; flacenc->got_headers = FALSE;
flacenc->last_flow = GST_FLOW_OK; flacenc->last_flow = GST_FLOW_OK;
flacenc->offset = 0; flacenc->offset = 0;
flacenc->channels = 0;
flacenc->depth = 0;
flacenc->sample_rate = 0;
flacenc->eos = FALSE; flacenc->eos = FALSE;
flacenc->tags = gst_tag_list_new_empty (); flacenc->tags = gst_tag_list_new_empty ();
@ -502,6 +488,8 @@ gst_flac_enc_set_metadata (GstFlacEnc * flacenc, guint64 total_samples)
GstTagList *copy; GstTagList *copy;
gint entries = 1; gint entries = 1;
gint n_images, n_preview_images; gint n_images, n_preview_images;
GstAudioInfo *info =
gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (flacenc));
g_return_if_fail (flacenc != NULL); g_return_if_fail (flacenc != NULL);
user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (flacenc)); user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (flacenc));
@ -586,7 +574,7 @@ gst_flac_enc_set_metadata (GstFlacEnc * flacenc, guint64 total_samples)
FLAC__metadata_object_seektable_template_append_spaced_points FLAC__metadata_object_seektable_template_append_spaced_points
(flacenc->meta[entries], flacenc->seekpoints, total_samples); (flacenc->meta[entries], flacenc->seekpoints, total_samples);
} else { } else {
samples = -flacenc->seekpoints * flacenc->sample_rate; samples = -flacenc->seekpoints * GST_AUDIO_INFO_RATE (info);
res = res =
FLAC__metadata_object_seektable_template_append_spaced_points_by_samples FLAC__metadata_object_seektable_template_append_spaced_points_by_samples
(flacenc->meta[entries], samples, total_samples); (flacenc->meta[entries], samples, total_samples);
@ -617,49 +605,6 @@ gst_flac_enc_set_metadata (GstFlacEnc * flacenc, guint64 total_samples)
gst_tag_list_free (copy); gst_tag_list_free (copy);
} }
static void
gst_flac_enc_caps_append_structure_with_widths (GstCaps * caps,
GstStructure * s)
{
GstStructure *tmp;
GValue list = { 0, };
GValue depth = { 0, };
tmp = gst_structure_copy (s);
gst_structure_set (tmp, "width", G_TYPE_INT, 8, "depth", G_TYPE_INT, 8, NULL);
gst_caps_append_structure (caps, tmp);
tmp = gst_structure_copy (s);
g_value_init (&depth, G_TYPE_INT);
g_value_init (&list, GST_TYPE_LIST);
g_value_set_int (&depth, 12);
gst_value_list_append_value (&list, &depth);
g_value_set_int (&depth, 16);
gst_value_list_append_value (&list, &depth);
gst_structure_set (tmp, "width", G_TYPE_INT, 16, NULL);
gst_structure_set_value (tmp, "depth", &list);
gst_caps_append_structure (caps, tmp);
g_value_reset (&list);
tmp = s;
g_value_set_int (&depth, 20);
gst_value_list_append_value (&list, &depth);
g_value_set_int (&depth, 24);
gst_value_list_append_value (&list, &depth);
gst_structure_set (tmp, "width", G_TYPE_INT, 32, NULL);
gst_structure_set_value (tmp, "depth", &list);
gst_caps_append_structure (caps, tmp);
g_value_unset (&list);
g_value_unset (&depth);
}
static GstCaps * static GstCaps *
gst_flac_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter) gst_flac_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter)
{ {
@ -673,41 +618,49 @@ gst_flac_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter)
if (gst_pad_has_current_caps (pad)) { if (gst_pad_has_current_caps (pad)) {
ret = gst_pad_get_current_caps (pad); ret = gst_pad_get_current_caps (pad);
} else { } else {
gint i, c; gint i;
GValue v_arr = { 0, };
GValue v = { 0, };
GstStructure *s, *s2;
g_value_init (&v_arr, GST_TYPE_ARRAY);
g_value_init (&v, G_TYPE_STRING);
g_value_set_string (&v, GST_AUDIO_NE (S8));
gst_value_array_append_value (&v_arr, &v);
g_value_set_string (&v, GST_AUDIO_NE (S16));
gst_value_array_append_value (&v_arr, &v);
g_value_set_string (&v, GST_AUDIO_NE (S24));
gst_value_array_append_value (&v_arr, &v);
g_value_set_string (&v, GST_AUDIO_NE (S32));
gst_value_array_append_value (&v_arr, &v);
g_value_unset (&v);
s = gst_structure_new_empty ("audio/x-raw");
gst_structure_set_value (s, "format", &v_arr);
g_value_unset (&v_arr);
gst_structure_set (s, "layout", G_TYPE_STRING, "interleaved",
"rate", GST_TYPE_INT_RANGE, 1, 655350, NULL);
ret = gst_caps_new_empty (); ret = gst_caps_new_empty ();
for (i = 1; i <= 8; i++) {
s2 = gst_structure_copy (s);
gst_flac_enc_caps_append_structure_with_widths (ret, if (i == 1) {
gst_structure_new ("audio/x-raw-int", gst_structure_set (s, "channels", G_TYPE_INT, 1, NULL);
"endianness", G_TYPE_INT, G_BYTE_ORDER, } else {
"signed", G_TYPE_BOOLEAN, TRUE, guint64 channel_mask;
"rate", GST_TYPE_INT_RANGE, 1, 655350,
"channels", GST_TYPE_INT_RANGE, 1, 2, NULL));
for (i = 3; i <= 8; i++) { gst_audio_channel_positions_to_mask (channel_positions[i - 1], i,
GValue positions = { 0, }; &channel_mask);
GValue pos = { 0, }; gst_structure_set (s, "channels", G_TYPE_INT, 1, "channel-mask",
GstStructure *s; GST_TYPE_BITMASK, channel_mask, NULL);
g_value_init (&positions, GST_TYPE_ARRAY);
g_value_init (&pos, GST_TYPE_AUDIO_CHANNEL_POSITION);
for (c = 0; c < i; c++) {
g_value_set_enum (&pos, channel_positions[i - 1][c]);
gst_value_array_append_value (&positions, &pos);
} }
g_value_unset (&pos);
s = gst_structure_new ("audio/x-raw-int", gst_caps_append_structure (ret, s2);
"endianness", G_TYPE_INT, G_BYTE_ORDER,
"signed", G_TYPE_BOOLEAN, TRUE,
"rate", GST_TYPE_INT_RANGE, 1, 655350,
"channels", G_TYPE_INT, i, NULL);
gst_structure_set_value (s, "channel-positions", &positions);
g_value_unset (&positions);
gst_flac_enc_caps_append_structure_with_widths (ret, s);
} }
gst_structure_free (s);
} }
GST_OBJECT_UNLOCK (pad); GST_OBJECT_UNLOCK (pad);
@ -724,6 +677,8 @@ static guint64
gst_flac_enc_peer_query_total_samples (GstFlacEnc * flacenc, GstPad * pad) gst_flac_enc_peer_query_total_samples (GstFlacEnc * flacenc, GstPad * pad)
{ {
gint64 duration; gint64 duration;
GstAudioInfo *info =
gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (flacenc));
GST_DEBUG_OBJECT (flacenc, "querying peer for DEFAULT format duration"); GST_DEBUG_OBJECT (flacenc, "querying peer for DEFAULT format duration");
if (gst_pad_peer_query_duration (pad, GST_FORMAT_DEFAULT, &duration) if (gst_pad_peer_query_duration (pad, GST_FORMAT_DEFAULT, &duration)
@ -736,7 +691,7 @@ gst_flac_enc_peer_query_total_samples (GstFlacEnc * flacenc, GstPad * pad)
&& duration != GST_CLOCK_TIME_NONE) { && duration != GST_CLOCK_TIME_NONE) {
GST_DEBUG_OBJECT (flacenc, "peer reported duration %" GST_TIME_FORMAT, GST_DEBUG_OBJECT (flacenc, "peer reported duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (duration)); GST_TIME_ARGS (duration));
duration = GST_CLOCK_TIME_TO_FRAMES (duration, flacenc->sample_rate); duration = GST_CLOCK_TIME_TO_FRAMES (duration, GST_AUDIO_INFO_RATE (info));
goto done; goto done;
} }
@ -766,26 +721,28 @@ gst_flac_enc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
FLAC__STREAM_ENCODER_UNINITIALIZED) FLAC__STREAM_ENCODER_UNINITIALIZED)
goto encoder_already_initialized; goto encoder_already_initialized;
flacenc->channels = GST_AUDIO_INFO_CHANNELS (info);
flacenc->width = GST_AUDIO_INFO_WIDTH (info);
flacenc->depth = GST_AUDIO_INFO_DEPTH (info);
flacenc->sample_rate = GST_AUDIO_INFO_RATE (info);
caps = gst_caps_new_simple ("audio/x-flac", caps = gst_caps_new_simple ("audio/x-flac",
"channels", G_TYPE_INT, flacenc->channels, "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
"rate", G_TYPE_INT, flacenc->sample_rate, NULL); "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL);
if (!gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps)) if (!gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps))
goto setting_src_caps_failed; goto setting_src_caps_failed;
gst_caps_unref (caps); gst_caps_unref (caps);
gst_audio_get_channel_reorder_map (GST_AUDIO_INFO_CHANNELS (info),
channel_positions[GST_AUDIO_INFO_CHANNELS (info) - 1], info->position,
flacenc->channel_reorder_map);
total_samples = gst_flac_enc_peer_query_total_samples (flacenc, total_samples = gst_flac_enc_peer_query_total_samples (flacenc,
GST_AUDIO_ENCODER_SINK_PAD (enc)); GST_AUDIO_ENCODER_SINK_PAD (enc));
FLAC__stream_encoder_set_bits_per_sample (flacenc->encoder, flacenc->depth); FLAC__stream_encoder_set_bits_per_sample (flacenc->encoder,
FLAC__stream_encoder_set_sample_rate (flacenc->encoder, flacenc->sample_rate); GST_AUDIO_INFO_WIDTH (info));
FLAC__stream_encoder_set_channels (flacenc->encoder, flacenc->channels); FLAC__stream_encoder_set_sample_rate (flacenc->encoder,
GST_AUDIO_INFO_RATE (info));
FLAC__stream_encoder_set_channels (flacenc->encoder,
GST_AUDIO_INFO_CHANNELS (info));
if (total_samples != GST_CLOCK_TIME_NONE) if (total_samples != GST_CLOCK_TIME_NONE)
FLAC__stream_encoder_set_total_samples_estimate (flacenc->encoder, FLAC__stream_encoder_set_total_samples_estimate (flacenc->encoder,
@ -833,6 +790,9 @@ failed_to_initialize:
static gboolean static gboolean
gst_flac_enc_update_quality (GstFlacEnc * flacenc, gint quality) gst_flac_enc_update_quality (GstFlacEnc * flacenc, gint quality)
{ {
GstAudioInfo *info =
gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (flacenc));
flacenc->quality = quality; flacenc->quality = quality;
#define DO_UPDATE(name, val, str) \ #define DO_UPDATE(name, val, str) \
@ -847,7 +807,8 @@ gst_flac_enc_update_quality (GstFlacEnc * flacenc, gint quality)
g_object_freeze_notify (G_OBJECT (flacenc)); g_object_freeze_notify (G_OBJECT (flacenc));
if (flacenc->channels == 2 || flacenc->channels == 0) { if (GST_AUDIO_INFO_CHANNELS (info) == 2
|| GST_AUDIO_INFO_CHANNELS (info) == 0) {
DO_UPDATE (do_mid_side_stereo, mid_side, "mid_side_stereo"); DO_UPDATE (do_mid_side_stereo, mid_side, "mid_side_stereo");
DO_UPDATE (loose_mid_side_stereo, loose_mid_side, "loose_mid_side"); DO_UPDATE (loose_mid_side_stereo, loose_mid_side, "loose_mid_side");
} }
@ -945,10 +906,12 @@ gst_flac_enc_process_stream_headers (GstFlacEnc * enc)
GstCaps *caps; GstCaps *caps;
GList *l; GList *l;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GstAudioInfo *info =
gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (enc));
caps = gst_caps_new_simple ("audio/x-flac", caps = gst_caps_new_simple ("audio/x-flac",
"channels", G_TYPE_INT, enc->channels, "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
"rate", G_TYPE_INT, enc->sample_rate, NULL); "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL);
for (l = enc->headers; l != NULL; l = l->next) { for (l = enc->headers; l != NULL; l = l->next) {
GstBuffer *buf; GstBuffer *buf;
@ -1193,23 +1156,36 @@ gst_flac_enc_sink_event (GstAudioEncoder * enc, GstEvent * event)
return ret; return ret;
} }
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define READ_INT24 GST_READ_UINT24_LE
#else
#define READ_INT24 GST_READ_UINT24_BE
#endif
static GstFlowReturn static GstFlowReturn
gst_flac_enc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer) gst_flac_enc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer)
{ {
GstFlacEnc *flacenc; GstFlacEnc *flacenc;
FLAC__int32 *data; FLAC__int32 *data;
gsize bsize; gsize bsize;
gint samples, width; gint samples, width, channels;
gulong i; gulong i;
gint j;
FLAC__bool res; FLAC__bool res;
gpointer bdata; gpointer bdata;
GstAudioInfo *info =
gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (enc));
gint *reorder_map;
flacenc = GST_FLAC_ENC (enc); flacenc = GST_FLAC_ENC (enc);
/* base class ensures configuration */ /* base class ensures configuration */
g_return_val_if_fail (flacenc->depth != 0, GST_FLOW_NOT_NEGOTIATED); g_return_val_if_fail (GST_AUDIO_INFO_WIDTH (info) != 0,
GST_FLOW_NOT_NEGOTIATED);
width = flacenc->width; width = GST_AUDIO_INFO_WIDTH (info);
channels = GST_AUDIO_INFO_CHANNELS (info);
reorder_map = flacenc->channel_reorder_map;
if (G_UNLIKELY (!buffer)) { if (G_UNLIKELY (!buffer)) {
if (flacenc->eos) { if (flacenc->eos) {
@ -1229,28 +1205,46 @@ gst_flac_enc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer)
data = g_malloc (samples * sizeof (FLAC__int32)); data = g_malloc (samples * sizeof (FLAC__int32));
samples /= channels;
if (width == 8) { if (width == 8) {
gint8 *indata = (gint8 *) bdata; gint8 *indata = (gint8 *) bdata;
for (i = 0; i < samples; i++) for (i = 0; i < samples; i++)
data[i] = (FLAC__int32) indata[i]; for (j = 0; j < channels; j++)
data[i * channels + reorder_map[j]] =
(FLAC__int32) indata[i * channels + j];
} else if (width == 16) { } else if (width == 16) {
gint16 *indata = (gint16 *) bdata; gint16 *indata = (gint16 *) bdata;
for (i = 0; i < samples; i++) for (i = 0; i < samples; i++)
data[i] = (FLAC__int32) indata[i]; for (j = 0; j < channels; j++)
data[i * channels + reorder_map[j]] =
(FLAC__int32) indata[i * channels + j];
} else if (width == 24) {
guint8 *indata = (guint8 *) bdata;
guint32 val;
for (i = 0; i < samples; i++)
for (j = 0; j < channels; j++) {
val = READ_INT24 (&indata[3 * (i * channels + j)]);
if (val & 0x00800000)
val |= 0xff000000;
data[i * channels + reorder_map[j]] = (FLAC__int32) val;
}
} else if (width == 32) { } else if (width == 32) {
gint32 *indata = (gint32 *) bdata; gint32 *indata = (gint32 *) bdata;
for (i = 0; i < samples; i++) for (i = 0; i < samples; i++)
data[i] = (FLAC__int32) indata[i]; for (j = 0; j < channels; j++)
data[i * channels + reorder_map[j]] =
(FLAC__int32) indata[i * channels + j];
} else { } else {
g_assert_not_reached (); g_assert_not_reached ();
} }
gst_buffer_unmap (buffer, bdata, bsize); gst_buffer_unmap (buffer, bdata, bsize);
res = FLAC__stream_encoder_process_interleaved (flacenc->encoder, res = FLAC__stream_encoder_process_interleaved (flacenc->encoder,
(const FLAC__int32 *) data, samples / flacenc->channels); (const FLAC__int32 *) data, samples / channels);
g_free (data); g_free (data);

View file

@ -47,10 +47,6 @@ struct _GstFlacEnc {
* fails for some reason */ * fails for some reason */
guint64 offset; guint64 offset;
gint channels;
gint width;
gint depth;
gint sample_rate;
gint quality; gint quality;
gboolean stopped; gboolean stopped;
guint padding; guint padding;
@ -66,6 +62,8 @@ struct _GstFlacEnc {
/* queue headers until we have them all so we can add streamheaders to caps */ /* queue headers until we have them all so we can add streamheaders to caps */
gboolean got_headers; gboolean got_headers;
GList *headers; GList *headers;
gint channel_reorder_map[8];
}; };
struct _GstFlacEncClass { struct _GstFlacEncClass {