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