1/* 2 * R3D REDCODE demuxer 3 * Copyright (c) 2008 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> 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//#define DEBUG 23 24#include "libavutil/intreadwrite.h" 25#include "avformat.h" 26 27typedef struct { 28 unsigned video_offsets_count; 29 unsigned *video_offsets; 30 unsigned rdvo_offset; 31} R3DContext; 32 33typedef struct { 34 unsigned size; 35 uint32_t tag; 36 uint64_t offset; 37} Atom; 38 39static int read_atom(AVFormatContext *s, Atom *atom) 40{ 41 atom->offset = url_ftell(s->pb); 42 atom->size = get_be32(s->pb); 43 if (atom->size < 8) 44 return -1; 45 atom->tag = get_le32(s->pb); 46 dprintf(s, "atom %d %.4s offset %#llx\n", 47 atom->size, (char*)&atom->tag, atom->offset); 48 return atom->size; 49} 50 51static int r3d_read_red1(AVFormatContext *s) 52{ 53 AVStream *st = av_new_stream(s, 0); 54 char filename[258]; 55 int tmp, tmp2; 56 57 if (!st) 58 return -1; 59 st->codec->codec_type = CODEC_TYPE_VIDEO; 60 st->codec->codec_id = CODEC_ID_JPEG2000; 61 62 tmp = get_byte(s->pb); // major version 63 tmp2 = get_byte(s->pb); // minor version 64 dprintf(s, "version %d.%d\n", tmp, tmp2); 65 66 tmp = get_be16(s->pb); // unknown 67 dprintf(s, "unknown1 %d\n", tmp); 68 69 tmp = get_be32(s->pb); 70 av_set_pts_info(st, 32, 1, tmp); 71 72 tmp = get_be32(s->pb); // filenum 73 dprintf(s, "filenum %d\n", tmp); 74 75 url_fskip(s->pb, 32); // unknown 76 77 st->codec->width = get_be32(s->pb); 78 st->codec->height = get_be32(s->pb); 79 80 tmp = get_be16(s->pb); // unknown 81 dprintf(s, "unknown2 %d\n", tmp); 82 83 st->codec->time_base.den = get_be16(s->pb); 84 st->codec->time_base.num = get_be16(s->pb); 85 86 tmp = get_byte(s->pb); // audio channels 87 dprintf(s, "audio channels %d\n", tmp); 88 if (tmp > 0) { 89 AVStream *ast = av_new_stream(s, 1); 90 ast->codec->codec_type = CODEC_TYPE_AUDIO; 91 ast->codec->codec_id = CODEC_ID_PCM_S32BE; 92 ast->codec->channels = tmp; 93 av_set_pts_info(ast, 32, 1, st->time_base.den); 94 } 95 96 get_buffer(s->pb, filename, 257); 97 filename[sizeof(filename)-1] = 0; 98 av_metadata_set(&st->metadata, "filename", filename); 99 100 dprintf(s, "filename %s\n", filename); 101 dprintf(s, "resolution %dx%d\n", st->codec->width, st->codec->height); 102 dprintf(s, "timescale %d\n", st->time_base.den); 103 dprintf(s, "frame rate %d/%d\n", 104 st->codec->time_base.num, st->codec->time_base.den); 105 106 return 0; 107} 108 109static int r3d_read_rdvo(AVFormatContext *s, Atom *atom) 110{ 111 R3DContext *r3d = s->priv_data; 112 AVStream *st = s->streams[0]; 113 int i; 114 115 r3d->video_offsets_count = (atom->size - 8) / 4; 116 r3d->video_offsets = av_malloc(atom->size); 117 if (!r3d->video_offsets) 118 return AVERROR(ENOMEM); 119 120 for (i = 0; i < r3d->video_offsets_count; i++) { 121 r3d->video_offsets[i] = get_be32(s->pb); 122 if (!r3d->video_offsets[i]) { 123 r3d->video_offsets_count = i; 124 break; 125 } 126 dprintf(s, "video offset %d: %#x\n", i, r3d->video_offsets[i]); 127 } 128 129 if (st->codec->time_base.den) 130 st->duration = (uint64_t)r3d->video_offsets_count* 131 st->time_base.den*st->codec->time_base.num/st->codec->time_base.den; 132 dprintf(s, "duration %lld\n", st->duration); 133 134 return 0; 135} 136 137static void r3d_read_reos(AVFormatContext *s) 138{ 139 R3DContext *r3d = s->priv_data; 140 int tmp; 141 142 r3d->rdvo_offset = get_be32(s->pb); 143 get_be32(s->pb); // rdvs offset 144 get_be32(s->pb); // rdao offset 145 get_be32(s->pb); // rdas offset 146 147 tmp = get_be32(s->pb); 148 dprintf(s, "num video chunks %d\n", tmp); 149 150 tmp = get_be32(s->pb); 151 dprintf(s, "num audio chunks %d\n", tmp); 152 153 url_fskip(s->pb, 6*4); 154} 155 156static int r3d_read_header(AVFormatContext *s, AVFormatParameters *ap) 157{ 158 R3DContext *r3d = s->priv_data; 159 Atom atom; 160 int ret; 161 162 if (read_atom(s, &atom) < 0) { 163 av_log(s, AV_LOG_ERROR, "error reading atom\n"); 164 return -1; 165 } 166 if (atom.tag == MKTAG('R','E','D','1')) { 167 if ((ret = r3d_read_red1(s)) < 0) { 168 av_log(s, AV_LOG_ERROR, "error parsing 'red1' atom\n"); 169 return ret; 170 } 171 } else { 172 av_log(s, AV_LOG_ERROR, "could not find 'red1' atom\n"); 173 return -1; 174 } 175 176 s->data_offset = url_ftell(s->pb); 177 dprintf(s, "data offset %#llx\n", s->data_offset); 178 if (url_is_streamed(s->pb)) 179 return 0; 180 // find REOB/REOF/REOS to load index 181 url_fseek(s->pb, url_fsize(s->pb)-48-8, SEEK_SET); 182 if (read_atom(s, &atom) < 0) 183 av_log(s, AV_LOG_ERROR, "error reading end atom\n"); 184 185 if (atom.tag != MKTAG('R','E','O','B') && 186 atom.tag != MKTAG('R','E','O','F') && 187 atom.tag != MKTAG('R','E','O','S')) 188 goto out; 189 190 r3d_read_reos(s); 191 192 if (r3d->rdvo_offset) { 193 url_fseek(s->pb, r3d->rdvo_offset, SEEK_SET); 194 if (read_atom(s, &atom) < 0) 195 av_log(s, AV_LOG_ERROR, "error reading 'rdvo' atom\n"); 196 if (atom.tag == MKTAG('R','D','V','O')) { 197 if (r3d_read_rdvo(s, &atom) < 0) 198 av_log(s, AV_LOG_ERROR, "error parsing 'rdvo' atom\n"); 199 } 200 } 201 202 out: 203 url_fseek(s->pb, s->data_offset, SEEK_SET); 204 return 0; 205} 206 207static int r3d_read_redv(AVFormatContext *s, AVPacket *pkt, Atom *atom) 208{ 209 AVStream *st = s->streams[0]; 210 int tmp, tmp2; 211 uint64_t pos = url_ftell(s->pb); 212 unsigned dts; 213 214 dts = get_be32(s->pb); 215 216 tmp = get_be32(s->pb); 217 dprintf(s, "frame num %d\n", tmp); 218 219 tmp = get_byte(s->pb); // major version 220 tmp2 = get_byte(s->pb); // minor version 221 dprintf(s, "version %d.%d\n", tmp, tmp2); 222 223 tmp = get_be16(s->pb); // unknown 224 dprintf(s, "unknown %d\n", tmp); 225 226 if (tmp > 4) { 227 tmp = get_be16(s->pb); // unknown 228 dprintf(s, "unknown %d\n", tmp); 229 230 tmp = get_be16(s->pb); // unknown 231 dprintf(s, "unknown %d\n", tmp); 232 233 tmp = get_be32(s->pb); 234 dprintf(s, "width %d\n", tmp); 235 tmp = get_be32(s->pb); 236 dprintf(s, "height %d\n", tmp); 237 238 tmp = get_be32(s->pb); 239 dprintf(s, "metadata len %d\n", tmp); 240 } 241 tmp = atom->size - 8 - (url_ftell(s->pb) - pos); 242 if (tmp < 0) 243 return -1; 244 245 if (av_get_packet(s->pb, pkt, tmp) != tmp) { 246 av_log(s, AV_LOG_ERROR, "error reading video packet\n"); 247 return -1; 248 } 249 250 pkt->stream_index = 0; 251 pkt->dts = dts; 252 if (st->codec->time_base.den) 253 pkt->duration = (uint64_t)st->time_base.den* 254 st->codec->time_base.num/st->codec->time_base.den; 255 dprintf(s, "pkt dts %lld duration %d\n", pkt->dts, pkt->duration); 256 257 return 0; 258} 259 260static int r3d_read_reda(AVFormatContext *s, AVPacket *pkt, Atom *atom) 261{ 262 AVStream *st = s->streams[1]; 263 int tmp, tmp2, samples, size; 264 uint64_t pos = url_ftell(s->pb); 265 unsigned dts; 266 267 dts = get_be32(s->pb); 268 269 st->codec->sample_rate = get_be32(s->pb); 270 271 samples = get_be32(s->pb); 272 273 tmp = get_be32(s->pb); 274 dprintf(s, "packet num %d\n", tmp); 275 276 tmp = get_be16(s->pb); // unkown 277 dprintf(s, "unknown %d\n", tmp); 278 279 tmp = get_byte(s->pb); // major version 280 tmp2 = get_byte(s->pb); // minor version 281 dprintf(s, "version %d.%d\n", tmp, tmp2); 282 283 tmp = get_be32(s->pb); // unknown 284 dprintf(s, "unknown %d\n", tmp); 285 286 size = atom->size - 8 - (url_ftell(s->pb) - pos); 287 if (size < 0) 288 return -1; 289 if (av_get_packet(s->pb, pkt, size) != size) { 290 av_log(s, AV_LOG_ERROR, "error reading video packet\n"); 291 return -1; 292 } 293 294 pkt->stream_index = 1; 295 pkt->dts = dts; 296 pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate); 297 dprintf(s, "pkt dts %lld duration %d samples %d sample rate %d\n", 298 pkt->dts, pkt->duration, samples, st->codec->sample_rate); 299 300 return 0; 301} 302 303static int r3d_read_packet(AVFormatContext *s, AVPacket *pkt) 304{ 305 Atom atom; 306 int err = 0; 307 308 while (!err) { 309 if (read_atom(s, &atom) < 0) { 310 err = -1; 311 break; 312 } 313 switch (atom.tag) { 314 case MKTAG('R','E','D','V'): 315 if (s->streams[0]->discard == AVDISCARD_ALL) 316 goto skip; 317 if (!(err = r3d_read_redv(s, pkt, &atom))) 318 return 0; 319 break; 320 case MKTAG('R','E','D','A'): 321 if (s->nb_streams < 2) 322 return -1; 323 if (s->streams[1]->discard == AVDISCARD_ALL) 324 goto skip; 325 if (!(err = r3d_read_reda(s, pkt, &atom))) 326 return 0; 327 break; 328 default: 329 skip: 330 url_fskip(s->pb, atom.size-8); 331 } 332 } 333 return err; 334} 335 336static int r3d_probe(AVProbeData *p) 337{ 338 if (AV_RL32(p->buf + 4) == MKTAG('R','E','D','1')) 339 return AVPROBE_SCORE_MAX; 340 return 0; 341} 342 343static int r3d_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags) 344{ 345 AVStream *st = s->streams[0]; // video stream 346 R3DContext *r3d = s->priv_data; 347 int frame_num; 348 349 if (!st->codec->time_base.num || !st->time_base.den) 350 return -1; 351 352 frame_num = sample_time*st->codec->time_base.den/ 353 ((int64_t)st->codec->time_base.num*st->time_base.den); 354 dprintf(s, "seek frame num %d timestamp %lld\n", frame_num, sample_time); 355 356 if (frame_num < r3d->video_offsets_count) { 357 url_fseek(s->pb, r3d->video_offsets_count, SEEK_SET); 358 } else { 359 av_log(s, AV_LOG_ERROR, "could not seek to frame %d\n", frame_num); 360 return -1; 361 } 362 363 return 0; 364} 365 366static int r3d_close(AVFormatContext *s) 367{ 368 R3DContext *r3d = s->priv_data; 369 370 av_freep(&r3d->video_offsets); 371 372 return 0; 373} 374 375AVInputFormat r3d_demuxer = { 376 "r3d", 377 NULL_IF_CONFIG_SMALL("REDCODE R3D format"), 378 sizeof(R3DContext), 379 r3d_probe, 380 r3d_read_header, 381 r3d_read_packet, 382 r3d_close, 383 r3d_seek, 384}; 385