Initial version of libomxil-vc4 for RPI3
[platform/adaptation/broadcom/libomxil-vc4.git] / containers / avi / avi_writer.c
1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the copyright holder nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #include <limits.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define CONTAINER_IS_LITTLE_ENDIAN
33 //#define ENABLE_CONTAINERS_LOG_FORMAT
34 //#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
35 #define CONTAINER_HELPER_LOG_INDENT(a) 0
36 #include "containers/core/containers_private.h"
37 #include "containers/core/containers_io_helpers.h"
38 #include "containers/core/containers_utils.h"
39 #include "containers/core/containers_writer_utils.h"
40 #include "containers/core/containers_logging.h"
41
42 /******************************************************************************
43 Function prototypes
44 ******************************************************************************/
45 VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx );
46
47 /******************************************************************************
48 Defines.
49 ******************************************************************************/
50 #define AVISF_DISABLED        0x00000001 /*< If set stream should not be enabled by default. */
51 #define AVIF_HASINDEX         0x00000010 
52 #define AVIF_TRUSTCKTYPE      0x00000800
53 #define AVIIF_KEYFRAME        0x00000010
54
55 #define AVI_INDEX_OF_INDEXES  0x00
56 #define AVI_INDEX_OF_CHUNKS   0x01
57 #define AVI_INDEX_DELTAFRAME  0x80000000
58
59 #define AVI_INDEX_ENTRY_SIZE        16
60 #define AVI_SUPER_INDEX_ENTRY_SIZE  16
61 #define AVI_STD_INDEX_ENTRY_SIZE     8
62 #define AVI_FRAME_BUFFER_SIZE       100000
63
64 #define AVI_TRACKS_MAX 3
65
66 #define AVI_AUDIO_CHUNK_SIZE_LIMIT 16384 /*< Watermark limit for data chunks when 'dwSampleSize'
67                                              is non-zero */
68
69 #define AVI_END_CHUNK(ctx)                                            \
70    do {                                                               \
71       if(STREAM_POSITION(ctx) & 1) WRITE_U8(ctx, 0, "AVI_END_CHUNK"); \
72    } while(0)
73
74 #define AVI_PACKET_KEYFRAME (VC_CONTAINER_PACKET_FLAG_KEYFRAME | VC_CONTAINER_PACKET_FLAG_FRAME_END)
75 #define AVI_PACKET_IS_KEYFRAME(flags) (((flags) & AVI_PACKET_KEYFRAME) == AVI_PACKET_KEYFRAME)
76
77 /******************************************************************************
78 Type definitions.
79 ******************************************************************************/
80 typedef struct VC_CONTAINER_TRACK_MODULE_T
81 {
82    uint32_t chunk_index;      /**< index of current chunk */
83    uint32_t chunk_offs;       /**< current offset into bytestream consisting of all 
84                                    chunks for this track  */
85    uint32_t sample_size;      /**< i.e. 'dwSampleSize' in 'strh' */
86    uint32_t max_chunk_size;   /**< largest chunk written so far */
87    uint64_t index_offset;     /**< Offset to the start of an OpenDML index for this track 
88                                    i.e. 'indx' */
89    uint32_t index_size;       /**< Size of the OpenDML index for this track i.e. 'indx' */
90 } VC_CONTAINER_TRACK_MODULE_T;
91
92 typedef struct VC_CONTAINER_MODULE_T
93 {
94    VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX];
95    VC_CONTAINER_WRITER_EXTRAIO_T null_io; /**< Null I/O for calculating chunk sizes, etc. */
96    VC_CONTAINER_WRITER_EXTRAIO_T temp_io; /**< I/O for temporary storage of index data */
97    int headers_written;
98
99    uint32_t header_list_offset;           /**< Offset to the header list chunk ('hdrl') */
100    uint32_t header_list_size;             /**< Size of the header list chunk ('hdrl') */
101    uint32_t data_offset;                  /**< Offset to the start of data packets i.e. 
102                                                the data in the AVI RIFF 'movi' list */
103    uint64_t data_size;                    /**< Size of the chunk containing data packets */
104    uint32_t index_offset;                 /**< Offset to the start of index data e.g. 
105                                                the data in an 'idx1' list */                                          
106    unsigned current_track_num;            /**< Number of track currently being written */
107    uint32_t chunk_size;                   /**< Final size of the current chunk being written (if known) */
108    uint32_t chunk_data_written;           /**< Data written to the current chunk so far */
109    uint8_t *avi_frame_buffer;             /**< For accumulating whole frames when seeking isn't available. */
110    VC_CONTAINER_PACKET_T frame_packet;    /**< Packet header for whole frame. */
111
112    VC_CONTAINER_STATUS_T index_status;
113 } VC_CONTAINER_MODULE_T;
114
115 /******************************************************************************
116 Local Functions
117 ******************************************************************************/
118 static void avi_chunk_id_from_track_num( VC_CONTAINER_T *p_ctx, 
119    VC_CONTAINER_FOURCC_T *p_chunk_id, unsigned int track_num )
120 {
121    VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num];
122    VC_CONTAINER_FOURCC_T chunk_id = 0;
123    char track_num_buf[3];
124
125    if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
126       chunk_id = VC_FOURCC('0','0','d','c');
127    else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
128       chunk_id = VC_FOURCC('0','0','w','b');
129    else
130    {
131       /* Note that avi_writer_add_track should ensure this 
132          can't happen */
133       *p_chunk_id  = VC_FOURCC('J','U','N','K'); return;
134    }
135
136    snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num);
137    memcpy(&chunk_id, track_num_buf, 2);
138
139    *p_chunk_id = chunk_id;
140 }
141
142 /*****************************************************************************/
143 static void avi_index_chunk_id_from_track_num(VC_CONTAINER_FOURCC_T *p_chunk_id, 
144    unsigned int track_num )
145 {
146    VC_CONTAINER_FOURCC_T chunk_id = 0;
147    char track_num_buf[3];
148
149    chunk_id = VC_FOURCC('i','x','0','0');
150
151    snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num);
152    memcpy(((uint8_t*)&chunk_id) + 2, track_num_buf, 2);
153
154    *p_chunk_id = chunk_id;
155 }
156
157 /*****************************************************************************/
158 static uint32_t avi_num_chunks( VC_CONTAINER_T *p_ctx )
159 {
160    unsigned int i;
161    uint32_t num_chunks = 0;
162    for (i = 0; i < p_ctx->tracks_num; i++)
163       num_chunks += p_ctx->tracks[i]->priv->module->chunk_index;
164
165    return num_chunks;
166 }
167
168 /*****************************************************************************/
169 static VC_CONTAINER_STATUS_T avi_finish_data_chunk( VC_CONTAINER_T *p_ctx, uint32_t chunk_size )
170 {
171    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
172  
173    if (chunk_size)
174    {
175       /* Rewrite the chunk size, this won't be efficient if it happens often */
176       if (STREAM_SEEKABLE(p_ctx))
177       {
178          SEEK(p_ctx, STREAM_POSITION(p_ctx) - chunk_size - 4);
179          WRITE_U32(p_ctx, chunk_size, "Chunk Size");
180          SKIP_BYTES(p_ctx, chunk_size);
181       }
182       else
183       {
184          LOG_DEBUG(p_ctx, "warning, can't rewrite chunk size, data will be malformed");
185          status = VC_CONTAINER_ERROR_FAILED;
186       }
187    }
188       
189    AVI_END_CHUNK(p_ctx);
190
191    if (status != VC_CONTAINER_SUCCESS) status = STREAM_STATUS(p_ctx);
192
193    return status;
194 }
195
196 /*****************************************************************************/
197 static VC_CONTAINER_STATUS_T avi_write_index_entry( VC_CONTAINER_T *p_ctx, uint8_t track_num, 
198    uint32_t chunk_size, int keyframe )
199 {
200    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
201    uint32_t deltaframe = keyframe ? 0 : AVI_INDEX_DELTAFRAME;
202
203    vc_container_io_write_uint8(module->temp_io.io, track_num);
204    vc_container_io_write_be_uint32(module->temp_io.io, chunk_size | deltaframe);
205
206    if (module->temp_io.io->status != VC_CONTAINER_SUCCESS)
207    {
208       module->index_status = module->temp_io.io->status;
209       LOG_DEBUG(p_ctx, "warning, couldn't store index data, index data will be incorrect");
210    }
211
212    return module->temp_io.io->status;
213 }
214
215 /*****************************************************************************/
216 static VC_CONTAINER_STATUS_T avi_read_index_entry( VC_CONTAINER_T *p_ctx,
217    unsigned int *p_track_num, uint32_t *p_chunk_size )
218 {
219    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
220    uint32_t chunk_size;
221    uint8_t track_num;
222    
223    track_num = vc_container_io_read_uint8(module->temp_io.io);
224    chunk_size = vc_container_io_read_be_uint32(module->temp_io.io);
225
226    /* This shouldn't really happen if the temporary I/O is reliable */
227    if (track_num >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED;
228       
229    *p_track_num = track_num;
230    *p_chunk_size = chunk_size;
231
232    return module->temp_io.io->status;
233 }
234
235 /*****************************************************************************/
236 static VC_CONTAINER_STATUS_T avi_write_stream_format_chunk(VC_CONTAINER_T *p_ctx, 
237    VC_CONTAINER_TRACK_T *track, uint32_t chunk_size)
238 {
239    VC_CONTAINER_STATUS_T status;
240    
241    WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','f'), "Chunk ID");
242    WRITE_U32(p_ctx, chunk_size, "Chunk Size");
243   
244    if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
245   
246    if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
247       status = vc_container_write_bitmapinfoheader(p_ctx, track->format);
248    else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
249       status = vc_container_write_waveformatex(p_ctx, track->format);
250
251    if (status != VC_CONTAINER_SUCCESS) return status;
252
253    AVI_END_CHUNK(p_ctx);
254    
255    return STREAM_STATUS(p_ctx);
256 }
257
258 /*****************************************************************************/
259 static VC_CONTAINER_STATUS_T avi_write_stream_header_chunk(VC_CONTAINER_T *p_ctx, 
260    VC_CONTAINER_TRACK_T *track)
261 {
262    VC_CONTAINER_FOURCC_T fourcc_type = 0, fourcc_handler = 0;
263    uint32_t flags, scale = 0, rate = 0, div, start = 0, sample_size = 0;
264    uint16_t left = 0, right = 0, top = 0, bottom = 0;
265    uint32_t max_chunk_size, length = 0;
266    
267    WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','h'), "Chunk ID");
268    WRITE_U32(p_ctx, 56, "Chunk Size");
269
270    if (!track->is_enabled)
271       flags = 0; /* AVISF_DISABLED; FIXME: write_media should set this correctly! */
272    else
273       flags = 0;
274
275    if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
276    {
277       fourcc_type = VC_FOURCC('v','i','d','s');
278       sample_size = 0;
279       scale = track->format->type->video.frame_rate_den;
280       rate = track->format->type->video.frame_rate_num;
281       if (rate == 0 || scale == 0)
282       {
283          LOG_DEBUG(p_ctx, "invalid video framerate (%d/%d)", rate, scale);
284          LOG_DEBUG(p_ctx, "using 30/1 (playback timing will almost certainly be incorrect)");
285          scale = 1; rate = 30;
286       }
287
288       top = track->format->type->video.y_offset;
289       left = track->format->type->video.x_offset;
290       bottom = track->format->type->video.y_offset + track->format->type->video.visible_height;
291       right = track->format->type->video.x_offset + track->format->type->video.visible_width;
292    }
293    else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
294    {
295       fourcc_type = VC_FOURCC('a','u','d','s');
296       sample_size = track->format->type->audio.block_align;
297       scale = 1;
298
299       if (track->format->type->audio.block_align) 
300          rate = (track->format->bitrate / track->format->type->audio.block_align) >> 3;
301
302       if (rate == 0)
303       {
304          rate = track->format->type->audio.sample_rate ? track->format->type->audio.sample_rate : 32000;
305          LOG_DEBUG(p_ctx, "invalid audio rate, using %d (playback timing will almost certainly be incorrect)", 
306                    rate);
307       }
308    }
309    else
310    {
311       /* avi_writer_add_track should ensure this can't happen */
312       vc_container_assert(0);
313    }
314    
315    fourcc_handler = codec_to_vfw_fourcc(track->format->codec);
316
317    div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate);
318    scale /= div;
319    rate /= div;
320
321    length = sample_size ? track->priv->module->chunk_offs : track->priv->module->chunk_index;
322    max_chunk_size = track->priv->module->max_chunk_size;
323    track->priv->module->sample_size = sample_size;
324
325    WRITE_FOURCC(p_ctx, fourcc_type, "fccType");
326    WRITE_FOURCC(p_ctx, fourcc_handler, "fccHandler");
327    WRITE_U32(p_ctx, flags, "dwFlags");
328    WRITE_U16(p_ctx, 0, "wPriority");
329    WRITE_U16(p_ctx, 0, "wLanguage");
330    WRITE_U32(p_ctx, 0, "dwInitialFrames");
331    WRITE_U32(p_ctx, scale, "dwScale");
332    WRITE_U32(p_ctx, rate, "dwRate");
333    WRITE_U32(p_ctx, start, "dwStart");
334    WRITE_U32(p_ctx, length, "dwLength");
335    WRITE_U32(p_ctx, max_chunk_size, "dwSuggestedBufferSize");
336    WRITE_U32(p_ctx, 0, "dwQuality");
337    WRITE_U32(p_ctx, sample_size, "dwSampleSize");
338    WRITE_U16(p_ctx, left, "rcFrame.left");
339    WRITE_U16(p_ctx, top, "rcFrame.top");
340    WRITE_U16(p_ctx, right, "rcFrame.right");
341    WRITE_U16(p_ctx, bottom, "rcFrame.bottom");
342    
343    return STREAM_STATUS(p_ctx);
344 }
345
346 /*****************************************************************************/
347 static VC_CONTAINER_STATUS_T avi_write_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned int index_track_num,
348    uint32_t index_size)
349 {
350    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
351    VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module;
352    VC_CONTAINER_FOURCC_T chunk_id; 
353    uint32_t num_indices = 1; /* FIXME: support for multiple RIFF chunks (AVIX) */
354    unsigned int i;
355
356    if(module->null_io.refcount)
357    {
358       /* Assume that we're not actually writing the data, just want know the index chunk size */
359       WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_indices * (int64_t)AVI_SUPER_INDEX_ENTRY_SIZE);
360       return STREAM_STATUS(p_ctx);
361    }
362   
363    if (track_module->index_offset)
364       WRITE_FOURCC(p_ctx, VC_FOURCC('i','n','d','x'), "Chunk ID");
365    else
366       WRITE_FOURCC(p_ctx, VC_FOURCC('J','U','N','K'), "Chunk ID");
367       
368    WRITE_U32(p_ctx, index_size, "Chunk Size");
369    
370    avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num);   
371    WRITE_U16(p_ctx, 4, "wLongsPerEntry");
372    WRITE_U8(p_ctx, 0, "bIndexSubType");
373    WRITE_U8(p_ctx, AVI_INDEX_OF_INDEXES, "bIndexType");
374    WRITE_U32(p_ctx, num_indices, "nEntriesInUse");
375    WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId");
376    WRITE_U32(p_ctx, 0, "dwReserved0");
377    WRITE_U32(p_ctx, 0, "dwReserved1");
378    WRITE_U32(p_ctx, 0, "dwReserved2");
379    
380    for (i = 0; i < num_indices; ++i)
381    {  
382       uint64_t index_offset = track_module->index_offset;
383       uint32_t chunk_size = track_module->index_size;  
384       uint32_t length = track_module->sample_size ? 
385          track_module->chunk_offs : track_module->chunk_index;
386       WRITE_U64(p_ctx, index_offset, "qwOffset");
387       WRITE_U32(p_ctx, chunk_size, "dwSize");
388       WRITE_U32(p_ctx, length, "dwDuration");
389    }
390
391    AVI_END_CHUNK(p_ctx);
392
393    return STREAM_STATUS(p_ctx);
394 }
395
396 /*****************************************************************************/
397 static VC_CONTAINER_STATUS_T avi_write_stream_header_list(VC_CONTAINER_T *p_ctx, 
398    unsigned int track_num, uint32_t list_size)
399 {
400    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
401    VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num];
402    VC_CONTAINER_STATUS_T status;
403    uint32_t chunk_size = 0;
404
405    WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID");
406    WRITE_U32(p_ctx, list_size, "LIST Size");
407    WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','l'), "Chunk ID");
408
409    if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
410
411    /* Write the stream header chunk ('strh') */
412    status = avi_write_stream_header_chunk(p_ctx, track);
413    if (status != VC_CONTAINER_SUCCESS) return status;
414
415    /* Write the stream format chunk ('strf') */
416    if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
417    {
418       status = avi_write_stream_format_chunk(p_ctx, track, 0);
419       chunk_size = STREAM_POSITION(p_ctx) - 8;
420    }
421    vc_container_writer_extraio_disable(p_ctx, &module->null_io);
422       
423    status = avi_write_stream_format_chunk(p_ctx, track, chunk_size);
424    if (status != VC_CONTAINER_SUCCESS) return status;
425
426    /* If the track has DRM data, write it into the 'strd' chunk (we don't write
427       write codec configuration data into 'strd') */
428    if (track->priv->drmdata && track->priv->drmdata_size)
429    {
430       WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','d'), "Chunk ID");
431       WRITE_U32(p_ctx, track->priv->drmdata_size, "Chunk Size");
432       WRITE_BYTES(p_ctx, track->priv->drmdata, track->priv->drmdata_size);
433       AVI_END_CHUNK(p_ctx);
434       if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
435    }
436
437    /* Write the super index chunk ('indx') */
438    if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
439    {
440       status = avi_write_super_index_chunk(p_ctx, track_num, 0);
441       chunk_size = STREAM_POSITION(p_ctx) - 8;
442    }
443    vc_container_writer_extraio_disable(p_ctx, &module->null_io);
444       
445    status = avi_write_super_index_chunk(p_ctx, track_num, chunk_size);
446    if (status != VC_CONTAINER_SUCCESS) return status;
447       
448    AVI_END_CHUNK(p_ctx);
449
450    return STREAM_STATUS(p_ctx);
451 }
452
453 /*****************************************************************************/
454 static VC_CONTAINER_STATUS_T avi_write_avi_header_chunk(VC_CONTAINER_T *p_ctx)
455 {
456    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
457    uint32_t bitrate = 0, width = 0, height = 0, frame_interval = 0;
458    uint32_t flags, num_chunks = 0, max_video_chunk_size = 0;
459    uint32_t num_streams = p_ctx->tracks_num;
460    unsigned int i;
461
462    for (i = 0; i < p_ctx->tracks_num; i++)
463    {
464       VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i];
465       VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module;
466       if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
467       {
468          width = track->format->type->video.width;
469          height = track->format->type->video.height;
470          if (track->format->type->video.frame_rate_num)
471             frame_interval = track->format->type->video.frame_rate_den * UINT64_C(1000000) / 
472                               track->format->type->video.frame_rate_num;
473          num_chunks = track_module->chunk_index;
474          max_video_chunk_size = track_module->max_chunk_size;
475          break;
476       }
477    }
478
479    flags = (module->index_offset && module->index_status == VC_CONTAINER_SUCCESS) ? 
480       (AVIF_HASINDEX | AVIF_TRUSTCKTYPE) : 0;
481
482    WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','i','h'), "Chunk ID");
483    WRITE_U32(p_ctx, 56, "Chunk Size");
484    WRITE_U32(p_ctx, frame_interval, "dwMicroSecPerFrame");
485    WRITE_U32(p_ctx, bitrate >> 3, "dwMaxBytesPerSec");
486    WRITE_U32(p_ctx, 0, "dwPaddingGranularity");
487    WRITE_U32(p_ctx, flags, "dwFlags");
488    WRITE_U32(p_ctx, num_chunks, "dwTotalFrames");
489    WRITE_U32(p_ctx, 0, "dwInitialFrames");
490    WRITE_U32(p_ctx, num_streams, "dwStreams");
491    WRITE_U32(p_ctx, max_video_chunk_size, "dwSuggestedBufferSize");
492    WRITE_U32(p_ctx, width, "dwWidth");
493    WRITE_U32(p_ctx, height, "dwHeight");
494    WRITE_U32(p_ctx, 0, "dwReserved0");
495    WRITE_U32(p_ctx, 0, "dwReserved1");
496    WRITE_U32(p_ctx, 0, "dwReserved2");
497    WRITE_U32(p_ctx, 0, "dwReserved3");
498
499    return STREAM_STATUS(p_ctx);
500 }
501
502 /*****************************************************************************/
503 static VC_CONTAINER_STATUS_T avi_write_header_list( VC_CONTAINER_T *p_ctx, uint32_t header_list_size )
504 {
505    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
506    VC_CONTAINER_STATUS_T status;
507    unsigned int i;
508    
509    WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID");
510    WRITE_U32(p_ctx, header_list_size, "LIST Size");
511    WRITE_FOURCC(p_ctx, VC_FOURCC('h','d','r','l'), "Chunk ID");
512    if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
513
514    /* Write the main AVI header chunk ('avih') */
515    if ((status = avi_write_avi_header_chunk(p_ctx)) != VC_CONTAINER_SUCCESS)
516       return status;
517
518    for (i = 0; i < p_ctx->tracks_num; i++)
519    {
520       uint32_t list_size = 0;
521       
522       /* Write a stream header list chunk ('strl') */
523       if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
524       {
525          status = avi_write_stream_header_list(p_ctx, i, 0);
526          if (status != VC_CONTAINER_SUCCESS) return status;
527          list_size = STREAM_POSITION(p_ctx) - 8;
528       }
529       vc_container_writer_extraio_disable(p_ctx, &module->null_io);
530    
531       status = avi_write_stream_header_list(p_ctx, i, list_size);
532       if (status != VC_CONTAINER_SUCCESS) return status;
533    }
534
535    return status;
536 }
537
538 /*****************************************************************************/
539 static VC_CONTAINER_STATUS_T avi_write_headers( VC_CONTAINER_T *p_ctx )
540 {
541    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
542    VC_CONTAINER_STATUS_T status;
543    uint32_t header_list_offset, header_list_size = 0;
544
545    /* Write the header list chunk ('hdrl') */
546    if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
547    {
548       status = avi_write_header_list(p_ctx, 0);
549       if (status != VC_CONTAINER_SUCCESS) return status;
550       header_list_size = STREAM_POSITION(p_ctx) - 8;
551    }
552    vc_container_writer_extraio_disable(p_ctx, &module->null_io);
553
554    header_list_offset = STREAM_POSITION(p_ctx);
555    status = avi_write_header_list(p_ctx, header_list_size);
556    if (status == VC_CONTAINER_SUCCESS && !module->header_list_offset)
557    {
558       module->header_list_offset = header_list_offset;
559       module->header_list_size = header_list_size;
560    }
561    
562    return status;
563 }
564
565 /*****************************************************************************/
566 static VC_CONTAINER_STATUS_T avi_write_legacy_index_chunk( VC_CONTAINER_T *p_ctx, uint32_t index_size )
567 {
568    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
569    VC_CONTAINER_STATUS_T status;
570    uint32_t chunk_offset = 4;
571    unsigned int track_num;
572
573    vc_container_assert(8 + avi_num_chunks(p_ctx) * INT64_C(16) <= (int64_t)ULONG_MAX);
574
575    if(module->null_io.refcount)
576    {
577       /* Assume that we're not actually writing the data, 
578          just want know the index size */
579       WRITE_BYTES(p_ctx, NULL, 8 + avi_num_chunks(p_ctx) * (int64_t)AVI_INDEX_ENTRY_SIZE);
580       return STREAM_STATUS(p_ctx);
581    }
582       
583    module->index_offset = STREAM_POSITION(p_ctx);
584   
585    WRITE_FOURCC(p_ctx, VC_FOURCC('i','d','x','1'), "Chunk ID");
586    WRITE_U32(p_ctx, index_size, "Chunk Size");
587
588    /* Scan through all written entries, convert to appropriate index format */
589    vc_container_io_seek(module->temp_io.io, INT64_C(0));
590    
591    while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS)
592    {      
593       VC_CONTAINER_FOURCC_T chunk_id;
594       uint32_t chunk_size, flags;
595       
596       status = avi_read_index_entry(p_ctx, &track_num, &chunk_size);
597       if (status != VC_CONTAINER_SUCCESS) break;
598
599       avi_chunk_id_from_track_num(p_ctx, &chunk_id, track_num);
600       flags = (chunk_size & AVI_INDEX_DELTAFRAME) ? 0 : AVIIF_KEYFRAME;
601       chunk_size &= ~AVI_INDEX_DELTAFRAME;
602    
603       WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID");
604       WRITE_U32(p_ctx, flags, "dwFlags");
605       WRITE_U32(p_ctx, chunk_offset, "dwOffset");
606       WRITE_U32(p_ctx, chunk_size, "dwSize");
607       
608       chunk_offset += ((chunk_size + 1) & ~1) + 8;
609    }
610    
611    AVI_END_CHUNK(p_ctx);
612
613    /* Note that currently, we might write a partial index but still set AVIF_HASINDEX */
614    /* if ( STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS ) module->index_offset = 0 */
615
616    return STREAM_STATUS(p_ctx);
617 }
618
619 /*****************************************************************************/
620 static VC_CONTAINER_STATUS_T avi_write_standard_index_chunk( VC_CONTAINER_T *p_ctx, unsigned int index_track_num, 
621    uint32_t index_size )
622 {
623    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
624    VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module;
625    VC_CONTAINER_STATUS_T status;
626    VC_CONTAINER_FOURCC_T chunk_id; 
627    int64_t base_offset = module->data_offset + 12;
628    uint32_t num_chunks = track_module->chunk_index;
629    uint32_t chunk_offset = 4;
630
631    vc_container_assert(32 + num_chunks * (int64_t)AVI_STD_INDEX_ENTRY_SIZE <= (int64_t)ULONG_MAX);
632
633    if(module->null_io.refcount)
634    {
635       /* Assume that we're not actually writing the data, just want know the index chunk size */
636       WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_chunks * INT64_C(8));
637       return STREAM_STATUS(p_ctx);
638    }
639
640    track_module->index_offset = STREAM_POSITION(p_ctx);
641    track_module->index_size = index_size ? (index_size - 8) : 0;
642
643    avi_index_chunk_id_from_track_num(&chunk_id, index_track_num);
644    WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID");
645    WRITE_U32(p_ctx, index_size, "Chunk Size");
646
647    avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num);
648    WRITE_U16(p_ctx, 2, "wLongsPerEntry");
649    WRITE_U8(p_ctx, 0, "bIndexSubType");
650    WRITE_U8(p_ctx, AVI_INDEX_OF_CHUNKS, "bIndexType");
651    WRITE_U32(p_ctx, num_chunks, "nEntriesInUse");
652    WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId");
653    WRITE_U64(p_ctx, base_offset, "qwBaseOffset");
654    WRITE_U32(p_ctx, 0, "dwReserved");
655
656    /* Scan through all written entries, convert to appropriate index format */
657    vc_container_io_seek(module->temp_io.io, INT64_C(0));
658    
659    while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS)
660    {      
661       uint32_t chunk_size;
662       unsigned int track_num;
663       
664       status = avi_read_index_entry(p_ctx, &track_num, &chunk_size);
665       if (status != VC_CONTAINER_SUCCESS) break;
666          
667       if(track_num != index_track_num) continue;
668    
669       WRITE_U32(p_ctx, chunk_offset, "dwOffset");
670       WRITE_U32(p_ctx, chunk_size, "dwSize");
671
672       chunk_offset += ((chunk_size + 1) & ~(1 | AVI_INDEX_DELTAFRAME)) + 12;
673    }
674    
675    AVI_END_CHUNK(p_ctx);
676
677    return STREAM_STATUS(p_ctx);
678 }
679
680 /*****************************************************************************/
681 static VC_CONTAINER_STATUS_T avi_write_legacy_index_data( VC_CONTAINER_T *p_ctx )
682 {
683    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
684    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;   
685    uint32_t chunk_size = 0;
686
687    /* Write the legacy index chunk ('idx1') */
688    if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
689    {
690       status = avi_write_legacy_index_chunk(p_ctx, 0);
691       if (status != VC_CONTAINER_SUCCESS) return status;
692       chunk_size = STREAM_POSITION(p_ctx) - 8;
693    }
694    vc_container_writer_extraio_disable(p_ctx, &module->null_io);
695
696    status = avi_write_legacy_index_chunk(p_ctx, chunk_size);  
697    return status;
698 }
699
700 /*****************************************************************************/
701 static VC_CONTAINER_STATUS_T avi_write_standard_index_data( VC_CONTAINER_T *p_ctx )
702 {
703    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
704    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
705    uint32_t chunk_size = 0;
706    unsigned int i;
707
708    /* Write the standard index chunks ('ix00') */
709    for (i = 0; i < p_ctx->tracks_num; i++)
710    {
711       if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
712       {
713          status = avi_write_standard_index_chunk(p_ctx, i, 0);
714          if (status != VC_CONTAINER_SUCCESS) return status;
715          chunk_size = STREAM_POSITION(p_ctx) - 8;
716       }
717       vc_container_writer_extraio_disable(p_ctx, &module->null_io);
718    
719       status = avi_write_standard_index_chunk(p_ctx, i, chunk_size);
720       if (status != VC_CONTAINER_SUCCESS) return status;
721    }
722    
723    return status;
724 }
725
726 /*****************************************************************************/
727 static int64_t avi_calculate_file_size( VC_CONTAINER_T *p_ctx, 
728    VC_CONTAINER_PACKET_T *p_packet )
729 {
730    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
731    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
732    int64_t filesize = 0;
733    int refcount;
734
735    /* Start from current file position */
736    filesize = STREAM_POSITION(p_ctx);
737
738    refcount = vc_container_writer_extraio_enable(p_ctx, &module->null_io);
739    vc_container_assert(refcount == 0); /* Although perfectly harmless, we should 
740                                           not be called with the null i/o enabled. */
741    VC_CONTAINER_PARAM_UNUSED(refcount);
742
743    do {
744       /* If we know what the final size of the chunk is going to be,
745          we can use that here to avoid writing a partial final packet */
746       WRITE_BYTES(p_ctx, NULL, p_packet->frame_size ? p_packet->frame_size : p_packet->size);
747       AVI_END_CHUNK(p_ctx);
748       
749       /* Index entries for the chunk */
750       WRITE_BYTES(p_ctx, NULL, AVI_INDEX_ENTRY_SIZE + AVI_STD_INDEX_ENTRY_SIZE);
751       
752       /* Current standard index data */
753       if (avi_write_standard_index_data(p_ctx) != VC_CONTAINER_SUCCESS) break;
754       
755       /* Current legacy index data */
756       status = avi_write_legacy_index_data(p_ctx);
757       if (status != VC_CONTAINER_SUCCESS) break;
758    } while(0);
759    
760    filesize += STREAM_POSITION(p_ctx);
761
762    vc_container_writer_extraio_disable(p_ctx, &module->null_io);
763
764    return filesize;
765 }
766
767 /*****************************************************************************
768 Functions exported as part of the Container Module API
769  *****************************************************************************/
770  
771 static VC_CONTAINER_STATUS_T avi_writer_write( VC_CONTAINER_T *p_ctx,
772                                                VC_CONTAINER_PACKET_T *p_packet )
773 {
774    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
775    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
776    VC_CONTAINER_TRACK_T *track = NULL;
777    VC_CONTAINER_TRACK_MODULE_T *track_module = NULL;
778
779    /* Check we have written headers before any data */
780    if(!module->headers_written)
781    {
782       if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
783       module->headers_written = 1;
784    }
785
786    /* Check that we have started the 'movi' list */
787    if (!module->data_offset)
788    {
789       module->data_offset = STREAM_POSITION(p_ctx);
790       vc_container_assert(module->data_offset != INT64_C(0));
791
792       WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID");
793       WRITE_U32(p_ctx, 0, "LIST Size");
794       WRITE_FOURCC(p_ctx, VC_FOURCC('m','o','v','i'), "Chunk ID");
795       if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
796    }
797
798    /* If the container is passing in a frame from a new track but we 
799       arent't finished with a chunk from another track we need to finish 
800       that chunk first */
801    if (module->chunk_data_written && p_packet->track != module->current_track_num)
802    {
803       track_module = p_ctx->tracks[module->current_track_num]->priv->module;
804       status = avi_finish_data_chunk(p_ctx, module->chunk_data_written);
805       avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0);
806       track_module->chunk_index++;
807       track_module->chunk_offs += module->chunk_data_written;
808       track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written);
809       module->chunk_data_written = 0;
810       if (status != VC_CONTAINER_SUCCESS) return status;
811    }
812
813    /* Check we are not about to go over the limit of total number of chunks */
814    if (avi_num_chunks(p_ctx) == (uint32_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
815
816     if(STREAM_SEEKABLE(p_ctx))
817     {
818        /* Check we are not about to go over the maximum file size */
819        if (avi_calculate_file_size(p_ctx, p_packet) >= (int64_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
820     }
821
822    /* FIXME: are we expected to handle this case or should it be picked up by the above layer? */
823    vc_container_assert(!(module->chunk_data_written && (p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)));
824    
825    track = p_ctx->tracks[p_packet->track];
826    track_module = p_ctx->tracks[p_packet->track]->priv->module;
827    module->current_track_num = p_packet->track;
828
829    if (module->chunk_data_written == 0)
830    {
831       /* This is the first fragment of the chunk */
832       VC_CONTAINER_FOURCC_T chunk_id;
833       uint32_t chunk_size;
834
835       avi_chunk_id_from_track_num(p_ctx, &chunk_id, p_packet->track);
836
837       if (p_packet->frame_size)
838       {
839          /* We know what the final size of the chunk is going to be */
840          chunk_size = module->chunk_size = p_packet->frame_size;
841       }
842       else
843       {
844          chunk_size = p_packet->size;
845          module->chunk_size = 0;
846       }
847
848       WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID");
849       if(STREAM_SEEKABLE(p_ctx) || p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)
850       {
851          /* If the output stream can seek we can fix up the frame size later, and if the
852           * packet holds the whole frame we won't need to, so write data straight out. */
853          WRITE_U32(p_ctx, chunk_size, "Chunk Size");
854          WRITE_BYTES(p_ctx, p_packet->data, p_packet->size);
855       }
856       else
857       {
858          vc_container_assert(module->avi_frame_buffer);
859          if(p_packet->size > AVI_FRAME_BUFFER_SIZE)
860             return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
861          module->frame_packet = *p_packet;
862          module->frame_packet.data = module->avi_frame_buffer;
863          memcpy(module->frame_packet.data, 
864                   p_packet->data, module->frame_packet.size);
865       }
866       
867       module->chunk_data_written = p_packet->size;
868    }
869    else
870    {
871       if(module->frame_packet.size > 0 && module->avi_frame_buffer)
872       {
873          if(module->frame_packet.size > 0)
874          {
875             if(module->frame_packet.size + p_packet->size > AVI_FRAME_BUFFER_SIZE)
876                return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
877             memcpy(module->frame_packet.data + module->frame_packet.size, 
878                      p_packet->data, p_packet->size);
879             module->frame_packet.size += p_packet->size;
880          }
881       }
882       else
883       {
884          WRITE_BYTES(p_ctx, p_packet->data, p_packet->size);
885       }
886       module->chunk_data_written += p_packet->size;
887    }
888
889    if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) 
890       return status;
891
892    if ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) ||
893        (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO && 
894         track->format->type->audio.block_align &&
895         module->chunk_data_written > AVI_AUDIO_CHUNK_SIZE_LIMIT))
896    {
897       if(module->frame_packet.size > 0)
898       {
899          WRITE_U32(p_ctx, module->frame_packet.size, "Chunk Size");
900          WRITE_BYTES(p_ctx, module->frame_packet.data, module->frame_packet.size);
901          p_packet->size = module->frame_packet.size;
902          module->frame_packet.size = 0;
903       }
904       
905       if (!module->chunk_size && module->chunk_data_written > p_packet->size)
906       {
907          /* The chunk size needs to be rewritten */
908          status = avi_finish_data_chunk(p_ctx, module->chunk_data_written);
909       }
910       else
911       {
912          status = avi_finish_data_chunk(p_ctx, 0);
913       }
914
915       if(!STREAM_SEEKABLE(p_ctx))
916       {
917          /* If we are streaming then flush to avoid delaying data transport. */
918          vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_FLUSH);
919       }
920
921       if(STREAM_SEEKABLE(p_ctx))
922       {
923           /* Keep track of data written so we can check we don't exceed file size and also for doing
924            * index fix-ups, but only do this if we are writing to a seekable IO. */
925           avi_write_index_entry(p_ctx, p_packet->track, module->chunk_data_written, AVI_PACKET_IS_KEYFRAME(p_packet->flags));
926       }
927       track_module->chunk_index++;
928       track_module->chunk_offs += module->chunk_data_written;
929       track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written);
930       module->chunk_data_written = 0;
931
932       if (status != VC_CONTAINER_SUCCESS) return status;
933    }
934
935
936    return STREAM_STATUS(p_ctx);
937 }
938
939 /*****************************************************************************/
940 static VC_CONTAINER_STATUS_T avi_writer_close( VC_CONTAINER_T *p_ctx )
941 {
942    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
943    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
944    unsigned int i;
945
946    /* If we arent't finished with a chunk we need to finish it first */
947    if (module->chunk_data_written)
948    {
949       VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track_num]->priv->module;
950       status = avi_finish_data_chunk(p_ctx, module->chunk_data_written);
951       if (status != VC_CONTAINER_SUCCESS)
952       {       
953          LOG_DEBUG(p_ctx, "warning, writing failed, last chunk truncated");
954       }      
955       avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0);
956       track_module->chunk_index++;
957       track_module->chunk_offs += module->chunk_data_written;
958       track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written);
959       module->chunk_data_written = 0;
960    }
961
962    if(STREAM_SEEKABLE(p_ctx))
963    {
964       uint32_t filesize;
965
966       /* Write standard index data before finalising the size of the 'movi' list */
967       status = avi_write_standard_index_data(p_ctx);
968       if (status != VC_CONTAINER_SUCCESS)
969       {
970          module->index_status = status;
971          LOG_DEBUG(p_ctx, "warning, writing standard index data failed, file will be malformed");
972       }
973
974       /* FIXME: support for multiple RIFF chunks (AVIX) */
975       module->data_size = STREAM_POSITION(p_ctx) - module->data_offset - 8;
976
977       /* Now write the legacy index */
978       status = avi_write_legacy_index_data(p_ctx);
979       if (status != VC_CONTAINER_SUCCESS)
980       {
981          module->index_status = status;
982          LOG_DEBUG(p_ctx, "warning, writing legacy index data failed, file will be malformed");
983       }
984
985       /* If we can, do the necessary fixups for values not know at the
986        time of writing chunk headers */
987
988       /* Rewrite the AVI RIFF chunk size */
989       filesize = (uint32_t)STREAM_POSITION(p_ctx);
990       SEEK(p_ctx, 4);
991       WRITE_U32(p_ctx, filesize, "fileSize");
992       if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
993       {
994          LOG_DEBUG(p_ctx, "warning, rewriting 'fileSize' failed, file will be malformed");
995       }   
996
997       /* Rewrite the header list chunk ('hdrl') */
998       SEEK(p_ctx, module->header_list_offset);
999       status = avi_write_header_list(p_ctx, module->header_list_size);
1000       if (status != VC_CONTAINER_SUCCESS)
1001       {
1002          LOG_DEBUG(p_ctx, "warning, rewriting 'hdrl' failed, file will be malformed");
1003       }
1004
1005       /* Rewrite the AVI RIFF 'movi' list size */
1006       SEEK(p_ctx, module->data_offset + 4);
1007       WRITE_U32(p_ctx, module->data_size, "Chunk Size");
1008       if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
1009       {
1010          LOG_DEBUG(p_ctx, "warning, rewriting 'movi' list size failed, file will be malformed");
1011       }
1012    }
1013
1014    vc_container_writer_extraio_delete(p_ctx, &module->null_io);
1015    if(module->temp_io.io) vc_container_writer_extraio_delete(p_ctx, &module->temp_io);
1016
1017    for(i = 0; i < p_ctx->tracks_num; i++)
1018       vc_container_free_track(p_ctx, p_ctx->tracks[i]);
1019    p_ctx->tracks_num = 0;
1020    p_ctx->tracks = NULL;
1021
1022    if(module->avi_frame_buffer) free(module->avi_frame_buffer);
1023    free(module);
1024
1025    return status;
1026 }
1027
1028 /*****************************************************************************/
1029 static VC_CONTAINER_STATUS_T avi_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format )
1030 {
1031    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1032    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1033    VC_CONTAINER_TRACK_T *track = NULL;
1034
1035    if (module->headers_written) return VC_CONTAINER_ERROR_FAILED;
1036
1037    /* FIXME: should we check the format in more detail? */
1038    if((format->es_type != VC_CONTAINER_ES_TYPE_VIDEO && format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) ||
1039       format->codec == VC_CONTAINER_CODEC_UNKNOWN)
1040       return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1041
1042    if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
1043       return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1044
1045    /* Allocate new track */
1046    if(p_ctx->tracks_num >= AVI_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
1047    p_ctx->tracks[p_ctx->tracks_num] = track =
1048       vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
1049    if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1050
1051    if(format->extradata_size)
1052    {
1053       status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size );
1054       if(status) goto error;
1055    }
1056
1057    status = vc_container_format_copy(track->format, format, format->extradata_size);
1058    if(status) goto error;
1059
1060    p_ctx->tracks_num++;
1061    return VC_CONTAINER_SUCCESS;
1062
1063 error:
1064    vc_container_free_track(p_ctx, track);
1065    return status;
1066 }
1067
1068 /*****************************************************************************/
1069 static VC_CONTAINER_STATUS_T avi_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args )
1070 {
1071    VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1072    VC_CONTAINER_STATUS_T status;
1073    
1074    switch(operation)
1075    {
1076       case VC_CONTAINER_CONTROL_TRACK_ADD:
1077       {
1078          VC_CONTAINER_ES_FORMAT_T *format =
1079             (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * );
1080          return avi_writer_add_track(p_ctx, format);
1081       }
1082       case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
1083       {
1084          if(!module->headers_written)
1085          {
1086             if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
1087             module->headers_written = 1;
1088             return VC_CONTAINER_SUCCESS;
1089          }
1090          else
1091             return VC_CONTAINER_ERROR_FAILED;
1092       }
1093       default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1094    }
1095 }
1096
1097 /******************************************************************************
1098 Global function definitions.
1099 ******************************************************************************/
1100 VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx )
1101 {
1102    const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
1103    VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1104    VC_CONTAINER_MODULE_T *module = 0;
1105
1106    /* Check if the user has specified a container */
1107    vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
1108
1109    /* Check we're the right writer for this */
1110    if(!extension)
1111       return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1112    if(strcasecmp(extension, "avi") && strcasecmp(extension, "divx"))
1113       return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1114
1115    /* Allocate our context */
1116    module = malloc(sizeof(*module));
1117    if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
1118    memset(module, 0, sizeof(*module));
1119    p_ctx->priv->module = module;
1120
1121    /* Create a null i/o writer to help us out in writing our data */
1122    status = vc_container_writer_extraio_create_null(p_ctx, &module->null_io);
1123    if(status != VC_CONTAINER_SUCCESS) goto error;
1124
1125    if(STREAM_SEEKABLE(p_ctx))
1126    {
1127        /* Create a temporary i/o writer for storage of index data while we are writing */
1128        status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp_io);
1129        if(status != VC_CONTAINER_SUCCESS) goto error;
1130    }
1131    else
1132    {
1133       module->avi_frame_buffer = malloc(AVI_FRAME_BUFFER_SIZE);
1134       if(!module->avi_frame_buffer)
1135          { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
1136    }
1137    module->frame_packet.size = 0;
1138
1139    p_ctx->tracks = module->tracks;
1140
1141    /* Write the RIFF chunk descriptor */
1142    WRITE_FOURCC(p_ctx, VC_FOURCC('R','I','F','F'), "RIFF ID");
1143    WRITE_U32(p_ctx, 0, "fileSize");
1144    WRITE_FOURCC(p_ctx, VC_FOURCC('A','V','I',' '), "fileType");
1145    
1146    if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
1147
1148    p_ctx->priv->pf_close = avi_writer_close;
1149    p_ctx->priv->pf_write = avi_writer_write;
1150    p_ctx->priv->pf_control = avi_writer_control;
1151
1152    return VC_CONTAINER_SUCCESS;
1153
1154  error:
1155    LOG_DEBUG(p_ctx, "error opening stream");
1156    p_ctx->tracks_num = 0;
1157    p_ctx->tracks = NULL;
1158    if(module)
1159    {
1160       if(module->avi_frame_buffer) free(module->avi_frame_buffer);
1161       free(module);
1162    }
1163    return status;
1164 }
1165
1166 /********************************************************************************
1167  Entrypoint function
1168  ********************************************************************************/
1169 #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
1170 # pragma weak writer_open avi_writer_open
1171 #endif