oggparsevorbis.c
Go to the documentation of this file.
1 
25 #include <stdlib.h>
26 #include "libavutil/avstring.h"
27 #include "libavutil/bswap.h"
28 #include "libavutil/dict.h"
29 #include "libavcodec/get_bits.h"
30 #include "libavcodec/bytestream.h"
31 #include "avformat.h"
32 #include "internal.h"
33 #include "oggdec.h"
34 #include "vorbiscomment.h"
35 
36 static int ogm_chapter(AVFormatContext *as, uint8_t *key, uint8_t *val)
37 {
38  int i, cnum, h, m, s, ms, keylen = strlen(key);
39  AVChapter *chapter = NULL;
40 
41  if (keylen < 9 || sscanf(key, "CHAPTER%02d", &cnum) != 1)
42  return 0;
43 
44  if (keylen == 9) {
45  if (sscanf(val, "%02d:%02d:%02d.%03d", &h, &m, &s, &ms) < 4)
46  return 0;
47 
48  avpriv_new_chapter(as, cnum, (AVRational){1,1000},
49  ms + 1000*(s + 60*(m + 60*h)),
51  av_free(val);
52  } else if (!strcmp(key+9, "NAME")) {
53  for(i = 0; i < as->nb_chapters; i++)
54  if (as->chapters[i]->id == cnum) {
55  chapter = as->chapters[i];
56  break;
57  }
58  if (!chapter)
59  return 0;
60 
61  av_dict_set(&chapter->metadata, "title", val,
63  } else
64  return 0;
65 
66  av_free(key);
67  return 1;
68 }
69 
70 int
71 ff_vorbis_comment(AVFormatContext * as, AVDictionary **m, const uint8_t *buf, int size)
72 {
73  const uint8_t *p = buf;
74  const uint8_t *end = buf + size;
75  unsigned n, j;
76  int s;
77 
78  if (size < 8) /* must have vendor_length and user_comment_list_length */
79  return -1;
80 
81  s = bytestream_get_le32(&p);
82 
83  if (end - p - 4 < s || s < 0)
84  return -1;
85 
86  p += s;
87 
88  n = bytestream_get_le32(&p);
89 
90  while (end - p >= 4 && n > 0) {
91  const char *t, *v;
92  int tl, vl;
93 
94  s = bytestream_get_le32(&p);
95 
96  if (end - p < s || s < 0)
97  break;
98 
99  t = p;
100  p += s;
101  n--;
102 
103  v = memchr(t, '=', s);
104  if (!v)
105  continue;
106 
107  tl = v - t;
108  vl = s - tl - 1;
109  v++;
110 
111  if (tl && vl) {
112  char *tt, *ct;
113 
114  tt = av_malloc(tl + 1);
115  ct = av_malloc(vl + 1);
116  if (!tt || !ct) {
117  av_freep(&tt);
118  av_freep(&ct);
119  av_log(as, AV_LOG_WARNING, "out-of-memory error. skipping VorbisComment tag.\n");
120  continue;
121  }
122 
123  for (j = 0; j < tl; j++)
124  tt[j] = toupper(t[j]);
125  tt[tl] = 0;
126 
127  memcpy(ct, v, vl);
128  ct[vl] = 0;
129 
130  if (!ogm_chapter(as, tt, ct))
131  av_dict_set(m, tt, ct,
134  }
135  }
136 
137  if (p != end)
138  av_log(as, AV_LOG_INFO, "%ti bytes of comment header remain\n", end-p);
139  if (n > 0)
140  av_log(as, AV_LOG_INFO,
141  "truncated comment header, %i comments not found\n", n);
142 
144 
145  return 0;
146 }
147 
148 
163  unsigned int len[3];
164  unsigned char *packet[3];
165 };
166 
167 
168 static unsigned int
170  uint8_t **buf)
171 {
172  int i,offset, len;
173  unsigned char *ptr;
174 
175  len = priv->len[0] + priv->len[1] + priv->len[2];
176  ptr = *buf = av_mallocz(len + len/255 + 64);
177 
178  ptr[0] = 2;
179  offset = 1;
180  offset += av_xiphlacing(&ptr[offset], priv->len[0]);
181  offset += av_xiphlacing(&ptr[offset], priv->len[1]);
182  for (i = 0; i < 3; i++) {
183  memcpy(&ptr[offset], priv->packet[i], priv->len[i]);
184  offset += priv->len[i];
185  av_freep(&priv->packet[i]);
186  }
187  *buf = av_realloc(*buf, offset + FF_INPUT_BUFFER_PADDING_SIZE);
188  return offset;
189 }
190 
191 static void vorbis_cleanup(AVFormatContext *s, int idx)
192 {
193  struct ogg *ogg = s->priv_data;
194  struct ogg_stream *os = ogg->streams + idx;
195  struct oggvorbis_private *priv = os->private;
196  int i;
197  if (os->private)
198  for (i = 0; i < 3; i++)
199  av_freep(&priv->packet[i]);
200 }
201 
202 static int
204 {
205  struct ogg *ogg = s->priv_data;
206  struct ogg_stream *os = ogg->streams + idx;
207  AVStream *st = s->streams[idx];
208  struct oggvorbis_private *priv;
209  int pkt_type = os->buf[os->pstart];
210 
211  if (!os->private) {
212  os->private = av_mallocz(sizeof(struct oggvorbis_private));
213  if (!os->private)
214  return 0;
215  }
216 
217  if (!(pkt_type & 1))
218  return 0;
219 
220  if (os->psize < 1 || pkt_type > 5)
221  return -1;
222 
223  priv = os->private;
224 
225  if (priv->packet[pkt_type>>1])
226  return -1;
227  if (pkt_type > 1 && !priv->packet[0] || pkt_type > 3 && !priv->packet[1])
228  return -1;
229 
230  priv->len[pkt_type >> 1] = os->psize;
231  priv->packet[pkt_type >> 1] = av_mallocz(os->psize);
232  memcpy(priv->packet[pkt_type >> 1], os->buf + os->pstart, os->psize);
233  if (os->buf[os->pstart] == 1) {
234  const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */
235  unsigned blocksize, bs0, bs1;
236  int srate;
237 
238  if (os->psize != 30)
239  return -1;
240 
241  if (bytestream_get_le32(&p) != 0) /* vorbis_version */
242  return -1;
243 
244  st->codec->channels = bytestream_get_byte(&p);
245  srate = bytestream_get_le32(&p);
246  p += 4; // skip maximum bitrate
247  st->codec->bit_rate = bytestream_get_le32(&p); // nominal bitrate
248  p += 4; // skip minimum bitrate
249 
250  blocksize = bytestream_get_byte(&p);
251  bs0 = blocksize & 15;
252  bs1 = blocksize >> 4;
253 
254  if (bs0 > bs1)
255  return -1;
256  if (bs0 < 6 || bs1 > 13)
257  return -1;
258 
259  if (bytestream_get_byte(&p) != 1) /* framing_flag */
260  return -1;
261 
264 
265  if (srate > 0) {
266  st->codec->sample_rate = srate;
267  avpriv_set_pts_info(st, 64, 1, srate);
268  }
269  } else if (os->buf[os->pstart] == 3) {
270  if (os->psize > 8 &&
271  ff_vorbis_comment(s, &st->metadata, os->buf + os->pstart + 7, os->psize - 8) >= 0) {
272  // drop all metadata we parsed and which is not required by libvorbis
273  unsigned new_len = 7 + 4 + AV_RL32(priv->packet[1] + 7) + 4 + 1;
274  if (new_len >= 16 && new_len < os->psize) {
275  AV_WL32(priv->packet[1] + new_len - 5, 0);
276  priv->packet[1][new_len - 1] = 1;
277  priv->len[1] = new_len;
278  }
279  }
280  } else {
281  st->codec->extradata_size =
282  fixup_vorbis_headers(s, priv, &st->codec->extradata);
283  }
284 
285  return 1;
286 }
287 
288 const struct ogg_codec ff_vorbis_codec = {
289  .magic = "\001vorbis",
290  .magicsize = 7,
291  .header = vorbis_header,
292  .cleanup= vorbis_cleanup,
293  .nb_header = 3,
294 };