Added support for iTunSMPB to MP3 parser; Replaced crappy MP3 seek function with accurate full file parse with 8 packet pre-roll; Changed top level decoder plugin to truncate output at the specified duration

CQTexperiment
Chris Moeller 2013-10-09 21:23:50 -07:00
parent 754a22166a
commit 81c62891bd
4 changed files with 159 additions and 55 deletions

View File

@ -309,6 +309,47 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
av_dict_set(metadata, key, (const char *) dst, dict_flags); av_dict_set(metadata, key, (const char *) dst, dict_flags);
} }
/**
* Parse a comment tag.
*/
static void read_comm(AVFormatContext *s, AVIOContext *pb, int taglen,
AVDictionary **metadata)
{
const char *key = "comment";
uint8_t *dst;
int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
int language;
if (taglen < 4)
return;
encoding = avio_r8(pb);
taglen--;
language = avio_rl24(pb);
taglen -= 3;
if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
return;
}
if (dst && dst[0]) {
key = (const char *) dst;
dict_flags |= AV_DICT_DONT_STRDUP_KEY;
}
if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
if (dict_flags & AV_DICT_DONT_STRDUP_KEY)
av_freep((void*)&key);
return;
}
if (dst)
av_dict_set(metadata, key, (const char *) dst, dict_flags);
}
/** /**
* Parse GEOB tag into a ID3v2ExtraMetaGEOB struct. * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct.
*/ */
@ -720,7 +761,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag); av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
avio_skip(s->pb, tlen); avio_skip(s->pb, tlen);
/* check for text tag or supported special meta tag */ /* check for text tag or supported special meta tag */
} else if (tag[0] == 'T' || } else if (tag[0] == 'T' || memcmp(tag, "COMM", 4) == 0 ||
(extra_meta && (extra_meta &&
(extra_func = get_extra_meta_func(tag, isv34)))) { (extra_func = get_extra_meta_func(tag, isv34)))) {
pbx = s->pb; pbx = s->pb;
@ -786,6 +827,8 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
if (tag[0] == 'T') if (tag[0] == 'T')
/* parse text tag */ /* parse text tag */
read_ttag(s, pbx, tlen, &s->metadata, tag); read_ttag(s, pbx, tlen, &s->metadata, tag);
else if (memcmp(tag, "COMM", 4) == 0)
read_comm(s, pbx, tlen, &s->metadata);
else else
/* parse special meta tag */ /* parse special meta tag */
extra_func->read(s, pbx, tlen, tag, extra_meta); extra_func->read(s, pbx, tlen, tag, extra_meta);

View File

@ -129,6 +129,8 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
MPADecodeHeader c; MPADecodeHeader c;
int vbrtag_size = 0; int vbrtag_size = 0;
int is_cbr; int is_cbr;
AVDictionaryEntry *de;
uint64_t duration = 0;
v = avio_rb32(s->pb); v = avio_rb32(s->pb);
if(ff_mpa_check_header(v) < 0) if(ff_mpa_check_header(v) < 0)
@ -139,6 +141,9 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
if(c.layer != 3) if(c.layer != 3)
return -1; return -1;
mp3->start_pad = 0;
mp3->end_pad = 0;
spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */ spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */
/* Check for Xing / Info tag */ /* Check for Xing / Info tag */
@ -164,6 +169,8 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
mp3->start_pad = v>>12; mp3->start_pad = v>>12;
mp3-> end_pad = v&4095; mp3-> end_pad = v&4095;
st->skip_samples = mp3->start_pad + 528 + 1; st->skip_samples = mp3->start_pad + 528 + 1;
if (mp3->end_pad >= 528 + 1)
mp3->end_pad -= 528 + 1;
av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3-> end_pad); av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3-> end_pad);
} }
} }
@ -181,17 +188,57 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
} }
} }
if(!frames && !size) if (!frames)
vbrtag_size = 0;
if (s->metadata && (de = av_dict_get(s->metadata, "iTunSMPB", NULL, 0))) {
uint32_t zero, start_pad, end_pad;
uint64_t last_eight_frames_offset;
if (sscanf(de->value, "%x %x %x %llx %x %llx", &zero, &start_pad, &end_pad, &duration, &zero, &last_eight_frames_offset) < 6) {
duration = 0;
}
else {
mp3->start_pad = start_pad;
mp3->end_pad = end_pad;
if (end_pad >= 528 + 1)
mp3->end_pad = end_pad - (528 + 1);
st->skip_samples = mp3->start_pad + 528 + 1;
av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3->end_pad);
if (s->pb->seekable) {
int i;
size = last_eight_frames_offset;
avio_seek(s->pb, base + vbrtag_size + last_eight_frames_offset, SEEK_SET);
for (i = 0; i < 8; ++i) {
v = avio_rb32(s->pb);
if (ff_mpa_check_header(v) < 0)
return -1;
if (avpriv_mpegaudio_decode_header(&c, v) != 0)
break;
size += c.frame_size;
avio_skip(s->pb, c.frame_size - 4);
}
}
}
}
if(!frames && !size && !duration)
return -1; return -1;
/* Skip the vbr tag frame */ /* Skip the vbr tag frame */
avio_seek(s->pb, base + vbrtag_size, SEEK_SET); avio_seek(s->pb, base + vbrtag_size, SEEK_SET);
if(frames) if (duration)
st->duration = av_rescale_q(duration, (AVRational){1, c.sample_rate}, st->time_base);
else if(frames)
st->duration = av_rescale_q(frames, (AVRational){spf, c.sample_rate}, st->duration = av_rescale_q(frames, (AVRational){spf, c.sample_rate},
st->time_base); st->time_base) - av_rescale_q(mp3->end_pad, (AVRational){1, c.sample_rate}, st->time_base);
if (size && frames && !is_cbr)
st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf); if (size) {
if (duration)
st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, duration);
else if (frames)
st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf);
}
mp3->is_cbr = is_cbr; mp3->is_cbr = is_cbr;
mp3->header_filesize = size; mp3->header_filesize = size;
@ -285,57 +332,58 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp,
int flags) int flags)
{ {
MP3DecContext *mp3 = s->priv_data; MP3DecContext *mp3 = s->priv_data;
AVIndexEntry *ie, ie1;
AVStream *st = s->streams[0]; AVStream *st = s->streams[0];
int64_t ret = av_index_search_timestamp(st, timestamp, flags); int64_t timestamp_samples = 0;
int i, j; uint32_t v, spf;
if (mp3->is_cbr && st->duration > 0 && mp3->header_filesize > s->data_offset) { timestamp = av_clip64(timestamp, 0, st->duration);
int64_t filesize = avio_size(s->pb);
int64_t duration;
if (filesize <= s->data_offset)
filesize = mp3->header_filesize;
filesize -= s->data_offset;
duration = av_rescale(st->duration, filesize, mp3->header_filesize - s->data_offset);
ie = &ie1;
timestamp = av_clip64(timestamp, 0, duration);
ie->timestamp = timestamp;
ie->pos = av_rescale(timestamp, filesize, duration) + s->data_offset;
} else if (mp3->xing_toc) {
if (ret < 0)
return ret;
ie = &st->index_entries[ret]; /* Screw it, we're doing a full stream parse! */
} else { avio_seek(s->pb, s->data_offset, SEEK_SET);
st->skip_samples = timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0;
return -1; st->skip_samples = 0;
if (timestamp > 0) {
int64_t skipped = 0;
int64_t skip_extra = 0;
do {
MPADecodeHeader c;
v = avio_rb32(s->pb);
avio_seek(s->pb, -4, SEEK_CUR);
if(ff_mpa_check_header(v) < 0)
return -1;
if (avpriv_mpegaudio_decode_header(&c, v) != 0)
return -1;
spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */
if (!timestamp_samples) {
timestamp_samples = av_rescale_q(timestamp, st->time_base, (AVRational){1, c.sample_rate}) + mp3->start_pad + 528 + 1;
if (timestamp_samples >= spf * 8 ) {
timestamp_samples -= spf * 8;
skip_extra = spf * 8;
}
else {
skip_extra = timestamp_samples;
timestamp_samples = 0;
}
}
if ( skipped + spf > timestamp_samples ) break;
skipped += spf;
avio_skip(s->pb, c.frame_size);
} while ( skipped < timestamp_samples );
st->skip_samples = timestamp_samples - skipped + skip_extra;
} }
ret = avio_seek(s->pb, ie->pos, SEEK_SET); ff_update_cur_dts(s, st, timestamp);
if (ret < 0)
return ret;
#define MIN_VALID 3
for(i=0; i<4096; i++) {
int64_t pos = ie->pos + i;
for(j=0; j<MIN_VALID; j++) {
ret = check(s, pos);
if(ret < 0)
break;
pos += ret;
}
if(j==MIN_VALID)
break;
}
if(j!=MIN_VALID)
i=0;
ret = avio_seek(s->pb, ie->pos + i, SEEK_SET);
if (ret < 0)
return ret;
ff_update_cur_dts(s, st, ie->timestamp);
st->skip_samples = ie->timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0;
return 0; return 0;
} }

