libavformat/oggparsevorbis.c
9146ca37
 /**
       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>
245976da
 #include "libavutil/avstring.h"
 #include "libavutil/bswap.h"
 #include "libavcodec/bitstream.h"
 #include "libavcodec/bytestream.h"
9146ca37
 #include "avformat.h"
a0ddef24
 #include "oggdec.h"
9146ca37
 
9686df2b
 int
4bd684bc
 vorbis_comment(AVFormatContext * as, uint8_t *buf, int size)
9146ca37
 {
47a0513b
     const uint8_t *p = buf;
     const uint8_t *end = buf + size;
ef84190a
     unsigned n, j;
     int s;
9146ca37
 
f5475e1b
     if (size < 8) /* must have vendor_length and user_comment_list_length */
9146ca37
         return -1;
 
0a770ae7
     s = bytestream_get_le32(&p);
9146ca37
 
ef84190a
     if (end - p - 4 < s || s < 0)
9146ca37
         return -1;
 
     p += s;
 
0a770ae7
     n = bytestream_get_le32(&p);
9146ca37
 
ef84190a
     while (end - p >= 4 && n > 0) {
47a0513b
         const char *t, *v;
9146ca37
         int tl, vl;
 
0a770ae7
         s = bytestream_get_le32(&p);
9146ca37
 
ef84190a
         if (end - p < s || s < 0)
9146ca37
             break;
 
         t = p;
         p += s;
         n--;
 
4bd684bc
         v = memchr(t, '=', s);
9146ca37
         if (!v)
             continue;
 
         tl = v - t;
         vl = s - tl - 1;
         v++;
 
4bd684bc
         if (tl && vl) {
e3b44649
             char *tt, *ct;
 
             tt = av_malloc(tl + 1);
             ct = av_malloc(vl + 1);
             if (!tt || !ct) {
                 av_freep(&tt);
                 av_freep(&ct);
                 av_log(as, AV_LOG_WARNING, "out-of-memory error. skipping VorbisComment tag.\n");
                 continue;
             }
9146ca37
 
             for (j = 0; j < tl; j++)
4bd684bc
                 tt[j] = toupper(t[j]);
9146ca37
             tt[tl] = 0;
 
4bd684bc
             memcpy(ct, v, vl);
9146ca37
             ct[vl] = 0;
 
bf6c32fe
             av_metadata_set(&as->metadata, tt, ct);
e3b44649
 
             av_freep(&tt);
             av_freep(&ct);
9146ca37
         }
     }
 
972c5f9e
     if (p != end)
         av_log(as, AV_LOG_INFO, "%ti bytes of comment header remain\n", p-end);
9146ca37
     if (n > 0)
4bd684bc
         av_log(as, AV_LOG_INFO,
                "truncated comment header, %i comments not found\n", n);
9146ca37
 
     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
115329f1
  * [audio_sample_rate] = read 32 bits as unsigned integer | Used
9146ca37
  * [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
  *    */
 
77be08ee
 struct oggvorbis_private {
ad2b531d
     unsigned int len[3];
     unsigned char *packet[3];
77be08ee
 };
ad2b531d
 
 
 static unsigned int
77be08ee
 fixup_vorbis_headers(AVFormatContext * as, struct oggvorbis_private *priv,
19f4ceca
                      uint8_t **buf)
ad2b531d
 {
     int i,offset, len;
     unsigned char *ptr;
 
     len = priv->len[0] + priv->len[1] + priv->len[2];
     ptr = *buf = av_mallocz(len + len/255 + 64);
 
     ptr[0] = 2;
     offset = 1;
     offset += av_xiphlacing(&ptr[offset], priv->len[0]);
     offset += av_xiphlacing(&ptr[offset], priv->len[1]);
4bd684bc
     for (i = 0; i < 3; i++) {
ad2b531d
         memcpy(&ptr[offset], priv->packet[i], priv->len[i]);
         offset += priv->len[i];
     }
589790c2
     *buf = av_realloc(*buf, offset + FF_INPUT_BUFFER_PADDING_SIZE);
ad2b531d
     return offset;
 }
 
 
9146ca37
 static int
 vorbis_header (AVFormatContext * s, int idx)
 {
77be08ee
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os = ogg->streams + idx;
9146ca37
     AVStream *st = s->streams[idx];
77be08ee
     struct oggvorbis_private *priv;
9146ca37
 
     if (os->seq > 2)
         return 0;
 
4bd684bc
     if (os->seq == 0) {
77be08ee
         os->private = av_mallocz(sizeof(struct oggvorbis_private));
4bd684bc
         if (!os->private)
ad2b531d
             return 0;
     }
9146ca37
 
f5475e1b
     if (os->psize < 1)
         return -1;
 
ad2b531d
     priv = os->private;
     priv->len[os->seq] = os->psize;
     priv->packet[os->seq] = av_mallocz(os->psize);
     memcpy(priv->packet[os->seq], os->buf + os->pstart, os->psize);
9146ca37
     if (os->buf[os->pstart] == 1) {
47a0513b
         const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */
736e63ed
         unsigned blocksize, bs0, bs1;
f5475e1b
 
         if (os->psize != 30)
             return -1;
 
736e63ed
         if (bytestream_get_le32(&p) != 0) /* vorbis_version */
             return -1;
 
739587bf
         st->codec->channels = bytestream_get_byte(&p);
         st->codec->sample_rate = bytestream_get_le32(&p);
         p += 4; // skip maximum bitrate
         st->codec->bit_rate = bytestream_get_le32(&p); // nominal bitrate
736e63ed
         p += 4; // skip minimum bitrate
 
         blocksize = bytestream_get_byte(&p);
         bs0 = blocksize & 15;
         bs1 = blocksize >> 4;
 
         if (bs0 > bs1)
             return -1;
         if (bs0 < 6 || bs1 > 13)
             return -1;
 
         if (bytestream_get_byte(&p) != 1) /* framing_flag */
             return -1;
9146ca37
 
01f4895c
         st->codec->codec_type = CODEC_TYPE_AUDIO;
         st->codec->codec_id = CODEC_ID_VORBIS;
9146ca37
 
3644cb8f
         st->time_base.num = 1;
         st->time_base.den = st->codec->sample_rate;
9146ca37
     } else if (os->buf[os->pstart] == 3) {
f5475e1b
         if (os->psize > 8)
             vorbis_comment (s, os->buf + os->pstart + 7, os->psize - 8);
ad2b531d
     } else {
01f4895c
         st->codec->extradata_size =
             fixup_vorbis_headers(s, priv, &st->codec->extradata);
9146ca37
     }
 
     return os->seq < 3;
 }
 
77be08ee
 const struct ogg_codec ff_vorbis_codec = {
9146ca37
     .magic = "\001vorbis",
     .magicsize = 7,
     .header = vorbis_header
 };