libavformat/oggparsespeex.c
cb4ddf77
 /*
       Copyright (C) 2008  Reimar Döffinger
 
       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>
5c31eaa9
 
245976da
 #include "libavutil/bswap.h"
 #include "libavutil/avstring.h"
d4088efb
 #include "libavutil/channel_layout.h"
5c31eaa9
 
245976da
 #include "libavcodec/bytestream.h"
5c31eaa9
 
cb4ddf77
 #include "avformat.h"
c3f9ebf7
 #include "internal.h"
cb4ddf77
 #include "oggdec.h"
 
15299b38
 struct speex_params {
0e69c047
     int packet_size;
15299b38
     int final_packet_duration;
8f8320d7
     int seq;
15299b38
 };
 
cb4ddf77
 static int speex_header(AVFormatContext *s, int idx) {
77be08ee
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os = ogg->streams + idx;
8f8320d7
     struct speex_params *spxp = os->private;
cb4ddf77
     AVStream *st = s->streams[idx];
     uint8_t *p = os->buf + os->pstart;
 
8f8320d7
     if (!spxp) {
         spxp = av_mallocz(sizeof(*spxp));
c8fa6478
         if (!spxp)
             return AVERROR(ENOMEM);
8f8320d7
         os->private = spxp;
     }
 
     if (spxp->seq > 1)
6e6abd02
         return 0;
cb4ddf77
 
8f8320d7
     if (spxp->seq == 0) {
eb5f3c54
         int frames_per_packet;
9200514a
         st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
         st->codecpar->codec_id = AV_CODEC_ID_SPEEX;
cb4ddf77
 
a0f659b2
         if (os->psize < 68) {
             av_log(s, AV_LOG_ERROR, "speex packet too small\n");
             return AVERROR_INVALIDDATA;
         }
 
9200514a
         st->codecpar->sample_rate = AV_RL32(p + 36);
         st->codecpar->channels = AV_RL32(p + 48);
         if (st->codecpar->channels < 1 || st->codecpar->channels > 2) {
d4088efb
             av_log(s, AV_LOG_ERROR, "invalid channel count. Speex must be mono or stereo.\n");
             return AVERROR_INVALIDDATA;
         }
9200514a
         st->codecpar->channel_layout = st->codecpar->channels == 1 ? AV_CH_LAYOUT_MONO :
                                                                      AV_CH_LAYOUT_STEREO;
eb5f3c54
 
0e69c047
         spxp->packet_size  = AV_RL32(p + 56);
         frames_per_packet  = AV_RL32(p + 64);
a0715c1e
         if (spxp->packet_size < 0 ||
             frames_per_packet < 0 ||
             spxp->packet_size * (int64_t)frames_per_packet > INT32_MAX / 256) {
             av_log(s, AV_LOG_ERROR, "invalid packet_size, frames_per_packet %d %d\n", spxp->packet_size, frames_per_packet);
             spxp->packet_size = 0;
             return AVERROR_INVALIDDATA;
         }
eb5f3c54
         if (frames_per_packet)
0e69c047
             spxp->packet_size *= frames_per_packet;
eb5f3c54
 
6f69f7a8
         if (ff_alloc_extradata(st->codecpar, os->psize) < 0)
eb5cc8fe
             return AVERROR(ENOMEM);
9200514a
         memcpy(st->codecpar->extradata, p, st->codecpar->extradata_size);
cb4ddf77
 
9200514a
         avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
6e6abd02
     } else
db68ef89
         ff_vorbis_stream_comment(s, st, p, os->psize);
cb4ddf77
 
8f8320d7
     spxp->seq++;
6e6abd02
     return 1;
cb4ddf77
 }
 
15299b38
 static int ogg_page_packets(struct ogg_stream *os)
 {
     int i;
     int packets = 0;
     for (i = 0; i < os->nsegs; i++)
         if (os->segments[i] < 255)
             packets++;
     return packets;
 }
 
 static int speex_packet(AVFormatContext *s, int idx)
 {
     struct ogg *ogg = s->priv_data;
     struct ogg_stream *os = ogg->streams + idx;
     struct speex_params *spxp = os->private;
0e69c047
     int packet_size = spxp->packet_size;
15299b38
 
5e15c7d9
     if (os->flags & OGG_FLAG_EOS && os->lastpts != AV_NOPTS_VALUE &&
         os->granule > 0) {
15299b38
         /* first packet of final page. we have to calculate the final packet
            duration here because it is the only place we know the next-to-last
            granule position. */
5e15c7d9
         spxp->final_packet_duration = os->granule - os->lastpts -
15299b38
                                       packet_size * (ogg_page_packets(os) - 1);
     }
 
5e15c7d9
     if (!os->lastpts && os->granule > 0)
15299b38
         /* first packet */
0e69c047
         os->lastpts = os->lastdts = os->granule - packet_size *
                                     ogg_page_packets(os);
     if (os->flags & OGG_FLAG_EOS && os->segp == os->nsegs &&
         spxp->final_packet_duration)
15299b38
         /* final packet */
         os->pduration = spxp->final_packet_duration;
     else
         os->pduration = packet_size;
 
     return 0;
 }
 
77be08ee
 const struct ogg_codec ff_speex_codec = {
cb4ddf77
     .magic = "Speex   ",
     .magicsize = 8,
15299b38
     .header = speex_header,
7751e469
     .packet = speex_packet,
     .nb_header = 2,
cb4ddf77
 };