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
)
60 char *str
= av_malloc(size
+ 1);
63 n
= avio_get_str(pb
, size
, str
, size
+ 1);
65 avio_skip(pb
, size
- n
);
69 static int var_read_int(AVIOContext
*pb
, int size
)
72 char *s
= var_read_string(pb
, size
);
75 v
= strtol(s
, NULL
, 10);
80 static AVRational
var_read_float(AVIOContext
*pb
, int size
)
83 char *s
= var_read_string(pb
, size
);
85 return (AVRational
) { 0, 0 };
86 v
= av_d2q(av_strtod(s
, NULL
), INT_MAX
);
91 static void var_read_metadata(AVFormatContext
*avctx
, const char *tag
, int size
)
93 char *value
= var_read_string(avctx
->pb
, size
);
95 av_dict_set(&avctx
->metadata
, tag
, value
, AV_DICT_DONT_STRDUP_VAL
);
98 static int set_channels(AVFormatContext
*avctx
, AVStream
*st
, int channels
)
101 av_log(avctx
, AV_LOG_ERROR
, "Channel count %d invalid.\n", channels
);
102 return AVERROR_INVALIDDATA
;
104 st
->codec
->channels
= channels
;
105 st
->codec
->channel_layout
= (st
->codec
->channels
== 1) ? AV_CH_LAYOUT_MONO
106 : AV_CH_LAYOUT_STEREO
;
111 * Parse global variable
112 * @return < 0 if unknown
114 static int parse_global_var(AVFormatContext
*avctx
, AVStream
*st
,
115 const char *name
, int size
)
117 MvContext
*mv
= avctx
->priv_data
;
118 AVIOContext
*pb
= avctx
->pb
;
119 if (!strcmp(name
, "__NUM_I_TRACKS")) {
120 mv
->nb_video_tracks
= var_read_int(pb
, size
);
121 } else if (!strcmp(name
, "__NUM_A_TRACKS")) {
122 mv
->nb_audio_tracks
= var_read_int(pb
, size
);
123 } else if (!strcmp(name
, "COMMENT") || !strcmp(name
, "TITLE")) {
124 var_read_metadata(avctx
, name
, size
);
125 } else if (!strcmp(name
, "LOOP_MODE") || !strcmp(name
, "NUM_LOOPS") ||
126 !strcmp(name
, "OPTIMIZED")) {
127 avio_skip(pb
, size
); // ignore
129 return AVERROR_INVALIDDATA
;
135 * Parse audio variable
136 * @return < 0 if unknown
138 static int parse_audio_var(AVFormatContext
*avctx
, AVStream
*st
,
139 const char *name
, int size
)
141 MvContext
*mv
= avctx
->priv_data
;
142 AVIOContext
*pb
= avctx
->pb
;
143 if (!strcmp(name
, "__DIR_COUNT")) {
144 st
->nb_frames
= var_read_int(pb
, size
);
145 } else if (!strcmp(name
, "AUDIO_FORMAT")) {
146 mv
->aformat
= var_read_int(pb
, size
);
147 } else if (!strcmp(name
, "COMPRESSION")) {
148 mv
->acompression
= var_read_int(pb
, size
);
149 } else if (!strcmp(name
, "DEFAULT_VOL")) {
150 var_read_metadata(avctx
, name
, size
);
151 } else if (!strcmp(name
, "NUM_CHANNELS")) {
152 return set_channels(avctx
, st
, var_read_int(pb
, size
));
153 } else if (!strcmp(name
, "SAMPLE_RATE")) {
154 st
->codec
->sample_rate
= var_read_int(pb
, size
);
155 avpriv_set_pts_info(st
, 33, 1, st
->codec
->sample_rate
);
156 } else if (!strcmp(name
, "SAMPLE_WIDTH")) {
157 st
->codec
->bits_per_coded_sample
= var_read_int(pb
, size
) * 8;
159 return AVERROR_INVALIDDATA
;
165 * Parse video variable
166 * @return < 0 if unknown
168 static int parse_video_var(AVFormatContext
*avctx
, AVStream
*st
,
169 const char *name
, int size
)
171 AVIOContext
*pb
= avctx
->pb
;
172 if (!strcmp(name
, "__DIR_COUNT")) {
173 st
->nb_frames
= st
->duration
= var_read_int(pb
, size
);
174 } else if (!strcmp(name
, "COMPRESSION")) {
175 char *str
= var_read_string(pb
, size
);
177 return AVERROR_INVALIDDATA
;
178 if (!strcmp(str
, "1")) {
179 st
->codec
->codec_id
= AV_CODEC_ID_MVC1
;
180 } else if (!strcmp(str
, "2")) {
181 st
->codec
->pix_fmt
= AV_PIX_FMT_ABGR
;
182 st
->codec
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
183 } else if (!strcmp(str
, "3")) {
184 st
->codec
->codec_id
= AV_CODEC_ID_SGIRLE
;
185 } else if (!strcmp(str
, "10")) {
186 st
->codec
->codec_id
= AV_CODEC_ID_MJPEG
;
187 } else if (!strcmp(str
, "MVC2")) {
188 st
->codec
->codec_id
= AV_CODEC_ID_MVC2
;
190 avpriv_request_sample(avctx
, "Video compression %s", str
);
193 } else if (!strcmp(name
, "FPS")) {
194 AVRational fps
= var_read_float(pb
, size
);
195 avpriv_set_pts_info(st
, 64, fps
.den
, fps
.num
);
196 st
->avg_frame_rate
= fps
;
197 } else if (!strcmp(name
, "HEIGHT")) {
198 st
->codec
->height
= var_read_int(pb
, size
);
199 } else if (!strcmp(name
, "PIXEL_ASPECT")) {
200 st
->sample_aspect_ratio
= var_read_float(pb
, size
);
201 av_reduce(&st
->sample_aspect_ratio
.num
, &st
->sample_aspect_ratio
.den
,
202 st
->sample_aspect_ratio
.num
, st
->sample_aspect_ratio
.den
,
204 } else if (!strcmp(name
, "WIDTH")) {
205 st
->codec
->width
= var_read_int(pb
, size
);
206 } else if (!strcmp(name
, "ORIENTATION")) {
207 if (var_read_int(pb
, size
) == 1101) {
208 st
->codec
->extradata
= av_strdup("BottomUp");
209 st
->codec
->extradata_size
= 9;
211 } else if (!strcmp(name
, "Q_SPATIAL") || !strcmp(name
, "Q_TEMPORAL")) {
212 var_read_metadata(avctx
, name
, size
);
213 } else if (!strcmp(name
, "INTERLACING") || !strcmp(name
, "PACKING")) {
214 avio_skip(pb
, size
); // ignore
216 return AVERROR_INVALIDDATA
;
221 static void read_table(AVFormatContext
*avctx
, AVStream
*st
,
222 int (*parse
)(AVFormatContext
*avctx
, AVStream
*st
,
223 const char *name
, int size
))
226 AVIOContext
*pb
= avctx
->pb
;
228 count
= avio_rb32(pb
);
230 for (i
= 0; i
< count
; i
++) {
233 avio_read(pb
, name
, 16);
234 name
[sizeof(name
) - 1] = 0;
235 size
= avio_rb32(pb
);
236 if (parse(avctx
, st
, name
, size
) < 0) {
237 avpriv_request_sample(avctx
, "Variable %s", name
);
243 static void read_index(AVIOContext
*pb
, AVStream
*st
)
245 uint64_t timestamp
= 0;
247 for (i
= 0; i
< st
->nb_frames
; i
++) {
248 uint32_t pos
= avio_rb32(pb
);
249 uint32_t size
= avio_rb32(pb
);
251 av_add_index_entry(st
, pos
, timestamp
, size
, 0, AVINDEX_KEYFRAME
);
252 if (st
->codec
->codec_type
== AVMEDIA_TYPE_AUDIO
) {
253 timestamp
+= size
/ (st
->codec
->channels
* 2);
260 static int mv_read_header(AVFormatContext
*avctx
)
262 MvContext
*mv
= avctx
->priv_data
;
263 AVIOContext
*pb
= avctx
->pb
;
264 AVStream
*ast
= NULL
, *vst
= NULL
; //initialization to suppress warning
269 version
= avio_rb16(pb
);
275 /* allocate audio track first to prevent unnecessary seeking
276 * (audio packet always precede video packet for a given frame) */
277 ast
= avformat_new_stream(avctx
, NULL
);
279 return AVERROR(ENOMEM
);
281 vst
= avformat_new_stream(avctx
, NULL
);
283 return AVERROR(ENOMEM
);
284 avpriv_set_pts_info(vst
, 64, 1, 15);
285 vst
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
286 vst
->avg_frame_rate
= av_inv_q(vst
->time_base
);
287 vst
->nb_frames
= avio_rb32(pb
);
291 vst
->codec
->codec_id
= AV_CODEC_ID_MVC1
;
294 vst
->codec
->pix_fmt
= AV_PIX_FMT_ARGB
;
295 vst
->codec
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
298 avpriv_request_sample(avctx
, "Video compression %i", v
);
301 vst
->codec
->codec_tag
= 0;
302 vst
->codec
->width
= avio_rb32(pb
);
303 vst
->codec
->height
= avio_rb32(pb
);
306 ast
->codec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
307 ast
->nb_frames
= vst
->nb_frames
;
308 ast
->codec
->sample_rate
= avio_rb32(pb
);
309 avpriv_set_pts_info(ast
, 33, 1, ast
->codec
->sample_rate
);
310 if (set_channels(avctx
, ast
, avio_rb32(pb
)) < 0)
311 return AVERROR_INVALIDDATA
;
314 if (v
== AUDIO_FORMAT_SIGNED
) {
315 ast
->codec
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
317 avpriv_request_sample(avctx
, "Audio compression (format %i)", v
);
321 var_read_metadata(avctx
, "title", 0x80);
322 var_read_metadata(avctx
, "comment", 0x100);
326 for (i
= 0; i
< vst
->nb_frames
; i
++) {
327 uint32_t pos
= avio_rb32(pb
);
328 uint32_t asize
= avio_rb32(pb
);
329 uint32_t vsize
= avio_rb32(pb
);
331 av_add_index_entry(ast
, pos
, timestamp
, asize
, 0, AVINDEX_KEYFRAME
);
332 av_add_index_entry(vst
, pos
+ asize
, i
, vsize
, 0, AVINDEX_KEYFRAME
);
333 timestamp
+= asize
/ (ast
->codec
->channels
* 2);
335 } else if (!version
&& avio_rb16(pb
) == 3) {
338 read_table(avctx
, NULL
, parse_global_var
);
340 if (mv
->nb_audio_tracks
> 1) {
341 avpriv_request_sample(avctx
, "Multiple audio streams support");
342 return AVERROR_PATCHWELCOME
;
343 } else if (mv
->nb_audio_tracks
) {
344 ast
= avformat_new_stream(avctx
, NULL
);
346 return AVERROR(ENOMEM
);
347 ast
->codec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
348 read_table(avctx
, ast
, parse_audio_var
);
349 if (mv
->acompression
== 100 &&
350 mv
->aformat
== AUDIO_FORMAT_SIGNED
&&
351 ast
->codec
->bits_per_coded_sample
== 16) {
352 ast
->codec
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
354 avpriv_request_sample(avctx
,
355 "Audio compression %i (format %i, sr %i)",
356 mv
->acompression
, mv
->aformat
,
357 ast
->codec
->bits_per_coded_sample
);
358 ast
->codec
->codec_id
= AV_CODEC_ID_NONE
;
360 if (ast
->codec
->channels
<= 0) {
361 av_log(avctx
, AV_LOG_ERROR
, "No valid channel count found.\n");
362 return AVERROR_INVALIDDATA
;
366 if (mv
->nb_video_tracks
> 1) {
367 avpriv_request_sample(avctx
, "Multiple video streams support");
368 return AVERROR_PATCHWELCOME
;
369 } else if (mv
->nb_video_tracks
) {
370 vst
= avformat_new_stream(avctx
, NULL
);
372 return AVERROR(ENOMEM
);
373 vst
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
374 read_table(avctx
, vst
, parse_video_var
);
377 if (mv
->nb_audio_tracks
)
380 if (mv
->nb_video_tracks
)
383 avpriv_request_sample(avctx
, "Version %i", version
);
384 return AVERROR_PATCHWELCOME
;
390 static int mv_read_packet(AVFormatContext
*avctx
, AVPacket
*pkt
)
392 MvContext
*mv
= avctx
->priv_data
;
393 AVIOContext
*pb
= avctx
->pb
;
394 AVStream
*st
= avctx
->streams
[mv
->stream_index
];
395 const AVIndexEntry
*index
;
396 int frame
= mv
->frame
[mv
->stream_index
];
400 if (frame
< st
->nb_index_entries
) {
401 index
= &st
->index_entries
[frame
];
403 if (index
->pos
> pos
)
404 avio_skip(pb
, index
->pos
- pos
);
405 else if (index
->pos
< pos
) {
408 ret
= avio_seek(pb
, index
->pos
, SEEK_SET
);
412 ret
= av_get_packet(pb
, pkt
, index
->size
);
416 pkt
->stream_index
= mv
->stream_index
;
417 pkt
->pts
= index
->timestamp
;
418 pkt
->flags
|= AV_PKT_FLAG_KEY
;
420 mv
->frame
[mv
->stream_index
]++;
424 if (mv
->eof_count
>= avctx
->nb_streams
)
427 // avoid returning 0 without a packet
428 return AVERROR(EAGAIN
);
432 if (mv
->stream_index
>= avctx
->nb_streams
)
433 mv
->stream_index
= 0;
438 static int mv_read_seek(AVFormatContext
*avctx
, int stream_index
,
439 int64_t timestamp
, int flags
)
441 MvContext
*mv
= avctx
->priv_data
;
442 AVStream
*st
= avctx
->streams
[stream_index
];
445 if ((flags
& AVSEEK_FLAG_FRAME
) || (flags
& AVSEEK_FLAG_BYTE
))
446 return AVERROR(ENOSYS
);
448 if (!avctx
->pb
->seekable
)
451 frame
= av_index_search_timestamp(st
, timestamp
, flags
);
453 return AVERROR_INVALIDDATA
;
455 for (i
= 0; i
< avctx
->nb_streams
; i
++)
456 mv
->frame
[i
] = frame
;
460 AVInputFormat ff_mv_demuxer
= {
462 .long_name
= NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"),
463 .priv_data_size
= sizeof(MvContext
),
464 .read_probe
= mv_probe
,
465 .read_header
= mv_read_header
,
466 .read_packet
= mv_read_packet
,
467 .read_seek
= mv_read_seek
,