1/* Electronic Arts Multimedia File Demuxer 2 * Copyright (c) 2004 The ffmpeg Project 3 * Copyright (c) 2006-2008 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 * Electronic Arts Multimedia file demuxer (WVE/UV2/etc.) 25 * by Robin Kay (komadori at gekkou.co.uk) 26 */ 27 28#include <inttypes.h> 29 30#include "libavutil/intreadwrite.h" 31#include "avformat.h" 32#include "internal.h" 33 34#define SCHl_TAG MKTAG('S', 'C', 'H', 'l') 35#define SEAD_TAG MKTAG('S', 'E', 'A', 'D') /* Sxxx header */ 36#define SNDC_TAG MKTAG('S', 'N', 'D', 'C') /* Sxxx data */ 37#define SEND_TAG MKTAG('S', 'E', 'N', 'D') /* Sxxx end */ 38#define SHEN_TAG MKTAG('S', 'H', 'E', 'N') /* SxEN header */ 39#define SDEN_TAG MKTAG('S', 'D', 'E', 'N') /* SxEN data */ 40#define SEEN_TAG MKTAG('S', 'E', 'E', 'N') /* SxEN end */ 41#define ISNh_TAG MKTAG('1', 'S', 'N', 'h') /* 1SNx header */ 42#define EACS_TAG MKTAG('E', 'A', 'C', 'S') 43#define ISNd_TAG MKTAG('1', 'S', 'N', 'd') /* 1SNx data */ 44#define ISNe_TAG MKTAG('1', 'S', 'N', 'e') /* 1SNx end */ 45#define PT00_TAG MKTAG('P', 'T', 0x0, 0x0) 46#define GSTR_TAG MKTAG('G', 'S', 'T', 'R') 47#define SCDl_TAG MKTAG('S', 'C', 'D', 'l') 48#define SCEl_TAG MKTAG('S', 'C', 'E', 'l') 49#define kVGT_TAG MKTAG('k', 'V', 'G', 'T') /* TGV I-frame */ 50#define fVGT_TAG MKTAG('f', 'V', 'G', 'T') /* TGV P-frame */ 51#define mTCD_TAG MKTAG('m', 'T', 'C', 'D') /* MDEC */ 52#define MADk_TAG MKTAG('M', 'A', 'D', 'k') /* MAD I-frame */ 53#define MADm_TAG MKTAG('M', 'A', 'D', 'm') /* MAD P-frame */ 54#define MADe_TAG MKTAG('M', 'A', 'D', 'e') /* MAD lqp-frame */ 55#define MPCh_TAG MKTAG('M', 'P', 'C', 'h') /* MPEG-2 */ 56#define TGQs_TAG MKTAG('T', 'G', 'Q', 's') /* TGQ I-frame (appears in .TGQ files) */ 57#define pQGT_TAG MKTAG('p', 'Q', 'G', 'T') /* TGQ I-frame (appears in .UV files) */ 58#define pIQT_TAG MKTAG('p', 'I', 'Q', 'T') /* TQI/UV2 I-frame (.UV2/.WVE) */ 59#define MVhd_TAG MKTAG('M', 'V', 'h', 'd') 60#define MV0K_TAG MKTAG('M', 'V', '0', 'K') 61#define MV0F_TAG MKTAG('M', 'V', '0', 'F') 62#define MVIh_TAG MKTAG('M', 'V', 'I', 'h') /* CMV header */ 63#define MVIf_TAG MKTAG('M', 'V', 'I', 'f') /* CMV I-frame */ 64 65typedef struct EaDemuxContext { 66 int big_endian; 67 68 enum AVCodecID video_codec; 69 AVRational time_base; 70 int width, height; 71 int nb_frames; 72 int video_stream_index; 73 74 enum AVCodecID audio_codec; 75 int audio_stream_index; 76 77 int bytes; 78 int sample_rate; 79 int num_channels; 80 int num_samples; 81} EaDemuxContext; 82 83static uint32_t read_arbitrary(AVIOContext *pb) 84{ 85 uint8_t size, byte; 86 int i; 87 uint32_t word; 88 89 size = avio_r8(pb); 90 91 word = 0; 92 for (i = 0; i < size; i++) { 93 byte = avio_r8(pb); 94 word <<= 8; 95 word |= byte; 96 } 97 98 return word; 99} 100 101static int process_audio_header_elements(AVFormatContext *s) 102{ 103 EaDemuxContext *ea = s->priv_data; 104 AVIOContext *pb = s->pb; 105 int in_header = 1; 106 int compression_type = -1, revision = -1, revision2 = -1; 107 108 ea->bytes = 2; 109 ea->sample_rate = -1; 110 ea->num_channels = 1; 111 112 while (!url_feof(pb) && in_header) { 113 int in_subheader; 114 uint8_t byte; 115 byte = avio_r8(pb); 116 117 switch (byte) { 118 case 0xFD: 119 av_log(s, AV_LOG_DEBUG, "entered audio subheader\n"); 120 in_subheader = 1; 121 while (!url_feof(pb) && in_subheader) { 122 uint8_t subbyte; 123 subbyte = avio_r8(pb); 124 125 switch (subbyte) { 126 case 0x80: 127 revision = read_arbitrary(pb); 128 av_log(s, AV_LOG_DEBUG, 129 "revision (element 0x80) set to 0x%08x\n", revision); 130 break; 131 case 0x82: 132 ea->num_channels = read_arbitrary(pb); 133 av_log(s, AV_LOG_DEBUG, 134 "num_channels (element 0x82) set to 0x%08x\n", 135 ea->num_channels); 136 break; 137 case 0x83: 138 compression_type = read_arbitrary(pb); 139 av_log(s, AV_LOG_DEBUG, 140 "compression_type (element 0x83) set to 0x%08x\n", 141 compression_type); 142 break; 143 case 0x84: 144 ea->sample_rate = read_arbitrary(pb); 145 av_log(s, AV_LOG_DEBUG, 146 "sample_rate (element 0x84) set to %i\n", 147 ea->sample_rate); 148 break; 149 case 0x85: 150 ea->num_samples = read_arbitrary(pb); 151 av_log(s, AV_LOG_DEBUG, 152 "num_samples (element 0x85) set to 0x%08x\n", 153 ea->num_samples); 154 break; 155 case 0x8A: 156 av_log(s, AV_LOG_DEBUG, 157 "element 0x%02x set to 0x%08"PRIx32"\n", 158 subbyte, read_arbitrary(pb)); 159 av_log(s, AV_LOG_DEBUG, "exited audio subheader\n"); 160 in_subheader = 0; 161 break; 162 case 0xA0: 163 revision2 = read_arbitrary(pb); 164 av_log(s, AV_LOG_DEBUG, 165 "revision2 (element 0xA0) set to 0x%08x\n", 166 revision2); 167 break; 168 case 0xFF: 169 av_log(s, AV_LOG_DEBUG, 170 "end of header block reached (within audio subheader)\n"); 171 in_subheader = 0; 172 in_header = 0; 173 break; 174 default: 175 av_log(s, AV_LOG_DEBUG, 176 "element 0x%02x set to 0x%08"PRIx32"\n", 177 subbyte, read_arbitrary(pb)); 178 break; 179 } 180 } 181 break; 182 case 0xFF: 183 av_log(s, AV_LOG_DEBUG, "end of header block reached\n"); 184 in_header = 0; 185 break; 186 default: 187 av_log(s, AV_LOG_DEBUG, 188 "header element 0x%02x set to 0x%08"PRIx32"\n", 189 byte, read_arbitrary(pb)); 190 break; 191 } 192 } 193 194 switch (compression_type) { 195 case 0: 196 ea->audio_codec = AV_CODEC_ID_PCM_S16LE; 197 break; 198 case 7: 199 ea->audio_codec = AV_CODEC_ID_ADPCM_EA; 200 break; 201 case -1: 202 switch (revision) { 203 case 1: 204 ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R1; 205 break; 206 case 2: 207 ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2; 208 break; 209 case 3: 210 ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R3; 211 break; 212 case -1: 213 break; 214 default: 215 avpriv_request_sample(s, "stream type; revision=%i", revision); 216 return 0; 217 } 218 switch (revision2) { 219 case 8: 220 ea->audio_codec = AV_CODEC_ID_PCM_S16LE_PLANAR; 221 break; 222 case 10: 223 switch (revision) { 224 case -1: 225 case 2: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R1; break; 226 case 3: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2; break; 227 default: 228 avpriv_request_sample(s, "stream type; revision=%i, revision2=%i", revision, revision2); 229 return 0; 230 } 231 break; 232 case 16: 233 ea->audio_codec = AV_CODEC_ID_MP3; 234 break; 235 case -1: 236 break; 237 default: 238 ea->audio_codec = AV_CODEC_ID_NONE; 239 avpriv_request_sample(s, "stream type; revision2=%i", revision2); 240 return 0; 241 } 242 break; 243 default: 244 avpriv_request_sample(s, 245 "stream type; compression_type=%i", 246 compression_type); 247 return 0; 248 } 249 250 if (ea->sample_rate == -1) 251 ea->sample_rate = revision == 3 ? 48000 : 22050; 252 253 return 1; 254} 255 256static void process_audio_header_eacs(AVFormatContext *s) 257{ 258 EaDemuxContext *ea = s->priv_data; 259 AVIOContext *pb = s->pb; 260 int compression_type; 261 262 ea->sample_rate = ea->big_endian ? avio_rb32(pb) : avio_rl32(pb); 263 ea->bytes = avio_r8(pb); /* 1=8-bit, 2=16-bit */ 264 ea->num_channels = avio_r8(pb); 265 compression_type = avio_r8(pb); 266 avio_skip(pb, 13); 267 268 switch (compression_type) { 269 case 0: 270 switch (ea->bytes) { 271 case 1: 272 ea->audio_codec = AV_CODEC_ID_PCM_S8; 273 break; 274 case 2: 275 ea->audio_codec = AV_CODEC_ID_PCM_S16LE; 276 break; 277 } 278 break; 279 case 1: 280 ea->audio_codec = AV_CODEC_ID_PCM_MULAW; 281 ea->bytes = 1; 282 break; 283 case 2: 284 ea->audio_codec = AV_CODEC_ID_ADPCM_IMA_EA_EACS; 285 break; 286 default: 287 avpriv_request_sample(s, 288 "stream type; audio compression_type=%i", 289 compression_type); 290 } 291} 292 293static void process_audio_header_sead(AVFormatContext *s) 294{ 295 EaDemuxContext *ea = s->priv_data; 296 AVIOContext *pb = s->pb; 297 298 ea->sample_rate = avio_rl32(pb); 299 ea->bytes = avio_rl32(pb); /* 1=8-bit, 2=16-bit */ 300 ea->num_channels = avio_rl32(pb); 301 ea->audio_codec = AV_CODEC_ID_ADPCM_IMA_EA_SEAD; 302} 303 304static void process_video_header_mdec(AVFormatContext *s) 305{ 306 EaDemuxContext *ea = s->priv_data; 307 AVIOContext *pb = s->pb; 308 avio_skip(pb, 4); 309 ea->width = avio_rl16(pb); 310 ea->height = avio_rl16(pb); 311 ea->time_base = (AVRational) { 1, 15 }; 312 ea->video_codec = AV_CODEC_ID_MDEC; 313} 314 315static int process_video_header_vp6(AVFormatContext *s) 316{ 317 EaDemuxContext *ea = s->priv_data; 318 AVIOContext *pb = s->pb; 319 320 avio_skip(pb, 8); 321 ea->nb_frames = avio_rl32(pb); 322 avio_skip(pb, 4); 323 ea->time_base.den = avio_rl32(pb); 324 ea->time_base.num = avio_rl32(pb); 325 if (ea->time_base.den <= 0 || ea->time_base.num <= 0) { 326 av_log(s, AV_LOG_ERROR, "Timebase is invalid\n"); 327 return AVERROR_INVALIDDATA; 328 } 329 ea->video_codec = AV_CODEC_ID_VP6; 330 331 return 1; 332} 333 334static void process_video_header_cmv(AVFormatContext *s) 335{ 336 EaDemuxContext *ea = s->priv_data; 337 int fps; 338 339 avio_skip(s->pb, 10); 340 fps = avio_rl16(s->pb); 341 if (fps) 342 ea->time_base = (AVRational) { 1, fps }; 343 ea->video_codec = AV_CODEC_ID_CMV; 344} 345 346/* Process EA file header. 347 * Return 1 if the EA file is valid and successfully opened, 0 otherwise. */ 348static int process_ea_header(AVFormatContext *s) 349{ 350 uint32_t blockid, size = 0; 351 EaDemuxContext *ea = s->priv_data; 352 AVIOContext *pb = s->pb; 353 int i; 354 355 for (i = 0; i < 5 && (!ea->audio_codec || !ea->video_codec); i++) { 356 uint64_t startpos = avio_tell(pb); 357 int err = 0; 358 359 blockid = avio_rl32(pb); 360 size = avio_rl32(pb); 361 if (i == 0) 362 ea->big_endian = size > av_bswap32(size); 363 if (ea->big_endian) 364 size = av_bswap32(size); 365 366 if (size < 8) { 367 av_log(s, AV_LOG_ERROR, "chunk size too small\n"); 368 return AVERROR_INVALIDDATA; 369 } 370 371 switch (blockid) { 372 case ISNh_TAG: 373 if (avio_rl32(pb) != EACS_TAG) { 374 avpriv_request_sample(s, "unknown 1SNh headerid"); 375 return 0; 376 } 377 process_audio_header_eacs(s); 378 break; 379 380 case SCHl_TAG: 381 case SHEN_TAG: 382 blockid = avio_rl32(pb); 383 if (blockid == GSTR_TAG) { 384 avio_skip(pb, 4); 385 } else if ((blockid & 0xFFFF) != PT00_TAG) { 386 avpriv_request_sample(s, "unknown SCHl headerid"); 387 return 0; 388 } 389 err = process_audio_header_elements(s); 390 break; 391 392 case SEAD_TAG: 393 process_audio_header_sead(s); 394 break; 395 396 case MVIh_TAG: 397 process_video_header_cmv(s); 398 break; 399 400 case kVGT_TAG: 401 ea->video_codec = AV_CODEC_ID_TGV; 402 break; 403 404 case mTCD_TAG: 405 process_video_header_mdec(s); 406 break; 407 408 case MPCh_TAG: 409 ea->video_codec = AV_CODEC_ID_MPEG2VIDEO; 410 break; 411 412 case pQGT_TAG: 413 case TGQs_TAG: 414 ea->video_codec = AV_CODEC_ID_TGQ; 415 break; 416 417 case pIQT_TAG: 418 ea->video_codec = AV_CODEC_ID_TQI; 419 break; 420 421 case MADk_TAG: 422 ea->video_codec = AV_CODEC_ID_MAD; 423 break; 424 425 case MVhd_TAG: 426 err = process_video_header_vp6(s); 427 break; 428 } 429 430 if (err < 0) { 431 av_log(s, AV_LOG_ERROR, "error parsing header: %i\n", err); 432 return err; 433 } 434 435 avio_seek(pb, startpos + size, SEEK_SET); 436 } 437 438 avio_seek(pb, 0, SEEK_SET); 439 440 return 1; 441} 442 443static int ea_probe(AVProbeData *p) 444{ 445 unsigned big_endian, size; 446 447 switch (AV_RL32(&p->buf[0])) { 448 case ISNh_TAG: 449 case SCHl_TAG: 450 case SEAD_TAG: 451 case SHEN_TAG: 452 case kVGT_TAG: 453 case MADk_TAG: 454 case MPCh_TAG: 455 case MVhd_TAG: 456 case MVIh_TAG: 457 break; 458 default: 459 return 0; 460 } 461 size = AV_RL32(&p->buf[4]); 462 big_endian = size > 0x000FFFFF; 463 if (big_endian) 464 size = av_bswap32(size); 465 if (size > 0xfffff || size < 8) 466 return 0; 467 468 return AVPROBE_SCORE_MAX; 469} 470 471static int ea_read_header(AVFormatContext *s) 472{ 473 EaDemuxContext *ea = s->priv_data; 474 AVStream *st; 475 476 if (process_ea_header(s)<=0) 477 return AVERROR(EIO); 478 479 if (ea->video_codec) { 480 /* initialize the video decoder stream */ 481 st = avformat_new_stream(s, NULL); 482 if (!st) 483 return AVERROR(ENOMEM); 484 ea->video_stream_index = st->index; 485 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 486 st->codec->codec_id = ea->video_codec; 487 // parsing is necessary to make FFmpeg generate correct timestamps 488 if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) 489 st->need_parsing = AVSTREAM_PARSE_HEADERS; 490 st->codec->codec_tag = 0; /* no fourcc */ 491 st->codec->width = ea->width; 492 st->codec->height = ea->height; 493 st->duration = st->nb_frames = ea->nb_frames; 494 if (ea->time_base.num) 495 avpriv_set_pts_info(st, 64, ea->time_base.num, ea->time_base.den); 496 st->r_frame_rate = 497 st->avg_frame_rate = av_inv_q(ea->time_base); 498 } 499 500 if (ea->audio_codec) { 501 if (ea->num_channels <= 0 || ea->num_channels > 2) { 502 av_log(s, AV_LOG_WARNING, 503 "Unsupported number of channels: %d\n", ea->num_channels); 504 ea->audio_codec = 0; 505 return 1; 506 } 507 if (ea->sample_rate <= 0) { 508 av_log(s, AV_LOG_ERROR, 509 "Unsupported sample rate: %d\n", ea->sample_rate); 510 ea->audio_codec = 0; 511 return 1; 512 } 513 if (ea->bytes <= 0) { 514 av_log(s, AV_LOG_ERROR, 515 "Invalid number of bytes per sample: %d\n", ea->bytes); 516 ea->audio_codec = AV_CODEC_ID_NONE; 517 return 1; 518 } 519 520 /* initialize the audio decoder stream */ 521 st = avformat_new_stream(s, NULL); 522 if (!st) 523 return AVERROR(ENOMEM); 524 avpriv_set_pts_info(st, 33, 1, ea->sample_rate); 525 st->codec->codec_type = AVMEDIA_TYPE_AUDIO; 526 st->codec->codec_id = ea->audio_codec; 527 st->codec->codec_tag = 0; /* no tag */ 528 st->codec->channels = ea->num_channels; 529 st->codec->sample_rate = ea->sample_rate; 530 st->codec->bits_per_coded_sample = ea->bytes * 8; 531 st->codec->bit_rate = st->codec->channels * 532 st->codec->sample_rate * 533 st->codec->bits_per_coded_sample / 4; 534 st->codec->block_align = st->codec->channels * 535 st->codec->bits_per_coded_sample; 536 ea->audio_stream_index = st->index; 537 st->start_time = 0; 538 } 539 540 return 1; 541} 542 543static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) 544{ 545 EaDemuxContext *ea = s->priv_data; 546 AVIOContext *pb = s->pb; 547 int partial_packet = 0; 548 unsigned int chunk_type, chunk_size; 549 int ret = 0, packet_read = 0, key = 0; 550 int av_uninit(num_samples); 551 552 while (!packet_read || partial_packet) { 553 chunk_type = avio_rl32(pb); 554 chunk_size = ea->big_endian ? avio_rb32(pb) : avio_rl32(pb); 555 if (chunk_size <= 8) 556 return AVERROR_INVALIDDATA; 557 chunk_size -= 8; 558 559 switch (chunk_type) { 560 /* audio data */ 561 case ISNh_TAG: 562 /* header chunk also contains data; skip over the header portion */ 563 if (chunk_size < 32) 564 return AVERROR_INVALIDDATA; 565 avio_skip(pb, 32); 566 chunk_size -= 32; 567 case ISNd_TAG: 568 case SCDl_TAG: 569 case SNDC_TAG: 570 case SDEN_TAG: 571 if (!ea->audio_codec) { 572 avio_skip(pb, chunk_size); 573 break; 574 } else if (ea->audio_codec == AV_CODEC_ID_PCM_S16LE_PLANAR || 575 ea->audio_codec == AV_CODEC_ID_MP3) { 576 num_samples = avio_rl32(pb); 577 avio_skip(pb, 8); 578 chunk_size -= 12; 579 } 580 if (partial_packet) { 581 avpriv_request_sample(s, "video header followed by audio packet"); 582 av_free_packet(pkt); 583 partial_packet = 0; 584 } 585 ret = av_get_packet(pb, pkt, chunk_size); 586 if (ret < 0) 587 return ret; 588 pkt->stream_index = ea->audio_stream_index; 589 590 switch (ea->audio_codec) { 591 case AV_CODEC_ID_ADPCM_EA: 592 case AV_CODEC_ID_ADPCM_EA_R1: 593 case AV_CODEC_ID_ADPCM_EA_R2: 594 case AV_CODEC_ID_ADPCM_IMA_EA_EACS: 595 case AV_CODEC_ID_ADPCM_EA_R3: 596 if (pkt->size < 4) { 597 av_log(s, AV_LOG_ERROR, "Packet is too short\n"); 598 av_free_packet(pkt); 599 return AVERROR_INVALIDDATA; 600 } 601 if (ea->audio_codec == AV_CODEC_ID_ADPCM_EA_R3) 602 pkt->duration = AV_RB32(pkt->data); 603 else 604 pkt->duration = AV_RL32(pkt->data); 605 break; 606 case AV_CODEC_ID_ADPCM_IMA_EA_SEAD: 607 pkt->duration = ret * 2 / ea->num_channels; 608 break; 609 case AV_CODEC_ID_PCM_S16LE_PLANAR: 610 case AV_CODEC_ID_MP3: 611 pkt->duration = num_samples; 612 break; 613 default: 614 pkt->duration = chunk_size / (ea->bytes * ea->num_channels); 615 } 616 617 packet_read = 1; 618 break; 619 620 /* ending tag */ 621 case 0: 622 case ISNe_TAG: 623 case SCEl_TAG: 624 case SEND_TAG: 625 case SEEN_TAG: 626 ret = AVERROR(EIO); 627 packet_read = 1; 628 break; 629 630 case MVIh_TAG: 631 case kVGT_TAG: 632 case pQGT_TAG: 633 case TGQs_TAG: 634 case MADk_TAG: 635 key = AV_PKT_FLAG_KEY; 636 case MVIf_TAG: 637 case fVGT_TAG: 638 case MADm_TAG: 639 case MADe_TAG: 640 avio_seek(pb, -8, SEEK_CUR); // include chunk preamble 641 chunk_size += 8; 642 goto get_video_packet; 643 644 case mTCD_TAG: 645 avio_skip(pb, 8); // skip ea DCT header 646 chunk_size -= 8; 647 goto get_video_packet; 648 649 case MV0K_TAG: 650 case MPCh_TAG: 651 case pIQT_TAG: 652 key = AV_PKT_FLAG_KEY; 653 case MV0F_TAG: 654get_video_packet: 655 if (partial_packet) { 656 ret = av_append_packet(pb, pkt, chunk_size); 657 } else 658 ret = av_get_packet(pb, pkt, chunk_size); 659 if (ret < 0) { 660 packet_read = 1; 661 break; 662 } 663 partial_packet = chunk_type == MVIh_TAG; 664 pkt->stream_index = ea->video_stream_index; 665 pkt->flags |= key; 666 packet_read = 1; 667 break; 668 669 default: 670 avio_skip(pb, chunk_size); 671 break; 672 } 673 } 674 675 if (ret < 0 && partial_packet) 676 av_free_packet(pkt); 677 return ret; 678} 679 680AVInputFormat ff_ea_demuxer = { 681 .name = "ea", 682 .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts Multimedia"), 683 .priv_data_size = sizeof(EaDemuxContext), 684 .read_probe = ea_probe, 685 .read_header = ea_read_header, 686 .read_packet = ea_read_packet, 687}; 688