From 9146ca3700a83c267b0f505a38c26b69992beb7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5ns=20Rullg=C3=A5rd?= Date: Sat, 9 Apr 2005 15:32:58 +0000 Subject: [PATCH] Ogg demuxer ported from tcvp by Luca Barbato , fixups by me. Originally committed as revision 4113 to svn://svn.ffmpeg.org/ffmpeg/trunk --- configure | 18 +- libavformat/Makefile | 4 +- libavformat/allformats.c | 5 +- libavformat/avformat.h | 5 +- libavformat/ogg.c | 8 +- libavformat/ogg2.c | 643 +++++++++++++++++++++++++++++++++++ libavformat/ogg2.h | 84 +++++ libavformat/oggparsevorbis.c | 169 +++++++++ 8 files changed, 918 insertions(+), 18 deletions(-) create mode 100644 libavformat/ogg2.c create mode 100644 libavformat/ogg2.h create mode 100644 libavformat/oggparsevorbis.c diff --git a/configure b/configure index 3c4c406099..044fa24d92 100755 --- a/configure +++ b/configure @@ -16,7 +16,7 @@ echo " --prefix=PREFIX install in PREFIX [$prefix]" echo " --libdir=DIR install libs in DIR [PREFIX/lib]" echo " --mandir=DIR man documentation in DIR [PREFIX/man]" echo " --enable-mp3lame enable mp3 encoding via libmp3lame [default=no]" -echo " --enable-ogg enable ogg support via libogg [default=no]" +echo " --enable-libogg enable ogg support via libogg [default=no]" echo " --enable-vorbis enable vorbis support via libvorbis [default=no]" echo " --enable-theora enable theora support via libtheora [default=no]" echo " --enable-faad enable faad support via libfaad [default=no]" @@ -154,7 +154,7 @@ dc1394="no" network="yes" zlib="yes" mp3lame="no" -ogg="no" +libogg="no" vorbis="no" theora="no" faad="no" @@ -432,7 +432,7 @@ for opt do ;; --enable-mp3lame) mp3lame="yes" ;; - --enable-ogg) ogg="yes" + --enable-libogg) libogg="yes" ;; --enable-vorbis) vorbis="yes" ;; @@ -492,16 +492,16 @@ for opt do done if test "$theora" = "yes" ; then - if test "$ogg" = "no" ; then - echo "Ogg must be enabled to enable Theora" + if test "$libogg" = "no"; then + echo "libogg must be enabled to enable Theora" fail="yes" theora="no" fi fi if test "$vorbis" = "yes" ; then - if test "$ogg" = "no" ; then - echo "Ogg must be enabled to enable Vorbis" + if test "$libogg" = "no"; then + echo "libogg must be enabled to enable Vorbis" fail="yes" vorbis="no" fi @@ -1095,7 +1095,7 @@ fi echo "gprof enabled $gprof" echo "zlib enabled $zlib" echo "mp3lame enabled $mp3lame" -echo "ogg enabled $ogg" +echo "libogg enabled $libogg" echo "vorbis enabled $vorbis" echo "theora enabled $theora" echo "faad enabled $faad" @@ -1395,7 +1395,7 @@ if test "$mp3lame" = "yes" ; then echo "CONFIG_MP3LAME=yes" >> config.mak fi -if test "$ogg" = "yes" ; then +if test "$libogg" = "yes" ; then echo "#define CONFIG_LIBOGG 1" >> $TMPH echo "CONFIG_LIBOGG=yes" >> config.mak fi diff --git a/libavformat/Makefile b/libavformat/Makefile index bdc0d67ff1..3b804d2c05 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -16,8 +16,8 @@ OBJS+=mpeg.o mpegts.o mpegtsenc.o ffm.o crc.o img.o img2.o raw.o rm.o \ avienc.o avidec.o wav.o swf.o au.o gif.o mov.o mpjpeg.o dv.o \ yuv4mpeg.o 4xm.o flvenc.o flvdec.o movenc.o psxstr.o idroq.o ipmovie.o \ nut.o wc3movie.o mp3.o westwood.o segafilm.o idcin.o flic.o \ - sierravmd.o matroska.o sol.o electronicarts.o nsvdec.o asf.o asf-enc.o - + sierravmd.o matroska.o sol.o electronicarts.o nsvdec.o asf.o asf-enc.o \ + ogg2.o oggparsevorbis.o AMROBJS= ifeq ($(AMR_NB),yes) AMROBJS= amr.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index bc7eff5066..d13c90ea58 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -81,9 +81,10 @@ void av_register_all(void) amr_init(); #endif yuv4mpeg_init(); - -#ifdef CONFIG_LIBOGG + ogg_init(); +#ifdef CONFIG_LIBOGG + libogg_init(); #endif ffm_init(); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 7beef99999..892b0f8c23 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -467,9 +467,12 @@ int mp3_init(void); /* yuv4mpeg.c */ int yuv4mpeg_init(void); -/* ogg.c */ +/* ogg2.c */ int ogg_init(void); +/* ogg.c */ +int libogg_init(void); + /* dv.c */ int ff_dv_init(void); diff --git a/libavformat/ogg.c b/libavformat/ogg.c index c30ccd2f24..ab47d029a5 100644 --- a/libavformat/ogg.c +++ b/libavformat/ogg.c @@ -134,7 +134,7 @@ static AVOutputFormat ogg_oformat = { } ; #endif //CONFIG_ENCODERS - +#if 0 static int next_packet(AVFormatContext *avfcontext, ogg_packet *op) { OggContext *context = avfcontext->priv_data ; ogg_page og ; @@ -247,12 +247,12 @@ static AVInputFormat ogg_iformat = { ogg_read_close, .extensions = "ogg", } ; +#endif - -int ogg_init(void) { +int libogg_init(void) { #ifdef CONFIG_ENCODERS av_register_output_format(&ogg_oformat) ; #endif - av_register_input_format(&ogg_iformat); +/* av_register_input_format(&ogg_iformat); */ return 0 ; } diff --git a/libavformat/ogg2.c b/libavformat/ogg2.c new file mode 100644 index 0000000000..c54b2a04c5 --- /dev/null +++ b/libavformat/ogg2.c @@ -0,0 +1,643 @@ +/* + * Ogg bitstream support + * Luca Barbato + * Based on tcvp implementation + * + */ + +/** + Copyright (C) 2005 Michael Ahlberg, Måns Rullgård + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + + +#include +#include "ogg2.h" +#include "avformat.h" + +#define MAX_PAGE_SIZE 65307 +#define DECODER_BUFFER_SIZE MAX_PAGE_SIZE + +static ogg_codec_t *ogg_codecs[] = { + &vorbis_codec, + NULL +}; + +#if 0 // CONFIG_ENCODERS +static int +ogg_write_header (AVFormatContext * avfcontext) +{ +} + +static int +ogg_write_packet (AVFormatContext * avfcontext, AVPacket * pkt) +{ +} + + +static int +ogg_write_trailer (AVFormatContext * avfcontext) +{ +} + + +static AVOutputFormat ogg_oformat = { + "ogg", + "Ogg Vorbis", + "audio/x-vorbis", + "ogg", + sizeof (OggContext), + CODEC_ID_VORBIS, + 0, + ogg_write_header, + ogg_write_packet, + ogg_write_trailer, +}; +#endif //CONFIG_ENCODERS + +//FIXME We could avoid some structure duplication +static int +ogg_save (AVFormatContext * s) +{ + ogg_t *ogg = s->priv_data; + ogg_state_t *ost = + av_malloc(sizeof (*ost) + ogg->nstreams * sizeof (*ogg->streams)); + int i; + ost->pos = url_ftell (&s->pb);; + ost->curidx = ogg->curidx; + ost->next = ogg->state; + memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams)); + + for (i = 0; i < ogg->nstreams; i++){ + ogg_stream_t *os = ogg->streams + i; + os->buf = av_malloc (os->bufsize); + memset (os->buf, 0, os->bufsize); + memcpy (os->buf, ost->streams[i].buf, os->bufpos); + } + + ogg->state = ost; + + return 0; +} + +static int +ogg_restore (AVFormatContext * s, int discard) +{ + ogg_t *ogg = s->priv_data; + ByteIOContext *bc = &s->pb; + ogg_state_t *ost = ogg->state; + int i; + + if (!ost) + return 0; + + ogg->state = ost->next; + + if (!discard){ + for (i = 0; i < ogg->nstreams; i++) + av_free (ogg->streams[i].buf); + + url_fseek (bc, ost->pos, SEEK_SET); + ogg->curidx = ost->curidx; + memcpy (ogg->streams, ost->streams, + ogg->nstreams * sizeof (*ogg->streams)); + } + + av_free (ost); + + return 0; +} + +static int +ogg_reset (ogg_t * ogg) +{ + int i; + + for (i = 0; i < ogg->nstreams; i++){ + ogg_stream_t *os = ogg->streams + i; + os->bufpos = 0; + os->pstart = 0; + os->psize = 0; + os->granule = -1; + os->lastgp = -1; + os->nsegs = 0; + os->segp = 0; + } + + ogg->curidx = -1; + + return 0; +} + +static ogg_codec_t * +ogg_find_codec (u_char * buf, int size) +{ + int i; + + for (i = 0; ogg_codecs[i]; i++) + if (size >= ogg_codecs[i]->magicsize && + !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize)) + return ogg_codecs[i]; + + return NULL; +} + +static int +ogg_find_stream (ogg_t * ogg, int serial) +{ + int i; + + for (i = 0; i < ogg->nstreams; i++) + if (ogg->streams[i].serial == serial) + return i; + + return -1; +} + +static int +ogg_new_stream (AVFormatContext * s, uint32_t serial) +{ + + ogg_t *ogg = s->priv_data; + int idx = ogg->nstreams++; + AVStream *st; + ogg_stream_t *os; + + ogg->streams = av_realloc (ogg->streams, + ogg->nstreams * sizeof (*ogg->streams)); + memset (ogg->streams + idx, 0, sizeof (*ogg->streams)); + os = ogg->streams + idx; + os->serial = serial; + os->bufsize = DECODER_BUFFER_SIZE; + os->buf = av_malloc (os->bufsize); + memset (os->buf, 0, os->bufsize); + os->header = -1; + + st = av_new_stream (s, idx); + if (!st) + return AVERROR_NOMEM; + + av_set_pts_info(st, 64, 1, 1000000); + st->start_time = 0; + + return idx; +} + +static int +ogg_read_page (AVFormatContext * s, int *str) +{ + ByteIOContext *bc = &s->pb; + ogg_t *ogg = s->priv_data; + ogg_stream_t *os; + int i = 0; + int flags, nsegs; + uint64_t gp; + uint32_t serial; + uint32_t seq; + uint32_t crc; + int size, idx; + char sync[4]; + int sp = 0; + + if (get_buffer (bc, sync, 4) < 4) + return -1; + + do{ + int c; + + if (sync[sp & 3] == 'O' && + sync[(sp + 1) & 3] == 'g' && + sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S') + break; + + c = url_fgetc (bc); + if (c < 0) + return -1; + sync[sp++ & 3] = c; + }while (i++ < MAX_PAGE_SIZE); + + if (i >= MAX_PAGE_SIZE){ + av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n"); + return -1; + } + + if (url_fgetc (bc) != 0) /* version */ + return -1; + + flags = url_fgetc (bc); + gp = get_le64 (bc); + serial = get_le32 (bc); + seq = get_le32 (bc); + crc = get_le32 (bc); + nsegs = url_fgetc (bc); + + idx = ogg_find_stream (ogg, serial); + if (idx < 0){ + idx = ogg_new_stream (s, serial); + if (idx < 0) + return -1; + } + + os = ogg->streams + idx; + + if (get_buffer (bc, os->segments, nsegs) < nsegs) + return -1; + + os->nsegs = nsegs; + os->segp = 0; + + size = 0; + for (i = 0; i < nsegs; i++) + size += os->segments[i]; + + if (flags & OGG_FLAG_CONT){ + if (!os->psize){ + while (os->segp < os->nsegs){ + int seg = os->segments[os->segp++]; + os->pstart += seg; + if (seg < 255) + break; + } + } + }else{ + os->psize = 0; + } + + if (os->bufsize - os->bufpos < size){ + u_char *nb = av_malloc (os->bufsize *= 2); + memset (nb, 0, os->bufsize); + memcpy (nb, os->buf, os->bufpos); + av_free (os->buf); + os->buf = nb; + } + + if (get_buffer (bc, os->buf + os->bufpos, size) < size) + return -1; + + os->lastgp = os->granule; + os->bufpos += size; + os->granule = gp; + os->flags = flags; + + if (str) + *str = idx; + + return 0; +} + +static int +ogg_packet (AVFormatContext * s, int *str) +{ + ogg_t *ogg = s->priv_data; + int idx; + ogg_stream_t *os; + int complete = 0; + int segp = 0, psize = 0; + +#if 0 + av_log (s, AV_LOG_DEBUG, "ogg_packet: curidx=%i\n", ogg->curidx); +#endif + + do{ + idx = ogg->curidx; + + while (idx < 0){ + if (ogg_read_page (s, &idx) < 0) + return -1; + } + + os = ogg->streams + idx; + +#if 0 + av_log (s, AV_LOG_DEBUG, + "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n", + idx, os->pstart, os->psize, os->segp, os->nsegs); +#endif + + if (!os->codec){ + if (os->header < 0){ + os->codec = ogg_find_codec (os->buf, os->bufpos); + if (!os->codec){ + os->header = 0; + return 0; + } + }else{ + return 0; + } + } + + segp = os->segp; + psize = os->psize; + + while (os->segp < os->nsegs){ + int ss = os->segments[os->segp++]; + os->psize += ss; + if (ss < 255){ + complete = 1; + break; + } + } + + if (!complete && os->segp == os->nsegs){ + u_char *nb = av_malloc (os->bufsize); + int size = os->bufpos - os->pstart; + memset (nb, 0, os->bufsize); + memcpy (nb, os->buf + os->pstart, size); + av_free (os->buf); + os->buf = nb; + os->bufpos = size; + os->pstart = 0; + ogg->curidx = -1; + } + }while (!complete); + +#if 0 + av_log (s, AV_LOG_DEBUG, + "ogg_packet: idx %i, frame size %i, start %i\n", + idx, os->psize, os->pstart); +#endif + + ogg->curidx = idx; + + if (os->header < 0){ + int hdr = os->codec->header (s, idx); + if (!hdr){ + os->header = os->seq; + os->segp = segp; + os->psize = psize; + ogg->headers = 1; + }else{ + os->pstart += os->psize; + os->psize = 0; + } + } + + if (os->header > -1 && os->seq > os->header){ + if (os->codec && os->codec->packet) + os->codec->packet (s, idx); + if (str) + *str = idx; + } + + os->seq++; + if (os->segp == os->nsegs) + ogg->curidx = -1; + + return 0; +} + +static int +ogg_get_headers (AVFormatContext * s) +{ + ogg_t *ogg = s->priv_data; + + do{ + if (ogg_packet (s, NULL) < 0) + return -1; + }while (!ogg->headers); + +#if 0 + av_log (s, AV_LOG_DEBUG, "found headers\n"); +#endif + + return 0; +} + +static uint64_t +ogg_gptopts (AVFormatContext * s, int i, uint64_t gp) +{ + AVStream *st = s->streams[i]; + AVCodecContext *codec = &st->codec; + uint64_t pts = AV_NOPTS_VALUE; + + if (codec->codec_type == CODEC_TYPE_AUDIO){ + pts = gp * 1000000LL / codec->sample_rate; + }else if (codec->codec_type == CODEC_TYPE_VIDEO){ +//FIXME + pts = gp * 1000000LL / codec->sample_rate; +// pts = gp * st->video.frame_rate.den * 27000000LL / +// st->video.frame_rate.num; + } + + return pts; +} + + +static int +ogg_get_length (AVFormatContext * s) +{ + ogg_t *ogg = s->priv_data; + URLContext *h = url_fileno (&s->pb); + int idx = -1, i; +//FIXME: get the right ctx flag to know if is seekable or not +// if(ogg->f->flags & URL_FLAG_STREAMED) +// return 0; + +// already set + if (s->duration != AV_NOPTS_VALUE) + return 0; + + ogg_save (s); + url_seek (h, -MAX_PAGE_SIZE, SEEK_END); + + while (!ogg_read_page (s, &i)){ + if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0) + idx = i; + } + + if (idx != -1){ + s->streams[idx]->duration = + ogg_gptopts (s, idx, ogg->streams[idx].granule); + } + + ogg->size = url_filesize(h); + ogg_restore (s, 0); + + return 0; +} + + +static int +ogg_read_header (AVFormatContext * s, AVFormatParameters * ap) +{ + ogg_t *ogg = s->priv_data; + ogg->curidx = -1; + //linear headers seek from start + if (ogg_get_headers (s) < 0){ + return -1; + } + + //linear granulepos seek from end + ogg_get_length (s); + + //fill the extradata in the per codec callbacks + return 0; +} + + +static int +ogg_read_packet (AVFormatContext * s, AVPacket * pkt) +{ + ogg_t *ogg; + ogg_stream_t *os; + int idx = -1; + + //Get an ogg packet + do{ + if (ogg_packet (s, &idx) < 0) + return AVERROR_IO; + }while (idx < 0 || !s->streams[idx]); + + ogg = s->priv_data; + os = ogg->streams + idx; + + //Alloc a pkt + if (av_new_packet (pkt, os->psize) < 0) + return AVERROR_IO; + pkt->stream_index = idx; + memcpy (pkt->data, os->buf + os->pstart, os->psize); + if (os->lastgp != -1LL){ + pkt->pts = ogg_gptopts (s, idx, os->lastgp); + os->lastgp = -1; + } + //next + os->pstart += os->psize; + os->psize = 0; + return os->psize; +} + + +static int +ogg_read_close (AVFormatContext * s) +{ + ogg_t *ogg = s->priv_data; + int i; + + for (i = 0; i < ogg->nstreams; i++){ + av_free (ogg->streams[i].buf); + av_freep (&s->streams[i]->codec.extradata); + } + av_free (ogg->streams); + return 0; +} + + +static int +ogg_read_seek (AVFormatContext * s, int stream_index, int64_t target_ts, + int flags) +{ + ogg_t *ogg = s->priv_data; + ByteIOContext *bc = &s->pb; + uint64_t min = 0, max = ogg->size; + uint64_t tmin = 0, tmax = s->duration; + int64_t pts = AV_NOPTS_VALUE; + + ogg_save (s); + + while (min <= max){ + uint64_t p = min + (max - min) * target_ts / (tmax - tmin); + int i = -1; + + url_fseek (bc, p, SEEK_SET); + + while (!ogg_read_page (s, &i)){ + if (ogg->streams[i].granule != 0 && ogg->streams[i].granule != -1) + break; + } + + if (i == -1) + break; + + pts = ogg_gptopts (s, i, ogg->streams[i].granule); + p = url_ftell (bc); + + if (ABS (pts - target_ts) < 1000000LL) + break; + + if (pts > target_ts){ + max = p; + tmax = pts; + }else{ + min = p; + tmin = pts; + } + } + + if (ABS (pts - target_ts) < 1000000LL){ + ogg_restore (s, 1); + ogg_reset (ogg); + }else{ + ogg_restore (s, 0); + pts = AV_NOPTS_VALUE; + } + + return pts; + +#if 0 + //later... + int64_t pos; + if (av_seek_frame_binary (s, stream_index, target_ts, flags) < 0) + return -1; + pos = url_ftell (&s->pb); + ogg_read_timestamp (s, stream_index, &pos, pos - 1); +#endif + +} + +#if 0 +static int64_t +ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg, + int64_t pos_limit) +{ + ogg_t *ogg = s->priv_data; + ByteIOContext *bc = &s->pb; + int64_t pos, pts; + + if (*pos_arg < 0) + return AV_NOPTS_VALUE; + + pos = *pos_arg; +} +#endif + +static AVInputFormat ogg_iformat = { + "ogg", + "Ogg", + sizeof (ogg_t), + NULL, + ogg_read_header, + ogg_read_packet, + ogg_read_close, + ogg_read_seek, +// ogg_read_timestamp, + .extensions = "ogg", +}; + +int +ogg_init (void) +{ +#if 0 // CONFIG_ENCODERS + av_register_output_format (&ogg_oformat); +#endif + av_register_input_format (&ogg_iformat); + return 0; +} diff --git a/libavformat/ogg2.h b/libavformat/ogg2.h new file mode 100644 index 0000000000..b1146e314c --- /dev/null +++ b/libavformat/ogg2.h @@ -0,0 +1,84 @@ +/** + Copyright (C) 2005 Michael Ahlberg, Måns Rullgård + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + +#ifndef OGG_H +#define OGG_H + +#include "avformat.h" + +typedef struct ogg_codec { + uint8_t *magic; + uint8_t magicsize; + int8_t *name; + int (*header)(AVFormatContext *, int); + int (*packet)(AVFormatContext *, int); +} ogg_codec_t; + +typedef struct ogg_stream { + uint8_t *buf; + unsigned int bufsize; + unsigned int bufpos; + unsigned int pstart; + unsigned int psize; + uint32_t serial; + uint32_t seq; + uint64_t granule, lastgp; + int flags; + ogg_codec_t *codec; + int header; + int nsegs, segp; + uint8_t segments[255]; +} ogg_stream_t; + +typedef struct ogg_state { + uint64_t pos; + int curidx; + struct ogg_state *next; + ogg_stream_t streams[]; +} ogg_state_t; + +typedef struct ogg { + ogg_stream_t *streams; + int nstreams; + int headers; + int curidx; + uint64_t size; + ogg_state_t *state; +} ogg_t; + +#define OGG_FLAG_CONT 1 +#define OGG_FLAG_BOS 2 +#define OGG_FLAG_EOS 4 + +extern ogg_codec_t vorbis_codec; +#if 0 +extern ogg_codec_t ogm_video_codec; +extern ogg_codec_t ogm_audio_codec; +extern ogg_codec_t ogm_old_codec; +extern ogg_codec_t flac_codec; +#endif + +extern int vorbis_comment(AVFormatContext *ms, char *buf, int size); + +#endif diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c new file mode 100644 index 0000000000..0c765e2b83 --- /dev/null +++ b/libavformat/oggparsevorbis.c @@ -0,0 +1,169 @@ +/** + Copyright (C) 2005 Michael Ahlberg, Måns Rullgård + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + +#include +#include "avformat.h" +#include "bitstream.h" +#include "bswap.h" +#include "ogg2.h" + +extern int +vorbis_comment (AVFormatContext * as, char *buf, int size) +{ + char *p = buf; + int s, n, j; + + if (size < 4) + return -1; + + s = le2me_32 (unaligned32 (p)); + p += 4; + size -= 4; + + if (size < s + 4) + return -1; + + p += s; + size -= s; + + n = le2me_32 (unaligned32 (p)); + p += 4; + size -= 4; + + while (size >= 4){ + char *t, *v; + int tl, vl; + + s = le2me_32 (unaligned32 (p)); + p += 4; + size -= 4; + + if (size < s) + break; + + t = p; + p += s; + size -= s; + n--; + + v = memchr (t, '=', s); + if (!v) + continue; + + tl = v - t; + vl = s - tl - 1; + v++; + + if (tl && vl){ + char tt[tl + 1]; + char ct[vl + 1]; + + for (j = 0; j < tl; j++) + tt[j] = toupper (t[j]); + tt[tl] = 0; + + memcpy (ct, v, vl); + ct[vl] = 0; + + // took from Vorbis_I_spec + if (!strcmp (tt, "AUTHOR")) + strncpy (as->author, ct, FFMIN(sizeof (as->author), vl)); + else if (!strcmp (tt, "TITLE")) + strncpy (as->title, ct, FFMIN(sizeof (as->title), vl)); + else if (!strcmp (tt, "COPYRIGHT")) + strncpy (as->copyright, ct, FFMIN(sizeof (as->copyright), vl)); + else if (!strcmp (tt, "DESCRIPTION")) + strncpy (as->comment, ct, FFMIN(sizeof (as->comment), vl)); + else if (!strcmp (tt, "GENRE")) + strncpy (as->genre, ct, FFMIN(sizeof (as->genre), vl)); + else if (!strcmp (tt, "TRACKNUMBER")) + as->track = atoi (ct); + //Too bored to add others for today + } + } + + if (size > 0) + av_log (as, AV_LOG_INFO, "%i bytes of comment header remain\n", size); + if (n > 0) + av_log (as, AV_LOG_INFO, + "truncated comment header, %i comments not found\n", n); + + return 0; +} + + +/** Parse the vorbis header + * Vorbis Identification header from Vorbis_I_spec.html#vorbis-spec-codec + * [vorbis_version] = read 32 bits as unsigned integer | Not used + * [audio_channels] = read 8 bit integer as unsigned | Used + * [audio_sample_rate] = read 32 bits as unsigned integer | Used + * [bitrate_maximum] = read 32 bits as signed integer | Not used yet + * [bitrate_nominal] = read 32 bits as signed integer | Not used yet + * [bitrate_minimum] = read 32 bits as signed integer | Used as bitrate + * [blocksize_0] = read 4 bits as unsigned integer | Not Used + * [blocksize_1] = read 4 bits as unsigned integer | Not Used + * [framing_flag] = read one bit | Not Used + * */ + +static int +vorbis_header (AVFormatContext * s, int idx) +{ + ogg_t *ogg = s->priv_data; + ogg_stream_t *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + int cds = st->codec.extradata_size + os->psize + 2; + u_char *cdp; + + if (os->seq > 2) + return 0; + + st->codec.extradata = av_realloc (st->codec.extradata, cds); + 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; + + if (os->buf[os->pstart] == 1) { + u_char *p = os->buf + os->pstart + 11; //skip up to the audio channels + st->codec.channels = *p++; + st->codec.sample_rate = le2me_32 (unaligned32 (p)); + p += 8; //skip maximum and and nominal bitrate + st->codec.bit_rate = le2me_32 (unaligned32 (p)); //Minimum bitrate + + st->codec.codec_type = CODEC_TYPE_AUDIO; + st->codec.codec_id = CODEC_ID_VORBIS; + + } else if (os->buf[os->pstart] == 3) { + vorbis_comment (s, os->buf + os->pstart + 7, os->psize - 8); + } + + return os->seq < 3; +} + +ogg_codec_t vorbis_codec = { + .magic = "\001vorbis", + .magicsize = 7, + .header = vorbis_header +};