2 * Silicon Graphics Movie demuxer
3 * Copyright (c) 2012 Peter Ross
5 * This file is part of FFmpeg.
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.
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.
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
24 * Silicon Graphics Movie demuxer
27 #include "libavutil/channel_layout.h"
28 #include "libavutil/eval.h"
29 #include "libavutil/intreadwrite.h"
30 #include "libavutil/rational.h"
35 typedef struct MvContext
{
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
43 int acompression
; ///< compression level for audio stream
44 int aformat
; ///< audio format
47 #define AUDIO_FORMAT_SIGNED 401
49 static int mv_probe(const AVProbeData
*p
)
51 if (AV_RB32(p
->buf
) == MKBETAG('M', 'O', 'V', 'I') &&
52 AV_RB16(p
->buf
+ 4) < 3)
53 return AVPROBE_SCORE_MAX
;
57 static char *var_read_string(AVIOContext
*pb
, int size
)
62 if (size
< 0 || size
== INT_MAX
)
65 str
= av_malloc(size
+ 1);
68 n
= avio_get_str(pb
, size
, str
, size
+ 1);
70 avio_skip(pb
, size
- n
);
74 static int var_read_int(AVIOContext
*pb
, int size
)
77 char *s
= var_read_string(pb
, size
);
80 v
= strtol(s
, NULL
, 10);
85 static AVRational
var_read_float(AVIOContext
*pb
, int size
)
88 char *s
= var_read_string(pb
, size
);
90 return (AVRational
) { 0, 0 };
91 v
= av_d2q(av_strtod(s
, NULL
), INT_MAX
);
96 static void var_read_metadata(AVFormatContext
*avctx
, const char *tag
, int size
)
98 char *value
= var_read_string(avctx
->pb
, size
);
100 av_dict_set(&avctx
->metadata
, tag
, value
, AV_DICT_DONT_STRDUP_VAL
);
103 static int set_channels(AVFormatContext
*avctx
, AVStream
*st
, int channels
)
106 av_log(avctx
, AV_LOG_ERROR
, "Channel count %d invalid.\n", channels
);
107 return AVERROR_INVALIDDATA
;
109 st
->codecpar
->channels
= channels
;
110 st
->codecpar
->channel_layout
= (st
->codecpar
->channels
== 1) ? AV_CH_LAYOUT_MONO
111 : AV_CH_LAYOUT_STEREO
;
116 * Parse global variable
117 * @return < 0 if unknown
119 static int parse_global_var(AVFormatContext
*avctx
, AVStream
*st
,
120 const char *name
, int size
)
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
134 return AVERROR_INVALIDDATA
;
140 * Parse audio variable
141 * @return < 0 if unknown
143 static int parse_audio_var(AVFormatContext
*avctx
, AVStream
*st
,
144 const char *name
, int size
)
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 int sample_rate
= var_read_int(pb
, size
);
160 if (sample_rate
<= 0)
161 return AVERROR_INVALIDDATA
;
162 st
->codecpar
->sample_rate
= sample_rate
;
163 avpriv_set_pts_info(st
, 33, 1, st
->codecpar
->sample_rate
);
164 } else if (!strcmp(name
, "SAMPLE_WIDTH")) {
165 uint64_t bpc
= var_read_int(pb
, size
) * (uint64_t)8;
167 return AVERROR_INVALIDDATA
;
168 st
->codecpar
->bits_per_coded_sample
= bpc
;
170 return AVERROR_INVALIDDATA
;
176 * Parse video variable
177 * @return < 0 if unknown
179 static int parse_video_var(AVFormatContext
*avctx
, AVStream
*st
,
180 const char *name
, int size
)
182 AVIOContext
*pb
= avctx
->pb
;
183 if (!strcmp(name
, "__DIR_COUNT")) {
184 st
->nb_frames
= st
->duration
= var_read_int(pb
, size
);
185 } else if (!strcmp(name
, "COMPRESSION")) {
186 char *str
= var_read_string(pb
, size
);
188 return AVERROR_INVALIDDATA
;
189 if (!strcmp(str
, "1")) {
190 st
->codecpar
->codec_id
= AV_CODEC_ID_MVC1
;
191 } else if (!strcmp(str
, "2")) {
192 st
->codecpar
->format
= AV_PIX_FMT_ABGR
;
193 st
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
194 } else if (!strcmp(str
, "3")) {
195 st
->codecpar
->codec_id
= AV_CODEC_ID_SGIRLE
;
196 } else if (!strcmp(str
, "10")) {
197 st
->codecpar
->codec_id
= AV_CODEC_ID_MJPEG
;
198 } else if (!strcmp(str
, "MVC2")) {
199 st
->codecpar
->codec_id
= AV_CODEC_ID_MVC2
;
201 avpriv_request_sample(avctx
, "Video compression %s", str
);
204 } else if (!strcmp(name
, "FPS")) {
205 AVRational fps
= var_read_float(pb
, size
);
206 avpriv_set_pts_info(st
, 64, fps
.den
, fps
.num
);
207 st
->avg_frame_rate
= fps
;
208 } else if (!strcmp(name
, "HEIGHT")) {
209 st
->codecpar
->height
= var_read_int(pb
, size
);
210 } else if (!strcmp(name
, "PIXEL_ASPECT")) {
211 st
->sample_aspect_ratio
= var_read_float(pb
, size
);
212 av_reduce(&st
->sample_aspect_ratio
.num
, &st
->sample_aspect_ratio
.den
,
213 st
->sample_aspect_ratio
.num
, st
->sample_aspect_ratio
.den
,
215 } else if (!strcmp(name
, "WIDTH")) {
216 st
->codecpar
->width
= var_read_int(pb
, size
);
217 } else if (!strcmp(name
, "ORIENTATION")) {
218 if (var_read_int(pb
, size
) == 1101) {
219 if (!st
->codecpar
->extradata
) {
220 st
->codecpar
->extradata
= av_strdup("BottomUp");
221 if (!st
->codecpar
->extradata
)
222 return AVERROR(ENOMEM
);
223 st
->codecpar
->extradata_size
= 9;
226 } else if (!strcmp(name
, "Q_SPATIAL") || !strcmp(name
, "Q_TEMPORAL")) {
227 var_read_metadata(avctx
, name
, size
);
228 } else if (!strcmp(name
, "INTERLACING") || !strcmp(name
, "PACKING")) {
229 avio_skip(pb
, size
); // ignore
231 return AVERROR_INVALIDDATA
;
236 static int read_table(AVFormatContext
*avctx
, AVStream
*st
,
237 int (*parse
)(AVFormatContext
*avctx
, AVStream
*st
,
238 const char *name
, int size
))
243 AVIOContext
*pb
= avctx
->pb
;
245 count
= avio_rb32(pb
);
247 for (i
= 0; i
< count
; i
++) {
254 if (avio_read(pb
, name
, 16) != 16)
255 return AVERROR_INVALIDDATA
;
256 name
[sizeof(name
) - 1] = 0;
257 size
= avio_rb32(pb
);
259 av_log(avctx
, AV_LOG_ERROR
, "entry size %d is invalid\n", size
);
260 return AVERROR_INVALIDDATA
;
262 if (parse(avctx
, st
, name
, size
) < 0) {
263 avpriv_request_sample(avctx
, "Variable %s", name
);
270 static void read_index(AVIOContext
*pb
, AVStream
*st
)
272 uint64_t timestamp
= 0;
274 for (i
= 0; i
< st
->nb_frames
; i
++) {
275 uint32_t pos
= avio_rb32(pb
);
276 uint32_t size
= avio_rb32(pb
);
280 av_add_index_entry(st
, pos
, timestamp
, size
, 0, AVINDEX_KEYFRAME
);
281 if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_AUDIO
) {
282 timestamp
+= size
/ (st
->codecpar
->channels
* 2LL);
289 static int mv_read_header(AVFormatContext
*avctx
)
291 MvContext
*mv
= avctx
->priv_data
;
292 AVIOContext
*pb
= avctx
->pb
;
293 AVStream
*ast
= NULL
, *vst
= NULL
; //initialization to suppress warning
299 version
= avio_rb16(pb
);
305 /* allocate audio track first to prevent unnecessary seeking
306 * (audio packet always precede video packet for a given frame) */
307 ast
= avformat_new_stream(avctx
, NULL
);
309 return AVERROR(ENOMEM
);
311 vst
= avformat_new_stream(avctx
, NULL
);
313 return AVERROR(ENOMEM
);
314 avpriv_set_pts_info(vst
, 64, 1, 15);
315 vst
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
316 vst
->avg_frame_rate
= av_inv_q(vst
->time_base
);
317 vst
->nb_frames
= avio_rb32(pb
);
321 vst
->codecpar
->codec_id
= AV_CODEC_ID_MVC1
;
324 vst
->codecpar
->format
= AV_PIX_FMT_ARGB
;
325 vst
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
328 avpriv_request_sample(avctx
, "Video compression %i", v
);
331 vst
->codecpar
->codec_tag
= 0;
332 vst
->codecpar
->width
= avio_rb32(pb
);
333 vst
->codecpar
->height
= avio_rb32(pb
);
336 ast
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
337 ast
->nb_frames
= vst
->nb_frames
;
338 ast
->codecpar
->sample_rate
= avio_rb32(pb
);
339 if (ast
->codecpar
->sample_rate
<= 0) {
340 av_log(avctx
, AV_LOG_ERROR
, "Invalid sample rate %d\n", ast
->codecpar
->sample_rate
);
341 return AVERROR_INVALIDDATA
;
343 avpriv_set_pts_info(ast
, 33, 1, ast
->codecpar
->sample_rate
);
344 if (set_channels(avctx
, ast
, avio_rb32(pb
)) < 0)
345 return AVERROR_INVALIDDATA
;
348 if (v
== AUDIO_FORMAT_SIGNED
) {
349 ast
->codecpar
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
351 avpriv_request_sample(avctx
, "Audio compression (format %i)", v
);
355 var_read_metadata(avctx
, "title", 0x80);
356 var_read_metadata(avctx
, "comment", 0x100);
360 for (i
= 0; i
< vst
->nb_frames
; i
++) {
361 uint32_t pos
= avio_rb32(pb
);
362 uint32_t asize
= avio_rb32(pb
);
363 uint32_t vsize
= avio_rb32(pb
);
365 return AVERROR_INVALIDDATA
;
367 av_add_index_entry(ast
, pos
, timestamp
, asize
, 0, AVINDEX_KEYFRAME
);
368 av_add_index_entry(vst
, pos
+ asize
, i
, vsize
, 0, AVINDEX_KEYFRAME
);
369 timestamp
+= asize
/ (ast
->codecpar
->channels
* 2LL);
371 } else if (!version
&& avio_rb16(pb
) == 3) {
374 if ((ret
= read_table(avctx
, NULL
, parse_global_var
)) < 0)
377 if (mv
->nb_audio_tracks
< 0 || mv
->nb_video_tracks
< 0 ||
378 (mv
->nb_audio_tracks
== 0 && mv
->nb_video_tracks
== 0)) {
379 av_log(avctx
, AV_LOG_ERROR
, "Stream count is invalid.\n");
380 return AVERROR_INVALIDDATA
;
383 if (mv
->nb_audio_tracks
> 1) {
384 avpriv_request_sample(avctx
, "Multiple audio streams support");
385 return AVERROR_PATCHWELCOME
;
386 } else if (mv
->nb_audio_tracks
) {
387 ast
= avformat_new_stream(avctx
, NULL
);
389 return AVERROR(ENOMEM
);
390 ast
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
391 if ((read_table(avctx
, ast
, parse_audio_var
)) < 0)
393 if (mv
->acompression
== 100 &&
394 mv
->aformat
== AUDIO_FORMAT_SIGNED
&&
395 ast
->codecpar
->bits_per_coded_sample
== 16) {
396 ast
->codecpar
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
398 avpriv_request_sample(avctx
,
399 "Audio compression %i (format %i, sr %i)",
400 mv
->acompression
, mv
->aformat
,
401 ast
->codecpar
->bits_per_coded_sample
);
402 ast
->codecpar
->codec_id
= AV_CODEC_ID_NONE
;
404 if (ast
->codecpar
->channels
<= 0) {
405 av_log(avctx
, AV_LOG_ERROR
, "No valid channel count found.\n");
406 return AVERROR_INVALIDDATA
;
410 if (mv
->nb_video_tracks
> 1) {
411 avpriv_request_sample(avctx
, "Multiple video streams support");
412 return AVERROR_PATCHWELCOME
;
413 } else if (mv
->nb_video_tracks
) {
414 vst
= avformat_new_stream(avctx
, NULL
);
416 return AVERROR(ENOMEM
);
417 vst
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
418 if ((ret
= read_table(avctx
, vst
, parse_video_var
))<0)
422 if (mv
->nb_audio_tracks
)
425 if (mv
->nb_video_tracks
)
428 avpriv_request_sample(avctx
, "Version %i", version
);
429 return AVERROR_PATCHWELCOME
;
435 static int mv_read_packet(AVFormatContext
*avctx
, AVPacket
*pkt
)
437 MvContext
*mv
= avctx
->priv_data
;
438 AVIOContext
*pb
= avctx
->pb
;
439 AVStream
*st
= avctx
->streams
[mv
->stream_index
];
440 const AVIndexEntry
*index
;
441 int frame
= mv
->frame
[mv
->stream_index
];
445 if (frame
< st
->nb_index_entries
) {
446 index
= &st
->index_entries
[frame
];
448 if (index
->pos
> pos
)
449 avio_skip(pb
, index
->pos
- pos
);
450 else if (index
->pos
< pos
) {
451 if (!(pb
->seekable
& AVIO_SEEKABLE_NORMAL
))
453 ret
= avio_seek(pb
, index
->pos
, SEEK_SET
);
457 ret
= av_get_packet(pb
, pkt
, index
->size
);
461 pkt
->stream_index
= mv
->stream_index
;
462 pkt
->pts
= index
->timestamp
;
463 pkt
->flags
|= AV_PKT_FLAG_KEY
;
465 mv
->frame
[mv
->stream_index
]++;
469 if (mv
->eof_count
>= avctx
->nb_streams
)
472 // avoid returning 0 without a packet
473 return AVERROR(EAGAIN
);
477 if (mv
->stream_index
>= avctx
->nb_streams
)
478 mv
->stream_index
= 0;
483 static int mv_read_seek(AVFormatContext
*avctx
, int stream_index
,
484 int64_t timestamp
, int flags
)
486 MvContext
*mv
= avctx
->priv_data
;
487 AVStream
*st
= avctx
->streams
[stream_index
];
490 if ((flags
& AVSEEK_FLAG_FRAME
) || (flags
& AVSEEK_FLAG_BYTE
))
491 return AVERROR(ENOSYS
);
493 if (!(avctx
->pb
->seekable
& AVIO_SEEKABLE_NORMAL
))
496 frame
= av_index_search_timestamp(st
, timestamp
, flags
);
498 return AVERROR_INVALIDDATA
;
500 for (i
= 0; i
< avctx
->nb_streams
; i
++)
501 mv
->frame
[i
] = frame
;
505 AVInputFormat ff_mv_demuxer
= {
507 .long_name
= NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"),
508 .priv_data_size
= sizeof(MvContext
),
509 .read_probe
= mv_probe
,
510 .read_header
= mv_read_header
,
511 .read_packet
= mv_read_packet
,
512 .read_seek
= mv_read_seek
,