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
->codec
->channels
= channels
;
110 st
->codec
->channel_layout
= (st
->codec
->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 st
->codec
->sample_rate
= var_read_int(pb
, size
);
160 avpriv_set_pts_info(st
, 33, 1, st
->codec
->sample_rate
);
161 } else if (!strcmp(name
, "SAMPLE_WIDTH")) {
162 st
->codec
->bits_per_coded_sample
= var_read_int(pb
, size
) * 8;
164 return AVERROR_INVALIDDATA
;
170 * Parse video variable
171 * @return < 0 if unknown
173 static int parse_video_var(AVFormatContext
*avctx
, AVStream
*st
,
174 const char *name
, int size
)
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
);
182 return AVERROR_INVALIDDATA
;
183 if (!strcmp(str
, "1")) {
184 st
->codec
->codec_id
= AV_CODEC_ID_MVC1
;
185 } else if (!strcmp(str
, "2")) {
186 st
->codec
->pix_fmt
= AV_PIX_FMT_ABGR
;
187 st
->codec
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
188 } else if (!strcmp(str
, "3")) {
189 st
->codec
->codec_id
= AV_CODEC_ID_SGIRLE
;
190 } else if (!strcmp(str
, "10")) {
191 st
->codec
->codec_id
= AV_CODEC_ID_MJPEG
;
192 } else if (!strcmp(str
, "MVC2")) {
193 st
->codec
->codec_id
= AV_CODEC_ID_MVC2
;
195 avpriv_request_sample(avctx
, "Video compression %s", 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
->codec
->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
,
209 } else if (!strcmp(name
, "WIDTH")) {
210 st
->codec
->width
= var_read_int(pb
, size
);
211 } else if (!strcmp(name
, "ORIENTATION")) {
212 if (var_read_int(pb
, size
) == 1101) {
213 st
->codec
->extradata
= av_strdup("BottomUp");
214 st
->codec
->extradata_size
= 9;
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
221 return AVERROR_INVALIDDATA
;
226 static int read_table(AVFormatContext
*avctx
, AVStream
*st
,
227 int (*parse
)(AVFormatContext
*avctx
, AVStream
*st
,
228 const char *name
, int size
))
231 AVIOContext
*pb
= avctx
->pb
;
233 count
= avio_rb32(pb
);
235 for (i
= 0; i
< count
; i
++) {
238 avio_read(pb
, name
, 16);
239 name
[sizeof(name
) - 1] = 0;
240 size
= avio_rb32(pb
);
242 av_log(avctx
, AV_LOG_ERROR
, "entry size %d is invalid\n", size
);
243 return AVERROR_INVALIDDATA
;
245 if (parse(avctx
, st
, name
, size
) < 0) {
246 avpriv_request_sample(avctx
, "Variable %s", name
);
253 static void read_index(AVIOContext
*pb
, AVStream
*st
)
255 uint64_t timestamp
= 0;
257 for (i
= 0; i
< st
->nb_frames
; i
++) {
258 uint32_t pos
= avio_rb32(pb
);
259 uint32_t size
= avio_rb32(pb
);
261 av_add_index_entry(st
, pos
, timestamp
, size
, 0, AVINDEX_KEYFRAME
);
262 if (st
->codec
->codec_type
== AVMEDIA_TYPE_AUDIO
) {
263 timestamp
+= size
/ (st
->codec
->channels
* 2);
270 static int mv_read_header(AVFormatContext
*avctx
)
272 MvContext
*mv
= avctx
->priv_data
;
273 AVIOContext
*pb
= avctx
->pb
;
274 AVStream
*ast
= NULL
, *vst
= NULL
; //initialization to suppress warning
280 version
= avio_rb16(pb
);
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
);
290 return AVERROR(ENOMEM
);
292 vst
= avformat_new_stream(avctx
, NULL
);
294 return AVERROR(ENOMEM
);
295 avpriv_set_pts_info(vst
, 64, 1, 15);
296 vst
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
297 vst
->avg_frame_rate
= av_inv_q(vst
->time_base
);
298 vst
->nb_frames
= avio_rb32(pb
);
302 vst
->codec
->codec_id
= AV_CODEC_ID_MVC1
;
305 vst
->codec
->pix_fmt
= AV_PIX_FMT_ARGB
;
306 vst
->codec
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
309 avpriv_request_sample(avctx
, "Video compression %i", v
);
312 vst
->codec
->codec_tag
= 0;
313 vst
->codec
->width
= avio_rb32(pb
);
314 vst
->codec
->height
= avio_rb32(pb
);
317 ast
->codec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
318 ast
->nb_frames
= vst
->nb_frames
;
319 ast
->codec
->sample_rate
= avio_rb32(pb
);
320 avpriv_set_pts_info(ast
, 33, 1, ast
->codec
->sample_rate
);
321 if (set_channels(avctx
, ast
, avio_rb32(pb
)) < 0)
322 return AVERROR_INVALIDDATA
;
325 if (v
== AUDIO_FORMAT_SIGNED
) {
326 ast
->codec
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
328 avpriv_request_sample(avctx
, "Audio compression (format %i)", v
);
332 var_read_metadata(avctx
, "title", 0x80);
333 var_read_metadata(avctx
, "comment", 0x100);
337 for (i
= 0; i
< vst
->nb_frames
; i
++) {
338 uint32_t pos
= avio_rb32(pb
);
339 uint32_t asize
= avio_rb32(pb
);
340 uint32_t vsize
= avio_rb32(pb
);
342 av_add_index_entry(ast
, pos
, timestamp
, asize
, 0, AVINDEX_KEYFRAME
);
343 av_add_index_entry(vst
, pos
+ asize
, i
, vsize
, 0, AVINDEX_KEYFRAME
);
344 timestamp
+= asize
/ (ast
->codec
->channels
* 2);
346 } else if (!version
&& avio_rb16(pb
) == 3) {
349 if ((ret
= read_table(avctx
, NULL
, parse_global_var
)) < 0)
352 if (mv
->nb_audio_tracks
> 1) {
353 avpriv_request_sample(avctx
, "Multiple audio streams support");
354 return AVERROR_PATCHWELCOME
;
355 } else if (mv
->nb_audio_tracks
) {
356 ast
= avformat_new_stream(avctx
, NULL
);
358 return AVERROR(ENOMEM
);
359 ast
->codec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
360 if ((read_table(avctx
, ast
, parse_audio_var
)) < 0)
362 if (mv
->acompression
== 100 &&
363 mv
->aformat
== AUDIO_FORMAT_SIGNED
&&
364 ast
->codec
->bits_per_coded_sample
== 16) {
365 ast
->codec
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
367 avpriv_request_sample(avctx
,
368 "Audio compression %i (format %i, sr %i)",
369 mv
->acompression
, mv
->aformat
,
370 ast
->codec
->bits_per_coded_sample
);
371 ast
->codec
->codec_id
= AV_CODEC_ID_NONE
;
373 if (ast
->codec
->channels
<= 0) {
374 av_log(avctx
, AV_LOG_ERROR
, "No valid channel count found.\n");
375 return AVERROR_INVALIDDATA
;
379 if (mv
->nb_video_tracks
> 1) {
380 avpriv_request_sample(avctx
, "Multiple video streams support");
381 return AVERROR_PATCHWELCOME
;
382 } else if (mv
->nb_video_tracks
) {
383 vst
= avformat_new_stream(avctx
, NULL
);
385 return AVERROR(ENOMEM
);
386 vst
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
387 if ((ret
= read_table(avctx
, vst
, parse_video_var
))<0)
391 if (mv
->nb_audio_tracks
)
394 if (mv
->nb_video_tracks
)
397 avpriv_request_sample(avctx
, "Version %i", version
);
398 return AVERROR_PATCHWELCOME
;
404 static int mv_read_packet(AVFormatContext
*avctx
, AVPacket
*pkt
)
406 MvContext
*mv
= avctx
->priv_data
;
407 AVIOContext
*pb
= avctx
->pb
;
408 AVStream
*st
= avctx
->streams
[mv
->stream_index
];
409 const AVIndexEntry
*index
;
410 int frame
= mv
->frame
[mv
->stream_index
];
414 if (frame
< st
->nb_index_entries
) {
415 index
= &st
->index_entries
[frame
];
417 if (index
->pos
> pos
)
418 avio_skip(pb
, index
->pos
- pos
);
419 else if (index
->pos
< pos
) {
422 ret
= avio_seek(pb
, index
->pos
, SEEK_SET
);
426 ret
= av_get_packet(pb
, pkt
, index
->size
);
430 pkt
->stream_index
= mv
->stream_index
;
431 pkt
->pts
= index
->timestamp
;
432 pkt
->flags
|= AV_PKT_FLAG_KEY
;
434 mv
->frame
[mv
->stream_index
]++;
438 if (mv
->eof_count
>= avctx
->nb_streams
)
441 // avoid returning 0 without a packet
442 return AVERROR(EAGAIN
);
446 if (mv
->stream_index
>= avctx
->nb_streams
)
447 mv
->stream_index
= 0;
452 static int mv_read_seek(AVFormatContext
*avctx
, int stream_index
,
453 int64_t timestamp
, int flags
)
455 MvContext
*mv
= avctx
->priv_data
;
456 AVStream
*st
= avctx
->streams
[stream_index
];
459 if ((flags
& AVSEEK_FLAG_FRAME
) || (flags
& AVSEEK_FLAG_BYTE
))
460 return AVERROR(ENOSYS
);
462 if (!avctx
->pb
->seekable
)
465 frame
= av_index_search_timestamp(st
, timestamp
, flags
);
467 return AVERROR_INVALIDDATA
;
469 for (i
= 0; i
< avctx
->nb_streams
; i
++)
470 mv
->frame
[i
] = frame
;
474 AVInputFormat ff_mv_demuxer
= {
476 .long_name
= NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"),
477 .priv_data_size
= sizeof(MvContext
),
478 .read_probe
= mv_probe
,
479 .read_header
= mv_read_header
,
480 .read_packet
= mv_read_packet
,
481 .read_seek
= mv_read_seek
,