mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
ext/flac/gstflacdec.*: If the stream header doesn't contain the total number of samples, search for the last flac fra...
Original commit message from CVS: * ext/flac/gstflacdec.c: (gst_flac_calculate_crc8), (gst_flac_dec_scan_got_frame), (gst_flac_dec_scan_for_last_block), (gst_flac_dec_metadata_callback): * ext/flac/gstflacdec.h: If the stream header doesn't contain the total number of samples, search for the last flac frame at the end of the file and calculate the total duration from that frame's offset (fixes #337609).
This commit is contained in:
parent
5900db993d
commit
661ddc65c3
3 changed files with 208 additions and 5 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2006-04-07 Tim-Philipp Müller <tim at centricular dot net>
|
||||
|
||||
* ext/flac/gstflacdec.c: (gst_flac_calculate_crc8),
|
||||
(gst_flac_dec_scan_got_frame), (gst_flac_dec_scan_for_last_block),
|
||||
(gst_flac_dec_metadata_callback):
|
||||
* ext/flac/gstflacdec.h:
|
||||
If the stream header doesn't contain the total number of samples,
|
||||
search for the last flac frame at the end of the file and calculate
|
||||
the total duration from that frame's offset (fixes #337609).
|
||||
|
||||
2006-04-07 Edward Hervey <edward@fluendo.com>
|
||||
|
||||
* gst/avi/gstavidemux.c: (gst_avi_demux_parse_stream):
|
||||
|
|
|
@ -254,6 +254,175 @@ gst_flac_dec_update_metadata (GstFlacDec * flacdec,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */
|
||||
static const guint8 crc8_table[256] = {
|
||||
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
|
||||
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
|
||||
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
|
||||
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
|
||||
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
|
||||
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
|
||||
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
|
||||
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
|
||||
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
|
||||
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
|
||||
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
|
||||
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
|
||||
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
|
||||
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
|
||||
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
|
||||
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
|
||||
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
|
||||
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
|
||||
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
|
||||
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
|
||||
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
|
||||
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
|
||||
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
|
||||
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
|
||||
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
|
||||
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
|
||||
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
|
||||
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
|
||||
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
|
||||
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
|
||||
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
|
||||
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
|
||||
};
|
||||
|
||||
static guint8
|
||||
gst_flac_calculate_crc8 (guint8 * data, guint length)
|
||||
{
|
||||
guint8 crc = 0;
|
||||
|
||||
while (length--) {
|
||||
crc = crc8_table[crc ^ *data];
|
||||
++data;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, guint8 * data, guint size,
|
||||
gint64 * last_sample_num)
|
||||
{
|
||||
guint headerlen;
|
||||
guint sr_from_end = 0; /* can be 0, 8 or 16 */
|
||||
guint bs_from_end = 0; /* can be 0, 8 or 16 */
|
||||
guint32 val = 0;
|
||||
guint8 bs, sr, ca, ss, pb;
|
||||
|
||||
if (size < 10)
|
||||
return FALSE;
|
||||
|
||||
/* sync */
|
||||
if (data[0] != 0xFF || data[1] != 0xF8)
|
||||
return FALSE;
|
||||
|
||||
bs = (data[2] & 0xF0) >> 8; /* blocksize marker */
|
||||
sr = (data[2] & 0x0F); /* samplerate marker */
|
||||
ca = (data[3] & 0xF0) >> 8; /* channel assignment */
|
||||
ss = (data[3] & 0x0F) >> 1; /* sample size marker */
|
||||
pb = (data[3] & 0x01); /* padding bit */
|
||||
|
||||
GST_LOG ("got sync, bs=%x,sr=%x,ca=%x,ss=%x,pb=%x", bs, sr, ca, ss, pb);
|
||||
|
||||
if (sr == 0x0F || sr == 0x01 || sr == 0x02 || sr == 0x03 ||
|
||||
ca >= 0x0B || ss == 0x03 || ss == 0x07) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* read block size from end of header? */
|
||||
if (bs == 6)
|
||||
bs_from_end = 8;
|
||||
else if (bs == 7)
|
||||
bs_from_end = 16;
|
||||
|
||||
/* read sample rate from end of header? */
|
||||
if (sr == 0x0C)
|
||||
sr_from_end = 8;
|
||||
else if (sr == 0x0D || sr == 0x0E)
|
||||
sr_from_end = 16;
|
||||
|
||||
val = (guint32) g_utf8_get_char_validated ((gchar *) data + 4, -1);
|
||||
|
||||
if (val == (guint32) - 1 || val == (guint32) - 2) {
|
||||
GST_LOG_OBJECT (flacdec, "failed to read sample/frame");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
headerlen = 4 + g_unichar_to_utf8 ((gunichar) val, NULL) +
|
||||
(bs_from_end / 8) + (sr_from_end / 8);
|
||||
|
||||
if (gst_flac_calculate_crc8 (data, headerlen) != data[headerlen])
|
||||
return FALSE;
|
||||
|
||||
if (flacdec->min_blocksize == flacdec->max_blocksize) {
|
||||
*last_sample_num = (val + 1) * flacdec->min_blocksize;
|
||||
} else {
|
||||
*last_sample_num = val; /* FIXME: + length of last block in samples */
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (flacdec, "last sample %" G_GINT64_FORMAT " = %"
|
||||
GST_TIME_FORMAT, *last_sample_num,
|
||||
GST_TIME_ARGS (*last_sample_num * GST_SECOND / flacdec->sample_rate));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define SCANBLOCK_SIZE (64*1024)
|
||||
|
||||
static void
|
||||
gst_flac_dec_scan_for_last_block (GstFlacDec * flacdec, gint64 * samples)
|
||||
{
|
||||
GstFormat format = GST_FORMAT_BYTES;
|
||||
gint64 file_size, offset;
|
||||
|
||||
GST_INFO_OBJECT (flacdec, "total number of samples unknown, scanning file");
|
||||
|
||||
if (!gst_pad_query_peer_duration (flacdec->sinkpad, &format, &file_size)) {
|
||||
GST_WARNING_OBJECT (flacdec, "failed to query upstream size!");
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (flacdec, "upstream size: %" G_GINT64_FORMAT, file_size);
|
||||
|
||||
offset = file_size - 1;
|
||||
while (offset >= MAX (SCANBLOCK_SIZE / 2, file_size / 2)) {
|
||||
GstFlowReturn flow;
|
||||
GstBuffer *buf = NULL;
|
||||
guint8 *data;
|
||||
guint size;
|
||||
|
||||
/* divide by 2 = not very sophisticated way to deal with overlapping */
|
||||
offset -= SCANBLOCK_SIZE / 2;
|
||||
GST_LOG_OBJECT (flacdec, "looking for frame at %" G_GINT64_FORMAT
|
||||
"-%" G_GINT64_FORMAT, offset, offset + SCANBLOCK_SIZE);
|
||||
|
||||
flow = gst_pad_pull_range (flacdec->sinkpad, offset, SCANBLOCK_SIZE, &buf);
|
||||
if (flow != GST_FLOW_OK) {
|
||||
GST_DEBUG_OBJECT (flacdec, "flow = %s", gst_flow_get_name (flow));
|
||||
return;
|
||||
}
|
||||
|
||||
size = GST_BUFFER_SIZE (buf);
|
||||
data = GST_BUFFER_DATA (buf);
|
||||
|
||||
while (size > 16) {
|
||||
if (gst_flac_dec_scan_got_frame (flacdec, data, size, samples)) {
|
||||
GST_DEBUG_OBJECT (flacdec, "frame sync at offset %" G_GINT64_FORMAT,
|
||||
offset + GST_BUFFER_SIZE (buf) - size);
|
||||
gst_buffer_unref (buf);
|
||||
return;
|
||||
}
|
||||
++data;
|
||||
--size;
|
||||
}
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_flac_dec_metadata_callback (const FLAC__SeekableStreamDecoder * decoder,
|
||||
|
@ -264,12 +433,32 @@ gst_flac_dec_metadata_callback (const FLAC__SeekableStreamDecoder * decoder,
|
|||
flacdec = GST_FLAC_DEC (client_data);
|
||||
|
||||
switch (metadata->type) {
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
gst_segment_set_duration (&flacdec->segment, GST_FORMAT_DEFAULT,
|
||||
metadata->data.stream_info.total_samples);
|
||||
if (flacdec->segment.stop == -1)
|
||||
flacdec->segment.stop = metadata->data.stream_info.total_samples;
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:{
|
||||
gint64 samples;
|
||||
|
||||
samples = metadata->data.stream_info.total_samples;
|
||||
|
||||
flacdec->min_blocksize = metadata->data.stream_info.min_blocksize;
|
||||
flacdec->max_blocksize = metadata->data.stream_info.max_blocksize;
|
||||
flacdec->sample_rate = metadata->data.stream_info.sample_rate;
|
||||
|
||||
GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u",
|
||||
flacdec->min_blocksize, flacdec->max_blocksize);
|
||||
|
||||
if (samples == 0) {
|
||||
gst_flac_dec_scan_for_last_block (flacdec, &samples);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
|
||||
|
||||
if (samples > 0) {
|
||||
gst_segment_set_duration (&flacdec->segment, GST_FORMAT_DEFAULT,
|
||||
samples);
|
||||
if (flacdec->segment.stop == -1)
|
||||
flacdec->segment.stop = samples;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
gst_flac_dec_update_metadata (flacdec, metadata);
|
||||
break;
|
||||
|
|
|
@ -64,6 +64,10 @@ struct _GstFlacDec {
|
|||
gint depth;
|
||||
gint width;
|
||||
gint sample_rate;
|
||||
|
||||
/* from the stream info, needed for scanning */
|
||||
guint16 min_blocksize;
|
||||
guint16 max_blocksize;
|
||||
};
|
||||
|
||||
struct _GstFlacDecClass {
|
||||
|
|
Loading…
Reference in a new issue