avienc.c 18.0 KB
Newer Older
F
Fabrice Bellard 已提交
1
/*
2
 * AVI muxer
F
Fabrice Bellard 已提交
3
 * Copyright (c) 2000 Fabrice Bellard.
F
Fabrice Bellard 已提交
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
F
Fabrice Bellard 已提交
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
F
Fabrice Bellard 已提交
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
F
Fabrice Bellard 已提交
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
F
Fabrice Bellard 已提交
14 15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
F
Fabrice Bellard 已提交
16
 *
F
Fabrice Bellard 已提交
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
F
Fabrice Bellard 已提交
20 21 22
 */
#include "avformat.h"
#include "avi.h"
23
#include "riff.h"
F
Fabrice Bellard 已提交
24 25

/*
26
 * TODO:
F
Fabrice Bellard 已提交
27 28 29
 *  - fill all fields if non streamed (nb_frames for example)
 */

30
#ifdef CONFIG_AVI_MUXER
31
typedef struct AVIIentry {
F
Fabrice Bellard 已提交
32
    unsigned int flags, pos, len;
33 34 35 36 37 38 39 40 41
} AVIIentry;

#define AVI_INDEX_CLUSTER_SIZE 16384

typedef struct AVIIndex {
    offset_t    indx_start;
    int         entry;
    int         ents_allocated;
    AVIIentry** cluster;
F
Fabrice Bellard 已提交
42 43 44
} AVIIndex;

typedef struct {
45 46
    offset_t riff_start, movi_list, odml_list;
    offset_t frames_hdr_all, frames_hdr_strm[MAX_STREAMS];
47
    int audio_strm_length[MAX_STREAMS];
48
    int riff_id;
49
    int packet_count[MAX_STREAMS];
50 51

    AVIIndex indexes[MAX_STREAMS];
F
Fabrice Bellard 已提交
52 53
} AVIContext;

54
static inline AVIIentry* avi_get_ientry(AVIIndex* idx, int ent_id)
55 56 57 58 59 60
{
    int cl = ent_id / AVI_INDEX_CLUSTER_SIZE;
    int id = ent_id % AVI_INDEX_CLUSTER_SIZE;
    return &idx->cluster[cl][id];
}

61
static offset_t avi_start_new_riff(AVIContext *avi, ByteIOContext *pb,
62 63 64
                                   const char* riff_tag, const char* list_tag)
{
    offset_t loff;
65
    int i;
66

67 68 69
    avi->riff_id++;
    for (i=0; i<MAX_STREAMS; i++)
         avi->indexes[i].entry = 0;
70

71 72 73 74 75 76 77
    avi->riff_start = start_tag(pb, "RIFF");
    put_tag(pb, riff_tag);
    loff = start_tag(pb, "LIST");
    put_tag(pb, list_tag);
    return loff;
}

M
Måns Rullgård 已提交
78
static char* avi_stream2fourcc(char* tag, int index, enum CodecType type)
79 80 81 82 83 84 85 86 87 88 89 90 91 92
{
    tag[0] = '0';
    tag[1] = '0' + index;
    if (type == CODEC_TYPE_VIDEO) {
        tag[2] = 'd';
        tag[3] = 'c';
    } else {
        tag[2] = 'w';
        tag[3] = 'b';
    }
    tag[4] = '\0';
    return tag;
}

93 94 95 96 97 98 99 100 101 102 103 104 105
static void avi_write_info_tag(ByteIOContext *pb, const char *tag, const char *str)
{
    int len = strlen(str);
    if (len > 0) {
        len++;
        put_tag(pb, tag);
        put_le32(pb, len);
        put_strz(pb, str);
        if (len & 1)
            put_byte(pb, 0);
    }
}

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
static int avi_write_counters(AVFormatContext* s, int riff_id)
{
    ByteIOContext *pb = &s->pb;
    AVIContext *avi = s->priv_data;
    int n, au_byterate, au_ssize, au_scale, nb_frames = 0;
    offset_t file_size;
    AVCodecContext* stream;

    file_size = url_ftell(pb);
    for(n = 0; n < s->nb_streams; n++) {
        assert(avi->frames_hdr_strm[n]);
        stream = s->streams[n]->codec;
        url_fseek(pb, avi->frames_hdr_strm[n], SEEK_SET);
        ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
        if(au_ssize == 0) {
            put_le32(pb, avi->packet_count[n]);
        } else {
            put_le32(pb, avi->audio_strm_length[n] / au_ssize);
        }
        if(stream->codec_type == CODEC_TYPE_VIDEO)
            nb_frames = FFMAX(nb_frames, avi->packet_count[n]);
    }
    if(riff_id == 1) {
        assert(avi->frames_hdr_all);
        url_fseek(pb, avi->frames_hdr_all, SEEK_SET);
        put_le32(pb, nb_frames);
    }
    url_fseek(pb, file_size, SEEK_SET);

    return 0;
}

