diff --git a/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/id3v2.c b/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/id3v2.c index 9b9c1c94b..c46147d12 100644 --- a/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/id3v2.c +++ b/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/id3v2.c @@ -309,6 +309,47 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, 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. */ @@ -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); avio_skip(s->pb, tlen); /* 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_func = get_extra_meta_func(tag, isv34)))) { pbx = s->pb; @@ -786,6 +827,8 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, if (tag[0] == 'T') /* parse text tag */ read_ttag(s, pbx, tlen, &s->metadata, tag); + else if (memcmp(tag, "COMM", 4) == 0) + read_comm(s, pbx, tlen, &s->metadata); else /* parse special meta tag */ extra_func->read(s, pbx, tlen, tag, extra_meta); diff --git a/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/mp3dec.c b/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/mp3dec.c index c82e41269..fe2129a03 100644 --- a/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/mp3dec.c +++ b/Frameworks/FFMPEG/ffmpeg-minimal/libavformat/mp3dec.c @@ -129,6 +129,8 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) MPADecodeHeader c; int vbrtag_size = 0; int is_cbr; + AVDictionaryEntry *de; + uint64_t duration = 0; v = avio_rb32(s->pb); if(ff_mpa_check_header(v) < 0) @@ -138,7 +140,10 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) vbrtag_size = c.frame_size; if(c.layer != 3) return -1; - + + mp3->start_pad = 0; + mp3->end_pad = 0; + spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */ /* 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-> end_pad = v&4095; 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); } } @@ -180,18 +187,58 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) frames = avio_rb32(s->pb); } } + + if (!frames) + vbrtag_size = 0; - if(!frames && !size) + 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; /* Skip the vbr tag frame */ 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->time_base); - if (size && frames && !is_cbr) - st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf); + st->time_base) - av_rescale_q(mp3->end_pad, (AVRational){1, c.sample_rate}, st->time_base); + + 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->header_filesize = size; @@ -285,57 +332,58 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { MP3DecContext *mp3 = s->priv_data; - AVIndexEntry *ie, ie1; AVStream *st = s->streams[0]; - int64_t ret = av_index_search_timestamp(st, timestamp, flags); - int i, j; + int64_t timestamp_samples = 0; + uint32_t v, spf; - if (mp3->is_cbr && st->duration > 0 && mp3->header_filesize > s->data_offset) { - 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; + timestamp = av_clip64(timestamp, 0, st->duration); + + /* Screw it, we're doing a full stream parse! */ + avio_seek(s->pb, s->data_offset, SEEK_SET); + + 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; - ie = &st->index_entries[ret]; - } else { - st->skip_samples = timestamp <= 0 ? mp3->start_pad + 528 + 1 : 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); - if (ret < 0) - return ret; - -#define MIN_VALID 3 - for(i=0; i<4096; i++) { - int64_t pos = ie->pos + i; - for(j=0; jpb, 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; + + ff_update_cur_dts(s, st, timestamp); + return 0; } diff --git a/Plugins/FFMPEG/FFMPEGDecoder.h b/Plugins/FFMPEG/FFMPEGDecoder.h index f7472cb00..241f826fe 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.h +++ b/Plugins/FFMPEG/FFMPEGDecoder.h @@ -35,6 +35,7 @@ float frequency; long totalFrames; long framesPlayed; + long framesToSkip; } @end diff --git a/Plugins/FFMPEG/FFMPEGDecoder.m b/Plugins/FFMPEG/FFMPEGDecoder.m index 8c722468d..91d05690d 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.m +++ b/Plugins/FFMPEG/FFMPEGDecoder.m @@ -128,7 +128,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op) NSLog(@"channels: %d", c->channels); channels = c->channels; - bitrate = ic->bit_rate / 1000; + bitrate = c->bit_rate / 1000; floatingPoint = NO; switch (c->sample_fmt) { case AV_SAMPLE_FMT_U8: @@ -162,6 +162,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op) return NO; } totalFrames = c->sample_rate * ((float)ic->duration/AV_TIME_BASE); + framesPlayed = 0; frequency = c->sample_rate; seekable = YES; @@ -204,8 +205,12 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op) } if (frames > 0) { + if (framesPlayed >= totalFrames) + break; + size_t sampleBufferOffset = 0; + if (av_read_frame(ic, &framePacket) < 0) { NSLog(@"Uh oh... av_read_frame returned negative"); @@ -266,6 +271,11 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op) numFrames = sampleBufferOffset / bytesPerFrame; 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); AVRational time_base = ic->streams[streamIndex]->time_base; av_seek_frame(ic, streamIndex, frame * time_base.den / time_base.num / frequency, 0); + numFrames = 0; + framesPlayed = frame; return frame; }