--- /dev/null
+/*
+ * Ogg bitstream support
+ * Luca Barbato <lu_zero@gentoo.org>
+ * 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 <stdio.h>
+#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;
+}
--- /dev/null
+/**
+ 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 <stdlib.h>
+#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
+};