2 Copyright (c) 2012, Broadcom Europe Ltd
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.
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.
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"
42 /******************************************************************************
44 ******************************************************************************/
45 VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx );
47 /******************************************************************************
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
55 #define AVI_INDEX_OF_INDEXES 0x00
56 #define AVI_INDEX_OF_CHUNKS 0x01
57 #define AVI_INDEX_DELTAFRAME 0x80000000
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
64 #define AVI_TRACKS_MAX 3
66 #define AVI_AUDIO_CHUNK_SIZE_LIMIT 16384 /*< Watermark limit for data chunks when 'dwSampleSize'
69 #define AVI_END_CHUNK(ctx) \
71 if(STREAM_POSITION(ctx) & 1) WRITE_U8(ctx, 0, "AVI_END_CHUNK"); \
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)
77 /******************************************************************************
79 ******************************************************************************/
80 typedef struct VC_CONTAINER_TRACK_MODULE_T
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
89 uint32_t index_size; /**< Size of the OpenDML index for this track i.e. 'indx' */
90 } VC_CONTAINER_TRACK_MODULE_T;
92 typedef struct VC_CONTAINER_MODULE_T
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 */
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. */
112 VC_CONTAINER_STATUS_T index_status;
113 } VC_CONTAINER_MODULE_T;
115 /******************************************************************************
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 )
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];
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');
131 /* Note that avi_writer_add_track should ensure this
133 *p_chunk_id = VC_FOURCC('J','U','N','K'); return;
136 snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num);
137 memcpy(&chunk_id, track_num_buf, 2);
139 *p_chunk_id = chunk_id;
142 /*****************************************************************************/
143 static void avi_index_chunk_id_from_track_num(VC_CONTAINER_FOURCC_T *p_chunk_id,
144 unsigned int track_num )
146 VC_CONTAINER_FOURCC_T chunk_id = 0;
147 char track_num_buf[3];
149 chunk_id = VC_FOURCC('i','x','0','0');
151 snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num);
152 memcpy(((uint8_t*)&chunk_id) + 2, track_num_buf, 2);
154 *p_chunk_id = chunk_id;
157 /*****************************************************************************/
158 static uint32_t avi_num_chunks( VC_CONTAINER_T *p_ctx )
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;
168 /*****************************************************************************/
169 static VC_CONTAINER_STATUS_T avi_finish_data_chunk( VC_CONTAINER_T *p_ctx, uint32_t chunk_size )
171 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
175 /* Rewrite the chunk size, this won't be efficient if it happens often */
176 if (STREAM_SEEKABLE(p_ctx))
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);
184 LOG_DEBUG(p_ctx, "warning, can't rewrite chunk size, data will be malformed");
185 status = VC_CONTAINER_ERROR_FAILED;
189 AVI_END_CHUNK(p_ctx);
191 if (status != VC_CONTAINER_SUCCESS) status = STREAM_STATUS(p_ctx);
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 )
200 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
201 uint32_t deltaframe = keyframe ? 0 : AVI_INDEX_DELTAFRAME;
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);
206 if (module->temp_io.io->status != VC_CONTAINER_SUCCESS)
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");
212 return module->temp_io.io->status;
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 )
219 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
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);
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;
229 *p_track_num = track_num;
230 *p_chunk_size = chunk_size;
232 return module->temp_io.io->status;
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)
239 VC_CONTAINER_STATUS_T status;
241 WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','f'), "Chunk ID");
242 WRITE_U32(p_ctx, chunk_size, "Chunk Size");
244 if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
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);
251 if (status != VC_CONTAINER_SUCCESS) return status;
253 AVI_END_CHUNK(p_ctx);
255 return STREAM_STATUS(p_ctx);
258 /*****************************************************************************/
259 static VC_CONTAINER_STATUS_T avi_write_stream_header_chunk(VC_CONTAINER_T *p_ctx,
260 VC_CONTAINER_TRACK_T *track)
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;
267 WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','h'), "Chunk ID");
268 WRITE_U32(p_ctx, 56, "Chunk Size");
270 if (!track->is_enabled)
271 flags = 0; /* AVISF_DISABLED; FIXME: write_media should set this correctly! */
275 if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
277 fourcc_type = VC_FOURCC('v','i','d','s');
279 scale = track->format->type->video.frame_rate_den;
280 rate = track->format->type->video.frame_rate_num;
281 if (rate == 0 || scale == 0)
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;
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;
293 else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
295 fourcc_type = VC_FOURCC('a','u','d','s');
296 sample_size = track->format->type->audio.block_align;
299 if (track->format->type->audio.block_align)
300 rate = (track->format->bitrate / track->format->type->audio.block_align) >> 3;
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)",
311 /* avi_writer_add_track should ensure this can't happen */
312 vc_container_assert(0);
315 fourcc_handler = codec_to_vfw_fourcc(track->format->codec);
317 div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate);
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;
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");
343 return STREAM_STATUS(p_ctx);
346 /*****************************************************************************/
347 static VC_CONTAINER_STATUS_T avi_write_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned int index_track_num,
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) */
356 if(module->null_io.refcount)
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);
363 if (track_module->index_offset)
364 WRITE_FOURCC(p_ctx, VC_FOURCC('i','n','d','x'), "Chunk ID");
366 WRITE_FOURCC(p_ctx, VC_FOURCC('J','U','N','K'), "Chunk ID");
368 WRITE_U32(p_ctx, index_size, "Chunk Size");
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");
380 for (i = 0; i < num_indices; ++i)
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");
391 AVI_END_CHUNK(p_ctx);
393 return STREAM_STATUS(p_ctx);
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)
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;
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");
409 if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
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;
415 /* Write the stream format chunk ('strf') */
416 if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
418 status = avi_write_stream_format_chunk(p_ctx, track, 0);
419 chunk_size = STREAM_POSITION(p_ctx) - 8;
421 vc_container_writer_extraio_disable(p_ctx, &module->null_io);
423 status = avi_write_stream_format_chunk(p_ctx, track, chunk_size);
424 if (status != VC_CONTAINER_SUCCESS) return status;
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)
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;
437 /* Write the super index chunk ('indx') */
438 if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
440 status = avi_write_super_index_chunk(p_ctx, track_num, 0);
441 chunk_size = STREAM_POSITION(p_ctx) - 8;
443 vc_container_writer_extraio_disable(p_ctx, &module->null_io);
445 status = avi_write_super_index_chunk(p_ctx, track_num, chunk_size);
446 if (status != VC_CONTAINER_SUCCESS) return status;
448 AVI_END_CHUNK(p_ctx);
450 return STREAM_STATUS(p_ctx);
453 /*****************************************************************************/
454 static VC_CONTAINER_STATUS_T avi_write_avi_header_chunk(VC_CONTAINER_T *p_ctx)
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;
462 for (i = 0; i < p_ctx->tracks_num; i++)
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)
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;
479 flags = (module->index_offset && module->index_status == VC_CONTAINER_SUCCESS) ?
480 (AVIF_HASINDEX | AVIF_TRUSTCKTYPE) : 0;
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");
499 return STREAM_STATUS(p_ctx);
502 /*****************************************************************************/
503 static VC_CONTAINER_STATUS_T avi_write_header_list( VC_CONTAINER_T *p_ctx, uint32_t header_list_size )
505 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
506 VC_CONTAINER_STATUS_T status;
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;
514 /* Write the main AVI header chunk ('avih') */
515 if ((status = avi_write_avi_header_chunk(p_ctx)) != VC_CONTAINER_SUCCESS)
518 for (i = 0; i < p_ctx->tracks_num; i++)
520 uint32_t list_size = 0;
522 /* Write a stream header list chunk ('strl') */
523 if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
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;
529 vc_container_writer_extraio_disable(p_ctx, &module->null_io);
531 status = avi_write_stream_header_list(p_ctx, i, list_size);
532 if (status != VC_CONTAINER_SUCCESS) return status;
538 /*****************************************************************************/
539 static VC_CONTAINER_STATUS_T avi_write_headers( VC_CONTAINER_T *p_ctx )
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;
545 /* Write the header list chunk ('hdrl') */
546 if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
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;
552 vc_container_writer_extraio_disable(p_ctx, &module->null_io);
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)
558 module->header_list_offset = header_list_offset;
559 module->header_list_size = header_list_size;
565 /*****************************************************************************/
566 static VC_CONTAINER_STATUS_T avi_write_legacy_index_chunk( VC_CONTAINER_T *p_ctx, uint32_t index_size )
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;
573 vc_container_assert(8 + avi_num_chunks(p_ctx) * INT64_C(16) <= (int64_t)ULONG_MAX);
575 if(module->null_io.refcount)
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);
583 module->index_offset = STREAM_POSITION(p_ctx);
585 WRITE_FOURCC(p_ctx, VC_FOURCC('i','d','x','1'), "Chunk ID");
586 WRITE_U32(p_ctx, index_size, "Chunk Size");
588 /* Scan through all written entries, convert to appropriate index format */
589 vc_container_io_seek(module->temp_io.io, INT64_C(0));
591 while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS)
593 VC_CONTAINER_FOURCC_T chunk_id;
594 uint32_t chunk_size, flags;
596 status = avi_read_index_entry(p_ctx, &track_num, &chunk_size);
597 if (status != VC_CONTAINER_SUCCESS) break;
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;
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");
608 chunk_offset += ((chunk_size + 1) & ~1) + 8;
611 AVI_END_CHUNK(p_ctx);
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 */
616 return STREAM_STATUS(p_ctx);
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 )
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;
631 vc_container_assert(32 + num_chunks * (int64_t)AVI_STD_INDEX_ENTRY_SIZE <= (int64_t)ULONG_MAX);
633 if(module->null_io.refcount)
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);
640 track_module->index_offset = STREAM_POSITION(p_ctx);
641 track_module->index_size = index_size ? (index_size - 8) : 0;
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");
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");
656 /* Scan through all written entries, convert to appropriate index format */
657 vc_container_io_seek(module->temp_io.io, INT64_C(0));
659 while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS)
662 unsigned int track_num;
664 status = avi_read_index_entry(p_ctx, &track_num, &chunk_size);
665 if (status != VC_CONTAINER_SUCCESS) break;
667 if(track_num != index_track_num) continue;
669 WRITE_U32(p_ctx, chunk_offset, "dwOffset");
670 WRITE_U32(p_ctx, chunk_size, "dwSize");
672 chunk_offset += ((chunk_size + 1) & ~(1 | AVI_INDEX_DELTAFRAME)) + 12;
675 AVI_END_CHUNK(p_ctx);
677 return STREAM_STATUS(p_ctx);
680 /*****************************************************************************/
681 static VC_CONTAINER_STATUS_T avi_write_legacy_index_data( VC_CONTAINER_T *p_ctx )
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;
687 /* Write the legacy index chunk ('idx1') */
688 if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
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;
694 vc_container_writer_extraio_disable(p_ctx, &module->null_io);
696 status = avi_write_legacy_index_chunk(p_ctx, chunk_size);
700 /*****************************************************************************/
701 static VC_CONTAINER_STATUS_T avi_write_standard_index_data( VC_CONTAINER_T *p_ctx )
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;
708 /* Write the standard index chunks ('ix00') */
709 for (i = 0; i < p_ctx->tracks_num; i++)
711 if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
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;
717 vc_container_writer_extraio_disable(p_ctx, &module->null_io);
719 status = avi_write_standard_index_chunk(p_ctx, i, chunk_size);
720 if (status != VC_CONTAINER_SUCCESS) return status;
726 /*****************************************************************************/
727 static int64_t avi_calculate_file_size( VC_CONTAINER_T *p_ctx,
728 VC_CONTAINER_PACKET_T *p_packet )
730 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
731 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
732 int64_t filesize = 0;
735 /* Start from current file position */
736 filesize = STREAM_POSITION(p_ctx);
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);
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);
749 /* Index entries for the chunk */
750 WRITE_BYTES(p_ctx, NULL, AVI_INDEX_ENTRY_SIZE + AVI_STD_INDEX_ENTRY_SIZE);
752 /* Current standard index data */
753 if (avi_write_standard_index_data(p_ctx) != VC_CONTAINER_SUCCESS) break;
755 /* Current legacy index data */
756 status = avi_write_legacy_index_data(p_ctx);
757 if (status != VC_CONTAINER_SUCCESS) break;
760 filesize += STREAM_POSITION(p_ctx);
762 vc_container_writer_extraio_disable(p_ctx, &module->null_io);
767 /*****************************************************************************
768 Functions exported as part of the Container Module API
769 *****************************************************************************/
771 static VC_CONTAINER_STATUS_T avi_writer_write( VC_CONTAINER_T *p_ctx,
772 VC_CONTAINER_PACKET_T *p_packet )
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;
779 /* Check we have written headers before any data */
780 if(!module->headers_written)
782 if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
783 module->headers_written = 1;
786 /* Check that we have started the 'movi' list */
787 if (!module->data_offset)
789 module->data_offset = STREAM_POSITION(p_ctx);
790 vc_container_assert(module->data_offset != INT64_C(0));
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;
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
801 if (module->chunk_data_written && p_packet->track != module->current_track_num)
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;
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;
816 if(STREAM_SEEKABLE(p_ctx))
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;
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)));
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;
829 if (module->chunk_data_written == 0)
831 /* This is the first fragment of the chunk */
832 VC_CONTAINER_FOURCC_T chunk_id;
835 avi_chunk_id_from_track_num(p_ctx, &chunk_id, p_packet->track);
837 if (p_packet->frame_size)
839 /* We know what the final size of the chunk is going to be */
840 chunk_size = module->chunk_size = p_packet->frame_size;
844 chunk_size = p_packet->size;
845 module->chunk_size = 0;
848 WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID");
849 if(STREAM_SEEKABLE(p_ctx) || p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)
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);
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);
867 module->chunk_data_written = p_packet->size;
871 if(module->frame_packet.size > 0 && module->avi_frame_buffer)
873 if(module->frame_packet.size > 0)
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;
884 WRITE_BYTES(p_ctx, p_packet->data, p_packet->size);
886 module->chunk_data_written += p_packet->size;
889 if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
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))
897 if(module->frame_packet.size > 0)
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;
905 if (!module->chunk_size && module->chunk_data_written > p_packet->size)
907 /* The chunk size needs to be rewritten */
908 status = avi_finish_data_chunk(p_ctx, module->chunk_data_written);
912 status = avi_finish_data_chunk(p_ctx, 0);
915 if(!STREAM_SEEKABLE(p_ctx))
917 /* If we are streaming then flush to avoid delaying data transport. */
918 vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_FLUSH);
921 if(STREAM_SEEKABLE(p_ctx))
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));
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;
932 if (status != VC_CONTAINER_SUCCESS) return status;
936 return STREAM_STATUS(p_ctx);
939 /*****************************************************************************/
940 static VC_CONTAINER_STATUS_T avi_writer_close( VC_CONTAINER_T *p_ctx )
942 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
943 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
946 /* If we arent't finished with a chunk we need to finish it first */
947 if (module->chunk_data_written)
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)
953 LOG_DEBUG(p_ctx, "warning, writing failed, last chunk truncated");
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;
962 if(STREAM_SEEKABLE(p_ctx))
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)
970 module->index_status = status;
971 LOG_DEBUG(p_ctx, "warning, writing standard index data failed, file will be malformed");
974 /* FIXME: support for multiple RIFF chunks (AVIX) */
975 module->data_size = STREAM_POSITION(p_ctx) - module->data_offset - 8;
977 /* Now write the legacy index */
978 status = avi_write_legacy_index_data(p_ctx);
979 if (status != VC_CONTAINER_SUCCESS)
981 module->index_status = status;
982 LOG_DEBUG(p_ctx, "warning, writing legacy index data failed, file will be malformed");
985 /* If we can, do the necessary fixups for values not know at the
986 time of writing chunk headers */
988 /* Rewrite the AVI RIFF chunk size */
989 filesize = (uint32_t)STREAM_POSITION(p_ctx);
991 WRITE_U32(p_ctx, filesize, "fileSize");
992 if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
994 LOG_DEBUG(p_ctx, "warning, rewriting 'fileSize' failed, file will be malformed");
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)
1002 LOG_DEBUG(p_ctx, "warning, rewriting 'hdrl' failed, file will be malformed");
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)
1010 LOG_DEBUG(p_ctx, "warning, rewriting 'movi' list size failed, file will be malformed");
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);
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;
1022 if(module->avi_frame_buffer) free(module->avi_frame_buffer);
1028 /*****************************************************************************/
1029 static VC_CONTAINER_STATUS_T avi_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format )
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;
1035 if (module->headers_written) return VC_CONTAINER_ERROR_FAILED;
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;
1042 if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
1043 return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
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;
1051 if(format->extradata_size)
1053 status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size );
1054 if(status) goto error;
1057 status = vc_container_format_copy(track->format, format, format->extradata_size);
1058 if(status) goto error;
1060 p_ctx->tracks_num++;
1061 return VC_CONTAINER_SUCCESS;
1064 vc_container_free_track(p_ctx, track);
1068 /*****************************************************************************/
1069 static VC_CONTAINER_STATUS_T avi_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args )
1071 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1072 VC_CONTAINER_STATUS_T status;
1076 case VC_CONTAINER_CONTROL_TRACK_ADD:
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);
1082 case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
1084 if(!module->headers_written)
1086 if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
1087 module->headers_written = 1;
1088 return VC_CONTAINER_SUCCESS;
1091 return VC_CONTAINER_ERROR_FAILED;
1093 default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1097 /******************************************************************************
1098 Global function definitions.
1099 ******************************************************************************/
1100 VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx )
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;
1106 /* Check if the user has specified a container */
1107 vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
1109 /* Check we're the right writer for this */
1111 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1112 if(strcasecmp(extension, "avi") && strcasecmp(extension, "divx"))
1113 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
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;
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;
1125 if(STREAM_SEEKABLE(p_ctx))
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;
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; }
1137 module->frame_packet.size = 0;
1139 p_ctx->tracks = module->tracks;
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");
1146 if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
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;
1152 return VC_CONTAINER_SUCCESS;
1155 LOG_DEBUG(p_ctx, "error opening stream");
1156 p_ctx->tracks_num = 0;
1157 p_ctx->tracks = NULL;
1160 if(module->avi_frame_buffer) free(module->avi_frame_buffer);
1166 /********************************************************************************
1168 ********************************************************************************/
1169 #if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
1170 # pragma weak writer_open avi_writer_open