diff options
Diffstat (limited to 'libavformat/flacdec.c')
-rw-r--r-- | libavformat/flacdec.c | 60 |
1 files changed, 59 insertions, 1 deletions
diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index 4c1f943581..4c7ee59ec5 100644 --- a/libavformat/flacdec.c +++ b/libavformat/flacdec.c @@ -28,11 +28,27 @@ #include "vorbiscomment.h" #include "replaygain.h" +#define SEEKPOINT_SIZE 18 + +typedef struct FLACDecContext { + int found_seektable; +} FLACDecContext; + +static void reset_index_position(int64_t metadata_head_size, AVStream *st) +{ + /* the real seek index offset should be the size of metadata blocks with the offset in the frame blocks */ + int i; + for(i=0; i<st->nb_index_entries; i++) { + st->index_entries[i].pos += metadata_head_size; + } +} + static int flac_read_header(AVFormatContext *s) { int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0; uint8_t header[4]; uint8_t *buffer=NULL; + FLACDecContext *flac = s->priv_data; AVStream *st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); @@ -58,6 +74,7 @@ static int flac_read_header(AVFormatContext *s) case FLAC_METADATA_TYPE_CUESHEET: case FLAC_METADATA_TYPE_PICTURE: case FLAC_METADATA_TYPE_VORBIS_COMMENT: + case FLAC_METADATA_TYPE_SEEKTABLE: buffer = av_mallocz(metadata_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!buffer) { return AVERROR(ENOMEM); @@ -132,7 +149,23 @@ static int flac_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Error parsing attached picture.\n"); return ret; } - } else { + } else if (metadata_type == FLAC_METADATA_TYPE_SEEKTABLE) { + const uint8_t *seekpoint = buffer; + int i, seek_point_count = metadata_size/SEEKPOINT_SIZE; + flac->found_seektable = 1; + if ((s->flags&AVFMT_FLAG_FAST_SEEK)) { + for(i=0; i<seek_point_count; i++) { + int64_t timestamp = bytestream_get_be64(&seekpoint); + int64_t pos = bytestream_get_be64(&seekpoint); + /* skip number of samples */ + bytestream_get_be16(&seekpoint); + av_add_index_entry(st, pos, timestamp, 0, 0, AVINDEX_KEYFRAME); + } + } + av_freep(&buffer); + } + else { + /* STREAMINFO must be the first block */ if (!found_streaminfo) { RETURN_ERROR(AVERROR_INVALIDDATA); @@ -169,6 +202,7 @@ static int flac_read_header(AVFormatContext *s) if (ret < 0) return ret; + reset_index_position(avio_tell(s->pb), st); return 0; fail: @@ -249,14 +283,38 @@ static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_inde return pts; } +static int flac_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { + int index; + int64_t pos; + AVIndexEntry e; + FLACDecContext *flac = s->priv_data; + + if (!flac->found_seektable || !(s->flags&AVFMT_FLAG_FAST_SEEK)) { + return -1; + } + + index = av_index_search_timestamp(s->streams[0], timestamp, flags); + if(index<0 || index >= s->streams[0]->nb_index_entries) + return -1; + + e = s->streams[0]->index_entries[index]; + pos = avio_seek(s->pb, e.pos, SEEK_SET); + if (pos >= 0) { + return 0; + } + return -1; +} + AVInputFormat ff_flac_demuxer = { .name = "flac", .long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), .read_probe = flac_probe, .read_header = flac_read_header, .read_packet = ff_raw_read_partial_packet, + .read_seek = flac_seek, .read_timestamp = flac_read_timestamp, .flags = AVFMT_GENERIC_INDEX, .extensions = "flac", .raw_codec_id = AV_CODEC_ID_FLAC, + .priv_data_size = sizeof(FLACDecContext), }; |