blob: 0e12c8c6c18a1f10eb910bffbcbd6f383a28b389
1 | /* |
2 | * Silicon Graphics Movie demuxer |
3 | * Copyright (c) 2012 Peter Ross |
4 | * |
5 | * This file is part of FFmpeg. |
6 | * |
7 | * FFmpeg is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * |
12 | * FFmpeg is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with FFmpeg; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ |
21 | |
22 | /** |
23 | * @file |
24 | * Silicon Graphics Movie demuxer |
25 | */ |
26 | |
27 | #include "libavutil/channel_layout.h" |
28 | #include "libavutil/eval.h" |
29 | #include "libavutil/intreadwrite.h" |
30 | #include "libavutil/rational.h" |
31 | |
32 | #include "avformat.h" |
33 | #include "internal.h" |
34 | |
35 | typedef struct MvContext { |
36 | int nb_video_tracks; |
37 | int nb_audio_tracks; |
38 | |
39 | int eof_count; ///< number of streams that have finished |
40 | int stream_index; ///< current stream index |
41 | int frame[2]; ///< frame nb for current stream |
42 | |
43 | int acompression; ///< compression level for audio stream |
44 | int aformat; ///< audio format |
45 | } MvContext; |
46 | |
47 | #define AUDIO_FORMAT_SIGNED 401 |
48 | |
49 | static int mv_probe(AVProbeData *p) |
50 | { |
51 | if (AV_RB32(p->buf) == MKBETAG('M', 'O', 'V', 'I') && |
52 | AV_RB16(p->buf + 4) < 3) |
53 | return AVPROBE_SCORE_MAX; |
54 | return 0; |
55 | } |
56 | |
57 | static char *var_read_string(AVIOContext *pb, int size) |
58 | { |
59 | int n; |
60 | char *str; |
61 | |
62 | if (size < 0 || size == INT_MAX) |
63 | return NULL; |
64 | |
65 | str = av_malloc(size + 1); |
66 | if (!str) |
67 | return NULL; |
68 | n = avio_get_str(pb, size, str, size + 1); |
69 | if (n < size) |
70 | avio_skip(pb, size - n); |
71 | return str; |
72 | } |
73 | |
74 | static int var_read_int(AVIOContext *pb, int size) |
75 | { |
76 | int v; |
77 | char *s = var_read_string(pb, size); |
78 | if (!s) |
79 | return 0; |
80 | v = strtol(s, NULL, 10); |
81 | av_free(s); |
82 | return v; |
83 | } |
84 | |
85 | static AVRational var_read_float(AVIOContext *pb, int size) |
86 | { |
87 | AVRational v; |
88 | char *s = var_read_string(pb, size); |
89 | if (!s) |
90 | return (AVRational) { 0, 0 }; |
91 | v = av_d2q(av_strtod(s, NULL), INT_MAX); |
92 | av_free(s); |
93 | return v; |
94 | } |
95 | |
96 | static void var_read_metadata(AVFormatContext *avctx, const char *tag, int size) |
97 | { |
98 | char *value = var_read_string(avctx->pb, size); |
99 | if (value) |
100 | av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL); |
101 | } |
102 | |
103 | static int set_channels(AVFormatContext *avctx, AVStream *st, int channels) |
104 | { |
105 | if (channels <= 0) { |
106 | av_log(avctx, AV_LOG_ERROR, "Channel count %d invalid.\n", channels); |
107 | return AVERROR_INVALIDDATA; |
108 | } |
109 | st->codecpar->channels = channels; |
110 | st->codecpar->channel_layout = (st->codecpar->channels == 1) ? AV_CH_LAYOUT_MONO |
111 | : AV_CH_LAYOUT_STEREO; |
112 | return 0; |
113 | } |
114 | |
115 | /** |
116 | * Parse global variable |
117 | * @return < 0 if unknown |
118 | */ |
119 | static int parse_global_var(AVFormatContext *avctx, AVStream *st, |
120 | const char *name, int size) |
121 | { |
122 | MvContext *mv = avctx->priv_data; |
123 | AVIOContext *pb = avctx->pb; |
124 | if (!strcmp(name, "__NUM_I_TRACKS")) { |
125 | mv->nb_video_tracks = var_read_int(pb, size); |
126 | } else if (!strcmp(name, "__NUM_A_TRACKS")) { |
127 | mv->nb_audio_tracks = var_read_int(pb, size); |
128 | } else if (!strcmp(name, "COMMENT") || !strcmp(name, "TITLE")) { |
129 | var_read_metadata(avctx, name, size); |
130 | } else if (!strcmp(name, "LOOP_MODE") || !strcmp(name, "NUM_LOOPS") || |
131 | !strcmp(name, "OPTIMIZED")) { |
132 | avio_skip(pb, size); // ignore |
133 | } else |
134 | return AVERROR_INVALIDDATA; |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | /** |
140 | * Parse audio variable |
141 | * @return < 0 if unknown |
142 | */ |
143 | static int parse_audio_var(AVFormatContext *avctx, AVStream *st, |
144 | const char *name, int size) |
145 | { |
146 | MvContext *mv = avctx->priv_data; |
147 | AVIOContext *pb = avctx->pb; |
148 | if (!strcmp(name, "__DIR_COUNT")) { |
149 | st->nb_frames = var_read_int(pb, size); |
150 | } else if (!strcmp(name, "AUDIO_FORMAT")) { |
151 | mv->aformat = var_read_int(pb, size); |
152 | } else if (!strcmp(name, "COMPRESSION")) { |
153 | mv->acompression = var_read_int(pb, size); |
154 | } else if (!strcmp(name, "DEFAULT_VOL")) { |
155 | var_read_metadata(avctx, name, size); |
156 | } else if (!strcmp(name, "NUM_CHANNELS")) { |
157 | return set_channels(avctx, st, var_read_int(pb, size)); |
158 | } else if (!strcmp(name, "SAMPLE_RATE")) { |
159 | st->codecpar->sample_rate = var_read_int(pb, size); |
160 | avpriv_set_pts_info(st, 33, 1, st->codecpar->sample_rate); |
161 | } else if (!strcmp(name, "SAMPLE_WIDTH")) { |
162 | st->codecpar->bits_per_coded_sample = var_read_int(pb, size) * 8; |
163 | } else |
164 | return AVERROR_INVALIDDATA; |
165 | |
166 | return 0; |
167 | } |
168 | |
169 | /** |
170 | * Parse video variable |
171 | * @return < 0 if unknown |
172 | */ |
173 | static int parse_video_var(AVFormatContext *avctx, AVStream *st, |
174 | const char *name, int size) |
175 | { |
176 | AVIOContext *pb = avctx->pb; |
177 | if (!strcmp(name, "__DIR_COUNT")) { |
178 | st->nb_frames = st->duration = var_read_int(pb, size); |
179 | } else if (!strcmp(name, "COMPRESSION")) { |
180 | char *str = var_read_string(pb, size); |
181 | if (!str) |
182 | return AVERROR_INVALIDDATA; |
183 | if (!strcmp(str, "1")) { |
184 | st->codecpar->codec_id = AV_CODEC_ID_MVC1; |
185 | } else if (!strcmp(str, "2")) { |
186 | st->codecpar->format = AV_PIX_FMT_ABGR; |
187 | st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; |
188 | } else if (!strcmp(str, "3")) { |
189 | st->codecpar->codec_id = AV_CODEC_ID_SGIRLE; |
190 | } else if (!strcmp(str, "10")) { |
191 | st->codecpar->codec_id = AV_CODEC_ID_MJPEG; |
192 | } else if (!strcmp(str, "MVC2")) { |
193 | st->codecpar->codec_id = AV_CODEC_ID_MVC2; |
194 | } else { |
195 | avpriv_request_sample(avctx, "Video compression %s", str); |
196 | } |
197 | av_free(str); |
198 | } else if (!strcmp(name, "FPS")) { |
199 | AVRational fps = var_read_float(pb, size); |
200 | avpriv_set_pts_info(st, 64, fps.den, fps.num); |
201 | st->avg_frame_rate = fps; |
202 | } else if (!strcmp(name, "HEIGHT")) { |
203 | st->codecpar->height = var_read_int(pb, size); |
204 | } else if (!strcmp(name, "PIXEL_ASPECT")) { |
205 | st->sample_aspect_ratio = var_read_float(pb, size); |
206 | av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, |
207 | st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, |
208 | INT_MAX); |
209 | } else if (!strcmp(name, "WIDTH")) { |
210 | st->codecpar->width = var_read_int(pb, size); |
211 | } else if (!strcmp(name, "ORIENTATION")) { |
212 | if (var_read_int(pb, size) == 1101) { |
213 | st->codecpar->extradata = av_strdup("BottomUp"); |
214 | st->codecpar->extradata_size = 9; |
215 | } |
216 | } else if (!strcmp(name, "Q_SPATIAL") || !strcmp(name, "Q_TEMPORAL")) { |
217 | var_read_metadata(avctx, name, size); |
218 | } else if (!strcmp(name, "INTERLACING") || !strcmp(name, "PACKING")) { |
219 | avio_skip(pb, size); // ignore |
220 | } else |
221 | return AVERROR_INVALIDDATA; |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | static int read_table(AVFormatContext *avctx, AVStream *st, |
227 | int (*parse)(AVFormatContext *avctx, AVStream *st, |
228 | const char *name, int size)) |
229 | { |
230 | int count, i; |
231 | AVIOContext *pb = avctx->pb; |
232 | avio_skip(pb, 4); |
233 | count = avio_rb32(pb); |
234 | avio_skip(pb, 4); |
235 | for (i = 0; i < count; i++) { |
236 | char name[17]; |
237 | int size; |
238 | avio_read(pb, name, 16); |
239 | name[sizeof(name) - 1] = 0; |
240 | size = avio_rb32(pb); |
241 | if (size < 0) { |
242 | av_log(avctx, AV_LOG_ERROR, "entry size %d is invalid\n", size); |
243 | return AVERROR_INVALIDDATA; |
244 | } |
245 | if (parse(avctx, st, name, size) < 0) { |
246 | avpriv_request_sample(avctx, "Variable %s", name); |
247 | avio_skip(pb, size); |
248 | } |
249 | } |
250 | return 0; |
251 | } |
252 | |
253 | static void read_index(AVIOContext *pb, AVStream *st) |
254 | { |
255 | uint64_t timestamp = 0; |
256 | int i; |
257 | for (i = 0; i < st->nb_frames; i++) { |
258 | uint32_t pos = avio_rb32(pb); |
259 | uint32_t size = avio_rb32(pb); |
260 | avio_skip(pb, 8); |
261 | av_add_index_entry(st, pos, timestamp, size, 0, AVINDEX_KEYFRAME); |
262 | if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { |
263 | timestamp += size / (st->codecpar->channels * 2); |
264 | } else { |
265 | timestamp++; |
266 | } |
267 | } |
268 | } |
269 | |
270 | static int mv_read_header(AVFormatContext *avctx) |
271 | { |
272 | MvContext *mv = avctx->priv_data; |
273 | AVIOContext *pb = avctx->pb; |
274 | AVStream *ast = NULL, *vst = NULL; //initialization to suppress warning |
275 | int version, i; |
276 | int ret; |
277 | |
278 | avio_skip(pb, 4); |
279 | |
280 | version = avio_rb16(pb); |
281 | if (version == 2) { |
282 | uint64_t timestamp; |
283 | int v; |
284 | avio_skip(pb, 22); |
285 | |
286 | /* allocate audio track first to prevent unnecessary seeking |
287 | * (audio packet always precede video packet for a given frame) */ |
288 | ast = avformat_new_stream(avctx, NULL); |
289 | if (!ast) |
290 | return AVERROR(ENOMEM); |
291 | |
292 | vst = avformat_new_stream(avctx, NULL); |
293 | if (!vst) |
294 | return AVERROR(ENOMEM); |
295 | avpriv_set_pts_info(vst, 64, 1, 15); |
296 | vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
297 | vst->avg_frame_rate = av_inv_q(vst->time_base); |
298 | vst->nb_frames = avio_rb32(pb); |
299 | v = avio_rb32(pb); |
300 | switch (v) { |
301 | case 1: |
302 | vst->codecpar->codec_id = AV_CODEC_ID_MVC1; |
303 | break; |
304 | case 2: |
305 | vst->codecpar->format = AV_PIX_FMT_ARGB; |
306 | vst->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; |
307 | break; |
308 | default: |
309 | avpriv_request_sample(avctx, "Video compression %i", v); |
310 | break; |
311 | } |
312 | vst->codecpar->codec_tag = 0; |
313 | vst->codecpar->width = avio_rb32(pb); |
314 | vst->codecpar->height = avio_rb32(pb); |
315 | avio_skip(pb, 12); |
316 | |
317 | ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
318 | ast->nb_frames = vst->nb_frames; |
319 | ast->codecpar->sample_rate = avio_rb32(pb); |
320 | if (ast->codecpar->sample_rate <= 0) { |
321 | av_log(avctx, AV_LOG_ERROR, "Invalid sample rate %d\n", ast->codecpar->sample_rate); |
322 | return AVERROR_INVALIDDATA; |
323 | } |
324 | avpriv_set_pts_info(ast, 33, 1, ast->codecpar->sample_rate); |
325 | if (set_channels(avctx, ast, avio_rb32(pb)) < 0) |
326 | return AVERROR_INVALIDDATA; |
327 | |
328 | v = avio_rb32(pb); |
329 | if (v == AUDIO_FORMAT_SIGNED) { |
330 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; |
331 | } else { |
332 | avpriv_request_sample(avctx, "Audio compression (format %i)", v); |
333 | } |
334 | |
335 | avio_skip(pb, 12); |
336 | var_read_metadata(avctx, "title", 0x80); |
337 | var_read_metadata(avctx, "comment", 0x100); |
338 | avio_skip(pb, 0x80); |
339 | |
340 | timestamp = 0; |
341 | for (i = 0; i < vst->nb_frames; i++) { |
342 | uint32_t pos = avio_rb32(pb); |
343 | uint32_t asize = avio_rb32(pb); |
344 | uint32_t vsize = avio_rb32(pb); |
345 | avio_skip(pb, 8); |
346 | av_add_index_entry(ast, pos, timestamp, asize, 0, AVINDEX_KEYFRAME); |
347 | av_add_index_entry(vst, pos + asize, i, vsize, 0, AVINDEX_KEYFRAME); |
348 | timestamp += asize / (ast->codecpar->channels * 2); |
349 | } |
350 | } else if (!version && avio_rb16(pb) == 3) { |
351 | avio_skip(pb, 4); |
352 | |
353 | if ((ret = read_table(avctx, NULL, parse_global_var)) < 0) |
354 | return ret; |
355 | |
356 | if (mv->nb_audio_tracks > 1) { |
357 | avpriv_request_sample(avctx, "Multiple audio streams support"); |
358 | return AVERROR_PATCHWELCOME; |
359 | } else if (mv->nb_audio_tracks) { |
360 | ast = avformat_new_stream(avctx, NULL); |
361 | if (!ast) |
362 | return AVERROR(ENOMEM); |
363 | ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
364 | if ((read_table(avctx, ast, parse_audio_var)) < 0) |
365 | return ret; |
366 | if (mv->acompression == 100 && |
367 | mv->aformat == AUDIO_FORMAT_SIGNED && |
368 | ast->codecpar->bits_per_coded_sample == 16) { |
369 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; |
370 | } else { |
371 | avpriv_request_sample(avctx, |
372 | "Audio compression %i (format %i, sr %i)", |
373 | mv->acompression, mv->aformat, |
374 | ast->codecpar->bits_per_coded_sample); |
375 | ast->codecpar->codec_id = AV_CODEC_ID_NONE; |
376 | } |
377 | if (ast->codecpar->channels <= 0) { |
378 | av_log(avctx, AV_LOG_ERROR, "No valid channel count found.\n"); |
379 | return AVERROR_INVALIDDATA; |
380 | } |
381 | } |
382 | |
383 | if (mv->nb_video_tracks > 1) { |
384 | avpriv_request_sample(avctx, "Multiple video streams support"); |
385 | return AVERROR_PATCHWELCOME; |
386 | } else if (mv->nb_video_tracks) { |
387 | vst = avformat_new_stream(avctx, NULL); |
388 | if (!vst) |
389 | return AVERROR(ENOMEM); |
390 | vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
391 | if ((ret = read_table(avctx, vst, parse_video_var))<0) |
392 | return ret; |
393 | } |
394 | |
395 | if (mv->nb_audio_tracks) |
396 | read_index(pb, ast); |
397 | |
398 | if (mv->nb_video_tracks) |
399 | read_index(pb, vst); |
400 | } else { |
401 | avpriv_request_sample(avctx, "Version %i", version); |
402 | return AVERROR_PATCHWELCOME; |
403 | } |
404 | |
405 | return 0; |
406 | } |
407 | |
408 | static int mv_read_packet(AVFormatContext *avctx, AVPacket *pkt) |
409 | { |
410 | MvContext *mv = avctx->priv_data; |
411 | AVIOContext *pb = avctx->pb; |
412 | AVStream *st = avctx->streams[mv->stream_index]; |
413 | const AVIndexEntry *index; |
414 | int frame = mv->frame[mv->stream_index]; |
415 | int64_t ret; |
416 | uint64_t pos; |
417 | |
418 | if (frame < st->nb_index_entries) { |
419 | index = &st->index_entries[frame]; |
420 | pos = avio_tell(pb); |
421 | if (index->pos > pos) |
422 | avio_skip(pb, index->pos - pos); |
423 | else if (index->pos < pos) { |
424 | if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) |
425 | return AVERROR(EIO); |
426 | ret = avio_seek(pb, index->pos, SEEK_SET); |
427 | if (ret < 0) |
428 | return ret; |
429 | } |
430 | ret = av_get_packet(pb, pkt, index->size); |
431 | if (ret < 0) |
432 | return ret; |
433 | |
434 | pkt->stream_index = mv->stream_index; |
435 | pkt->pts = index->timestamp; |
436 | pkt->flags |= AV_PKT_FLAG_KEY; |
437 | |
438 | mv->frame[mv->stream_index]++; |
439 | mv->eof_count = 0; |
440 | } else { |
441 | mv->eof_count++; |
442 | if (mv->eof_count >= avctx->nb_streams) |
443 | return AVERROR_EOF; |
444 | |
445 | // avoid returning 0 without a packet |
446 | return AVERROR(EAGAIN); |
447 | } |
448 | |
449 | mv->stream_index++; |
450 | if (mv->stream_index >= avctx->nb_streams) |
451 | mv->stream_index = 0; |
452 | |
453 | return 0; |
454 | } |
455 | |
456 | static int mv_read_seek(AVFormatContext *avctx, int stream_index, |
457 | int64_t timestamp, int flags) |
458 | { |
459 | MvContext *mv = avctx->priv_data; |
460 | AVStream *st = avctx->streams[stream_index]; |
461 | int frame, i; |
462 | |
463 | if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) |
464 | return AVERROR(ENOSYS); |
465 | |
466 | if (!(avctx->pb->seekable & AVIO_SEEKABLE_NORMAL)) |
467 | return AVERROR(EIO); |
468 | |
469 | frame = av_index_search_timestamp(st, timestamp, flags); |
470 | if (frame < 0) |
471 | return AVERROR_INVALIDDATA; |
472 | |
473 | for (i = 0; i < avctx->nb_streams; i++) |
474 | mv->frame[i] = frame; |
475 | return 0; |
476 | } |
477 | |
478 | AVInputFormat ff_mv_demuxer = { |
479 | .name = "mv", |
480 | .long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"), |
481 | .priv_data_size = sizeof(MvContext), |
482 | .read_probe = mv_probe, |
483 | .read_header = mv_read_header, |
484 | .read_packet = mv_read_packet, |
485 | .read_seek = mv_read_seek, |
486 | }; |
487 |