F
Fabrice Bellard 已提交
138 139
static int avi_write_header(AVFormatContext *s)
{
F
Fabrice Bellard 已提交
140
    AVIContext *avi = s->priv_data;
F
Fabrice Bellard 已提交
141
    ByteIOContext *pb = &s->pb;
142
    int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale;
F
Fabrice Bellard 已提交
143 144 145 146
    AVCodecContext *stream, *video_enc;
    offset_t list1, list2, strh, strf;

    /* header list */
147
    avi->riff_id = 0;
148
    list1 = avi_start_new_riff(avi, pb, "AVI ", "hdrl");
F
Fabrice Bellard 已提交
149 150 151 152 153 154 155 156

    /* avi header */
    put_tag(pb, "avih");
    put_le32(pb, 14 * 4);
    bitrate = 0;

    video_enc = NULL;
    for(n=0;n<s->nb_streams;n++) {
157
        stream = s->streams[n]->codec;
F
Fabrice Bellard 已提交
158 159 160 161
        bitrate += stream->bit_rate;
        if (stream->codec_type == CODEC_TYPE_VIDEO)
            video_enc = stream;
    }
162

F
Fabrice Bellard 已提交
163 164
    nb_frames = 0;

165
    if(video_enc){
166
        put_le32(pb, (uint32_t)(INT64_C(1000000) * video_enc->time_base.num / video_enc->time_base.den));
167
    } else {
168
        put_le32(pb, 0);
169
    }
F
Fabrice Bellard 已提交
170 171
    put_le32(pb, bitrate / 8); /* XXX: not quite exact */
    put_le32(pb, 0); /* padding */
172
    if (url_is_streamed(pb))
173
        put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_ISINTERLEAVED); /* flags */
174
    else
175
        put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* flags */
176
    avi->frames_hdr_all = url_ftell(pb); /* remember this offset to fill later */
F
Fabrice Bellard 已提交
177 178 179 180
    put_le32(pb, nb_frames); /* nb frames, filled later */
    put_le32(pb, 0); /* initial frame */
    put_le32(pb, s->nb_streams); /* nb streams */
    put_le32(pb, 1024 * 1024); /* suggested buffer size */
181
    if(video_enc){
M
Michael Niedermayer 已提交
182 183
        put_le32(pb, video_enc->width);
        put_le32(pb, video_enc->height);
184
    } else {
185 186
        put_le32(pb, 0);
        put_le32(pb, 0);
187
    }
F
Fabrice Bellard 已提交
188 189 190 191
    put_le32(pb, 0); /* reserved */
    put_le32(pb, 0); /* reserved */
    put_le32(pb, 0); /* reserved */
    put_le32(pb, 0); /* reserved */
192

