blob: 39c0f4ce86c18eb20f6e43365cb1d22cf53c5300 [file] [log] [blame]
/*
* Raw FLAC demuxer
* Copyright (c) 2001 Fabrice Bellard
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavcodec/flac.h"
#include "avformat.h"
#include "raw.h"
#include "id3v2.h"
#include "oggdec.h"
typedef struct FlacSeekPoint {
uint64_t samplenum; // time in the stream (in samples)
uint64_t offset; // offset from first frame
uint16_t samplecount; // number of samples in that frame
} FlacSeekPoint;
typedef struct FlacContext {
int hasseektable;
int firstpacket; // send a dts on first packets after seek
uint64_t firstdts;
int64_t frameoffset; // Offset of the first frame
uint32_t seekcount;
FlacSeekPoint *seekpoints;
} FlacContext;
static int flac_read_header(AVFormatContext *s,
AVFormatParameters *ap)
{
FlacContext *flac = s->priv_data;
uint8_t buf[ID3v2_HEADER_SIZE];
int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0;
uint8_t header[4];
uint8_t *buffer=NULL;
AVStream *st = av_new_stream(s, 0);
if (!st)
return AVERROR(ENOMEM);
st->codec->codec_type = CODEC_TYPE_AUDIO;
st->codec->codec_id = CODEC_ID_FLAC;
st->need_parsing = AVSTREAM_PARSE_FULL;
/* the parameters will be extracted from the compressed bitstream */
flac->hasseektable=0;
flac->firstpacket=1;
flac->firstdts=0;
flac->seekcount=0;
/* skip ID3v2 header if found */
ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE);
if (ret == ID3v2_HEADER_SIZE && ff_id3v2_match(buf)) {
int len = ff_id3v2_tag_len(buf);
url_fseek(s->pb, len - ID3v2_HEADER_SIZE, SEEK_CUR);
} else {
url_fseek(s->pb, 0, SEEK_SET);
}
/* if fLaC marker is not found, assume there is no header */
if (get_le32(s->pb) != MKTAG('f','L','a','C')) {
url_fseek(s->pb, -4, SEEK_CUR);
return 0;
}
/* process metadata blocks */
while (!url_feof(s->pb) && !metadata_last) {
get_buffer(s->pb, header, 4);
ff_flac_parse_block_header(header, &metadata_last, &metadata_type,
&metadata_size);
switch (metadata_type) {
/* allocate and read metadata block for supported types */
case FLAC_METADATA_TYPE_STREAMINFO:
case FLAC_METADATA_TYPE_VORBIS_COMMENT:
buffer = av_mallocz(metadata_size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!buffer) {
return AVERROR_NOMEM;
}
if (get_buffer(s->pb, buffer, metadata_size) != metadata_size) {
av_freep(&buffer);
return AVERROR_IO;
}
break;
// SageTV: report picture size and offset for external extraction
case FLAC_METADATA_TYPE_PICTURE: {
char value[64];
uint32_t length;
get_le32(s->pb); // Skip picture type
length = get_be32(s->pb);
url_fskip(s->pb, length); // mime type
length = get_be32(s->pb);
url_fskip(s->pb, length); // description
url_fskip(s->pb, 16); // DWORDS: width, height, depth, palette size
length = get_be32(s->pb);
snprintf(value, sizeof(value), "%d", length);
av_metadata_set(&s->metadata, "ThumbnailSize", value);
snprintf(value, sizeof(value), "%"PRIi64, url_ftell(s->pb));
av_metadata_set(&s->metadata, "ThumbnailOffset", value);
ret = url_fseek(s->pb, length, SEEK_CUR);
if (ret < 0)
return ret;
}
break;
case FLAC_METADATA_TYPE_SEEKTABLE:
// Each entry is 64 bits sample number, 64bits offset from first frame
// 16 bit number of samples
{
int32_t ind=0;
flac->seekcount=metadata_size/18;
flac->seekpoints = av_mallocz(flac->seekcount*sizeof(FlacSeekPoint));
if (!flac->seekpoints)
return AVERROR(ENOMEM);
for(ind=0;ind<flac->seekcount;ind++)
{
flac->seekpoints[ind].samplenum=get_be64(s->pb);
flac->seekpoints[ind].offset=get_be64(s->pb);
flac->seekpoints[ind].samplecount=get_be16(s->pb);
metadata_size-=18;
}
flac->hasseektable=1;
}
break;
break;
/* skip metadata block for unsupported types */
default:
ret = url_fseek(s->pb, metadata_size, SEEK_CUR);
if (ret < 0)
return ret;
}
if (metadata_type == FLAC_METADATA_TYPE_STREAMINFO) {
FLACStreaminfo si;
/* STREAMINFO can only occur once */
if (found_streaminfo) {
av_freep(&buffer);
return AVERROR_INVALIDDATA;
}
if (metadata_size != FLAC_STREAMINFO_SIZE) {
av_freep(&buffer);
return AVERROR_INVALIDDATA;
}
found_streaminfo = 1;
st->codec->extradata = buffer;
st->codec->extradata_size = metadata_size;
buffer = NULL;
/* get codec params from STREAMINFO header */
ff_flac_parse_streaminfo(st->codec, &si, st->codec->extradata);
/* set time base and duration */
if (si.samplerate > 0) {
av_set_pts_info(st, 64, 1, si.samplerate);
if (si.samples > 0)
st->duration = si.samples;
}
} else {
/* STREAMINFO must be the first block */
if (!found_streaminfo) {
av_freep(&buffer);
return AVERROR_INVALIDDATA;
}
/* process supported blocks other than STREAMINFO */
if (metadata_type == FLAC_METADATA_TYPE_VORBIS_COMMENT) {
if (vorbis_comment(s, buffer, metadata_size)) {
av_log(s, AV_LOG_WARNING, "error parsing VorbisComment metadata\n");
}
}
av_freep(&buffer);
}
}
// The index we have built doesn't use the right base
flac->frameoffset=url_ftell(s->pb);
return 0;
}
static int flac_probe(AVProbeData *p)
{
uint8_t *bufptr = p->buf;
uint8_t *end = p->buf + p->buf_size;
if(ff_id3v2_match(bufptr))
bufptr += ff_id3v2_tag_len(bufptr);
if(bufptr > end-4 || memcmp(bufptr, "fLaC", 4)) return 0;
else return AVPROBE_SCORE_MAX/2;
}
#define FLAC_PACKET_SIZE 1024
static int flac_read_partial_packet(AVFormatContext *s, AVPacket *pkt)
{
FlacContext *flac = s->priv_data;
int ret, size;
size = FLAC_PACKET_SIZE;
if (av_new_packet(pkt, size) < 0)
return AVERROR(EIO);
if(flac->firstpacket)
{
pkt->dts=flac->firstdts;
flac->firstpacket=0;
}
pkt->pos= url_ftell(s->pb);
pkt->stream_index = 0;
ret = get_partial_buffer(s->pb, pkt->data, size);
if (ret <= 0) {
av_free_packet(pkt);
return AVERROR(EIO);
}
pkt->size = ret;
return ret;
}
static int flac_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags)
{
FlacContext *flac = s->priv_data;
AVStream *st;
int i, ret, ind;
uint64_t pos;
if (stream_index >= s->nb_streams)
return -1;
if (sample_time < 0)
sample_time = 0;
st = s->streams[stream_index];
for(ind=0;ind<flac->seekcount;ind++)
{
if(flac->seekpoints[ind].samplenum>=sample_time)
break;
}
ind-=1;
if(ind<0)
{
pos=0;
flac->firstdts=0;
}
else
{
pos=flac->seekpoints[ind].offset;
flac->firstdts=flac->seekpoints[ind].samplenum;
}
pos+=flac->frameoffset;
if ((ret = url_fseek(s->pb, pos, SEEK_SET)) < 0)
return ret;
flac->firstpacket=1;
/* for each stream, reset read state */
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->parser) {
av_parser_close(st->parser);
st->parser = NULL;
av_free_packet(&st->cur_pkt);
}
st->last_IP_pts = AV_NOPTS_VALUE;
st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */
st->reference_dts = AV_NOPTS_VALUE;
/* fail safe */
st->cur_ptr = NULL;
st->cur_len = 0;
}
return 0;
}
static int flac_read_close(AVFormatContext *s)
{
FlacContext *flac = s->priv_data;
av_freep(&flac->seekpoints);
return 0;
}
AVInputFormat flac_demuxer = {
"flac",
NULL_IF_CONFIG_SMALL("raw FLAC"),
sizeof(FlacContext),
flac_probe,
flac_read_header,
flac_read_partial_packet,
flac_read_close,
flac_read_seek,
.flags= AVFMT_GENERIC_INDEX,
.extensions = "flac",
.value = CODEC_ID_FLAC,
};