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>
245976da
 #include "libavutil/bswap.h"
 #include "libavutil/avstring.h"
d4088efb
 #include "libavutil/channel_layout.h"
9106a698
 #include "libavcodec/get_bits.h"
245976da
 #include "libavcodec/bytestream.h"
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));
         os->private = spxp;
     }
 
     if (spxp->seq > 1)
6e6abd02
         return 0;
cb4ddf77
 
8f8320d7
     if (spxp->seq == 0) {
eb5f3c54
         int frames_per_packet;
72415b2a
         st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
36ef5369
         st->codec->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;
         }
 
1a4ab332
         st->codec->sample_rate = AV_RL32(p + 36);
         st->codec->channels = AV_RL32(p + 48);
d4088efb
         if (st->codec->channels < 1 || st->codec->channels > 2) {
             av_log(s, AV_LOG_ERROR, "invalid channel count. Speex must be mono or stereo.\n");
             return AVERROR_INVALIDDATA;
         }
         st->codec->channel_layout = st->codec->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);
eb5f3c54
         if (frames_per_packet)
0e69c047
             spxp->packet_size *= frames_per_packet;
eb5f3c54
 
eb5cc8fe
         if (ff_alloc_extradata(st->codec, os->psize) < 0)
             return AVERROR(ENOMEM);
1a4ab332
         memcpy(st->codec->extradata, p, st->codec->extradata_size);
cb4ddf77
 
c3f9ebf7
         avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
6e6abd02
     } else
b53cde48
         ff_vorbis_comment(s, &st->metadata, 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
 };