F
Fabrice Bellard 已提交
193 194 195 196
    /* stream list */
    for(i=0;i<n;i++) {
        list2 = start_tag(pb, "LIST");
        put_tag(pb, "strl");
197

198
        stream = s->streams[i]->codec;
F
Fabrice Bellard 已提交
199 200 201 202

        /* stream generic header */
        strh = start_tag(pb, "strh");
        switch(stream->codec_type) {
203 204 205 206
        case CODEC_TYPE_VIDEO: put_tag(pb, "vids"); break;
        case CODEC_TYPE_AUDIO: put_tag(pb, "auds"); break;
//        case CODEC_TYPE_TEXT : put_tag(pb, "txts"); break;
        case CODEC_TYPE_DATA : put_tag(pb, "dats"); break;
F
Fabrice Bellard 已提交
207
        }
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
        if(stream->codec_type == CODEC_TYPE_VIDEO)
            put_le32(pb, stream->codec_tag);
        else
            put_le32(pb, 1);
        put_le32(pb, 0); /* flags */
        put_le16(pb, 0); /* priority */
        put_le16(pb, 0); /* language */
        put_le32(pb, 0); /* initial frame */

        ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);

        put_le32(pb, au_scale); /* scale */
        put_le32(pb, au_byterate); /* rate */
        av_set_pts_info(s->streams[i], 64, au_scale, au_byterate);

        put_le32(pb, 0); /* start */
        avi->frames_hdr_strm[i] = url_ftell(pb); /* remember this offset to fill later */
225 226 227 228
        if (url_is_streamed(pb))
            put_le32(pb, AVI_MAX_RIFF_SIZE); /* FIXME: this may be broken, but who cares */
        else
            put_le32(pb, 0); /* length, XXX: filled later */
229

230 231
        /* suggested buffer size */ //FIXME set at the end to largest chunk
        if(stream->codec_type == CODEC_TYPE_VIDEO)
232
            put_le32(pb, 1024 * 1024);
233
        else if(stream->codec_type == CODEC_TYPE_AUDIO)
234
            put_le32(pb, 12 * 1024);
235
        else
236
            put_le32(pb, 0);
237 238 239 240 241
        put_le32(pb, -1); /* quality */
        put_le32(pb, au_ssize); /* sample size */
        put_le32(pb, 0);
        put_le16(pb, stream->width);
        put_le16(pb, stream->height);
F
Fabrice Bellard 已提交
242 243
        end_tag(pb, strh);

244
      if(stream->codec_type != CODEC_TYPE_DATA){
F
Fabrice Bellard 已提交
245 246 247
        strf = start_tag(pb, "strf");
        switch(stream->codec_type) {
        case CODEC_TYPE_VIDEO:
248
            put_bmp_header(pb, stream, codec_bmp_tags, 0);
F
Fabrice Bellard 已提交
249 250
            break;
        case CODEC_TYPE_AUDIO:
251
            if (put_wav_header(pb, stream) < 0) {
252
                av_free(avi);
253 254
                return -1;
            }
F
Fabrice Bellard 已提交
255
            break;
256
        default:
M
Michael Niedermayer 已提交
257
            return -1;
F
Fabrice Bellard 已提交
258 259
        }
        end_tag(pb, strf);
260
      }
261

262 263 264
        if (!url_is_streamed(pb)) {
            unsigned char tag[5];
            int j;
265 266

            /* Starting to lay out AVI OpenDML master index.
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
             * We want to make it JUNK entry for now, since we'd
             * like to get away without making AVI an OpenDML one
             * for compatibility reasons.
             */
            avi->indexes[i].entry = avi->indexes[i].ents_allocated = 0;
            avi->indexes[i].indx_start = start_tag(pb, "JUNK");
            put_le16(pb, 4);        /* wLongsPerEntry */
            put_byte(pb, 0);        /* bIndexSubType (0 == frame index) */
            put_byte(pb, 0);        /* bIndexType (0 == AVI_INDEX_OF_INDEXES) */
            put_le32(pb, 0);        /* nEntriesInUse (will fill out later on) */
            put_tag(pb, avi_stream2fourcc(&tag[0], i, stream->codec_type));
                                    /* dwChunkId */
            put_le64(pb, 0);        /* dwReserved[3]
            put_le32(pb, 0);           Must be 0.    */
            for (j=0; j < AVI_MASTER_INDEX_SIZE * 2; j++)
                 put_le64(pb, 0);
            end_tag(pb, avi->indexes[i].indx_start);
        }
285

F
Fabrice Bellard 已提交
286 287
        end_tag(pb, list2);
    }
288

