1/* 2 * AVI muxer 3 * Copyright (c) 2000 Fabrice Bellard 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#include "avformat.h" 22#include "avi.h" 23#include "riff.h" 24#include "libavutil/intreadwrite.h" 25 26/* 27 * TODO: 28 * - fill all fields if non streamed (nb_frames for example) 29 */ 30 31typedef struct AVIIentry { 32 unsigned int flags, pos, len; 33} AVIIentry; 34 35#define AVI_INDEX_CLUSTER_SIZE 16384 36 37typedef struct AVIIndex { 38 int64_t indx_start; 39 int entry; 40 int ents_allocated; 41 AVIIentry** cluster; 42} AVIIndex; 43 44typedef struct { 45 int64_t riff_start, movi_list, odml_list; 46 int64_t frames_hdr_all; 47 int riff_id; 48} AVIContext; 49 50typedef struct { 51 int64_t frames_hdr_strm; 52 int audio_strm_length; 53 int packet_count; 54 int entry; 55 56 AVIIndex indexes; 57} AVIStream ; 58 59static inline AVIIentry* avi_get_ientry(AVIIndex* idx, int ent_id) 60{ 61 int cl = ent_id / AVI_INDEX_CLUSTER_SIZE; 62 int id = ent_id % AVI_INDEX_CLUSTER_SIZE; 63 return &idx->cluster[cl][id]; 64} 65 66static int64_t avi_start_new_riff(AVFormatContext *s, ByteIOContext *pb, 67 const char* riff_tag, const char* list_tag) 68{ 69 AVIContext *avi= s->priv_data; 70 int64_t loff; 71 int i; 72 73 avi->riff_id++; 74 for (i=0; i<s->nb_streams; i++){ 75 AVIStream *avist= s->streams[i]->priv_data; 76 avist->indexes.entry = 0; 77 } 78 79 avi->riff_start = ff_start_tag(pb, "RIFF"); 80 put_tag(pb, riff_tag); 81 loff = ff_start_tag(pb, "LIST"); 82 put_tag(pb, list_tag); 83 return loff; 84} 85 86static char* avi_stream2fourcc(char* tag, int index, enum AVMediaType type) 87{ 88 tag[0] = '0'; 89 tag[1] = '0' + index; 90 if (type == AVMEDIA_TYPE_VIDEO) { 91 tag[2] = 'd'; 92 tag[3] = 'c'; 93 } else if (type == AVMEDIA_TYPE_SUBTITLE) { 94 // note: this is not an official code 95 tag[2] = 's'; 96 tag[3] = 'b'; 97 } else { 98 tag[2] = 'w'; 99 tag[3] = 'b'; 100 } 101 tag[4] = '\0'; 102 return tag; 103} 104 105static void avi_write_info_tag(ByteIOContext *pb, const char *tag, const char *str) 106{ 107 int len = strlen(str); 108 if (len > 0) { 109 len++; 110 put_tag(pb, tag); 111 put_le32(pb, len); 112 put_strz(pb, str); 113 if (len & 1) 114 put_byte(pb, 0); 115 } 116} 117 118static int avi_write_counters(AVFormatContext* s, int riff_id) 119{ 120 ByteIOContext *pb = s->pb; 121 AVIContext *avi = s->priv_data; 122 int n, au_byterate, au_ssize, au_scale, nb_frames = 0; 123 int64_t file_size; 124 AVCodecContext* stream; 125 126 file_size = url_ftell(pb); 127 for(n = 0; n < s->nb_streams; n++) { 128 AVIStream *avist= s->streams[n]->priv_data; 129 130 assert(avist->frames_hdr_strm); 131 stream = s->streams[n]->codec; 132 url_fseek(pb, avist->frames_hdr_strm, SEEK_SET); 133 ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); 134 if(au_ssize == 0) { 135 put_le32(pb, avist->packet_count); 136 } else { 137 put_le32(pb, avist->audio_strm_length / au_ssize); 138 } 139 if(stream->codec_type == AVMEDIA_TYPE_VIDEO) 140 nb_frames = FFMAX(nb_frames, avist->packet_count); 141 } 142 if(riff_id == 1) { 143 assert(avi->frames_hdr_all); 144 url_fseek(pb, avi->frames_hdr_all, SEEK_SET); 145 put_le32(pb, nb_frames); 146 } 147 url_fseek(pb, file_size, SEEK_SET); 148 149 return 0; 150} 151 152static int avi_write_header(AVFormatContext *s) 153{ 154 AVIContext *avi = s->priv_data; 155 ByteIOContext *pb = s->pb; 156 int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale; 157 AVCodecContext *stream, *video_enc; 158 int64_t list1, list2, strh, strf; 159 AVMetadataTag *t = NULL; 160 161 for(n=0;n<s->nb_streams;n++) { 162 s->streams[n]->priv_data= av_mallocz(sizeof(AVIStream)); 163 if(!s->streams[n]->priv_data) 164 return AVERROR(ENOMEM); 165 } 166 167 /* header list */ 168 avi->riff_id = 0; 169 list1 = avi_start_new_riff(s, pb, "AVI ", "hdrl"); 170 171 /* avi header */ 172 put_tag(pb, "avih"); 173 put_le32(pb, 14 * 4); 174 bitrate = 0; 175 176 video_enc = NULL; 177 for(n=0;n<s->nb_streams;n++) { 178 stream = s->streams[n]->codec; 179 bitrate += stream->bit_rate; 180 if (stream->codec_type == AVMEDIA_TYPE_VIDEO) 181 video_enc = stream; 182 } 183 184 nb_frames = 0; 185 186 if(video_enc){ 187 put_le32(pb, (uint32_t)(INT64_C(1000000) * video_enc->time_base.num / video_enc->time_base.den)); 188 } else { 189 put_le32(pb, 0); 190 } 191 put_le32(pb, bitrate / 8); /* XXX: not quite exact */ 192 put_le32(pb, 0); /* padding */ 193 if (url_is_streamed(pb)) 194 put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_ISINTERLEAVED); /* flags */ 195 else 196 put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* flags */ 197 avi->frames_hdr_all = url_ftell(pb); /* remember this offset to fill later */ 198 put_le32(pb, nb_frames); /* nb frames, filled later */ 199 put_le32(pb, 0); /* initial frame */ 200 put_le32(pb, s->nb_streams); /* nb streams */ 201 put_le32(pb, 1024 * 1024); /* suggested buffer size */ 202 if(video_enc){ 203 put_le32(pb, video_enc->width); 204 put_le32(pb, video_enc->height); 205 } else { 206 put_le32(pb, 0); 207 put_le32(pb, 0); 208 } 209 put_le32(pb, 0); /* reserved */ 210 put_le32(pb, 0); /* reserved */ 211 put_le32(pb, 0); /* reserved */ 212 put_le32(pb, 0); /* reserved */ 213 214 /* stream list */ 215 for(i=0;i<n;i++) { 216 AVIStream *avist= s->streams[i]->priv_data; 217 list2 = ff_start_tag(pb, "LIST"); 218 put_tag(pb, "strl"); 219 220 stream = s->streams[i]->codec; 221 222 /* stream generic header */ 223 strh = ff_start_tag(pb, "strh"); 224 switch(stream->codec_type) { 225 case AVMEDIA_TYPE_SUBTITLE: 226 // XSUB subtitles behave like video tracks, other subtitles 227 // are not (yet) supported. 228 if (stream->codec_id != CODEC_ID_XSUB) break; 229 case AVMEDIA_TYPE_VIDEO: put_tag(pb, "vids"); break; 230 case AVMEDIA_TYPE_AUDIO: put_tag(pb, "auds"); break; 231// case AVMEDIA_TYPE_TEXT : put_tag(pb, "txts"); break; 232 case AVMEDIA_TYPE_DATA : put_tag(pb, "dats"); break; 233 } 234 if(stream->codec_type == AVMEDIA_TYPE_VIDEO || 235 stream->codec_id == CODEC_ID_XSUB) 236 put_le32(pb, stream->codec_tag); 237 else 238 put_le32(pb, 1); 239 put_le32(pb, 0); /* flags */ 240 put_le16(pb, 0); /* priority */ 241 put_le16(pb, 0); /* language */ 242 put_le32(pb, 0); /* initial frame */ 243 244 ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); 245 246 put_le32(pb, au_scale); /* scale */ 247 put_le32(pb, au_byterate); /* rate */ 248 av_set_pts_info(s->streams[i], 64, au_scale, au_byterate); 249 250 put_le32(pb, 0); /* start */ 251 avist->frames_hdr_strm = url_ftell(pb); /* remember this offset to fill later */ 252 if (url_is_streamed(pb)) 253 put_le32(pb, AVI_MAX_RIFF_SIZE); /* FIXME: this may be broken, but who cares */ 254 else 255 put_le32(pb, 0); /* length, XXX: filled later */ 256 257 /* suggested buffer size */ //FIXME set at the end to largest chunk 258 if(stream->codec_type == AVMEDIA_TYPE_VIDEO) 259 put_le32(pb, 1024 * 1024); 260 else if(stream->codec_type == AVMEDIA_TYPE_AUDIO) 261 put_le32(pb, 12 * 1024); 262 else 263 put_le32(pb, 0); 264 put_le32(pb, -1); /* quality */ 265 put_le32(pb, au_ssize); /* sample size */ 266 put_le32(pb, 0); 267 put_le16(pb, stream->width); 268 put_le16(pb, stream->height); 269 ff_end_tag(pb, strh); 270 271 if(stream->codec_type != AVMEDIA_TYPE_DATA){ 272 strf = ff_start_tag(pb, "strf"); 273 switch(stream->codec_type) { 274 case AVMEDIA_TYPE_SUBTITLE: 275 // XSUB subtitles behave like video tracks, other subtitles 276 // are not (yet) supported. 277 if (stream->codec_id != CODEC_ID_XSUB) break; 278 case AVMEDIA_TYPE_VIDEO: 279 ff_put_bmp_header(pb, stream, ff_codec_bmp_tags, 0); 280 break; 281 case AVMEDIA_TYPE_AUDIO: 282 if (ff_put_wav_header(pb, stream) < 0) { 283 return -1; 284 } 285 break; 286 default: 287 return -1; 288 } 289 ff_end_tag(pb, strf); 290 if ((t = av_metadata_get(s->streams[i]->metadata, "strn", NULL, 0))) { 291 avi_write_info_tag(s->pb, t->key, t->value); 292 t = NULL; 293 } 294 //FIXME a limitation of metadata conversion system 295 else if ((t = av_metadata_get(s->streams[i]->metadata, "INAM", NULL, 0))) { 296 avi_write_info_tag(s->pb, "strn", t->value); 297 t = NULL; 298 } 299 } 300 301 if (!url_is_streamed(pb)) { 302 unsigned char tag[5]; 303 int j; 304 305 /* Starting to lay out AVI OpenDML master index. 306 * We want to make it JUNK entry for now, since we'd 307 * like to get away without making AVI an OpenDML one 308 * for compatibility reasons. 309 */ 310 avist->indexes.entry = avist->indexes.ents_allocated = 0; 311 avist->indexes.indx_start = ff_start_tag(pb, "JUNK"); 312 put_le16(pb, 4); /* wLongsPerEntry */ 313 put_byte(pb, 0); /* bIndexSubType (0 == frame index) */ 314 put_byte(pb, 0); /* bIndexType (0 == AVI_INDEX_OF_INDEXES) */ 315 put_le32(pb, 0); /* nEntriesInUse (will fill out later on) */ 316 put_tag(pb, avi_stream2fourcc(&tag[0], i, stream->codec_type)); 317 /* dwChunkId */ 318 put_le64(pb, 0); /* dwReserved[3] 319 put_le32(pb, 0); Must be 0. */ 320 for (j=0; j < AVI_MASTER_INDEX_SIZE * 2; j++) 321 put_le64(pb, 0); 322 ff_end_tag(pb, avist->indexes.indx_start); 323 } 324 325 if( stream->codec_type == AVMEDIA_TYPE_VIDEO 326 && s->streams[i]->sample_aspect_ratio.num>0 327 && s->streams[i]->sample_aspect_ratio.den>0){ 328 int vprp= ff_start_tag(pb, "vprp"); 329 AVRational dar = av_mul_q(s->streams[i]->sample_aspect_ratio, 330 (AVRational){stream->width, stream->height}); 331 int num, den; 332 av_reduce(&num, &den, dar.num, dar.den, 0xFFFF); 333 334 put_le32(pb, 0); //video format = unknown 335 put_le32(pb, 0); //video standard= unknown 336 put_le32(pb, lrintf(1.0/av_q2d(stream->time_base))); 337 put_le32(pb, stream->width ); 338 put_le32(pb, stream->height); 339 put_le16(pb, den); 340 put_le16(pb, num); 341 put_le32(pb, stream->width ); 342 put_le32(pb, stream->height); 343 put_le32(pb, 1); //progressive FIXME 344 345 put_le32(pb, stream->height); 346 put_le32(pb, stream->width ); 347 put_le32(pb, stream->height); 348 put_le32(pb, stream->width ); 349 put_le32(pb, 0); 350 put_le32(pb, 0); 351 352 put_le32(pb, 0); 353 put_le32(pb, 0); 354 ff_end_tag(pb, vprp); 355 } 356 357 ff_end_tag(pb, list2); 358 } 359 360 if (!url_is_streamed(pb)) { 361 /* AVI could become an OpenDML one, if it grows beyond 2Gb range */ 362 avi->odml_list = ff_start_tag(pb, "JUNK"); 363 put_tag(pb, "odml"); 364 put_tag(pb, "dmlh"); 365 put_le32(pb, 248); 366 for (i = 0; i < 248; i+= 4) 367 put_le32(pb, 0); 368 ff_end_tag(pb, avi->odml_list); 369 } 370 371 ff_end_tag(pb, list1); 372 373 list2 = ff_start_tag(pb, "LIST"); 374 put_tag(pb, "INFO"); 375 for (i = 0; *ff_avi_tags[i]; i++) { 376 if ((t = av_metadata_get(s->metadata, ff_avi_tags[i], NULL, AV_METADATA_MATCH_CASE))) 377 avi_write_info_tag(s->pb, t->key, t->value); 378 } 379 ff_end_tag(pb, list2); 380 381 /* some padding for easier tag editing */ 382 list2 = ff_start_tag(pb, "JUNK"); 383 for (i = 0; i < 1016; i += 4) 384 put_le32(pb, 0); 385 ff_end_tag(pb, list2); 386 387 avi->movi_list = ff_start_tag(pb, "LIST"); 388 put_tag(pb, "movi"); 389 390 put_flush_packet(pb); 391 392 return 0; 393} 394 395static int avi_write_ix(AVFormatContext *s) 396{ 397 ByteIOContext *pb = s->pb; 398 AVIContext *avi = s->priv_data; 399 char tag[5]; 400 char ix_tag[] = "ix00"; 401 int i, j; 402 403 assert(!url_is_streamed(pb)); 404 405 if (avi->riff_id > AVI_MASTER_INDEX_SIZE) 406 return -1; 407 408 for (i=0;i<s->nb_streams;i++) { 409 AVIStream *avist= s->streams[i]->priv_data; 410 int64_t ix, pos; 411 412 avi_stream2fourcc(&tag[0], i, s->streams[i]->codec->codec_type); 413 ix_tag[3] = '0' + i; 414 415 /* Writing AVI OpenDML leaf index chunk */ 416 ix = url_ftell(pb); 417 put_tag(pb, &ix_tag[0]); /* ix?? */ 418 put_le32(pb, avist->indexes.entry * 8 + 24); 419 /* chunk size */ 420 put_le16(pb, 2); /* wLongsPerEntry */ 421 put_byte(pb, 0); /* bIndexSubType (0 == frame index) */ 422 put_byte(pb, 1); /* bIndexType (1 == AVI_INDEX_OF_CHUNKS) */ 423 put_le32(pb, avist->indexes.entry); 424 /* nEntriesInUse */ 425 put_tag(pb, &tag[0]); /* dwChunkId */ 426 put_le64(pb, avi->movi_list);/* qwBaseOffset */ 427 put_le32(pb, 0); /* dwReserved_3 (must be 0) */ 428 429 for (j=0; j<avist->indexes.entry; j++) { 430 AVIIentry* ie = avi_get_ientry(&avist->indexes, j); 431 put_le32(pb, ie->pos + 8); 432 put_le32(pb, ((uint32_t)ie->len & ~0x80000000) | 433 (ie->flags & 0x10 ? 0 : 0x80000000)); 434 } 435 put_flush_packet(pb); 436 pos = url_ftell(pb); 437 438 /* Updating one entry in the AVI OpenDML master index */ 439 url_fseek(pb, avist->indexes.indx_start - 8, SEEK_SET); 440 put_tag(pb, "indx"); /* enabling this entry */ 441 url_fskip(pb, 8); 442 put_le32(pb, avi->riff_id); /* nEntriesInUse */ 443 url_fskip(pb, 16*avi->riff_id); 444 put_le64(pb, ix); /* qwOffset */ 445 put_le32(pb, pos - ix); /* dwSize */ 446 put_le32(pb, avist->indexes.entry); /* dwDuration */ 447 448 url_fseek(pb, pos, SEEK_SET); 449 } 450 return 0; 451} 452 453static int avi_write_idx1(AVFormatContext *s) 454{ 455 ByteIOContext *pb = s->pb; 456 AVIContext *avi = s->priv_data; 457 int64_t idx_chunk; 458 int i; 459 char tag[5]; 460 461 if (!url_is_streamed(pb)) { 462 AVIStream *avist; 463 AVIIentry* ie = 0, *tie; 464 int empty, stream_id = -1; 465 466 idx_chunk = ff_start_tag(pb, "idx1"); 467 for(i=0; i<s->nb_streams; i++){ 468 avist= s->streams[i]->priv_data; 469 avist->entry=0; 470 } 471 472 do { 473 empty = 1; 474 for (i=0; i<s->nb_streams; i++) { 475 avist= s->streams[i]->priv_data; 476 if (avist->indexes.entry <= avist->entry) 477 continue; 478 479 tie = avi_get_ientry(&avist->indexes, avist->entry); 480 if (empty || tie->pos < ie->pos) { 481 ie = tie; 482 stream_id = i; 483 } 484 empty = 0; 485 } 486 if (!empty) { 487 avist= s->streams[stream_id]->priv_data; 488 avi_stream2fourcc(&tag[0], stream_id, 489 s->streams[stream_id]->codec->codec_type); 490 put_tag(pb, &tag[0]); 491 put_le32(pb, ie->flags); 492 put_le32(pb, ie->pos); 493 put_le32(pb, ie->len); 494 avist->entry++; 495 } 496 } while (!empty); 497 ff_end_tag(pb, idx_chunk); 498 499 avi_write_counters(s, avi->riff_id); 500 } 501 return 0; 502} 503 504static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) 505{ 506 AVIContext *avi = s->priv_data; 507 ByteIOContext *pb = s->pb; 508 unsigned char tag[5]; 509 unsigned int flags=0; 510 const int stream_index= pkt->stream_index; 511 AVIStream *avist= s->streams[stream_index]->priv_data; 512 AVCodecContext *enc= s->streams[stream_index]->codec; 513 int size= pkt->size; 514 515// av_log(s, AV_LOG_DEBUG, "%"PRId64" %d %d\n", pkt->dts, avi->packet_count[stream_index], stream_index); 516 while(enc->block_align==0 && pkt->dts != AV_NOPTS_VALUE && pkt->dts > avist->packet_count){ 517 AVPacket empty_packet; 518 519 av_init_packet(&empty_packet); 520 empty_packet.size= 0; 521 empty_packet.data= NULL; 522 empty_packet.stream_index= stream_index; 523 avi_write_packet(s, &empty_packet); 524// av_log(s, AV_LOG_DEBUG, "dup %"PRId64" %d\n", pkt->dts, avi->packet_count[stream_index]); 525 } 526 avist->packet_count++; 527 528 // Make sure to put an OpenDML chunk when the file size exceeds the limits 529 if (!url_is_streamed(pb) && 530 (url_ftell(pb) - avi->riff_start > AVI_MAX_RIFF_SIZE)) { 531 532 avi_write_ix(s); 533 ff_end_tag(pb, avi->movi_list); 534 535 if (avi->riff_id == 1) 536 avi_write_idx1(s); 537 538 ff_end_tag(pb, avi->riff_start); 539 avi->movi_list = avi_start_new_riff(s, pb, "AVIX", "movi"); 540 } 541 542 avi_stream2fourcc(&tag[0], stream_index, enc->codec_type); 543 if(pkt->flags&AV_PKT_FLAG_KEY) 544 flags = 0x10; 545 if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { 546 avist->audio_strm_length += size; 547 } 548 549 if (!url_is_streamed(s->pb)) { 550 AVIIndex* idx = &avist->indexes; 551 int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE; 552 int id = idx->entry % AVI_INDEX_CLUSTER_SIZE; 553 if (idx->ents_allocated <= idx->entry) { 554 idx->cluster = av_realloc(idx->cluster, (cl+1)*sizeof(void*)); 555 if (!idx->cluster) 556 return -1; 557 idx->cluster[cl] = av_malloc(AVI_INDEX_CLUSTER_SIZE*sizeof(AVIIentry)); 558 if (!idx->cluster[cl]) 559 return -1; 560 idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE; 561 } 562 563 idx->cluster[cl][id].flags = flags; 564 idx->cluster[cl][id].pos = url_ftell(pb) - avi->movi_list; 565 idx->cluster[cl][id].len = size; 566 idx->entry++; 567 } 568 569 put_buffer(pb, tag, 4); 570 put_le32(pb, size); 571 put_buffer(pb, pkt->data, size); 572 if (size & 1) 573 put_byte(pb, 0); 574 575 put_flush_packet(pb); 576 return 0; 577} 578 579static int avi_write_trailer(AVFormatContext *s) 580{ 581 AVIContext *avi = s->priv_data; 582 ByteIOContext *pb = s->pb; 583 int res = 0; 584 int i, j, n, nb_frames; 585 int64_t file_size; 586 587 if (!url_is_streamed(pb)){ 588 if (avi->riff_id == 1) { 589 ff_end_tag(pb, avi->movi_list); 590 res = avi_write_idx1(s); 591 ff_end_tag(pb, avi->riff_start); 592 } else { 593 avi_write_ix(s); 594 ff_end_tag(pb, avi->movi_list); 595 ff_end_tag(pb, avi->riff_start); 596 597 file_size = url_ftell(pb); 598 url_fseek(pb, avi->odml_list - 8, SEEK_SET); 599 put_tag(pb, "LIST"); /* Making this AVI OpenDML one */ 600 url_fskip(pb, 16); 601 602 for (n=nb_frames=0;n<s->nb_streams;n++) { 603 AVCodecContext *stream = s->streams[n]->codec; 604 AVIStream *avist= s->streams[n]->priv_data; 605 606 if (stream->codec_type == AVMEDIA_TYPE_VIDEO) { 607 if (nb_frames < avist->packet_count) 608 nb_frames = avist->packet_count; 609 } else { 610 if (stream->codec_id == CODEC_ID_MP2 || stream->codec_id == CODEC_ID_MP3) { 611 nb_frames += avist->packet_count; 612 } 613 } 614 } 615 put_le32(pb, nb_frames); 616 url_fseek(pb, file_size, SEEK_SET); 617 618 avi_write_counters(s, avi->riff_id); 619 } 620 } 621 put_flush_packet(pb); 622 623 for (i=0; i<s->nb_streams; i++) { 624 AVIStream *avist= s->streams[i]->priv_data; 625 for (j=0; j<avist->indexes.ents_allocated/AVI_INDEX_CLUSTER_SIZE; j++) 626 av_free(avist->indexes.cluster[j]); 627 av_freep(&avist->indexes.cluster); 628 avist->indexes.ents_allocated = avist->indexes.entry = 0; 629 } 630 631 return res; 632} 633 634AVOutputFormat avi_muxer = { 635 "avi", 636 NULL_IF_CONFIG_SMALL("AVI format"), 637 "video/x-msvideo", 638 "avi", 639 sizeof(AVIContext), 640 CODEC_ID_MP2, 641 CODEC_ID_MPEG4, 642 avi_write_header, 643 avi_write_packet, 644 avi_write_trailer, 645 .codec_tag= (const AVCodecTag* const []){ff_codec_bmp_tags, ff_codec_wav_tags, 0}, 646 .flags= AVFMT_VARIABLE_FPS, 647 .metadata_conv = ff_avi_metadata_conv, 648}; 649