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(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 st
->codecpar
->extradata
= av_strdup("BottomUp");
220 st
->codecpar
->extradata_size
= 9;
222 } else if (!strcmp(name
, "Q_SPATIAL") || !strcmp(name
, "Q_TEMPORAL")) {
223 var_read_metadata(avctx
, name
, size
);
224 } else if (!strcmp(name
, "INTERLACING") || !strcmp(name
, "PACKING")) {
225 avio_skip(pb
, size
); // ignore
227 return AVERROR_INVALIDDATA
;
232 static int read_table(AVFormatContext
*avctx
, AVStream
*st
,
233 int (*parse
)(AVFormatContext
*avctx
, AVStream
*st
,
234 const char *name
, int size
))
237 AVIOContext
*pb
= avctx
->pb
;
239 count
= avio_rb32(pb
);
241 for (i
= 0; i
< count
; i
++) {
244 avio_read(pb
, name
, 16);
245 name
[sizeof(name
) - 1] = 0;
246 size
= avio_rb32(pb
);
248 av_log(avctx
, AV_LOG_ERROR
, "entry size %d is invalid\n", size
);
249 return AVERROR_INVALIDDATA
;
251 if (parse(avctx
, st
, name
, size
) < 0) {
252 avpriv_request_sample(avctx
, "Variable %s", name
);
259 static void read_index(AVIOContext
*pb
, AVStream
*st
)
261 uint64_t timestamp
= 0;
263 for (i
= 0; i
< st
->nb_frames
; i
++) {
264 uint32_t pos
= avio_rb32(pb
);
265 uint32_t size
= avio_rb32(pb
);
269 av_add_index_entry(st
, pos
, timestamp
, size
, 0, AVINDEX_KEYFRAME
);
270 if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_AUDIO
) {
271 timestamp
+= size
/ (st
->codecpar
->channels
* 2LL);
278 static int mv_read_header(AVFormatContext
*avctx
)
280 MvContext
*mv
= avctx
->priv_data
;
281 AVIOContext
*pb
= avctx
->pb
;
282 AVStream
*ast
= NULL
, *vst
= NULL
; //initialization to suppress warning
288 version
= avio_rb16(pb
);
294 /* allocate audio track first to prevent unnecessary seeking
295 * (audio packet always precede video packet for a given frame) */
296 ast
= avformat_new_stream(avctx
, NULL
);
298 return AVERROR(ENOMEM
);
300 vst
= avformat_new_stream(avctx
, NULL
);
302 return AVERROR(ENOMEM
);
303 avpriv_set_pts_info(vst
, 64, 1, 15);
304 vst
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
305 vst
->avg_frame_rate
= av_inv_q(vst
->time_base
);
306 vst
->nb_frames
= avio_rb32(pb
);
310 vst
->codecpar
->codec_id
= AV_CODEC_ID_MVC1
;
313 vst
->codecpar
->format
= AV_PIX_FMT_ARGB
;
314 vst
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
317 avpriv_request_sample(avctx
, "Video compression %i", v
);
320 vst
->codecpar
->codec_tag
= 0;
321 vst
->codecpar
->width
= avio_rb32(pb
);
322 vst
->codecpar
->height
= avio_rb32(pb
);
325 ast
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
326 ast
->nb_frames
= vst
->nb_frames
;
327 ast
->codecpar
->sample_rate
= avio_rb32(pb
);
328 avpriv_set_pts_info(ast
, 33, 1, ast
->codecpar
->sample_rate
);
329 if (set_channels(avctx
, ast
, avio_rb32(pb
)) < 0)
330 return AVERROR_INVALIDDATA
;
333 if (v
== AUDIO_FORMAT_SIGNED
) {
334 ast
->codecpar
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
336 avpriv_request_sample(avctx
, "Audio compression (format %i)", v
);
340 var_read_metadata(avctx
, "title", 0x80);
341 var_read_metadata(avctx
, "comment", 0x100);
345 for (i
= 0; i
< vst
->nb_frames
; i
++) {
346 uint32_t pos
= avio_rb32(pb
);
347 uint32_t asize
= avio_rb32(pb
);
348 uint32_t vsize
= avio_rb32(pb
);
350 return AVERROR_INVALIDDATA
;
352 av_add_index_entry(ast
, pos
, timestamp
, asize
, 0, AVINDEX_KEYFRAME
);
353 av_add_index_entry(vst
, pos
+ asize
, i
, vsize
, 0, AVINDEX_KEYFRAME
);
354 timestamp
+= asize
/ (ast
->codecpar
->channels
* 2LL);
356 } else if (!version
&& avio_rb16(pb
) == 3) {
359 if ((ret
= read_table(avctx
, NULL
, parse_global_var
)) < 0)
362 if (mv
->nb_audio_tracks
< 0 || mv
->nb_video_tracks
< 0 ||
363 (mv
->nb_audio_tracks
== 0 && mv
->nb_video_tracks
== 0)) {
364 av_log(avctx
, AV_LOG_ERROR
, "Stream count is invalid.\n");
365 return AVERROR_INVALIDDATA
;
368 if (mv
->nb_audio_tracks
> 1) {
369 avpriv_request_sample(avctx
, "Multiple audio streams support");
370 return AVERROR_PATCHWELCOME
;
371 } else if (mv
->nb_audio_tracks
) {
372 ast
= avformat_new_stream(avctx
, NULL
);
374 return AVERROR(ENOMEM
);
375 ast
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
376 if ((read_table(avctx
, ast
, parse_audio_var
)) < 0)
378 if (mv
->acompression
== 100 &&
379 mv
->aformat
== AUDIO_FORMAT_SIGNED
&&
380 ast
->codecpar
->bits_per_coded_sample
== 16) {
381 ast
->codecpar
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
383 avpriv_request_sample(avctx
,
384 "Audio compression %i (format %i, sr %i)",
385 mv
->acompression
, mv
->aformat
,
386 ast
->codecpar
->bits_per_coded_sample
);
387 ast
->codecpar
->codec_id
= AV_CODEC_ID_NONE
;
389 if (ast
->codecpar
->channels
<= 0) {
390 av_log(avctx
, AV_LOG_ERROR
, "No valid channel count found.\n");
391 return AVERROR_INVALIDDATA
;
395 if (mv
->nb_video_tracks
> 1) {
396 avpriv_request_sample(avctx
, "Multiple video streams support");
397 return AVERROR_PATCHWELCOME
;
398 } else if (mv
->nb_video_tracks
) {
399 vst
= avformat_new_stream(avctx
, NULL
);
401 return AVERROR(ENOMEM
);
402 vst
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
403 if ((ret
= read_table(avctx
, vst
, parse_video_var
))<0)
407 if (mv
->nb_audio_tracks
)
410 if (mv
->nb_video_tracks
)
413 avpriv_request_sample(avctx
, "Version %i", version
);
414 return AVERROR_PATCHWELCOME
;
420 static int mv_read_packet(AVFormatContext
*avctx
, AVPacket
*pkt
)
422 MvContext
*mv
= avctx
->priv_data
;
423 AVIOContext
*pb
= avctx
->pb
;
424 AVStream
*st
= avctx
->streams
[mv
->stream_index
];
425 const AVIndexEntry
*index
;
426 int frame
= mv
->frame
[mv
->stream_index
];
430 if (frame
< st
->nb_index_entries
) {
431 index
= &st
->index_entries
[frame
];
433 if (index
->pos
> pos
)
434 avio_skip(pb
, index
->pos
- pos
);
435 else if (index
->pos
< pos
) {
438 ret
= avio_seek(pb
, index
->pos
, SEEK_SET
);
442 ret
= av_get_packet(pb
, pkt
, index
->size
);
446 pkt
->stream_index
= mv
->stream_index
;
447 pkt
->pts
= index
->timestamp
;
448 pkt
->flags
|= AV_PKT_FLAG_KEY
;
450 mv
->frame
[mv
->stream_index
]++;
454 if (mv
->eof_count
>= avctx
->nb_streams
)
457 // avoid returning 0 without a packet
458 return AVERROR(EAGAIN
);
462 if (mv
->stream_index
>= avctx
->nb_streams
)
463 mv
->stream_index
= 0;
468 static int mv_read_seek(AVFormatContext
*avctx
, int stream_index
,
469 int64_t timestamp
, int flags
)
471 MvContext
*mv
= avctx
->priv_data
;
472 AVStream
*st
= avctx
->streams
[stream_index
];
475 if ((flags
& AVSEEK_FLAG_FRAME
) || (flags
& AVSEEK_FLAG_BYTE
))
476 return AVERROR(ENOSYS
);
478 if (!avctx
->pb
->seekable
)
481 frame
= av_index_search_timestamp(st
, timestamp
, flags
);
483 return AVERROR_INVALIDDATA
;
485 for (i
= 0; i
< avctx
->nb_streams
; i
++)
486 mv
->frame
[i
] = frame
;
490 AVInputFormat ff_mv_demuxer
= {
492 .long_name
= NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"),
493 .priv_data_size
= sizeof(MvContext
),
494 .read_probe
= mv_probe
,
495 .read_header
= mv_read_header
,
496 .read_packet
= mv_read_packet
,
497 .read_seek
= mv_read_seek
,