289 290 291 292 293 294 295 296 297 298
    if (!url_is_streamed(pb)) {
        /* AVI could become an OpenDML one, if it grows beyond 2Gb range */
        avi->odml_list = start_tag(pb, "JUNK");
        put_tag(pb, "odml");
        put_tag(pb, "dmlh");
        put_le32(pb, 248);
        for (i = 0; i < 248; i+= 4)
             put_le32(pb, 0);
        end_tag(pb, avi->odml_list);
    }
F
Fabrice Bellard 已提交
299 300

    end_tag(pb, list1);
301

302 303 304 305 306 307 308 309
    list2 = start_tag(pb, "LIST");
    put_tag(pb, "INFO");
    avi_write_info_tag(pb, "INAM", s->title);
    avi_write_info_tag(pb, "IART", s->author);
    avi_write_info_tag(pb, "ICOP", s->copyright);
    avi_write_info_tag(pb, "ICMT", s->comment);
    avi_write_info_tag(pb, "IPRD", s->album);
    avi_write_info_tag(pb, "IGNR", s->genre);
310 311 312 313 314
    if (s->track) {
        char str_track[4];
        snprintf(str_track, 4, "%d", s->track);
        avi_write_info_tag(pb, "IPRT", str_track);
    }
315 316 317 318 319 320 321 322 323 324
    if(!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT))
        avi_write_info_tag(pb, "ISFT", LIBAVFORMAT_IDENT);
    end_tag(pb, list2);

    /* some padding for easier tag editing */
    list2 = start_tag(pb, "JUNK");
    for (i = 0; i < 1016; i += 4)
        put_le32(pb, 0);
    end_tag(pb, list2);

F
Fabrice Bellard 已提交
325 326 327 328 329 330 331 332
    avi->movi_list = start_tag(pb, "LIST");
    put_tag(pb, "movi");

    put_flush_packet(pb);

    return 0;
}

333 334 335 336
static int avi_write_ix(AVFormatContext *s)
{
    ByteIOContext *pb = &s->pb;
    AVIContext *avi = s->priv_data;
M
Måns Rullgård 已提交
337 338
    char tag[5];
    char ix_tag[] = "ix00";
339
    int i, j;
340

341
    assert(!url_is_streamed(pb));
342

343 344
    if (avi->riff_id > AVI_MASTER_INDEX_SIZE)
        return -1;
345

346
    for (i=0;i<s->nb_streams;i++) {
347
         offset_t ix, pos;
348

349 350
         avi_stream2fourcc(&tag[0], i, s->streams[i]->codec->codec_type);
         ix_tag[3] = '0' + i;
351

352 353 354 355 356
         /* Writing AVI OpenDML leaf index chunk */
         ix = url_ftell(pb);
         put_tag(pb, &ix_tag[0]);     /* ix?? */
         put_le32(pb, avi->indexes[i].entry * 8 + 24);
                                      /* chunk size */
357
         put_le16(pb, 2);             /* wLongsPerEntry */
358 359 360 361 362 363 364
         put_byte(pb, 0);             /* bIndexSubType (0 == frame index) */
         put_byte(pb, 1);             /* bIndexType (1 == AVI_INDEX_OF_CHUNKS) */
         put_le32(pb, avi->indexes[i].entry);
                                      /* nEntriesInUse */
         put_tag(pb, &tag[0]);        /* dwChunkId */
         put_le64(pb, avi->movi_list);/* qwBaseOffset */
         put_le32(pb, 0);             /* dwReserved_3 (must be 0) */
365 366 367

         for (j=0; j<avi->indexes[i].entry; j++) {
             AVIIentry* ie = avi_get_ientry(&avi->indexes[i], j);
368 369 370
             put_le32(pb, ie->pos + 8);
             put_le32(pb, ((uint32_t)ie->len & ~0x80000000) |
                          (ie->flags & 0x10 ? 0 : 0x80000000));
371
         }
372
         put_flush_packet(pb);
373
         pos = url_ftell(pb);
374

375 376 377 378 379 380 381 382 383 384 385
         /* Updating one entry in the AVI OpenDML master index */
         url_fseek(pb, avi->indexes[i].indx_start - 8, SEEK_SET);
         put_tag(pb, "indx");                 /* enabling this entry */
         url_fskip(pb, 8);
         put_le32(pb, avi->riff_id);          /* nEntriesInUse */
         url_fskip(pb, 16*avi->riff_id);
         put_le64(pb, ix);                    /* qwOffset */
         put_le32(pb, pos - ix);              /* dwSize */
         put_le32(pb, avi->indexes[i].entry); /* dwDuration */

         url_fseek(pb, pos, SEEK_SET);
386 387 388 389 390
    }
    return 0;
}

