00001 #include "funcprotos.h"
00002 #include "quicktime.h"
00003
00004 #include <stdio.h>
00005 #include <stdlib.h>
00006 #include <string.h>
00007 #include <sys/stat.h>
00008 #include <sys/time.h>
00009 #include <stdint.h>
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #define FSEEK fseeko64
00041
00042
00043 #define WIDTH 720
00044 #define HEIGHT 480
00045 #define FRAMERATE (double)30000/1001
00046 #define CHANNELS 2
00047 #define SAMPLERATE 48000
00048 #define BITS 24
00049 #define TEMP_FILE "/tmp/temp.mov"
00050 #define VCODEC QUICKTIME_MJPA
00051
00052
00053
00054
00055
00056
00057 #define SEARCH_FRAGMENT (int64_t)0x100000
00058
00059 #define SEARCH_PAD 16
00060
00061
00062 #define GOT_NOTHING 0
00063 #define IN_FIELD1 1
00064 #define GOT_FIELD1 2
00065 #define IN_FIELD2 3
00066 #define GOT_FIELD2 4
00067 #define GOT_AUDIO 5
00068 #define GOT_IMAGE_START 6
00069 #define GOT_IMAGE_END 7
00070
00071
00072 #define NEW_TABLE(ptr, size, allocation) \
00073 { \
00074 (ptr) = 0; \
00075 (size) = 0; \
00076 (allocation) = 0; \
00077 }
00078
00079 #define APPEND_TABLE(ptr, size, allocation, value) \
00080 { \
00081 if((allocation) <= (size)) \
00082 { \
00083 if(!(allocation)) \
00084 (allocation) = 1024; \
00085 else \
00086 (allocation) *= 2; \
00087 int64_t *new_table = calloc(1, sizeof(int64_t) * (allocation)); \
00088 memcpy(new_table, (ptr), sizeof(int64_t) * (size)); \
00089 free((ptr)); \
00090 (ptr) = new_table; \
00091 } \
00092 (ptr)[(size)] = (value); \
00093 (size)++; \
00094 }
00095
00096
00097
00098 int main(int argc, char *argv[])
00099 {
00100 FILE *in;
00101 FILE *temp;
00102 quicktime_t *out;
00103 int64_t current_byte, ftell_byte;
00104 int64_t jpeg_end;
00105 int64_t audio_start = 0, audio_end = 0;
00106 unsigned char *search_buffer = calloc(1, SEARCH_FRAGMENT);
00107 unsigned char *copy_buffer = 0;
00108 int i;
00109 int64_t file_size;
00110 struct stat status;
00111 unsigned char data[8];
00112 struct stat ostat;
00113 int fields = 1;
00114 time_t current_time = time(0);
00115 time_t prev_time = 0;
00116 int jpeg_header_offset;
00117 int64_t field1_offset = 0;
00118 int64_t field2_offset = 0;
00119 int64_t image_start = 0;
00120 int64_t image_end = 0;
00121 int update_time = 0;
00122 int state = GOT_NOTHING;
00123 char *in_path;
00124 int audio_frame;
00125 int total_samples;
00126 int field;
00127
00128
00129 int audio_chunk = 131072;
00130
00131
00132 int64_t *start_table;
00133 int start_size;
00134 int start_allocation;
00135 int64_t *end_table;
00136 int end_size;
00137 int end_allocation;
00138 int64_t *field_table;
00139 int64_t field_size;
00140 int64_t field_allocation;
00141
00142
00143 printf("Codec settings:\n"
00144 " WIDTH=%d HEIGHT=%d\n"
00145 " FRAMERATE=%.2f\n"
00146 " CHANNELS=%d\n"
00147 " SAMPLERATE=%d\n"
00148 " BITS=%d\n"
00149 " audio chunk=%d\n"
00150 " VCODEC=\"%s\"\n",
00151 WIDTH,
00152 HEIGHT,
00153 FRAMERATE,
00154 CHANNELS,
00155 SAMPLERATE,
00156 BITS,
00157 audio_chunk,
00158 VCODEC);
00159
00160 if(argc < 2)
00161 {
00162 printf("Recover JPEG and PCM audio in a corrupted movie.\n"
00163 "Usage: recover [options] <input>\n"
00164 "Options:\n"
00165 " -b samples number of samples in an audio chunk (%d)\n"
00166 "\n",
00167 audio_chunk);
00168 exit(1);
00169 }
00170
00171 for(i = 1; i < argc; i++)
00172 {
00173 if(!strcmp(argv[i], "-b"))
00174 {
00175 if(i + 1 < argc)
00176 {
00177 audio_chunk = atol(argv[i + 1]);
00178 i++;
00179 if(audio_chunk <= 0)
00180 {
00181 printf("Sample count for -b is out of range.\n");
00182 exit(1);
00183 }
00184 }
00185 else
00186 {
00187 printf("-b needs a sample count.\n");
00188 exit(1);
00189 }
00190 }
00191 else
00192 {
00193 in_path = argv[i];
00194 }
00195 }
00196
00197
00198
00199
00200 in = fopen(in_path, "rb+");
00201 out = quicktime_open(TEMP_FILE, 0, 1);
00202
00203 if(!in)
00204 {
00205 perror("open input");
00206 exit(1);
00207 }
00208 if(!out)
00209 {
00210 perror("open temp");
00211 exit(1);
00212 }
00213
00214 quicktime_set_audio(out,
00215 CHANNELS,
00216 SAMPLERATE,
00217 BITS,
00218 QUICKTIME_TWOS);
00219 quicktime_set_video(out,
00220 1,
00221 WIDTH,
00222 HEIGHT,
00223 FRAMERATE,
00224 VCODEC);
00225 audio_start = (int64_t)0x10;
00226 ftell_byte = 0;
00227
00228 if(fstat(fileno(in), &status))
00229 perror("get_file_length fstat:");
00230 file_size = status.st_size;
00231
00232
00233 NEW_TABLE(start_table, start_size, start_allocation)
00234 NEW_TABLE(end_table, end_size, end_allocation)
00235 NEW_TABLE(field_table, field_size, field_allocation)
00236
00237
00238
00239 if(!memcmp(VCODEC, QUICKTIME_MJPA, 4))
00240 {
00241 fields = 2;
00242 }
00243 else
00244 {
00245 fields = 1;
00246 }
00247
00248 audio_frame = BITS * CHANNELS / 8;
00249
00250
00251
00252
00253 printf("Pass 1 video only.\n");
00254 while(ftell_byte < file_size)
00255 {
00256 current_byte = ftell_byte;
00257 fread(search_buffer, SEARCH_FRAGMENT, 1, in);
00258 ftell_byte = current_byte + SEARCH_FRAGMENT - SEARCH_PAD;
00259 FSEEK(in, ftell_byte, SEEK_SET);
00260
00261 for(i = 0; i < SEARCH_FRAGMENT - SEARCH_PAD; i++)
00262 {
00263
00264 if(state == GOT_NOTHING)
00265 {
00266 if(search_buffer[i] == 0xff &&
00267 search_buffer[i + 1] == 0xd8 &&
00268 search_buffer[i + 2] == 0xff &&
00269 search_buffer[i + 3] == 0xe1 &&
00270 search_buffer[i + 10] == 'm' &&
00271 search_buffer[i + 11] == 'j' &&
00272 search_buffer[i + 12] == 'p' &&
00273 search_buffer[i + 13] == 'g')
00274 {
00275 state = GOT_IMAGE_START;
00276 image_start = current_byte + i;
00277
00278
00279 if(fields == 2)
00280 {
00281
00282 if(search_buffer[i + 22] != 0 ||
00283 search_buffer[i + 23] != 0 ||
00284 search_buffer[i + 24] != 0 ||
00285 search_buffer[i + 25] != 0)
00286 {
00287 field = 0;
00288 }
00289 else
00290 {
00291 field = 1;
00292 }
00293 APPEND_TABLE(field_table, field_size, field_allocation, field)
00294 }
00295 }
00296 else
00297 if(search_buffer[i] == 0xff &&
00298 search_buffer[i + 1] == 0xd8 &&
00299 search_buffer[i + 2] == 0xff &&
00300 search_buffer[i + 3] == 0xe0 &&
00301 search_buffer[i + 6] == 'J' &&
00302 search_buffer[i + 7] == 'F' &&
00303 search_buffer[i + 8] == 'I' &&
00304 search_buffer[i + 9] == 'F')
00305 {
00306 state = GOT_IMAGE_START;
00307 image_start = current_byte + i;
00308 }
00309 }
00310 else
00311
00312 if(state == GOT_IMAGE_START)
00313 {
00314 if(search_buffer[i] == 0xff &&
00315 search_buffer[i + 1] == 0xd9)
00316 {
00317
00318 if(current_byte + i - image_start > 0x2a)
00319 {
00320 state = GOT_NOTHING;
00321
00322 image_end = current_byte + i + 2;
00323
00324
00325
00326 if(image_end - image_start > audio_chunk * audio_frame)
00327 {
00328 printf("Possibly lost image between %llx and %llx\n",
00329 image_start,
00330 image_end);
00331
00332
00333
00334
00335
00336
00337
00338 }
00339
00340 APPEND_TABLE(start_table, start_size, start_allocation, image_start)
00341 APPEND_TABLE(end_table, end_size, end_allocation, image_end)
00342
00343
00344
00345 if(!(start_size % 100))
00346 {
00347 printf("Got %d frames. %d%%\r",
00348 start_size,
00349 current_byte * (int64_t)100 / file_size);
00350 fflush(stdout);
00351 }
00352 }
00353 }
00354 }
00355 }
00356 }
00357
00358
00359
00360
00361
00362
00363 printf("Pass 2 audio table.\n");
00364 total_samples = 0;
00365 for(i = 1; i < start_size; i++)
00366 {
00367 int64_t next_image_start = start_table[i];
00368 int64_t prev_image_end = end_table[i - 1];
00369
00370
00371 if(next_image_start - prev_image_end >= audio_chunk * audio_frame)
00372 {
00373 long samples = (next_image_start - prev_image_end) / audio_frame;
00374 quicktime_atom_t chunk_atom;
00375
00376 quicktime_set_position(out, prev_image_end);
00377 quicktime_write_chunk_header(out,
00378 out->atracks[0].track,
00379 &chunk_atom);
00380 quicktime_set_position(out, next_image_start);
00381 quicktime_write_chunk_footer(out,
00382 out->atracks[0].track,
00383 out->atracks[0].current_chunk,
00384 &chunk_atom,
00385 samples);
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 out->atracks[0].current_position += samples;
00396 out->atracks[0].current_chunk++;
00397 total_samples += samples;
00398 }
00399 }
00400
00401
00402
00403
00404
00405
00406 printf("Got %d frames %d samples total.\n", start_size, total_samples);
00407 for(i = 0; i < start_size - fields; i += fields)
00408 {
00409
00410 if(fields == 2 && field_table[i] != 0)
00411 {
00412 printf("Got field out of order at 0x%llx\n", start_table[i]);
00413 i--;
00414 }
00415 else
00416 {
00417 quicktime_atom_t chunk_atom;
00418 quicktime_set_position(out, start_table[i]);
00419 quicktime_write_chunk_header(out,
00420 out->vtracks[0].track,
00421 &chunk_atom);
00422 quicktime_set_position(out, end_table[i + fields - 1]);
00423 quicktime_write_chunk_footer(out,
00424 out->vtracks[0].track,
00425 out->vtracks[0].current_chunk,
00426 &chunk_atom,
00427 1);
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437 out->vtracks[0].current_position++;
00438 out->vtracks[0].current_chunk++;
00439 }
00440 }
00441
00442
00443
00444
00445
00446
00447
00448
00449 quicktime_set_position(out, 0x10);
00450 quicktime_close(out);
00451
00452
00453 FSEEK(in, 0x8, SEEK_SET);
00454
00455 data[0] = (ftell_byte & 0xff00000000000000LL) >> 56;
00456 data[1] = (ftell_byte & 0xff000000000000LL) >> 48;
00457 data[2] = (ftell_byte & 0xff0000000000LL) >> 40;
00458 data[3] = (ftell_byte & 0xff00000000LL) >> 32;
00459 data[4] = (ftell_byte & 0xff000000LL) >> 24;
00460 data[5] = (ftell_byte & 0xff0000LL) >> 16;
00461 data[6] = (ftell_byte & 0xff00LL) >> 8;
00462 data[7] = ftell_byte & 0xff;
00463 fwrite(data, 8, 1, in);
00464
00465 FSEEK(in, ftell_byte, SEEK_SET);
00466 stat(TEMP_FILE, &ostat);
00467
00468 temp = fopen(TEMP_FILE, "rb");
00469 FSEEK(temp, 0x10, SEEK_SET);
00470 copy_buffer = calloc(1, ostat.st_size);
00471 fread(copy_buffer, ostat.st_size, 1, temp);
00472 fclose(temp);
00473 fwrite(copy_buffer, ostat.st_size, 1, in);
00474
00475 fclose(in);
00476 }
00477
00478
00479
00480