1 #include "funcprotos.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
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
45 #define FRAMERATE (double)30000/1001
47 #define SAMPLERATE 48000
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
68 #define GOT_IMAGE_START 6
69 #define GOT_IMAGE_END 7
72 #define NEW_TABLE(ptr, size, allocation) \
79 #define APPEND_TABLE(ptr, size, allocation, value) \
81 if((allocation) <= (size)) \
84 (allocation) = 1024; \
87 int64_t *new_table = calloc(1, sizeof(int64_t) * (allocation)); \
88 memcpy(new_table, (ptr), sizeof(int64_t) * (size)); \
92 (ptr)[(size)] = (value); \
98 int main(int argc
, char *argv
[])
103 int64_t current_byte
, ftell_byte
;
105 int64_t audio_start
= 0, audio_end
= 0;
106 unsigned char *search_buffer
= calloc(1, SEARCH_FRAGMENT
);
107 unsigned char *copy_buffer
= 0;
111 unsigned char data
[8];
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;
122 int state
= GOT_NOTHING
;
128 // Value taken from Cinelerra preferences
129 int audio_chunk
= 131072;
132 int64_t *start_table
;
134 int start_allocation
;
138 int64_t *field_table
;
140 int64_t field_allocation
;
142 // Dump codec settings
143 printf("Codec settings:\n"
144 " WIDTH=%d HEIGHT=%d\n"
162 printf("Recover JPEG and PCM audio in a corrupted movie.\n"
163 "Usage: recover [options] <input>\n"
165 " -b samples number of samples in an audio chunk (%d)\n"
171 for(i
= 1; i
< argc
; i
++)
173 if(!strcmp(argv
[i
], "-b"))
177 audio_chunk
= atol(argv
[i
+ 1]);
181 printf("Sample count for -b is out of range.\n");
187 printf("-b needs a sample count.\n");
200 in
= fopen(in_path
, "rb+");
201 out
= quicktime_open(TEMP_FILE
, 0, 1);
205 perror("open input");
214 quicktime_set_audio(out
,
219 quicktime_set_video(out
,
225 audio_start
= (int64_t)0x10;
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))
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
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)
293 APPEND_TABLE(field_table
, field_size
, field_allocation
, field
)
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
;
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)
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",
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",
349 current_byte
* (int64_t)100 / file_size
);
361 // With the image table complete,
362 // write chunk table from the gaps in the image table
363 printf("Pass 2 audio table.\n");
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];
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
,
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
,
387 * quicktime_update_tables(out,
388 * out->atracks[0].track,
390 * out->atracks[0].current_chunk,
391 * out->atracks[0].current_position,
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
]);
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
,
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
,
429 * quicktime_update_tables(out,
430 * out->vtracks[0].track,
432 * out->vtracks[0].current_chunk,
433 * out->vtracks[0].current_position,
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
);
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
);
473 fwrite(copy_buffer
, ostat
.st_size
, 1, in
);