View File

@ -35,6 +35,7 @@
float frequency; float frequency;
long totalFrames; long totalFrames;
long framesPlayed; long framesPlayed;
long framesToSkip;
} }
@end @end

View File

@ -128,7 +128,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
NSLog(@"channels: %d", c->channels); NSLog(@"channels: %d", c->channels);
channels = c->channels; channels = c->channels;
bitrate = ic->bit_rate / 1000; bitrate = c->bit_rate / 1000;
floatingPoint = NO; floatingPoint = NO;
switch (c->sample_fmt) { switch (c->sample_fmt) {
case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8:
@ -162,6 +162,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
return NO; return NO;
} }
totalFrames = c->sample_rate * ((float)ic->duration/AV_TIME_BASE); totalFrames = c->sample_rate * ((float)ic->duration/AV_TIME_BASE);
framesPlayed = 0;
frequency = c->sample_rate; frequency = c->sample_rate;
seekable = YES; seekable = YES;
@ -204,8 +205,12 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
} }
if (frames > 0) if (frames > 0)
{ {
if (framesPlayed >= totalFrames)
break;
size_t sampleBufferOffset = 0; size_t sampleBufferOffset = 0;
if (av_read_frame(ic, &framePacket) < 0) if (av_read_frame(ic, &framePacket) < 0)
{ {
NSLog(@"Uh oh... av_read_frame returned negative"); NSLog(@"Uh oh... av_read_frame returned negative");
@ -266,6 +271,11 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
numFrames = sampleBufferOffset / bytesPerFrame; numFrames = sampleBufferOffset / bytesPerFrame;
samplePos = 0; samplePos = 0;
if (numFrames + framesPlayed > totalFrames)
numFrames = totalFrames - framesPlayed;
framesPlayed += numFrames;
} }
} }
@ -278,6 +288,8 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
NSLog(@"frame: %ld", frame); NSLog(@"frame: %ld", frame);
AVRational time_base = ic->streams[streamIndex]->time_base; AVRational time_base = ic->streams[streamIndex]->time_base;
av_seek_frame(ic, streamIndex, frame * time_base.den / time_base.num / frequency, 0); av_seek_frame(ic, streamIndex, frame * time_base.den / time_base.num / frequency, 0);
numFrames = 0;
framesPlayed = frame;
return frame; return frame;
} }