| Line | Branch | Exec | Source |
|---|---|---|---|
| 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/intfloat.h" | ||
| 30 | #include "libavutil/intreadwrite.h" | ||
| 31 | #include "libavutil/mem.h" | ||
| 32 | #include "libavutil/rational.h" | ||
| 33 | |||
| 34 | #include "avformat.h" | ||
| 35 | #include "demux.h" | ||
| 36 | #include "internal.h" | ||
| 37 | |||
| 38 | typedef struct MvContext { | ||
| 39 | int nb_video_tracks; | ||
| 40 | int nb_audio_tracks; | ||
| 41 | |||
| 42 | int eof_count; ///< number of streams that have finished | ||
| 43 | int stream_index; ///< current stream index | ||
| 44 | int frame[2]; ///< frame nb for current stream | ||
| 45 | |||
| 46 | int acompression; ///< compression level for audio stream | ||
| 47 | int aformat; ///< audio format | ||
| 48 | } MvContext; | ||
| 49 | |||
| 50 | /* these magic numbers are defined in moviefile.h on Silicon Graphics IRIX */ | ||
| 51 | #define MOVIE_SOUND 1 | ||
| 52 | #define MOVIE_SILENT 2 | ||
| 53 | |||
| 54 | #define AUDIO_FORMAT_SIGNED 401 | ||
| 55 | |||
| 56 | 7474 | static int mv_probe(const AVProbeData *p) | |
| 57 | { | ||
| 58 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7471 times.
|
7474 | if (AV_RB32(p->buf) == MKBETAG('M', 'O', 'V', 'I') && |
| 59 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | AV_RB16(p->buf + 4) < 3) |
| 60 | 3 | return AVPROBE_SCORE_MAX; | |
| 61 | 7471 | return 0; | |
| 62 | } | ||
| 63 | |||
| 64 | 42 | static char *var_read_string(AVIOContext *pb, int size) | |
| 65 | { | ||
| 66 | int n; | ||
| 67 | char *str; | ||
| 68 | |||
| 69 |
2/4✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
|
42 | if (size < 0 || size == INT_MAX) |
| 70 | ✗ | return NULL; | |
| 71 | |||
| 72 | 42 | str = av_malloc(size + 1); | |
| 73 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
|
42 | if (!str) |
| 74 | ✗ | return NULL; | |
| 75 | 42 | n = avio_get_str(pb, size, str, size + 1); | |
| 76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
|
42 | if (n < size) |
| 77 | ✗ | avio_skip(pb, size - n); | |
| 78 | 42 | return str; | |
| 79 | } | ||
| 80 | |||
| 81 | 30 | static int var_read_int(AVIOContext *pb, int size) | |
| 82 | { | ||
| 83 | int v; | ||
| 84 | 30 | char *s = var_read_string(pb, size); | |
| 85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | if (!s) |
| 86 | ✗ | return 0; | |
| 87 | 30 | v = strtol(s, NULL, 10); | |
| 88 | 30 | av_free(s); | |
| 89 | 30 | return v; | |
| 90 | } | ||
| 91 | |||
| 92 | 5 | static AVRational var_read_float(AVIOContext *pb, int size) | |
| 93 | { | ||
| 94 | AVRational v; | ||
| 95 | 5 | char *s = var_read_string(pb, size); | |
| 96 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (!s) |
| 97 | ✗ | return (AVRational) { 0, 0 }; | |
| 98 | 5 | v = av_d2q(av_strtod(s, NULL), INT_MAX); | |
| 99 | 5 | av_free(s); | |
| 100 | 5 | return v; | |
| 101 | } | ||
| 102 | |||
| 103 | 4 | static void var_read_metadata(AVFormatContext *avctx, const char *tag, int size) | |
| 104 | { | ||
| 105 | 4 | char *value = var_read_string(avctx->pb, size); | |
| 106 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (value) |
| 107 | 4 | av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL); | |
| 108 | 4 | } | |
| 109 | |||
| 110 | 2 | static int set_channels(AVFormatContext *avctx, AVStream *st, int channels) | |
| 111 | { | ||
| 112 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (channels <= 0) { |
| 113 | ✗ | av_log(avctx, AV_LOG_ERROR, "Channel count %d invalid.\n", channels); | |
| 114 | ✗ | return AVERROR_INVALIDDATA; | |
| 115 | } | ||
| 116 | 2 | av_channel_layout_default(&st->codecpar->ch_layout, channels); | |
| 117 | 2 | return 0; | |
| 118 | } | ||
| 119 | |||
| 120 | /** | ||
| 121 | * Parse global variable | ||
| 122 | * @return < 0 if unknown | ||
| 123 | */ | ||
| 124 | 15 | static int parse_global_var(AVFormatContext *avctx, AVStream *st, | |
| 125 | const char *name, int size) | ||
| 126 | { | ||
| 127 | 15 | MvContext *mv = avctx->priv_data; | |
| 128 | 15 | AVIOContext *pb = avctx->pb; | |
| 129 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 12 times.
|
15 | if (!strcmp(name, "__NUM_I_TRACKS")) { |
| 130 | 3 | mv->nb_video_tracks = var_read_int(pb, size); | |
| 131 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
|
12 | } else if (!strcmp(name, "__NUM_A_TRACKS")) { |
| 132 | 3 | mv->nb_audio_tracks = var_read_int(pb, size); | |
| 133 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
9 | } else if (!strcmp(name, "COMMENT") || !strcmp(name, "TITLE")) { |
| 134 | ✗ | var_read_metadata(avctx, name, size); | |
| 135 |
4/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
|
9 | } else if (!strcmp(name, "LOOP_MODE") || !strcmp(name, "NUM_LOOPS") || |
| 136 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | !strcmp(name, "OPTIMIZED")) { |
| 137 | 9 | avio_skip(pb, size); // ignore | |
| 138 | } else | ||
| 139 | ✗ | return AVERROR_INVALIDDATA; | |
| 140 | |||
| 141 | 15 | return 0; | |
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Parse audio variable | ||
| 146 | * @return < 0 if unknown | ||
| 147 | */ | ||
| 148 | 12 | static int parse_audio_var(AVFormatContext *avctx, AVStream *st, | |
| 149 | const char *name, int size) | ||
| 150 | { | ||
| 151 | 12 | MvContext *mv = avctx->priv_data; | |
| 152 | 12 | AVIOContext *pb = avctx->pb; | |
| 153 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
|
12 | if (!strcmp(name, "__DIR_COUNT")) { |
| 154 | 2 | st->nb_frames = var_read_int(pb, size); | |
| 155 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
|
10 | } else if (!strcmp(name, "AUDIO_FORMAT")) { |
| 156 | 2 | mv->aformat = var_read_int(pb, size); | |
| 157 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
|
8 | } else if (!strcmp(name, "COMPRESSION")) { |
| 158 | 2 | mv->acompression = var_read_int(pb, size); | |
| 159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | } else if (!strcmp(name, "DEFAULT_VOL")) { |
| 160 | ✗ | var_read_metadata(avctx, name, size); | |
| 161 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | } else if (!strcmp(name, "NUM_CHANNELS")) { |
| 162 | 2 | return set_channels(avctx, st, var_read_int(pb, size)); | |
| 163 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | } else if (!strcmp(name, "SAMPLE_RATE")) { |
| 164 | 2 | int sample_rate = var_read_int(pb, size); | |
| 165 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (sample_rate <= 0) |
| 166 | ✗ | return AVERROR_INVALIDDATA; | |
| 167 | 2 | st->codecpar->sample_rate = sample_rate; | |
| 168 | 2 | avpriv_set_pts_info(st, 33, 1, st->codecpar->sample_rate); | |
| 169 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | } else if (!strcmp(name, "SAMPLE_WIDTH")) { |
| 170 | 2 | uint64_t bpc = var_read_int(pb, size) * (uint64_t)8; | |
| 171 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (bpc > 16) |
| 172 | ✗ | return AVERROR_INVALIDDATA; | |
| 173 | 2 | st->codecpar->bits_per_coded_sample = bpc; | |
| 174 | } else | ||
| 175 | ✗ | return AVERROR_INVALIDDATA; | |
| 176 | |||
| 177 | 10 | return 0; | |
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * Parse video variable | ||
| 182 | * @return < 0 if unknown | ||
| 183 | */ | ||
| 184 | 30 | static int parse_video_var(AVFormatContext *avctx, AVStream *st, | |
| 185 | const char *name, int size) | ||
| 186 | { | ||
| 187 | 30 | AVIOContext *pb = avctx->pb; | |
| 188 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 27 times.
|
30 | if (!strcmp(name, "__DIR_COUNT")) { |
| 189 | 3 | st->nb_frames = st->duration = var_read_int(pb, size); | |
| 190 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 24 times.
|
27 | } else if (!strcmp(name, "COMPRESSION")) { |
| 191 | 3 | char *str = var_read_string(pb, size); | |
| 192 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (!str) |
| 193 | ✗ | return AVERROR_INVALIDDATA; | |
| 194 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (!strcmp(str, "1")) { |
| 195 | 1 | st->codecpar->codec_id = AV_CODEC_ID_MVC1; | |
| 196 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | } else if (!strcmp(str, "2")) { |
| 197 | ✗ | st->codecpar->format = AV_PIX_FMT_ABGR; | |
| 198 | ✗ | st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; | |
| 199 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | } else if (!strcmp(str, "3")) { |
| 200 | 1 | st->codecpar->codec_id = AV_CODEC_ID_SGIRLE; | |
| 201 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | } else if (!strcmp(str, "10")) { |
| 202 | ✗ | st->codecpar->codec_id = AV_CODEC_ID_MJPEG; | |
| 203 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | } else if (!strcmp(str, "MVC2")) { |
| 204 | 1 | st->codecpar->codec_id = AV_CODEC_ID_MVC2; | |
| 205 | } else { | ||
| 206 | ✗ | avpriv_request_sample(avctx, "Video compression %s", str); | |
| 207 | } | ||
| 208 | 3 | av_free(str); | |
| 209 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21 times.
|
24 | } else if (!strcmp(name, "FPS")) { |
| 210 | 3 | AVRational fps = var_read_float(pb, size); | |
| 211 | 3 | avpriv_set_pts_info(st, 64, fps.den, fps.num); | |
| 212 | 3 | st->avg_frame_rate = fps; | |
| 213 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 18 times.
|
21 | } else if (!strcmp(name, "HEIGHT")) { |
| 214 | 3 | st->codecpar->height = var_read_int(pb, size); | |
| 215 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
|
18 | } else if (!strcmp(name, "PIXEL_ASPECT")) { |
| 216 | 2 | st->sample_aspect_ratio = var_read_float(pb, size); | |
| 217 | 2 | av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, | |
| 218 | 2 | st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, | |
| 219 | INT_MAX); | ||
| 220 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
|
16 | } else if (!strcmp(name, "WIDTH")) { |
| 221 | 3 | st->codecpar->width = var_read_int(pb, size); | |
| 222 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
|
13 | } else if (!strcmp(name, "ORIENTATION")) { |
| 223 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
|
3 | if (var_read_int(pb, size) == 1101) { |
| 224 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (!st->codecpar->extradata) { |
| 225 | 2 | st->codecpar->extradata = av_strdup("BottomUp"); | |
| 226 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!st->codecpar->extradata) |
| 227 | ✗ | return AVERROR(ENOMEM); | |
| 228 | 2 | st->codecpar->extradata_size = 9; | |
| 229 | } | ||
| 230 | } | ||
| 231 |
4/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
|
10 | } else if (!strcmp(name, "Q_SPATIAL") || !strcmp(name, "Q_TEMPORAL")) { |
| 232 | 4 | var_read_metadata(avctx, name, size); | |
| 233 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
6 | } else if (!strcmp(name, "INTERLACING") || !strcmp(name, "PACKING")) { |
| 234 | 6 | avio_skip(pb, size); // ignore | |
| 235 | } else | ||
| 236 | ✗ | return AVERROR_INVALIDDATA; | |
| 237 | |||
| 238 | 30 | return 0; | |
| 239 | } | ||
| 240 | |||
| 241 | 8 | static int read_table(AVFormatContext *avctx, AVStream *st, | |
| 242 | int (*parse)(AVFormatContext *avctx, AVStream *st, | ||
| 243 | const char *name, int size)) | ||
| 244 | { | ||
| 245 | unsigned count; | ||
| 246 | int i; | ||
| 247 | |||
| 248 | 8 | AVIOContext *pb = avctx->pb; | |
| 249 | 8 | avio_skip(pb, 4); | |
| 250 | 8 | count = avio_rb32(pb); | |
| 251 | 8 | avio_skip(pb, 4); | |
| 252 |
2/2✓ Branch 0 taken 57 times.
✓ Branch 1 taken 8 times.
|
65 | for (i = 0; i < count; i++) { |
| 253 | char name[17]; | ||
| 254 | int size; | ||
| 255 | |||
| 256 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 57 times.
|
57 | if (avio_feof(pb)) |
| 257 | ✗ | return AVERROR_EOF; | |
| 258 | |||
| 259 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 57 times.
|
57 | if (avio_read(pb, name, 16) != 16) |
| 260 | ✗ | return AVERROR_INVALIDDATA; | |
| 261 | 57 | name[sizeof(name) - 1] = 0; | |
| 262 | 57 | size = avio_rb32(pb); | |
| 263 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
|
57 | if (size < 0) { |
| 264 | ✗ | av_log(avctx, AV_LOG_ERROR, "entry size %d is invalid\n", size); | |
| 265 | ✗ | return AVERROR_INVALIDDATA; | |
| 266 | } | ||
| 267 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 57 times.
|
57 | if (parse(avctx, st, name, size) < 0) { |
| 268 | ✗ | avpriv_request_sample(avctx, "Variable %s", name); | |
| 269 | ✗ | avio_skip(pb, size); | |
| 270 | } | ||
| 271 | } | ||
| 272 | 8 | return 0; | |
| 273 | } | ||
| 274 | |||
| 275 | 5 | static void read_index(AVIOContext *pb, AVStream *st) | |
| 276 | { | ||
| 277 | 5 | uint64_t timestamp = 0; | |
| 278 | int i; | ||
| 279 |
2/2✓ Branch 0 taken 727 times.
✓ Branch 1 taken 5 times.
|
732 | for (i = 0; i < st->nb_frames; i++) { |
| 280 | 727 | uint32_t pos = avio_rb32(pb); | |
| 281 | 727 | uint32_t size = avio_rb32(pb); | |
| 282 | 727 | avio_skip(pb, 8); | |
| 283 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 727 times.
|
727 | if (avio_feof(pb)) |
| 284 | ✗ | return ; | |
| 285 | 727 | av_add_index_entry(st, pos, timestamp, size, 0, AVINDEX_KEYFRAME); | |
| 286 |
2/2✓ Branch 0 taken 391 times.
✓ Branch 1 taken 336 times.
|
727 | if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { |
| 287 | 391 | timestamp += size / (st->codecpar->ch_layout.nb_channels * 2LL); | |
| 288 | } else { | ||
| 289 | 336 | timestamp++; | |
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | 3 | static int mv_read_header(AVFormatContext *avctx) | |
| 295 | { | ||
| 296 | 3 | MvContext *mv = avctx->priv_data; | |
| 297 | 3 | AVIOContext *pb = avctx->pb; | |
| 298 | 3 | AVStream *ast = NULL, *vst = NULL; //initialization to suppress warning | |
| 299 | int version, i; | ||
| 300 | int ret; | ||
| 301 | |||
| 302 | 3 | avio_skip(pb, 4); | |
| 303 | |||
| 304 | 3 | version = avio_rb16(pb); | |
| 305 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (version == 2) { |
| 306 | uint64_t timestamp; | ||
| 307 | int v; | ||
| 308 | uint32_t bytes_per_sample; | ||
| 309 | AVRational fps; | ||
| 310 | |||
| 311 | ✗ | avio_skip(pb, 10); | |
| 312 | |||
| 313 | ✗ | fps = av_d2q(av_int2double(avio_rb64(pb)), INT_MAX); | |
| 314 | |||
| 315 | /* allocate audio track first to prevent unnecessary seeking | ||
| 316 | * (audio packet always precede video packet for a given frame) */ | ||
| 317 | ✗ | v = avio_rb16(pb); | |
| 318 | ✗ | if (v == MOVIE_SOUND) { | |
| 319 | /* movie has sound so allocate an audio stream */ | ||
| 320 | ✗ | ast = avformat_new_stream(avctx, NULL); | |
| 321 | ✗ | if (!ast) | |
| 322 | ✗ | return AVERROR(ENOMEM); | |
| 323 | ✗ | } else if (v != MOVIE_SILENT) | |
| 324 | ✗ | return AVERROR_INVALIDDATA; | |
| 325 | |||
| 326 | ✗ | avio_skip(pb, 2); | |
| 327 | |||
| 328 | ✗ | vst = avformat_new_stream(avctx, NULL); | |
| 329 | ✗ | if (!vst) | |
| 330 | ✗ | return AVERROR(ENOMEM); | |
| 331 | ✗ | avpriv_set_pts_info(vst, 64, fps.den, fps.num); | |
| 332 | ✗ | vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; | |
| 333 | ✗ | vst->avg_frame_rate = fps; | |
| 334 | ✗ | vst->duration = vst->nb_frames = avio_rb32(pb); | |
| 335 | ✗ | v = avio_rb32(pb); | |
| 336 | ✗ | switch (v) { | |
| 337 | ✗ | case 1: | |
| 338 | ✗ | vst->codecpar->codec_id = AV_CODEC_ID_MVC1; | |
| 339 | ✗ | break; | |
| 340 | ✗ | case 2: | |
| 341 | ✗ | vst->codecpar->format = AV_PIX_FMT_ARGB; | |
| 342 | ✗ | vst->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; | |
| 343 | ✗ | break; | |
| 344 | ✗ | default: | |
| 345 | ✗ | avpriv_request_sample(avctx, "Video compression %i", v); | |
| 346 | ✗ | break; | |
| 347 | } | ||
| 348 | ✗ | vst->codecpar->codec_tag = 0; | |
| 349 | ✗ | vst->codecpar->width = avio_rb32(pb); | |
| 350 | ✗ | vst->codecpar->height = avio_rb32(pb); | |
| 351 | ✗ | avio_skip(pb, 12); | |
| 352 | |||
| 353 | ✗ | if (ast) { | |
| 354 | ✗ | ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | |
| 355 | ✗ | ast->nb_frames = vst->nb_frames; | |
| 356 | ✗ | ast->codecpar->sample_rate = avio_rb32(pb); | |
| 357 | ✗ | if (ast->codecpar->sample_rate <= 0) { | |
| 358 | ✗ | av_log(avctx, AV_LOG_ERROR, "Invalid sample rate %d\n", ast->codecpar->sample_rate); | |
| 359 | ✗ | return AVERROR_INVALIDDATA; | |
| 360 | } | ||
| 361 | ✗ | avpriv_set_pts_info(ast, 33, 1, ast->codecpar->sample_rate); | |
| 362 | |||
| 363 | ✗ | bytes_per_sample = avio_rb32(pb); | |
| 364 | |||
| 365 | ✗ | v = avio_rb32(pb); | |
| 366 | ✗ | if (v == AUDIO_FORMAT_SIGNED) { | |
| 367 | ✗ | switch (bytes_per_sample) { | |
| 368 | ✗ | case 1: | |
| 369 | ✗ | ast->codecpar->codec_id = AV_CODEC_ID_PCM_S8; | |
| 370 | ✗ | break; | |
| 371 | ✗ | case 2: | |
| 372 | ✗ | ast->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; | |
| 373 | ✗ | break; | |
| 374 | ✗ | default: | |
| 375 | ✗ | avpriv_request_sample(avctx, "Audio sample size %i bytes", bytes_per_sample); | |
| 376 | ✗ | break; | |
| 377 | } | ||
| 378 | } else { | ||
| 379 | ✗ | avpriv_request_sample(avctx, "Audio compression (format %i)", v); | |
| 380 | } | ||
| 381 | |||
| 382 | ✗ | if (bytes_per_sample == 0) | |
| 383 | ✗ | return AVERROR_INVALIDDATA; | |
| 384 | |||
| 385 | ✗ | if (set_channels(avctx, ast, avio_rb32(pb)) < 0) | |
| 386 | ✗ | return AVERROR_INVALIDDATA; | |
| 387 | |||
| 388 | ✗ | avio_skip(pb, 8); | |
| 389 | } else | ||
| 390 | ✗ | avio_skip(pb, 24); /* skip meaningless audio metadata */ | |
| 391 | |||
| 392 | ✗ | var_read_metadata(avctx, "title", 0x80); | |
| 393 | ✗ | var_read_metadata(avctx, "comment", 0x100); | |
| 394 | ✗ | avio_skip(pb, 0x80); | |
| 395 | |||
| 396 | ✗ | timestamp = 0; | |
| 397 | ✗ | for (i = 0; i < vst->nb_frames; i++) { | |
| 398 | ✗ | uint32_t pos = avio_rb32(pb); | |
| 399 | ✗ | uint32_t asize = avio_rb32(pb); | |
| 400 | ✗ | uint32_t vsize = avio_rb32(pb); | |
| 401 | ✗ | if (avio_feof(pb)) | |
| 402 | ✗ | return AVERROR_INVALIDDATA; | |
| 403 | ✗ | avio_skip(pb, 8); | |
| 404 | ✗ | if (ast) { | |
| 405 | ✗ | av_add_index_entry(ast, pos, timestamp, asize, 0, AVINDEX_KEYFRAME); | |
| 406 | ✗ | timestamp += asize / (ast->codecpar->ch_layout.nb_channels * (uint64_t)bytes_per_sample); | |
| 407 | } | ||
| 408 | ✗ | av_add_index_entry(vst, pos + asize, i, vsize, 0, AVINDEX_KEYFRAME); | |
| 409 | } | ||
| 410 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
3 | } else if (!version && avio_rb16(pb) == 3) { |
| 411 | 3 | avio_skip(pb, 4); | |
| 412 | |||
| 413 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((ret = read_table(avctx, NULL, parse_global_var)) < 0) |
| 414 | ✗ | return ret; | |
| 415 | |||
| 416 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | if (mv->nb_audio_tracks < 0 || mv->nb_video_tracks < 0 || |
| 417 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
3 | (mv->nb_audio_tracks == 0 && mv->nb_video_tracks == 0)) { |
| 418 | ✗ | av_log(avctx, AV_LOG_ERROR, "Stream count is invalid.\n"); | |
| 419 | ✗ | return AVERROR_INVALIDDATA; | |
| 420 | } | ||
| 421 | |||
| 422 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (mv->nb_audio_tracks > 1) { |
| 423 | ✗ | avpriv_request_sample(avctx, "Multiple audio streams support"); | |
| 424 | ✗ | return AVERROR_PATCHWELCOME; | |
| 425 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | } else if (mv->nb_audio_tracks) { |
| 426 | 2 | ast = avformat_new_stream(avctx, NULL); | |
| 427 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!ast) |
| 428 | ✗ | return AVERROR(ENOMEM); | |
| 429 | 2 | ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | |
| 430 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if ((ret = read_table(avctx, ast, parse_audio_var)) < 0) |
| 431 | ✗ | return ret; | |
| 432 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (mv->acompression == 100 && |
| 433 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | mv->aformat == AUDIO_FORMAT_SIGNED && |
| 434 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | ast->codecpar->bits_per_coded_sample == 16) { |
| 435 | 2 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; | |
| 436 | } else { | ||
| 437 | ✗ | avpriv_request_sample(avctx, | |
| 438 | "Audio compression %i (format %i, sr %i)", | ||
| 439 | mv->acompression, mv->aformat, | ||
| 440 | ✗ | ast->codecpar->bits_per_coded_sample); | |
| 441 | ✗ | ast->codecpar->codec_id = AV_CODEC_ID_NONE; | |
| 442 | } | ||
| 443 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (ast->codecpar->ch_layout.nb_channels <= 0) { |
| 444 | ✗ | av_log(avctx, AV_LOG_ERROR, "No valid channel count found.\n"); | |
| 445 | ✗ | return AVERROR_INVALIDDATA; | |
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (mv->nb_video_tracks > 1) { |
| 450 | ✗ | avpriv_request_sample(avctx, "Multiple video streams support"); | |
| 451 | ✗ | return AVERROR_PATCHWELCOME; | |
| 452 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | } else if (mv->nb_video_tracks) { |
| 453 | 3 | vst = avformat_new_stream(avctx, NULL); | |
| 454 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (!vst) |
| 455 | ✗ | return AVERROR(ENOMEM); | |
| 456 | 3 | vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; | |
| 457 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((ret = read_table(avctx, vst, parse_video_var))<0) |
| 458 | ✗ | return ret; | |
| 459 | } | ||
| 460 | |||
| 461 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (mv->nb_audio_tracks) |
| 462 | 2 | read_index(pb, ast); | |
| 463 | |||
| 464 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (mv->nb_video_tracks) |
| 465 | 3 | read_index(pb, vst); | |
| 466 | } else { | ||
| 467 | ✗ | avpriv_request_sample(avctx, "Version %i", version); | |
| 468 | ✗ | return AVERROR_PATCHWELCOME; | |
| 469 | } | ||
| 470 | |||
| 471 | 3 | return 0; | |
| 472 | } | ||
| 473 | |||
| 474 | 185 | static int mv_read_packet(AVFormatContext *avctx, AVPacket *pkt) | |
| 475 | { | ||
| 476 | 185 | MvContext *mv = avctx->priv_data; | |
| 477 | 185 | AVIOContext *pb = avctx->pb; | |
| 478 | 185 | AVStream *st = avctx->streams[mv->stream_index]; | |
| 479 | 185 | FFStream *const sti = ffstream(st); | |
| 480 | const AVIndexEntry *index; | ||
| 481 | 185 | int frame = mv->frame[mv->stream_index]; | |
| 482 | int64_t ret; | ||
| 483 | uint64_t pos; | ||
| 484 | |||
| 485 |
2/2✓ Branch 0 taken 183 times.
✓ Branch 1 taken 2 times.
|
185 | if (frame < sti->nb_index_entries) { |
| 486 | 183 | index = &sti->index_entries[frame]; | |
| 487 | 183 | pos = avio_tell(pb); | |
| 488 |
2/2✓ Branch 0 taken 94 times.
✓ Branch 1 taken 89 times.
|
183 | if (index->pos > pos) |
| 489 | 94 | avio_skip(pb, index->pos - pos); | |
| 490 |
2/2✓ Branch 0 taken 73 times.
✓ Branch 1 taken 16 times.
|
89 | else if (index->pos < pos) { |
| 491 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
|
73 | if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) |
| 492 | ✗ | return AVERROR(ENOSYS); | |
| 493 | 73 | ret = avio_seek(pb, index->pos, SEEK_SET); | |
| 494 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
|
73 | if (ret < 0) |
| 495 | ✗ | return ret; | |
| 496 | } | ||
| 497 | 183 | ret = av_get_packet(pb, pkt, index->size); | |
| 498 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 183 times.
|
183 | if (ret < 0) |
| 499 | ✗ | return ret; | |
| 500 | |||
| 501 | 183 | pkt->stream_index = mv->stream_index; | |
| 502 | 183 | pkt->pts = index->timestamp; | |
| 503 | 183 | pkt->flags |= AV_PKT_FLAG_KEY; | |
| 504 | |||
| 505 | 183 | mv->frame[mv->stream_index]++; | |
| 506 | 183 | mv->eof_count = 0; | |
| 507 | } else { | ||
| 508 | 2 | mv->eof_count++; | |
| 509 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (mv->eof_count >= avctx->nb_streams) |
| 510 | 1 | return AVERROR_EOF; | |
| 511 | |||
| 512 | // avoid returning 0 without a packet | ||
| 513 | 1 | return AVERROR(EAGAIN); | |
| 514 | } | ||
| 515 | |||
| 516 | 183 | mv->stream_index++; | |
| 517 |
2/2✓ Branch 0 taken 109 times.
✓ Branch 1 taken 74 times.
|
183 | if (mv->stream_index >= avctx->nb_streams) |
| 518 | 109 | mv->stream_index = 0; | |
| 519 | |||
| 520 | 183 | return 0; | |
| 521 | } | ||
| 522 | |||
| 523 | ✗ | static int mv_read_seek(AVFormatContext *avctx, int stream_index, | |
| 524 | int64_t timestamp, int flags) | ||
| 525 | { | ||
| 526 | ✗ | MvContext *mv = avctx->priv_data; | |
| 527 | ✗ | AVStream *st = avctx->streams[stream_index]; | |
| 528 | int frame, i; | ||
| 529 | |||
| 530 | ✗ | if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) | |
| 531 | ✗ | return AVERROR(ENOSYS); | |
| 532 | |||
| 533 | ✗ | if (!(avctx->pb->seekable & AVIO_SEEKABLE_NORMAL)) | |
| 534 | ✗ | return AVERROR(ENOSYS); | |
| 535 | |||
| 536 | ✗ | frame = av_index_search_timestamp(st, timestamp, flags); | |
| 537 | ✗ | if (frame < 0) | |
| 538 | ✗ | return AVERROR_INVALIDDATA; | |
| 539 | |||
| 540 | ✗ | for (i = 0; i < avctx->nb_streams; i++) | |
| 541 | ✗ | mv->frame[i] = frame; | |
| 542 | ✗ | return 0; | |
| 543 | } | ||
| 544 | |||
| 545 | const FFInputFormat ff_mv_demuxer = { | ||
| 546 | .p.name = "mv", | ||
| 547 | .p.long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"), | ||
| 548 | .priv_data_size = sizeof(MvContext), | ||
| 549 | .read_probe = mv_probe, | ||
| 550 | .read_header = mv_read_header, | ||
| 551 | .read_packet = mv_read_packet, | ||
| 552 | .read_seek = mv_read_seek, | ||
| 553 | }; | ||
| 554 |