1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci      Copyright (C) 2008  Reimar Döffinger
3cabdff1aSopenharmony_ci
4cabdff1aSopenharmony_ci      Permission is hereby granted, free of charge, to any person
5cabdff1aSopenharmony_ci      obtaining a copy of this software and associated documentation
6cabdff1aSopenharmony_ci      files (the "Software"), to deal in the Software without
7cabdff1aSopenharmony_ci      restriction, including without limitation the rights to use, copy,
8cabdff1aSopenharmony_ci      modify, merge, publish, distribute, sublicense, and/or sell copies
9cabdff1aSopenharmony_ci      of the Software, and to permit persons to whom the Software is
10cabdff1aSopenharmony_ci      furnished to do so, subject to the following conditions:
11cabdff1aSopenharmony_ci
12cabdff1aSopenharmony_ci      The above copyright notice and this permission notice shall be
13cabdff1aSopenharmony_ci      included in all copies or substantial portions of the Software.
14cabdff1aSopenharmony_ci
15cabdff1aSopenharmony_ci      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16cabdff1aSopenharmony_ci      EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17cabdff1aSopenharmony_ci      MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18cabdff1aSopenharmony_ci      NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19cabdff1aSopenharmony_ci      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20cabdff1aSopenharmony_ci      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21cabdff1aSopenharmony_ci      OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22cabdff1aSopenharmony_ci      DEALINGS IN THE SOFTWARE.
23cabdff1aSopenharmony_ci**/
24cabdff1aSopenharmony_ci
25cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h"
26cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
27cabdff1aSopenharmony_ci
28cabdff1aSopenharmony_ci#include "avformat.h"
29cabdff1aSopenharmony_ci#include "internal.h"
30cabdff1aSopenharmony_ci#include "oggdec.h"
31cabdff1aSopenharmony_ci
32cabdff1aSopenharmony_cistruct speex_params {
33cabdff1aSopenharmony_ci    int packet_size;
34cabdff1aSopenharmony_ci    int final_packet_duration;
35cabdff1aSopenharmony_ci    int seq;
36cabdff1aSopenharmony_ci};
37cabdff1aSopenharmony_ci
38cabdff1aSopenharmony_cistatic int speex_header(AVFormatContext *s, int idx) {
39cabdff1aSopenharmony_ci    struct ogg *ogg = s->priv_data;
40cabdff1aSopenharmony_ci    struct ogg_stream *os = ogg->streams + idx;
41cabdff1aSopenharmony_ci    struct speex_params *spxp = os->private;
42cabdff1aSopenharmony_ci    AVStream *st = s->streams[idx];
43cabdff1aSopenharmony_ci    uint8_t *p = os->buf + os->pstart;
44cabdff1aSopenharmony_ci    int ret;
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_ci    if (!spxp) {
47cabdff1aSopenharmony_ci        spxp = av_mallocz(sizeof(*spxp));
48cabdff1aSopenharmony_ci        if (!spxp)
49cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
50cabdff1aSopenharmony_ci        os->private = spxp;
51cabdff1aSopenharmony_ci    }
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_ci    if (spxp->seq > 1)
54cabdff1aSopenharmony_ci        return 0;
55cabdff1aSopenharmony_ci
56cabdff1aSopenharmony_ci    if (spxp->seq == 0) {
57cabdff1aSopenharmony_ci        int frames_per_packet;
58cabdff1aSopenharmony_ci        int channels;
59cabdff1aSopenharmony_ci        st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
60cabdff1aSopenharmony_ci        st->codecpar->codec_id = AV_CODEC_ID_SPEEX;
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_ci        if (os->psize < 68) {
63cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "speex packet too small\n");
64cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
65cabdff1aSopenharmony_ci        }
66cabdff1aSopenharmony_ci
67cabdff1aSopenharmony_ci        st->codecpar->sample_rate = AV_RL32(p + 36);
68cabdff1aSopenharmony_ci        if (st->codecpar->sample_rate <= 0) {
69cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Invalid sample rate %d\n", st->codecpar->sample_rate);
70cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
71cabdff1aSopenharmony_ci        }
72cabdff1aSopenharmony_ci        channels = AV_RL32(p + 48);
73cabdff1aSopenharmony_ci        if (channels < 1 || channels > 2) {
74cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "invalid channel count. Speex must be mono or stereo.\n");
75cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
76cabdff1aSopenharmony_ci        }
77cabdff1aSopenharmony_ci        av_channel_layout_default(&st->codecpar->ch_layout, channels);
78cabdff1aSopenharmony_ci
79cabdff1aSopenharmony_ci        spxp->packet_size  = AV_RL32(p + 56);
80cabdff1aSopenharmony_ci        frames_per_packet  = AV_RL32(p + 64);
81cabdff1aSopenharmony_ci        if (spxp->packet_size < 0 ||
82cabdff1aSopenharmony_ci            frames_per_packet < 0 ||
83cabdff1aSopenharmony_ci            spxp->packet_size * (int64_t)frames_per_packet > INT32_MAX / 256) {
84cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "invalid packet_size, frames_per_packet %d %d\n", spxp->packet_size, frames_per_packet);
85cabdff1aSopenharmony_ci            spxp->packet_size = 0;
86cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
87cabdff1aSopenharmony_ci        }
88cabdff1aSopenharmony_ci        if (frames_per_packet)
89cabdff1aSopenharmony_ci            spxp->packet_size *= frames_per_packet;
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ci        if ((ret = ff_alloc_extradata(st->codecpar, os->psize)) < 0)
92cabdff1aSopenharmony_ci            return ret;
93cabdff1aSopenharmony_ci        memcpy(st->codecpar->extradata, p, st->codecpar->extradata_size);
94cabdff1aSopenharmony_ci
95cabdff1aSopenharmony_ci        avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
96cabdff1aSopenharmony_ci    } else
97cabdff1aSopenharmony_ci        ff_vorbis_stream_comment(s, st, p, os->psize);
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_ci    spxp->seq++;
100cabdff1aSopenharmony_ci    return 1;
101cabdff1aSopenharmony_ci}
102cabdff1aSopenharmony_ci
103cabdff1aSopenharmony_cistatic int ogg_page_packets(struct ogg_stream *os)
104cabdff1aSopenharmony_ci{
105cabdff1aSopenharmony_ci    int i;
106cabdff1aSopenharmony_ci    int packets = 0;
107cabdff1aSopenharmony_ci    for (i = 0; i < os->nsegs; i++)
108cabdff1aSopenharmony_ci        if (os->segments[i] < 255)
109cabdff1aSopenharmony_ci            packets++;
110cabdff1aSopenharmony_ci    return packets;
111cabdff1aSopenharmony_ci}
112cabdff1aSopenharmony_ci
113cabdff1aSopenharmony_cistatic int speex_packet(AVFormatContext *s, int idx)
114cabdff1aSopenharmony_ci{
115cabdff1aSopenharmony_ci    struct ogg *ogg = s->priv_data;
116cabdff1aSopenharmony_ci    struct ogg_stream *os = ogg->streams + idx;
117cabdff1aSopenharmony_ci    struct speex_params *spxp = os->private;
118cabdff1aSopenharmony_ci    int packet_size = spxp->packet_size;
119cabdff1aSopenharmony_ci
120cabdff1aSopenharmony_ci    if (os->flags & OGG_FLAG_EOS && os->lastpts != AV_NOPTS_VALUE &&
121cabdff1aSopenharmony_ci        os->granule > 0) {
122cabdff1aSopenharmony_ci        /* first packet of final page. we have to calculate the final packet
123cabdff1aSopenharmony_ci           duration here because it is the only place we know the next-to-last
124cabdff1aSopenharmony_ci           granule position. */
125cabdff1aSopenharmony_ci        spxp->final_packet_duration = os->granule - os->lastpts -
126cabdff1aSopenharmony_ci                                      packet_size * (ogg_page_packets(os) - 1);
127cabdff1aSopenharmony_ci    }
128cabdff1aSopenharmony_ci
129cabdff1aSopenharmony_ci    if (!os->lastpts && os->granule > 0)
130cabdff1aSopenharmony_ci        /* first packet */
131cabdff1aSopenharmony_ci        os->lastpts = os->lastdts = os->granule - packet_size *
132cabdff1aSopenharmony_ci                                    ogg_page_packets(os);
133cabdff1aSopenharmony_ci    if (os->flags & OGG_FLAG_EOS && os->segp == os->nsegs &&
134cabdff1aSopenharmony_ci        spxp->final_packet_duration)
135cabdff1aSopenharmony_ci        /* final packet */
136cabdff1aSopenharmony_ci        os->pduration = spxp->final_packet_duration;
137cabdff1aSopenharmony_ci    else
138cabdff1aSopenharmony_ci        os->pduration = packet_size;
139cabdff1aSopenharmony_ci
140cabdff1aSopenharmony_ci    return 0;
141cabdff1aSopenharmony_ci}
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ciconst struct ogg_codec ff_speex_codec = {
144cabdff1aSopenharmony_ci    .magic = "Speex   ",
145cabdff1aSopenharmony_ci    .magicsize = 8,
146cabdff1aSopenharmony_ci    .header = speex_header,
147cabdff1aSopenharmony_ci    .packet = speex_packet,
148cabdff1aSopenharmony_ci    .nb_header = 2,
149cabdff1aSopenharmony_ci};
150