diff --git a/libavformat/Makefile b/libavformat/Makefile index de776340f2..42b1e1970b 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -298,6 +298,7 @@ OBJS-$(CONFIG_NUT_MUXER) += nutenc.o nut.o OBJS-$(CONFIG_NUV_DEMUXER) += nuv.o OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \ oggparsecelt.o \ + oggparsedaala.o \ oggparsedirac.o \ oggparseflac.o \ oggparseogm.o \ diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index ae56915b58..9682ee9fca 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -41,6 +41,7 @@ static const struct ogg_codec * const ogg_codecs[] = { &ff_skeleton_codec, + &ff_daala_codec, &ff_dirac_codec, &ff_speex_codec, &ff_vorbis_codec, diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h index 7dc7716036..d7af1cfabd 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -114,6 +114,7 @@ struct ogg { #define OGG_NOGRANULE_VALUE (-1ull) extern const struct ogg_codec ff_celt_codec; +extern const struct ogg_codec ff_daala_codec; extern const struct ogg_codec ff_dirac_codec; extern const struct ogg_codec ff_flac_codec; extern const struct ogg_codec ff_ogm_audio_codec; diff --git a/libavformat/oggparsedaala.c b/libavformat/oggparsedaala.c new file mode 100644 index 0000000000..af6dce0a0d --- /dev/null +++ b/libavformat/oggparsedaala.c @@ -0,0 +1,241 @@ +/* + * Ogg Daala parser + * Copyright (C) 2015 Rostislav Pehlivanov + * Copyright (C) 2015 Vittorio Giovara + * + * 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 +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +struct DaalaPixFmtMap { + enum AVPixelFormat ffmpeg_fmt; + int depth; + int planes; + int xdec[4]; + int ydec[4]; +}; + +/* Currently supported formats only */ +static const struct DaalaPixFmtMap list_fmts[] = { + { AV_PIX_FMT_YUV420P, 8, 3, {0, 1, 1, 0}, {0, 1, 1, 0} }, + { AV_PIX_FMT_YUV444P, 8, 3, {0, 0, 0, 0}, {0, 0, 0, 0} } +}; + +typedef struct DaalaInfoHeader { + int init_d; + int gpshift; + int gpmask; + int version_maj; + int version_min; + int version_sub; + int frame_duration; + int keyframe_granule_shift; + struct DaalaPixFmtMap format; +} DaalaInfoHeader; + +static inline int daala_match_pix_fmt(struct DaalaPixFmtMap *fmt) +{ + int i, j; + for (i = 0; i < FF_ARRAY_ELEMS(list_fmts); i++) { + int match = 0; + if (fmt->depth != list_fmts[i].depth) + continue; + if (fmt->planes != list_fmts[i].planes) + continue; + for (j = 0; j < fmt->planes; j++) { + if (fmt->xdec[j] != list_fmts[i].xdec[j]) + continue; + if (fmt->ydec[j] != list_fmts[i].ydec[j]) + continue; + match++; + } + if (match == fmt->planes) + return list_fmts[i].ffmpeg_fmt; + } + return -1; +} + +static int daala_header(AVFormatContext *s, int idx) +{ + int i, err; + uint8_t *cdp; + GetByteContext gb; + AVRational timebase; + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + int cds = st->codec->extradata_size + os->psize + 2; + DaalaInfoHeader *hdr = os->private; + + if (!(os->buf[os->pstart] & 0x80)) + return 0; + + if (!hdr) { + hdr = av_mallocz(sizeof(*hdr)); + if (!hdr) + return AVERROR(ENOMEM); + os->private = hdr; + } + + switch (os->buf[os->pstart]) { + case 0x80: + bytestream2_init(&gb, os->buf + os->pstart, os->psize); + bytestream2_skip(&gb, ff_daala_codec.magicsize); + + hdr->version_maj = bytestream2_get_byte(&gb); + hdr->version_min = bytestream2_get_byte(&gb); + hdr->version_sub = bytestream2_get_byte(&gb); + + st->codec->width = bytestream2_get_ne32(&gb); + st->codec->height = bytestream2_get_ne32(&gb); + + st->sample_aspect_ratio.num = bytestream2_get_ne32(&gb); + st->sample_aspect_ratio.den = bytestream2_get_ne32(&gb); + + timebase.num = bytestream2_get_ne32(&gb); + timebase.den = bytestream2_get_ne32(&gb); + if (timebase.num < 0 && timebase.den < 0) { + av_log(s, AV_LOG_WARNING, "Invalid timebase, assuming 30 FPS\n"); + timebase.num = 1; + timebase.den = 30; + } + avpriv_set_pts_info(st, 64, timebase.den, timebase.num); + + hdr->frame_duration = bytestream2_get_ne32(&gb); + hdr->gpshift = bytestream2_get_byte(&gb); + hdr->gpmask = (1 << hdr->gpshift) - 1; + + hdr->format.depth = 8 + 2*(bytestream2_get_byte(&gb)-1); + hdr->format.planes = bytestream2_get_byte(&gb); + for (i = 0; i < hdr->format.planes; i++) { + hdr->format.xdec[i] = bytestream2_get_byte(&gb); + hdr->format.ydec[i] = bytestream2_get_byte(&gb); + } + + if ((st->codec->pix_fmt = daala_match_pix_fmt(&hdr->format)) < 0) + av_log(s, AV_LOG_ERROR, "Unsupported pixel format - %i %i\n", + hdr->format.depth, hdr->format.planes); + + st->codec->codec_id = AV_CODEC_ID_DAALA; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + + hdr->init_d = 1; + break; + case 0x81: + if (!hdr->init_d) + return AVERROR_INVALIDDATA; + ff_vorbis_stream_comment(s, st, + os->buf + os->pstart + ff_daala_codec.magicsize, + os->psize - ff_daala_codec.magicsize); + break; + case 0x82: + if (!hdr->init_d) + return AVERROR_INVALIDDATA; + break; + default: + av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]); + return AVERROR_INVALIDDATA; + break; + } + + if ((err = av_reallocp(&st->codec->extradata, + cds + AV_INPUT_BUFFER_PADDING_SIZE)) < 0) { + st->codec->extradata_size = 0; + return err; + } + + memset(st->codec->extradata + cds, 0, AV_INPUT_BUFFER_PADDING_SIZE); + cdp = st->codec->extradata + st->codec->extradata_size; + *cdp++ = os->psize >> 8; + *cdp++ = os->psize & 0xff; + memcpy(cdp, os->buf + os->pstart, os->psize); + st->codec->extradata_size = cds; + + return 1; +} + +static uint64_t daala_gptopts(AVFormatContext *ctx, int idx, uint64_t gp, + int64_t *dts) +{ + uint64_t iframe, pframe; + struct ogg *ogg = ctx->priv_data; + struct ogg_stream *os = ogg->streams + idx; + DaalaInfoHeader *hdr = os->private; + + if (!hdr) + return AV_NOPTS_VALUE; + + iframe = gp >> hdr->gpshift; + pframe = gp & hdr->gpmask; + + if (!pframe) + os->pflags |= AV_PKT_FLAG_KEY; + + if (dts) + *dts = iframe + pframe; + + return iframe + pframe; +} + +static int daala_packet(AVFormatContext *s, int idx) +{ + int seg, duration = 1; + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + + /* + * first packet handling: here we parse the duration of each packet in the + * first page and compare the total duration to the page granule to find the + * encoder delay and set the first timestamp + */ + + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + for (seg = os->segp; seg < os->nsegs; seg++) + if (os->segments[seg] < 255) + duration++; + + os->lastpts = os->lastdts = daala_gptopts(s, idx, os->granule, NULL) - duration; + if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { + s->streams[idx]->start_time = os->lastpts; + if (s->streams[idx]->duration) + s->streams[idx]->duration -= s->streams[idx]->start_time; + } + } + + /* parse packet duration */ + if (os->psize > 0) + os->pduration = 1; + + return 0; +} + +const struct ogg_codec ff_daala_codec = { + .name = "Daala", + .magic = "\200daala", + .magicsize = 6, + .header = daala_header, + .packet = daala_packet, + .gptopts = daala_gptopts, + .granule_is_start = 1, + .nb_header = 3, +}; diff --git a/libavformat/version.h b/libavformat/version.h index 3b11e6b09c..7ba0c718df 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 57 #define LIBAVFORMAT_VERSION_MINOR 10 -#define LIBAVFORMAT_VERSION_MICRO 100 +#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \