1 #include <cstdlib>
2 #include <cstdint>
3 #include <cstdio>
4 #include <iostream>
5 #include <fstream>
6 #include <cstring>
7 #include "pipp_avi_write.h"
8 #include "pipp_utf8.h"
9
10 #include <cwchar>
11 #include <memory>
12 #include <sstream>
13
14 using namespace std;
15
16
17 //
18 // index_type codes
19 //
20 #define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex array points to an index chunk
21 #define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex array points to a chunk in the file
22 #define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is really the data
23
24 //
25 // index_sub_type codes for INDEX_OF_CHUNKS
26 //
27 #define AVI_INDEX_2FIELD 0x01 // when fields within frames are also indexed
28
29
30 // ------------------------------------------
31 // Constructor
32 // ------------------------------------------
c_pipp_avi_write()33 c_pipp_avi_write::c_pipp_avi_write() :
34 mp_avi_file(NULL),
35 m_open(false),
36 m_split_count(0),
37 m_old_avi_format(0),
38 m_width(0),
39 m_height(0),
40 m_frame_size(0),
41 m_colour(false),
42 m_write_colour_table(false),
43 m_total_frame_count(0),
44 m_current_frame_count(0),
45 m_riff_count(0),
46 m_bytes_per_pixel(1),
47 m_last_frame_pos(0),
48 m_file_write_error(false)
49 {
50 // Initialise RIFF AVI header
51 m_avi_riff_header.list.u32 = FCC_RIFF;
52 m_avi_riff_header.size = 0;
53 m_avi_riff_header.four_cc.u32 = FCC_AVI;
54
55 // Initialise the RIFF AVIX header
56 m_avix_riff_header.list.u32 = FCC_RIFF;
57 m_avix_riff_header.size = 0;
58 m_avix_riff_header.four_cc.u32 = FCC_AVIX;
59
60 // Initialise hdrl list header
61 m_hdrl_list_header.list.u32 = FCC_LIST;
62 m_hdrl_list_header.size = 0;
63 m_hdrl_list_header.four_cc.u32 = FCC_hdrl;
64
65 // Initialise avih chunk header
66 m_avih_chunk_header.four_cc.u32 = FCC_avih;
67 m_avih_chunk_header.size = sizeof(s_main_avi_header);
68
69 // Initialise the junk chunk header
70 m_junk_chunk_header.four_cc.u32 = FCC_JUNK;
71 m_junk_chunk_header.size = 0;
72
73 // Initialise the main AVI header
74 m_main_avih_header.micro_sec_per_frame = 100000; // 10 fps by default
75 m_main_avih_header.max_bytes_per_sec = 0;
76 m_main_avih_header.padding_granularity = 0;
77 m_main_avih_header.flags = AVIF_HASINDEX;// | AVIF_MUSTUSEINDEX;
78 m_main_avih_header.total_frames = 0; // Increment as frames are added
79 m_main_avih_header.initial_frames = 0; // Always 0
80 m_main_avih_header.streams = 1; // Always 1 stream
81 m_main_avih_header.suggested_buffer_size = 0; // Fill in later
82 m_main_avih_header.width = 0; // Fill in later
83 m_main_avih_header.height = 0; // Fill in later
84 m_main_avih_header.reserved[0] = 0; // Always 0
85 m_main_avih_header.reserved[1] = 0; // Always 0
86 m_main_avih_header.reserved[2] = 0; // Always 0
87 m_main_avih_header.reserved[3] = 0; // Always 0
88
89 // Initialise strl list header
90 m_strl_list_header.list.u32 = FCC_LIST;
91 m_strl_list_header.size = 0;
92 m_strl_list_header.four_cc.u32 = FCC_strl;
93
94 // Initialise strh chunk header
95 m_strh_chunk_header.four_cc.u32 = FCC_strh;
96 m_strh_chunk_header.size = sizeof(s_avi_stream_header);
97
98 // Initialise vids stream header
99 m_vids_stream_header.type.u32 = FCC_vids;
100 m_vids_stream_header.handler.u32 = FCC_DIB;
101 m_vids_stream_header.flags = 0;
102 m_vids_stream_header.priority = 0;
103 m_vids_stream_header.language = 0;
104 m_vids_stream_header.initial_frames = 0;
105 m_vids_stream_header.scale = 1;
106 m_vids_stream_header.rate = 10; // 10 fps by default
107 m_vids_stream_header.start = 0;
108 m_vids_stream_header.length = 0; // Fill in later
109 m_vids_stream_header.suggested_buffer_size = 0; // Fill in later
110 m_vids_stream_header.quality = 0xFFFFFFFF;
111 m_vids_stream_header.sample_size = 0;
112 m_vids_stream_header.frame.left = 0;
113 m_vids_stream_header.frame.top = 0;
114 m_vids_stream_header.frame.right = 0;
115 m_vids_stream_header.frame.bottom = 0;
116
117 // Initialise STRF chunk header
118 m_strf_chunk_header.four_cc.u32 = FCC_strf;
119 m_strf_chunk_header.size = 0; // Fill in later
120
121 // Initialise the BITMAP info header
122 m_bitmap_info_header.size = 0; // Fill in later
123 m_bitmap_info_header.width = 0; // Fill in later
124 m_bitmap_info_header.height = 0; // Fill in later
125 m_bitmap_info_header.planes = 1;
126 m_bitmap_info_header.bit_count = 8; // Fill in later
127 m_bitmap_info_header.compression.u32 = 0; // No compression
128 m_bitmap_info_header.size_image = 0; // Fill in later
129 m_bitmap_info_header.x_pels_per_meter = 0;
130 m_bitmap_info_header.y_pels_per_meter = 0;
131 m_bitmap_info_header.clr_used = 0; // Fill in later
132 m_bitmap_info_header.clr_important = 0;
133
134 // Initialise the indx chunk header
135 m_indx_chunk_header.four_cc.u32 = FCC_indx;
136 m_indx_chunk_header.size = sizeof(s_avi_superindex_header)
137 + sizeof(s_avi_superindex_entry) * NUMBER_SUPERINDEX_ENTRIES;
138
139 // Initialise the AVI superindex header
140 m_avi_superindex_header.longs_per_entry = 4;
141 m_avi_superindex_header.index_sub_type = 0;//AVI_INDEX_OF_CHUNKS;
142 m_avi_superindex_header.index_type = AVI_INDEX_OF_INDEXES;
143 m_avi_superindex_header.entries_in_use = 0; // Will be increment as needed
144 m_avi_superindex_header.chunk_id.u32 = FCC_00db;
145 m_avi_superindex_header.reserved[0] = 0;
146 m_avi_superindex_header.reserved[1] = 0;
147 m_avi_superindex_header.reserved[2] = 0;
148
149 // Initialise the AVI superindex entries
150 for (int x = 0; x < NUMBER_SUPERINDEX_ENTRIES; x++) {
151 m_avi_superindex_entries[x].offset = 0; // Fill in later
152 m_avi_superindex_entries[x].size = 0; // Fill in later - the size of the standard index this entry point to
153 m_avi_superindex_entries[x].duration = 0; // Fill in later - the number of frames in the sub-index
154 }
155
156 // Initialise the odml list header
157 m_odml_list_header.list.u32 = FCC_LIST;
158 m_odml_list_header.size = sizeof(m_odml_list_header.four_cc.u32) + sizeof(m_dmlh_chunk_header) + sizeof(m_extended_avi_header);
159 m_odml_list_header.four_cc.u32 = FCC_odml;
160
161 // Initialise the dmlh chunk header
162 m_dmlh_chunk_header.four_cc.u32 = FCC_dmlh;
163 m_dmlh_chunk_header.size = sizeof(m_extended_avi_header);
164
165 // Initialise the extended AVI header
166 m_extended_avi_header.total_frames = 0; // Fill in as the file closes
167
168 // Initialise the movie list header (AVI RIFF version)
169 m_movi_list_header.list.u32 = FCC_LIST;
170 m_movi_list_header.size = 0; // Fill in later
171 m_movi_list_header.four_cc.u32 = FCC_movi;
172
173 // Initialise the movie list header (AVIX RIFF version)
174 m_movi_avix_list_header.list.u32 = FCC_LIST;
175 m_movi_avix_list_header.size = 0; // Fill in later
176 m_movi_avix_list_header.four_cc.u32 = FCC_movi;
177
178 // Initialise 00db chunk header
179 m_00db_chunk_header.four_cc.u32 = FCC_00db;
180 m_00db_chunk_header.size = 0; // Fill in later
181
182 // Initialise idx1 chunk header
183 m_idx1_chunk_header.four_cc.u32 = FCC_idx1;
184 m_idx1_chunk_header.size = 0; // Fill in with frames * sizeof(s_avi_old_index_entry)
185
186 // Initialise the ix00 chunk header
187 m_ix00_chunk_header.four_cc.u32 = FCC_ix00;
188 m_ix00_chunk_header.size = 0; // Fill in later
189
190 // Initialise the standard index header
191 m_avi_stdindex_header.longs_per_entry = 2;
192 m_avi_stdindex_header.index_sub_type = 0;
193 m_avi_stdindex_header.index_type = AVI_INDEX_OF_CHUNKS;
194 m_avi_stdindex_header.entries_in_use = 0;
195 m_avi_stdindex_header.chunk_id.u32 = FCC_00db;
196 m_avi_stdindex_header.base_offset[0] = 0;
197 m_avi_stdindex_header.base_offset[1] = 0;
198 m_avi_stdindex_header.reserved3 = 0;
199
200 // Initialise the standard index entry
201 m_avi_stdindex_entry.offset = 0;
202 m_avi_stdindex_entry.size = 0;
203
204 // Initialise avi index entry
205 m_avi_index_entry.chunk_id.u32 = FCC_00db;
206 m_avi_index_entry.flags = AVIIF_KEYFRAME;
207 m_avi_index_entry.offset = 0; // Fill in as required
208 m_avi_index_entry.size = 0; // Fill in later
209 }
210
211
212 // ------------------------------------------
213 // fwrite() function with error checking
214 // ------------------------------------------
fwrite_error_check(const void * ptr,size_t size,size_t count,FILE * p_stream)215 void c_pipp_avi_write::fwrite_error_check(
216 const void *ptr,
217 size_t size,
218 size_t count,
219 FILE *p_stream)
220 {
221 if (!m_file_write_error) { // Do not continue writing after an error has occured
222 size_t size_written = fwrite(ptr, size, count, p_stream);
223 if (size_written != count) {
224 m_file_write_error = true;
225 }
226 }
227 }
228
229
230 // ------------------------------------------
231 // Write headers to file
232 // ------------------------------------------
write_headers()233 void c_pipp_avi_write::write_headers()
234 {
235 if (m_file_write_error) {
236 // Early exit if there has already been a file write error
237 return;
238 }
239
240 // Write RIF Header to file
241 fwrite_error_check(&m_avi_riff_header , 1 , sizeof(m_avi_riff_header) , mp_avi_file);
242
243 // Write hdrl list header to file
244 fwrite_error_check(&m_hdrl_list_header , 1 , sizeof(m_hdrl_list_header) , mp_avi_file);
245
246 // Write avih chunk header to file
247 fwrite_error_check(&m_avih_chunk_header , 1 , sizeof(m_avih_chunk_header) , mp_avi_file);
248
249 // Write Main avi header to file
250 fwrite_error_check(&m_main_avih_header , 1 , sizeof(m_main_avih_header) , mp_avi_file);
251
252 // Write strl list header to file
253 fwrite_error_check(&m_strl_list_header , 1 , sizeof(m_strl_list_header) , mp_avi_file);
254
255 // Write strh chunk header to file
256 fwrite_error_check(&m_strh_chunk_header , 1 , sizeof(m_strh_chunk_header) , mp_avi_file);
257
258 // Write video stream header to file
259 fwrite_error_check(&m_vids_stream_header , 1 , sizeof(m_vids_stream_header) , mp_avi_file);
260
261 // Write strf chunk header to file
262 fwrite_error_check(&m_strf_chunk_header , 1 , sizeof(m_strf_chunk_header) , mp_avi_file);
263
264 // Write BITMAPINFO header to file
265 fwrite_error_check(&m_bitmap_info_header , 1 , sizeof(m_bitmap_info_header) , mp_avi_file);
266
267 // Write colour table to file if required (for DIB mono images)
268 if (m_write_colour_table) {
269 uint8_t colour_table[256 * 4];
270 for (int32_t x = 0; x < 256 * 4; x++) {
271 if (x % 4 != 3) {
272 colour_table[x] = x / 4;
273 } else {
274 colour_table[x] = 0;
275 }
276 }
277
278 // Write colour table to file
279 fwrite_error_check(colour_table , 1 , 256 * 4, mp_avi_file);
280 }
281
282 if (m_old_avi_format != 0) {
283 // Junk chunk moves next pos to 0x2000
284 m_junk_chunk_header.size = 0x2000 - (int32_t)ftell64(mp_avi_file) - sizeof(m_junk_chunk_header);
285
286 // Write junk header to file
287 fwrite_error_check(&m_junk_chunk_header , 1 , sizeof(m_junk_chunk_header) , mp_avi_file);
288
289 // Write junk data to file
290 uint8_t junk_byte = 0;
291 for (uint32_t junk_count = 0; junk_count < m_junk_chunk_header.size; junk_count++) {
292 fwrite_error_check(&junk_byte , 1 , 1, mp_avi_file);
293 }
294 } else {
295 // These fields are not present with the old AVI format
296
297 // Write indx chunk header to file
298 fwrite_error_check(&m_indx_chunk_header , 1 , sizeof(m_indx_chunk_header) , mp_avi_file);
299
300 // Write AVI Superindex header to file
301 fwrite_error_check(&m_avi_superindex_header , 1 , sizeof(m_avi_superindex_header), mp_avi_file);
302
303 // Write AVI Superindex entries to file
304 fwrite_error_check(m_avi_superindex_entries , 1 , sizeof(m_avi_superindex_entries[0]) * NUMBER_SUPERINDEX_ENTRIES, mp_avi_file);
305
306 // Write odml list header to file
307 fwrite_error_check(&m_odml_list_header , 1 , sizeof(m_odml_list_header) , mp_avi_file);
308
309 // Write dmlh chunk header to file
310 fwrite_error_check(&m_dmlh_chunk_header , 1 , sizeof(m_dmlh_chunk_header) , mp_avi_file);
311
312 // Write extended AVI header to file
313 fwrite_error_check(&m_extended_avi_header , 1 , sizeof(m_extended_avi_header) , mp_avi_file);
314 }
315
316 // Write movi list header
317 fwrite_error_check(&m_movi_list_header , 1 , sizeof(m_movi_list_header) , mp_avi_file);
318 }
319
320
321 // ------------------------------------------
322 // A new frame has been added
323 // ------------------------------------------
frame_added()324 void c_pipp_avi_write::frame_added()
325 {
326 // Split AVI files to prevent max size from being exceeded
327 if (m_old_avi_format != 0) {
328 if (m_current_frame_count == m_max_frames_in_first_riff) {
329 split_close();
330 split_create();
331 }
332 } else {
333 // Not old format
334 if (m_riff_count == (NUMBER_SUPERINDEX_ENTRIES-1) && m_current_frame_count == m_max_frames_in_other_riffs) {
335 split_close();
336 split_create();
337 }
338 }
339
340 if (m_total_frame_count == 0) {
341 // Grab position of first frame in this RIFF for the base offset
342 m_avi_superindex_header.entries_in_use++;
343 uint64_t base_offset = ftell64(mp_avi_file) + sizeof(m_00db_chunk_header);
344 //uint64_t base_offset = ftell64(avi_fp) - frame_size;
345 m_avi_stdindex_header.base_offset[1] = (uint32_t)(base_offset >> 32);
346 m_avi_stdindex_header.base_offset[0] = (uint32_t)(base_offset & 0xFFFFFFFF);
347 }
348
349 m_total_frame_count++; // Increment frame counts
350
351 if (m_old_avi_format == 0) {
352 if ((m_riff_count == 0 && m_current_frame_count == m_max_frames_in_first_riff)
353 || (m_riff_count > 0 && m_current_frame_count == m_max_frames_in_other_riffs)) {
354 // This frame needs to be in a new RIFF
355 finish_riff(); // Finish this RIFF
356
357 // Start the next RIFF
358 fwrite_error_check(&m_avix_riff_header , 1, sizeof(m_avix_riff_header), mp_avi_file);
359
360 // Start the new movi LIST
361 fwrite_error_check(&m_movi_avix_list_header , 1, sizeof(m_movi_avix_list_header), mp_avi_file);
362
363 // Grab position of first frame in this RIFF for the base offset
364 m_avi_superindex_header.entries_in_use++;
365 uint64_t base_offset = ftell64(mp_avi_file) + sizeof(m_00db_chunk_header);
366 m_avi_stdindex_header.base_offset[1] = (uint32_t)(base_offset >> 32);
367 m_avi_stdindex_header.base_offset[0] = (uint32_t)(base_offset & 0xFFFFFFFF);
368 m_current_frame_count = 0;
369 }
370 }
371
372 m_current_frame_count++;
373 }
374
375
376 // ------------------------------------------
377 // Create a new AVI file
378 // ------------------------------------------
create(const char * filename,int32_t width,int32_t height,bool colour,int32_t fps_rate,int32_t fps_scale,int32_t old_avi_format,int32_t quality,void * extra_data)379 bool c_pipp_avi_write::create(
380 const char *filename,
381 int32_t width,
382 int32_t height,
383 bool colour,
384 int32_t fps_rate,
385 int32_t fps_scale,
386 int32_t old_avi_format,
387 int32_t quality,
388 void *extra_data)
389 {
390 // Remove unused argument warnings
391 (void)quality;
392 (void)extra_data;
393
394 // Set member variables
395 m_width = width;
396 m_height = height;
397 m_colour = colour;
398 m_old_avi_format = old_avi_format;
399 if (!colour) {
400 m_bytes_per_pixel = 1;
401 } else {
402 m_bytes_per_pixel = 3;
403 }
404
405 // Call derived class method to set codec specific values
406 set_codec_values();
407
408 // Calculate how many frames fit into each RIFF
409 if (old_avi_format != 0) {
410 // Calculate how many frames can go into the RIFF
411 if (old_avi_format == 4) {
412 // Max RIFF size = 4GB
413 m_max_frames_in_first_riff = 0xFFFFFFFF; // Maximum RIFF size (4GB - 1)
414 } else {
415 // Max RIFF size = 2GB
416 m_max_frames_in_first_riff = 0x7FFFFFFF; // Maximum RIFF size (2GB - 1)
417 }
418 //max_frames_in_first_riff -= sizeof(avi_riff_header) ;
419 //max_frames_in_first_riff -= (sizeof(hdrl_list_header) - sizeof(hdrl_list_header.four_cc) + hdrl_list_header.size);
420 //max_frames_in_first_riff -= (sizeof(junk_chunk_header) + junk_chunk_header.size);
421 m_max_frames_in_first_riff -= 0x2000; // Junk field always takes us to 0x2000
422 m_max_frames_in_first_riff -= sizeof(m_movi_list_header);
423 m_max_frames_in_first_riff -= sizeof(m_idx1_chunk_header);
424 m_max_frames_in_first_riff /= (sizeof(m_00db_chunk_header) + m_frame_size + sizeof(m_avi_index_entry));
425 } else {
426 // Calculate how many frames can go into the first RIFF
427 m_max_frames_in_first_riff = 0x3FFFFFFF; // Maximum RIFF size (1GB - 1)
428 m_max_frames_in_first_riff -= sizeof(m_avi_riff_header) ;
429 m_max_frames_in_first_riff -= (sizeof(m_hdrl_list_header) - sizeof(m_hdrl_list_header.four_cc) + m_hdrl_list_header.size);
430 m_max_frames_in_first_riff -= sizeof(m_movi_list_header);
431 m_max_frames_in_first_riff -= (sizeof(m_ix00_chunk_header) + sizeof(m_avi_stdindex_header));
432 m_max_frames_in_first_riff -= sizeof(m_idx1_chunk_header);
433 m_max_frames_in_first_riff /= (sizeof(m_00db_chunk_header) + m_frame_size + sizeof(m_avi_stdindex_entry) + sizeof(m_avi_index_entry));
434
435 // Calculate how many frames can go into the subsequent RIFFs
436 m_max_frames_in_other_riffs = 0x7FFFFFFF; // Maximum RIFF size (2GB - 1)
437 m_max_frames_in_other_riffs -= (sizeof(m_avix_riff_header) + sizeof(m_movi_avix_list_header) + sizeof(m_ix00_chunk_header) + sizeof(m_avi_stdindex_header));
438 m_max_frames_in_other_riffs /= (sizeof(m_00db_chunk_header) + m_frame_size + sizeof(m_avi_stdindex_entry));
439 }
440
441 m_split_count = 0;
442
443 mp_filename.reset(new char[strlen(filename) + 1]);
444 // Copy filename into buffer
445 strcpy(mp_filename.get(), filename);
446
447 // Get extension
448 char *extension = strrchr(mp_filename.get(), '.');
449 if (extension == NULL) {
450 // No extension found - create one
451 mp_extension.reset(new char[strlen(".avi") + 1]);
452 strcpy(mp_extension.get(), ".avi"); // Copy extension
453 } else {
454 mp_extension.reset(new char[strlen(extension) + 1]);
455 strcpy(mp_extension.get(), extension); // Copy extension
456 *extension = 0; // Remove extension from filename
457 }
458
459 // Reset counts
460 m_total_frame_count = 0;
461 m_current_frame_count = 0;
462 m_riff_count = 0;
463
464 // Set flag to write colour table if required
465 if (!colour) {
466 m_write_colour_table = 1;
467 }
468
469 // Update AVI structures
470 m_main_avih_header.width = width;
471 m_main_avih_header.height = height;
472 uint64_t us_per_frame = (uint64_t)1000000 * (uint64_t)fps_scale;
473 us_per_frame /= fps_rate;
474 m_main_avih_header.micro_sec_per_frame = (uint32_t)us_per_frame;
475 m_vids_stream_header.rate = fps_rate;
476 m_vids_stream_header.scale = fps_scale;
477 m_vids_stream_header.frame.right = width;
478 m_vids_stream_header.frame.bottom = height;
479
480 // Reset fields to count frames and indexes
481 m_avi_superindex_header.entries_in_use = 0; // Will be increment as needed
482 m_extended_avi_header.total_frames = 0; // Increment as frames are added
483
484 // Set size of strf chunk
485 m_strf_chunk_header.size = sizeof(m_bitmap_info_header);
486 if (m_write_colour_table == 1) {
487 m_strf_chunk_header.size += 256 * 4;
488 }
489
490 // Set size of strl LIST
491 m_strl_list_header.size = sizeof(m_strl_list_header.four_cc)
492 + sizeof(m_strh_chunk_header) + m_strh_chunk_header.size
493 + sizeof(m_strf_chunk_header) + m_strf_chunk_header.size;
494
495 if (old_avi_format == 0) {
496 m_strl_list_header.size = m_strl_list_header.size
497 + sizeof(m_indx_chunk_header) + m_indx_chunk_header.size
498 + sizeof(m_odml_list_header) - sizeof(m_odml_list_header.four_cc) + m_odml_list_header.size;
499 }
500
501 // Set the size of hdrl LIST
502 m_hdrl_list_header.size = sizeof(m_hdrl_list_header.four_cc)
503 + sizeof(m_avih_chunk_header) + m_avih_chunk_header.size
504 + sizeof(m_strl_list_header) - sizeof(m_strl_list_header.four_cc) + m_strl_list_header.size;
505
506 m_main_avih_header.suggested_buffer_size = m_frame_size + 8;
507 m_vids_stream_header.suggested_buffer_size = m_frame_size + 8;
508
509 m_bitmap_info_header.size = sizeof(m_bitmap_info_header);
510 m_bitmap_info_header.width = width;
511 m_bitmap_info_header.height = height;
512 m_bitmap_info_header.bit_count = 8 * m_bytes_per_pixel;
513
514 // Set colour table length
515 if (m_bytes_per_pixel == 1) {
516 m_bitmap_info_header.clr_used = 256;
517 }
518
519 m_00db_chunk_header.size = m_frame_size;
520
521 // Set up MOVI LIST size for AVIX RIFFs as maximum size
522 m_movi_avix_list_header.size = sizeof(m_movi_avix_list_header.four_cc)
523 + m_max_frames_in_other_riffs * sizeof(s_chunk_header) // 00db chunks
524 + m_max_frames_in_other_riffs * m_frame_size // frame data
525 + sizeof(m_ix00_chunk_header) // ix00 chunk header
526 + sizeof(m_avi_stdindex_header) // Standard index header
527 + m_max_frames_in_other_riffs * sizeof(m_avi_stdindex_entry); // Standard index entries
528
529 // Set up AVIX RIFF size as maximum size
530 m_avix_riff_header.size = sizeof(m_avix_riff_header.four_cc)
531 + sizeof(m_movi_avix_list_header) - sizeof(m_movi_avix_list_header.four_cc)
532 + m_movi_avix_list_header.size;
533
534 mp_avi_file = fopen(filename, "wb+");
535
536 // Check file opened
537 // Return if file did not open
538 if (mp_avi_file) {
539 m_open = true;
540 } else {
541 m_open = false;
542 m_file_write_error = true;
543 }
544
545 // Write headers to file
546 write_headers();
547
548 // Handle case where file opened but subsequent write failed
549 if (m_open && m_file_write_error) {
550 fclose(mp_avi_file);
551 m_open = false;
552 }
553
554 bool ret = m_file_write_error;
555 m_file_write_error = false;
556 return ret;
557 }
558
559
560 // ------------------------------------------
561 // Create a new AVI file
562 // ------------------------------------------
split_create()563 void c_pipp_avi_write::split_create()
564 {
565 // Increment split count
566 m_split_count++;
567
568 // Reset counts
569 m_total_frame_count = 0;
570 m_current_frame_count = 0;
571 m_riff_count = 0;
572
573 // Reset fields to count frames and indexes
574 m_avi_superindex_header.entries_in_use = 0; // Will be increment as needed
575 m_extended_avi_header.total_frames = 0; // Increment as frames are added
576
577 // Call derived class method to set codec specific values
578 set_codec_values();
579
580 // Create split filename
581 std::unique_ptr<char[]> p_split_filename(new char[strlen(mp_filename.get()) + 3 + strlen(mp_extension.get()) + 1]);
582 if (m_split_count == 0) {
583 // No split count to be inserted at end of filename
584 sprintf(p_split_filename.get(), "%s%s", mp_filename.get(), mp_extension.get());
585 } else {
586 // Split count required at end of filename
587 sprintf(p_split_filename.get(), "%s_%02d%s", mp_filename.get(), m_split_count, mp_extension.get());
588 }
589
590 // Open new file
591 mp_avi_file = fopen(p_split_filename.get(), "wb+");
592
593 // Check file opened
594 // Return if file did not open
595 if (mp_avi_file) {
596 m_open = true;
597 } else {
598 m_file_write_error = true;
599 m_open = false;
600 }
601
602 // Write headers to file
603 write_headers();
604 }
605
606
607 // ------------------------------------------
608 // Finish the current RIFF
609 // ------------------------------------------
finish_riff()610 void c_pipp_avi_write::finish_riff()
611 {
612 if (m_file_write_error) {
613 // Early return for previous file write errors
614 return;
615 }
616
617 // Add odml indexes
618 if (m_old_avi_format == 0) {
619 m_ix00_chunk_header.size = sizeof(m_avi_stdindex_header)
620 + m_current_frame_count * sizeof(m_avi_stdindex_entry);
621
622 // Grab position of the ix00 chunk
623 m_avi_superindex_entries[m_riff_count].duration = m_current_frame_count;
624 m_avi_superindex_entries[m_riff_count].offset = ftell64(mp_avi_file);
625 m_avi_superindex_entries[m_riff_count].size = sizeof(m_ix00_chunk_header) + m_ix00_chunk_header.size;
626
627 // Write ix00 chunk header to file
628 fwrite_error_check(&m_ix00_chunk_header, 1, sizeof(m_ix00_chunk_header), mp_avi_file);
629
630 // Write AVI standard header to file
631 m_avi_stdindex_header.entries_in_use = m_current_frame_count;
632 fwrite_error_check(&m_avi_stdindex_header, 1, sizeof(m_avi_stdindex_header), mp_avi_file);
633
634 // Write AVI standard indexes to file
635 m_avi_stdindex_entry.size = m_frame_size;
636 for (int x = 0; x < m_current_frame_count; x++) {
637 // Update entry
638 m_avi_stdindex_entry.offset = x * (m_frame_size + sizeof(m_00db_chunk_header));
639 fwrite_error_check(&m_avi_stdindex_entry, 1, sizeof(m_avi_stdindex_entry), mp_avi_file);
640 }
641 }
642
643 // Update final fields
644 m_extended_avi_header.total_frames = m_total_frame_count;
645 m_vids_stream_header.length = m_total_frame_count;
646
647 if (m_riff_count == 0) {
648 // This is the first RIFF - update fields that rely on the first first RIFF
649
650 m_main_avih_header.total_frames = m_current_frame_count;
651
652 m_bitmap_info_header.size_image = m_frame_size;
653 m_avi_index_entry.size = m_frame_size;
654 m_idx1_chunk_header.size = m_current_frame_count * sizeof(s_avi_old_index_entry);
655 m_movi_list_header.size = sizeof(m_movi_list_header.four_cc)
656 + m_current_frame_count * sizeof(s_chunk_header) // 00db chunks
657 + m_current_frame_count * m_frame_size; // frame data
658
659 if (m_old_avi_format == 0) {
660 // Add ix00 index list size on
661 m_movi_list_header.size = m_movi_list_header.size
662 + sizeof(m_ix00_chunk_header)
663 + m_ix00_chunk_header.size;
664 }
665
666 // Write index chunk header to file
667 fwrite_error_check(&m_idx1_chunk_header , 1 , sizeof(m_idx1_chunk_header), mp_avi_file);
668
669 // Write AVI 1.0 index entries to file
670 m_avi_index_entry.offset = 0x4;
671
672 // Write all entries
673 for (int32_t x = 0; x < m_current_frame_count; x++) {
674 fwrite_error_check(&m_avi_index_entry , 1 , sizeof(m_avi_index_entry), mp_avi_file); // Write entry to file
675 m_avi_index_entry.offset += (sizeof(s_chunk_header) + m_frame_size); // Increment offset
676 }
677
678 // Get the filesize
679 int64_t filesize = ftell64(mp_avi_file);
680
681 // Update final headers
682 m_avi_riff_header.size = (uint32_t)filesize - 8;
683 }
684
685 // Grab start position of the next RIFF
686 int64_t riff_end_position = ftell64(mp_avi_file);
687
688 // Processing for subsequent RIFFs
689 if (m_riff_count > 0 && m_current_frame_count != m_max_frames_in_other_riffs) {
690 // This RIFF must be the last RIFF as it does not have the maximum number of frames in it
691 // We need to correct the RIFF and LIST sizes as it is not completely full
692
693 // Go back to the start of this RIFF
694 fseek64(mp_avi_file, m_riff_start_position, SEEK_SET);
695
696 // Write RIFF header again with correct length now that we know it
697 m_avix_riff_header.size = (int32_t)(riff_end_position - m_riff_start_position) - sizeof(m_avix_riff_header) + sizeof(m_avix_riff_header.four_cc);
698 fwrite_error_check(&m_avix_riff_header , 1, sizeof(m_avix_riff_header), mp_avi_file);
699
700 // Write the movi LIST header again with the correct length now that we know it
701 m_movi_avix_list_header.size = m_avix_riff_header.size - sizeof(m_movi_avix_list_header);
702 fwrite_error_check(&m_movi_avix_list_header , 1 , sizeof(m_movi_avix_list_header) , mp_avi_file);
703
704 // Go back to the end of this RIFF
705 fseek64(mp_avi_file, riff_end_position, SEEK_SET);
706 }
707
708 // Grab start position of the next RIFF
709 m_riff_start_position = riff_end_position;
710
711 // Reset current frame count as this RIFF has been closed
712 m_current_frame_count = 0;
713
714 // Increment RIFF count
715 m_riff_count++;
716 }
717
718
719
720 // ------------------------------------------
721 // Write header and close AVI file
722 // ------------------------------------------
close()723 bool c_pipp_avi_write::close()
724 {
725 if (m_open) {
726 // Finish off this RIFF
727 finish_riff();
728
729 // Go back to start of file
730 fseek64(mp_avi_file, 0, SEEK_SET);
731
732 // Write the updated headers to the file
733 write_headers();
734
735 // Note that the AVI file is closed
736 m_open = false;
737
738 fclose(mp_avi_file);
739 mp_avi_file = NULL;
740 }
741
742 bool ret = m_file_write_error;
743 m_file_write_error = false;
744 return ret;
745 }
746
747
748 // ------------------------------------------
749 // Write header and close AVI file
750 // ------------------------------------------
split_close()751 void c_pipp_avi_write::split_close()
752 {
753 // Finish off this RIFF
754 finish_riff();
755
756 // Go back to start of file
757 fseek64(mp_avi_file, 0, SEEK_SET);
758
759 // Write the updated headers to the file
760 write_headers();
761
762 fclose(mp_avi_file);
763 mp_avi_file = NULL;
764 }
765
766
debug_headers()767 int32_t c_pipp_avi_write::debug_headers() {
768 // Debug output
769 printf("frame_count: %d\n\n", m_total_frame_count);
770
771 printf("avi_riff_header.list: %c%c%c%c\n",
772 m_avi_riff_header.list.chr[0],
773 m_avi_riff_header.list.chr[1],
774 m_avi_riff_header.list.chr[2],
775 m_avi_riff_header.list.chr[3]);
776
777 printf("avi_riff_header.size: 0x%x\n", m_avi_riff_header.size);
778
779 printf("avi_riff_header.four_cc: %c%c%c%c\n\n",
780 m_avi_riff_header.four_cc.chr[0],
781 m_avi_riff_header.four_cc.chr[1],
782 m_avi_riff_header.four_cc.chr[2],
783 m_avi_riff_header.four_cc.chr[3]);
784
785 printf("hdrl_list_header.list: %c%c%c%c\n",
786 m_hdrl_list_header.list.chr[0],
787 m_hdrl_list_header.list.chr[1],
788 m_hdrl_list_header.list.chr[2],
789 m_hdrl_list_header.list.chr[3]);
790
791 printf("hdrl_list_header.size: 0x%x\n", m_hdrl_list_header.size);
792
793 printf("hdrl_list_header.four_cc: %c%c%c%c\n\n",
794 m_hdrl_list_header.four_cc.chr[0],
795 m_hdrl_list_header.four_cc.chr[1],
796 m_hdrl_list_header.four_cc.chr[2],
797 m_hdrl_list_header.four_cc.chr[3]);
798
799 printf("avih_chunk_header.four_cc: %c%c%c%c\n",
800 m_avih_chunk_header.four_cc.chr[0],
801 m_avih_chunk_header.four_cc.chr[1],
802 m_avih_chunk_header.four_cc.chr[2],
803 m_avih_chunk_header.four_cc.chr[3]);
804
805 printf("avih_chunk_header.size: 0x%x\n\n", m_avih_chunk_header.size);
806
807 printf("main_avih_header.micro_sec_per_frame: %ud\n", m_main_avih_header.micro_sec_per_frame);
808 printf("main_avih_header.max_bytes_per_sec: %ud\n", m_main_avih_header.max_bytes_per_sec);
809 printf("main_avih_header.padding_granularity: %ud\n", m_main_avih_header.padding_granularity);
810 printf("main_avih_header.flags: 0x%x\n", m_main_avih_header.flags);
811 printf("main_avih_header.total_frames: %ud\n", m_main_avih_header.total_frames);
812 printf("main_avih_header.initial_frames: %ud\n", m_main_avih_header.initial_frames);
813 printf("main_avih_header.streams: %ud\n", m_main_avih_header.streams);
814 printf("main_avih_header.suggested_buffer_size: 0x%x\n", m_main_avih_header.suggested_buffer_size);
815 printf("main_avih_header.width: %d\n", m_main_avih_header.width);
816 printf("main_avih_header.height: %d\n\n", m_main_avih_header.height);
817
818 printf("strl_list_header.list: %c%c%c%c\n",
819 m_strl_list_header.list.chr[0],
820 m_strl_list_header.list.chr[1],
821 m_strl_list_header.list.chr[2],
822 m_strl_list_header.list.chr[3]);
823
824 printf("strl_list_header.size: 0x%x\n", m_strl_list_header.size);
825
826 printf("strl_list_header.four_cc: %c%c%c%c\n\n",
827 m_strl_list_header.four_cc.chr[0],
828 m_strl_list_header.four_cc.chr[1],
829 m_strl_list_header.four_cc.chr[2],
830 m_strl_list_header.four_cc.chr[3]);
831
832 printf("strh_chunk_header.four_cc: %c%c%c%c\n",
833 m_strh_chunk_header.four_cc.chr[0],
834 m_strh_chunk_header.four_cc.chr[1],
835 m_strh_chunk_header.four_cc.chr[2],
836 m_strh_chunk_header.four_cc.chr[3]);
837
838 printf("strh_chunk_header.size: 0x%x\n", m_strh_chunk_header.size);
839
840 printf("vids_stream_header.four_cc: %c%c%c%c\n",
841 m_vids_stream_header.handler.chr[0],
842 m_vids_stream_header.handler.chr[1],
843 m_vids_stream_header.handler.chr[2],
844 m_vids_stream_header.handler.chr[3]);
845
846 printf("vids_stream_header.flags: 0x%x\n", m_vids_stream_header.flags);
847 printf("vids_stream_header.priority: 0x%x\n", m_vids_stream_header.priority);
848 printf("vids_stream_header.language: 0x%x\n", m_vids_stream_header.language);
849 printf("vids_stream_header.initial_frames: 0x%x\n", m_vids_stream_header.initial_frames);
850 printf("vids_stream_header.scale: 0x%x\n", m_vids_stream_header.scale);
851 printf("vids_stream_header.rate: %ud\n", m_vids_stream_header.rate);
852 printf("vids_stream_header.start: 0x%x\n", m_vids_stream_header.start);
853 printf("vids_stream_header.length: 0x%x\n", m_vids_stream_header.length);
854 printf("vids_stream_header.suggested_buffer_size: 0x%x\n", m_vids_stream_header.suggested_buffer_size);
855 printf("vids_stream_header.quality: 0x%x\n", m_vids_stream_header.quality);
856 printf("vids_stream_header.sample_size: 0x%x\n", m_vids_stream_header.sample_size);
857 printf("vids_stream_header.frame.left: 0x%x\n", m_vids_stream_header.frame.left);
858 printf("vids_stream_header.frame.top: 0x%x\n", m_vids_stream_header.frame.top);
859 printf("vids_stream_header.frame.right: 0x%x\n", m_vids_stream_header.frame.right);
860 printf("vids_stream_header.frame.bottom: 0x%x\n\n", m_vids_stream_header.frame.bottom);
861
862 printf("strf_chunk_header.four_cc: %c%c%c%c\n",
863 m_strf_chunk_header.four_cc.chr[0],
864 m_strf_chunk_header.four_cc.chr[1],
865 m_strf_chunk_header.four_cc.chr[2],
866 m_strf_chunk_header.four_cc.chr[3]);
867 printf("strf_chunk_header.size: 0x%x\n\n", m_strf_chunk_header.size);
868
869 printf("bitmap_info_header.size: %ud\n", m_bitmap_info_header.size);
870 printf("bitmap_info_header.width: %d\n", m_bitmap_info_header.width);
871 printf("bitmap_info_header.height: %d\n", m_bitmap_info_header.height);
872 printf("bitmap_info_header.planes: %d\n", m_bitmap_info_header.planes);
873 printf("bitmap_info_header.bit_count: %d\n", m_bitmap_info_header.bit_count);
874 printf("bitmap_info_header.compression.u32: 0x%x\n", m_bitmap_info_header.compression.u32);
875 printf("bitmap_info_header.size_image: %ud\n", m_bitmap_info_header.size_image);
876 printf("bitmap_info_header.x_pels_per_meter: %ud\n", m_bitmap_info_header.x_pels_per_meter);
877 printf("bitmap_info_header.y_pels_per_meter: %ud\n", m_bitmap_info_header.y_pels_per_meter);
878 printf("bitmap_info_header.clr_used: %ud\n", m_bitmap_info_header.clr_used);
879 printf("bitmap_info_header.clr_important: %ud\n\n", m_bitmap_info_header.clr_important);
880
881 printf("movi_list_header.list: %c%c%c%c\n",
882 m_movi_list_header.list.chr[0],
883 m_movi_list_header.list.chr[1],
884 m_movi_list_header.list.chr[2],
885 m_movi_list_header.list.chr[3]);
886
887 printf("movi_list_header.size: 0x%x\n", m_movi_list_header.size);
888
889 printf("movi_list_header.four_cc: %c%c%c%c\n\n",
890 m_movi_list_header.four_cc.chr[0],
891 m_movi_list_header.four_cc.chr[1],
892 m_movi_list_header.four_cc.chr[2],
893 m_movi_list_header.four_cc.chr[3]);
894
895 printf("_00db_chunk_header.four_cc: %c%c%c%c\n",
896 m_00db_chunk_header.four_cc.chr[0],
897 m_00db_chunk_header.four_cc.chr[1],
898 m_00db_chunk_header.four_cc.chr[2],
899 m_00db_chunk_header.four_cc.chr[3]);
900 printf("_00db_chunk_header.size: 0x%x\n\n", m_00db_chunk_header.size);
901
902 printf("idx1_chunk_header.four_cc: %c%c%c%c\n",
903 m_idx1_chunk_header.four_cc.chr[0],
904 m_idx1_chunk_header.four_cc.chr[1],
905 m_idx1_chunk_header.four_cc.chr[2],
906 m_idx1_chunk_header.four_cc.chr[3]);
907 printf("idx1_chunk_header.size: 0x%x\n\n", m_idx1_chunk_header.size);
908
909 printf("avi_index_entry.chunk_id: %c%c%c%c\n",
910 m_avi_index_entry.chunk_id.chr[0],
911 m_avi_index_entry.chunk_id.chr[1],
912 m_avi_index_entry.chunk_id.chr[2],
913 m_avi_index_entry.chunk_id.chr[3]);
914 printf("avi_index_entry.flags: 0x%x\n", m_avi_index_entry.flags);
915 printf("avi_index_entry.offset: 0x%x\n", m_avi_index_entry.offset);
916 printf("avi_index_entry.size: %ud\n\n", m_avi_index_entry.size);
917
918 return 0;
919 }
920
921