static int avi_write_idx1(AVFormatContext *s)
391 392 393
{
    ByteIOContext *pb = &s->pb;
    AVIContext *avi = s->priv_data;
394 395
    offset_t idx_chunk;
    int i;
M
Måns Rullgård 已提交
396
    char tag[5];
397 398

    if (!url_is_streamed(pb)) {
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
        AVIIentry* ie = 0, *tie;
        int entry[MAX_STREAMS];
        int empty, stream_id = -1;

        idx_chunk = start_tag(pb, "idx1");
        memset(&entry[0], 0, sizeof(entry));
        do {
            empty = 1;
            for (i=0; i<s->nb_streams; i++) {
                 if (avi->indexes[i].entry <= entry[i])
                     continue;

                 tie = avi_get_ientry(&avi->indexes[i], entry[i]);
                 if (empty || tie->pos < ie->pos) {
                     ie = tie;
                     stream_id = i;
                 }
                 empty = 0;
            }
            if (!empty) {
                avi_stream2fourcc(&tag[0], stream_id,
                                  s->streams[stream_id]->codec->codec_type);
                put_tag(pb, &tag[0]);
                put_le32(pb, ie->flags);
423 424
                put_le32(pb, ie->pos);
                put_le32(pb, ie->len);
425 426 427 428
                entry[stream_id]++;
            }
        } while (!empty);
        end_tag(pb, idx_chunk);
429

430
        avi_write_counters(s, avi->riff_id);
431 432 433 434
    }
    return 0;
}

435
static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
F
Fabrice Bellard 已提交
436 437 438 439
{
    AVIContext *avi = s->priv_data;
    ByteIOContext *pb = &s->pb;
    unsigned char tag[5];
440 441
    unsigned int flags=0;
    const int stream_index= pkt->stream_index;
442
    AVCodecContext *enc= s->streams[stream_index]->codec;
443
    int size= pkt->size;
444

445
//    av_log(s, AV_LOG_DEBUG, "%"PRId64" %d %d\n", pkt->dts, avi->packet_count[stream_index], stream_index);
M
Michael Niedermayer 已提交
446
    while(enc->block_align==0 && pkt->dts != AV_NOPTS_VALUE && pkt->dts > avi->packet_count[stream_index]){
447 448 449 450 451 452 453
        AVPacket empty_packet;

        av_init_packet(&empty_packet);
        empty_packet.size= 0;
        empty_packet.data= NULL;
        empty_packet.stream_index= stream_index;
        avi_write_packet(s, &empty_packet);
454
//        av_log(s, AV_LOG_DEBUG, "dup %"PRId64" %d\n", pkt->dts, avi->packet_count[stream_index]);
455 456 457
    }
    avi->packet_count[stream_index]++;

458
    // Make sure to put an OpenDML chunk when the file size exceeds the limits
459
    if (!url_is_streamed(pb) &&
460
        (url_ftell(pb) - avi->riff_start > AVI_MAX_RIFF_SIZE)) {
461

462 463
        avi_write_ix(s);
        end_tag(pb, avi->movi_list);
464

465 466
        if (avi->riff_id == 1)
            avi_write_idx1(s);
467

468 469
        end_tag(pb, avi->riff_start);
        avi->movi_list = avi_start_new_riff(avi, pb, "AVIX", "movi");
470
    }
471

472
    avi_stream2fourcc(&tag[0], stream_index, enc->codec_type);
473 474
    if(pkt->flags&PKT_FLAG_KEY)
        flags = 0x10;
475
    if (enc->codec_type == CODEC_TYPE_AUDIO) {
476
       avi->audio_strm_length[stream_index] += size;
477
    }
F
Fabrice Bellard 已提交
478 479

    if (!url_is_streamed(&s->pb)) {
480
        AVIIndex* idx = &avi->indexes[stream_index];
481 482
        int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE;
        int id = idx->entry % AVI_INDEX_CLUSTER_SIZE;
483
        if (idx->ents_allocated <= idx->entry) {
484 485 486
            idx->cluster = av_realloc(idx->cluster, (cl+1)*sizeof(void*));
            if (!idx->cluster)
                return -1;
487
            idx->cluster[cl] = av_malloc(AVI_INDEX_CLUSTER_SIZE*sizeof(AVIIentry));
488 489 490 491
            if (!idx->cluster[cl])
                return -1;
            idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE;
        }
492

493
        idx->cluster[cl][id].flags = flags;
494 495
        idx->cluster[cl][id].pos = url_ftell(pb) - avi->movi_list;
        idx->cluster[cl][id].len = size;
496
        idx->entry++;
F
Fabrice Bellard 已提交
497
    }
498

F
Fabrice Bellard 已提交
499 500
    put_buffer(pb, tag, 4);
    put_le32(pb, size);
501
    put_buffer(pb, pkt->data, size);
F
Fabrice Bellard 已提交
502 503 504 505 506 507 508 509 510 511
    if (size & 1)
        put_byte(pb, 0);

    put_flush_packet(pb);
    return 0;
}

