1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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/******************************************************************************
43Function prototypes
44******************************************************************************/
45VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx );
46
47/******************************************************************************
48Defines.
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/******************************************************************************
78Type definitions.
79******************************************************************************/
80typedef 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
92typedef 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/******************************************************************************
116Local Functions
117******************************************************************************/
118static 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/*****************************************************************************/
143static 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/*****************************************************************************/
158static 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/*****************************************************************************/
169static 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/*****************************************************************************/
197static 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/*****************************************************************************/
216static 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/*****************************************************************************/
236static 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/*****************************************************************************/
259static 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/*****************************************************************************/
347static 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/*****************************************************************************/
397static 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/*****************************************************************************/
454static 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/*****************************************************************************/
503static 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/*****************************************************************************/
539static 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/*****************************************************************************/
566static 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/*****************************************************************************/
620static 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/*****************************************************************************/
681static 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/*****************************************************************************/
701static 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/*****************************************************************************/
727static 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/*****************************************************************************
768Functions exported as part of the Container Module API
769 *****************************************************************************/
770
771static 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/*****************************************************************************/
940static 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/*****************************************************************************/
1029static 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
1063error:
1064 vc_container_free_track(p_ctx, track);
1065 return status;
1066}
1067
1068/*****************************************************************************/
1069static 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/******************************************************************************
1098Global function definitions.
1099******************************************************************************/
1100VC_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
1172