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