static int avi_write_trailer(AVFormatContext *s)
{
    AVIContext *avi = s->priv_data;
512 513
    ByteIOContext *pb = &s->pb;
    int res = 0;
514 515 516
    int i, j, n, nb_frames;
    offset_t file_size;

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
    if (!url_is_streamed(pb)){
        if (avi->riff_id == 1) {
            end_tag(pb, avi->movi_list);
            res = avi_write_idx1(s);
            end_tag(pb, avi->riff_start);
        } else {
            avi_write_ix(s);
            end_tag(pb, avi->movi_list);
            end_tag(pb, avi->riff_start);

            file_size = url_ftell(pb);
            url_fseek(pb, avi->odml_list - 8, SEEK_SET);
            put_tag(pb, "LIST"); /* Making this AVI OpenDML one */
            url_fskip(pb, 16);

            for (n=nb_frames=0;n<s->nb_streams;n++) {
                AVCodecContext *stream = s->streams[n]->codec;
                if (stream->codec_type == CODEC_TYPE_VIDEO) {
                    if (nb_frames < avi->packet_count[n])
                        nb_frames = avi->packet_count[n];
                } else {
                    if (stream->codec_id == CODEC_ID_MP2 || stream->codec_id == CODEC_ID_MP3) {
                        nb_frames += avi->packet_count[n];
                    }
541 542
                }
            }
543 544
            put_le32(pb, nb_frames);
            url_fseek(pb, file_size, SEEK_SET);
545

546 547
            avi_write_counters(s, avi->riff_id);
        }
548
    }
F
Fabrice Bellard 已提交
549
    put_flush_packet(pb);
550 551

    for (i=0; i<MAX_STREAMS; i++) {
552
         for (j=0; j<avi->indexes[i].ents_allocated/AVI_INDEX_CLUSTER_SIZE; j++)
553
              av_free(avi->indexes[i].cluster[j]);
554 555 556
         av_free(avi->indexes[i].cluster);
         avi->indexes[i].cluster = NULL;
         avi->indexes[i].ents_allocated = avi->indexes[i].entry = 0;
557
    }
558

559
    return res;
F
Fabrice Bellard 已提交
560 561
}

562
AVOutputFormat avi_muxer = {
F
Fabrice Bellard 已提交
563 564 565 566
    "avi",
    "avi format",
    "video/x-msvideo",
    "avi",
F
Fabrice Bellard 已提交
567
    sizeof(AVIContext),
F
Fabrice Bellard 已提交
568
    CODEC_ID_MP2,
569
    CODEC_ID_MPEG4,
F
Fabrice Bellard 已提交
570 571 572
    avi_write_header,
    avi_write_packet,
    avi_write_trailer,
573
    .codec_tag= (const AVCodecTag*[]){codec_bmp_tags, codec_wav_tags, 0},
F
Fabrice Bellard 已提交
574
};
575
#endif //CONFIG_AVI_MUXER