r954: Improve icons of the liveaudio and livevideo effects.
[cinelerra_cv.git] / quicktime / recover.c
blob84aba724aefa356613fd4b8989fa8ec4c64dde16
1 #include "funcprotos.h"
2 #include "quicktime.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <sys/time.h>
9 #include <stdint.h>
16 Face it kids, Linux crashes. Regardless of how good the free software
17 model is, you still need to run on unreliable hardware and to get any
18 hardware support at all you need to compete with McRosoft on features.
19 Ever since RedHat started trying to copy McRosoft's every move,
20 reliability has been fickle at best.
22 The X server crashes, the filesystem crashes, the network crashes, the
23 sound driver crashes, the video driver crashes. Signal handlers only
24 handle application failures but most of the crashes are complete system
25 lockups.
27 This utility should read through a truncated movie file and put a
28 header on it, no matter what immitated McRosoft feature caused the
29 crash. It only handles JPEG encoded with jpeg-6b and MJPEG encoded
30 with the BUZ driver with PCM audio.
40 #define FSEEK fseeko64
43 #define WIDTH 720
44 #define HEIGHT 480
45 #define FRAMERATE (double)30000/1001
46 #define CHANNELS 2
47 #define SAMPLERATE 48000
48 #define BITS 24
49 #define TEMP_FILE "/tmp/temp.mov"
50 #define VCODEC QUICKTIME_MJPA
51 //#define VCODEC QUICKTIME_JPEG
57 #define SEARCH_FRAGMENT (int64_t)0x100000
58 //#define SEARCH_PAD 8
59 #define SEARCH_PAD 16
62 #define GOT_NOTHING 0
63 #define IN_FIELD1 1
64 #define GOT_FIELD1 2
65 #define IN_FIELD2 3
66 #define GOT_FIELD2 4
67 #define GOT_AUDIO 5
68 #define GOT_IMAGE_START 6
69 #define GOT_IMAGE_END 7
71 // Table utilities
72 #define NEW_TABLE(ptr, size, allocation) \
73 { \
74 (ptr) = 0; \
75 (size) = 0; \
76 (allocation) = 0; \
79 #define APPEND_TABLE(ptr, size, allocation, value) \
80 { \
81 if((allocation) <= (size)) \
82 { \
83 if(!(allocation)) \
84 (allocation) = 1024; \
85 else \
86 (allocation) *= 2; \
87 int64_t *new_table = calloc(1, sizeof(int64_t) * (allocation)); \
88 memcpy(new_table, (ptr), sizeof(int64_t) * (size)); \
89 free((ptr)); \
90 (ptr) = new_table; \
91 } \
92 (ptr)[(size)] = (value); \
93 (size)++; \
98 int main(int argc, char *argv[])
100 FILE *in;
101 FILE *temp;
102 quicktime_t *out;
103 int64_t current_byte, ftell_byte;
104 int64_t jpeg_end;
105 int64_t audio_start = 0, audio_end = 0;
106 unsigned char *search_buffer = calloc(1, SEARCH_FRAGMENT);
107 unsigned char *copy_buffer = 0;
108 int i;
109 int64_t file_size;
110 struct stat status;
111 unsigned char data[8];
112 struct stat ostat;
113 int fields = 1;
114 time_t current_time = time(0);
115 time_t prev_time = 0;
116 int jpeg_header_offset;
117 int64_t field1_offset = 0;
118 int64_t field2_offset = 0;
119 int64_t image_start = 0;
120 int64_t image_end = 0;
121 int update_time = 0;
122 int state = GOT_NOTHING;
123 char *in_path;
124 int audio_frame;
125 int total_samples;
126 int field;
128 // Value taken from Cinelerra preferences
129 int audio_chunk = 131072;
132 int64_t *start_table;
133 int start_size;
134 int start_allocation;
135 int64_t *end_table;
136 int end_size;
137 int end_allocation;
138 int64_t *field_table;
139 int64_t field_size;
140 int64_t field_allocation;
142 // Dump codec settings
143 printf("Codec settings:\n"
144 " WIDTH=%d HEIGHT=%d\n"
145 " FRAMERATE=%.2f\n"
146 " CHANNELS=%d\n"
147 " SAMPLERATE=%d\n"
148 " BITS=%d\n"
149 " audio chunk=%d\n"
150 " VCODEC=\"%s\"\n",
151 WIDTH,
152 HEIGHT,
153 FRAMERATE,
154 CHANNELS,
155 SAMPLERATE,
156 BITS,
157 audio_chunk,
158 VCODEC);
160 if(argc < 2)
162 printf("Recover JPEG and PCM audio in a corrupted movie.\n"
163 "Usage: recover [options] <input>\n"
164 "Options:\n"
165 " -b samples number of samples in an audio chunk (%d)\n"
166 "\n",
167 audio_chunk);
168 exit(1);
171 for(i = 1; i < argc; i++)
173 if(!strcmp(argv[i], "-b"))
175 if(i + 1 < argc)
177 audio_chunk = atol(argv[i + 1]);
178 i++;
179 if(audio_chunk <= 0)
181 printf("Sample count for -b is out of range.\n");
182 exit(1);
185 else
187 printf("-b needs a sample count.\n");
188 exit(1);
191 else
193 in_path = argv[i];
200 in = fopen(in_path, "rb+");
201 out = quicktime_open(TEMP_FILE, 0, 1);
203 if(!in)
205 perror("open input");
206 exit(1);
208 if(!out)
210 perror("open temp");
211 exit(1);
214 quicktime_set_audio(out,
215 CHANNELS,
216 SAMPLERATE,
217 BITS,
218 QUICKTIME_TWOS);
219 quicktime_set_video(out,
221 WIDTH,
222 HEIGHT,
223 FRAMERATE,
224 VCODEC);
225 audio_start = (int64_t)0x10;
226 ftell_byte = 0;
228 if(fstat(fileno(in), &status))
229 perror("get_file_length fstat:");
230 file_size = status.st_size;
233 NEW_TABLE(start_table, start_size, start_allocation)
234 NEW_TABLE(end_table, end_size, end_allocation)
235 NEW_TABLE(field_table, field_size, field_allocation)
238 // Get the field count
239 if(!memcmp(VCODEC, QUICKTIME_MJPA, 4))
241 fields = 2;
243 else
245 fields = 1;
248 audio_frame = BITS * CHANNELS / 8;
250 // Tabulate the start and end of all the JPEG images.
251 // This search is intended to be as simple as possible, reserving more
252 // complicated operations for a table pass.
253 printf("Pass 1 video only.\n");
254 while(ftell_byte < file_size)
256 current_byte = ftell_byte;
257 fread(search_buffer, SEARCH_FRAGMENT, 1, in);
258 ftell_byte = current_byte + SEARCH_FRAGMENT - SEARCH_PAD;
259 FSEEK(in, ftell_byte, SEEK_SET);
261 for(i = 0; i < SEARCH_FRAGMENT - SEARCH_PAD; i++)
263 // Search for image start
264 if(state == GOT_NOTHING)
266 if(search_buffer[i] == 0xff &&
267 search_buffer[i + 1] == 0xd8 &&
268 search_buffer[i + 2] == 0xff &&
269 search_buffer[i + 3] == 0xe1 &&
270 search_buffer[i + 10] == 'm' &&
271 search_buffer[i + 11] == 'j' &&
272 search_buffer[i + 12] == 'p' &&
273 search_buffer[i + 13] == 'g')
275 state = GOT_IMAGE_START;
276 image_start = current_byte + i;
278 // Determine the field
279 if(fields == 2)
281 // Next field offset is nonzero in first field
282 if(search_buffer[i + 22] != 0 ||
283 search_buffer[i + 23] != 0 ||
284 search_buffer[i + 24] != 0 ||
285 search_buffer[i + 25] != 0)
287 field = 0;
289 else
291 field = 1;
293 APPEND_TABLE(field_table, field_size, field_allocation, field)
296 else
297 if(search_buffer[i] == 0xff &&
298 search_buffer[i + 1] == 0xd8 &&
299 search_buffer[i + 2] == 0xff &&
300 search_buffer[i + 3] == 0xe0 &&
301 search_buffer[i + 6] == 'J' &&
302 search_buffer[i + 7] == 'F' &&
303 search_buffer[i + 8] == 'I' &&
304 search_buffer[i + 9] == 'F')
306 state = GOT_IMAGE_START;
307 image_start = current_byte + i;
310 else
311 // Search for image end
312 if(state == GOT_IMAGE_START)
314 if(search_buffer[i] == 0xff &&
315 search_buffer[i + 1] == 0xd9)
317 // ffd9 sometimes occurs inside the mjpg tag
318 if(current_byte + i - image_start > 0x2a)
320 state = GOT_NOTHING;
321 // Put it in the table
322 image_end = current_byte + i + 2;
324 // An image may have been lost due to encoding errors but we can't do anything
325 // because the audio may by misaligned. Use the extract utility to get the audio.
326 if(image_end - image_start > audio_chunk * audio_frame)
328 printf("Possibly lost image between %llx and %llx\n",
329 image_start,
330 image_end);
331 // Put in fake image
333 * APPEND_TABLE(start_table, start_size, start_allocation, image_start)
334 * APPEND_TABLE(end_table, end_size, end_allocation, image_start + 1024)
335 * APPEND_TABLE(start_table, start_size, start_allocation, image_end - 1024)
336 * APPEND_TABLE(end_table, end_size, end_allocation, image_end)
340 APPEND_TABLE(start_table, start_size, start_allocation, image_start)
341 APPEND_TABLE(end_table, end_size, end_allocation, image_end)
343 //printf("%d %llx - %llx\n", start_size, image_start, image_end - image_start);
345 if(!(start_size % 100))
347 printf("Got %d frames. %d%%\r",
348 start_size,
349 current_byte * (int64_t)100 / file_size);
350 fflush(stdout);
361 // With the image table complete,
362 // write chunk table from the gaps in the image table
363 printf("Pass 2 audio table.\n");
364 total_samples = 0;
365 for(i = 1; i < start_size; i++)
367 int64_t next_image_start = start_table[i];
368 int64_t prev_image_end = end_table[i - 1];
370 // Got a chunk
371 if(next_image_start - prev_image_end >= audio_chunk * audio_frame)
373 long samples = (next_image_start - prev_image_end) / audio_frame;
374 quicktime_atom_t chunk_atom;
376 quicktime_set_position(out, prev_image_end);
377 quicktime_write_chunk_header(out,
378 out->atracks[0].track,
379 &chunk_atom);
380 quicktime_set_position(out, next_image_start);
381 quicktime_write_chunk_footer(out,
382 out->atracks[0].track,
383 out->atracks[0].current_chunk,
384 &chunk_atom,
385 samples);
387 * quicktime_update_tables(out,
388 * out->atracks[0].track,
389 * prev_image_end,
390 * out->atracks[0].current_chunk,
391 * out->atracks[0].current_position,
392 * samples,
393 * 0);
395 out->atracks[0].current_position += samples;
396 out->atracks[0].current_chunk++;
397 total_samples += samples;
405 // Put image table in movie
406 printf("Got %d frames %d samples total.\n", start_size, total_samples);
407 for(i = 0; i < start_size - fields; i += fields)
409 // Got a field out of order. Skip just 1 image instead of 2.
410 if(fields == 2 && field_table[i] != 0)
412 printf("Got field out of order at 0x%llx\n", start_table[i]);
413 i--;
415 else
417 quicktime_atom_t chunk_atom;
418 quicktime_set_position(out, start_table[i]);
419 quicktime_write_chunk_header(out,
420 out->vtracks[0].track,
421 &chunk_atom);
422 quicktime_set_position(out, end_table[i + fields - 1]);
423 quicktime_write_chunk_footer(out,
424 out->vtracks[0].track,
425 out->vtracks[0].current_chunk,
426 &chunk_atom,
429 * quicktime_update_tables(out,
430 * out->vtracks[0].track,
431 * start_table[i],
432 * out->vtracks[0].current_chunk,
433 * out->vtracks[0].current_position,
434 * 1,
435 * end_table[i + fields - 1] - start_table[i]);
437 out->vtracks[0].current_position++;
438 out->vtracks[0].current_chunk++;
448 // Force header out at beginning of temp file
449 quicktime_set_position(out, 0x10);
450 quicktime_close(out);
452 // Transfer header
453 FSEEK(in, 0x8, SEEK_SET);
455 data[0] = (ftell_byte & 0xff00000000000000LL) >> 56;
456 data[1] = (ftell_byte & 0xff000000000000LL) >> 48;
457 data[2] = (ftell_byte & 0xff0000000000LL) >> 40;
458 data[3] = (ftell_byte & 0xff00000000LL) >> 32;
459 data[4] = (ftell_byte & 0xff000000LL) >> 24;
460 data[5] = (ftell_byte & 0xff0000LL) >> 16;
461 data[6] = (ftell_byte & 0xff00LL) >> 8;
462 data[7] = ftell_byte & 0xff;
463 fwrite(data, 8, 1, in);
465 FSEEK(in, ftell_byte, SEEK_SET);
466 stat(TEMP_FILE, &ostat);
468 temp = fopen(TEMP_FILE, "rb");
469 FSEEK(temp, 0x10, SEEK_SET);
470 copy_buffer = calloc(1, ostat.st_size);
471 fread(copy_buffer, ostat.st_size, 1, temp);
472 fclose(temp);
473 fwrite(copy_buffer, ostat.st_size, 1, in);
475 fclose(in);