1 #include "funcprotos.h"
19 #define FSEEK fseeko64
26 #define FRAMERATE (double)30000/1001
27 //#define FRAMERATE (double)30
28 #define STARTING_OFFSET 0x28
31 #define SAMPLERATE 48000
32 #define AUDIO_CHUNK 2048
34 #define TEMP_FILE "/tmp/temp.mov"
37 #define AUDIO_FILE "/tmp/audio.pcm"
38 #define VIDEO_FILE "/tmp/video.mov"
39 //#define VCODEC QUICKTIME_MJPA
40 //#define VCODEC QUICKTIME_JPEG
43 // Only 1 variation of this, recorded by 1 camcorder
44 #define VCODEC QUICKTIME_H264
45 // H264 rendered by Cinelerra
48 #define ACODEC QUICKTIME_MP4A
53 #define SEARCH_FRAGMENT (int64_t)0x100000
54 //#define SEARCH_PAD 8
64 #define GOT_IMAGE_START 6
65 #define GOT_IMAGE_END 7
70 unsigned char h264_desc[] =
73 0x01, 0x4d, 0x40, 0x1f, 0xff, 0xe1, 0x00, 0x16, 0x67, 0x4d, 0x40, 0x33, 0x9a,
74 0x74, 0x07, 0x80, 0x8b, 0xf7, 0x08, 0x00, 0x00, 0x1f, 0x48, 0x00, 0x07, 0x53, 0x04,
75 0x78, 0xc1, 0x95, 0x01, 0x00, 0x04, 0x68, 0xee, 0x1f, 0x20
81 // H264 description for cheap camcorder format
82 unsigned char h264_desc[] =
84 0x01, 0x4d, 0x00, 0x28, 0xff, 0xe1, 0x00, 0x30, 0x27, 0x4d, 0x00, 0x28,
85 0x9a, 0x62, 0x80, 0xa0, 0x0b, 0x76, 0x02, 0x20, 0x00, 0x00, 0x7d, 0x20,
86 0x00, 0x1d, 0x4c, 0x1d, 0x0c, 0x00, 0x26, 0x26, 0x00, 0x02, 0xae, 0xa9,
87 0x77, 0x97, 0x1a, 0x18, 0x00, 0x4c, 0x4c, 0x00, 0x05, 0x5d, 0x52, 0xef,
88 0x2e, 0x1f, 0x08, 0x84, 0x51, 0xe0, 0x00, 0x00, 0x01, 0x00, 0x04, 0x28,
95 #define NEW_TABLE(ptr, size, allocation) \
102 #define APPEND_TABLE(ptr, size, allocation, value) \
104 if((allocation) <= (size)) \
107 (allocation) = 1024; \
110 int64_t *new_table = calloc(1, sizeof(int64_t) * (allocation)); \
111 memcpy(new_table, (ptr), sizeof(int64_t) * (size)); \
115 (ptr)[(size)] = (value); \
119 int get_h264_size(unsigned char *frame_buffer, int frame_size)
121 int result = frame_size;
125 while(offset < frame_size)
127 int nal_size = ((frame_buffer[offset + 0] << 24) |
128 (frame_buffer[offset + 1] << 16) |
129 (frame_buffer[offset + 2] << 8) |
130 (frame_buffer[offset + 3])) + 4;
131 //printf("get_h264_size %d %d %d\n", __LINE__, offset, nal_size);
132 if(nal_size <= 0 || nal_size + offset >= frame_size)
144 int main(int argc, char *argv[])
150 quicktime_t *video_out;
151 int64_t current_byte, ftell_byte;
153 int64_t audio_start = 0, audio_end = 0;
154 unsigned char *search_buffer = calloc(1, SEARCH_FRAGMENT);
155 unsigned char *frame_buffer = calloc(1, SEARCH_FRAGMENT);
156 unsigned char *copy_buffer = 0;
160 unsigned char data[8];
165 int next_is_keyframe = 0;
166 time_t current_time = time(0);
167 time_t prev_time = 0;
168 int jpeg_header_offset;
169 int64_t field1_offset = 0;
170 int64_t field2_offset = 0;
171 int64_t image_start = STARTING_OFFSET;
172 int64_t image_end = STARTING_OFFSET;
174 int state = GOT_NOTHING;
180 // Value taken from Cinelerra preferences
181 int audio_chunk = AUDIO_CHUNK;
184 int64_t *start_table;
186 int start_allocation;
190 int64_t *field_table;
192 int64_t field_allocation;
194 // Dump codec settings
195 printf("Codec settings:\n"
196 " WIDTH=%d HEIGHT=%d\n"
214 printf(" READ ONLY\n");
219 printf("Recover JPEG and PCM audio in a corrupted movie.\n"
220 "Usage: recover [options] <input>\n"
222 " -b samples number of samples in an audio chunk (%d)\n"
228 for(i = 1; i < argc; i++)
230 if(!strcmp(argv[i], "-b"))
234 audio_chunk = atol(argv[i + 1]);
238 printf("Sample count for -b is out of range.\n");
244 printf("-b needs a sample count.\n");
255 // Get the field count
256 if(!memcmp(VCODEC, QUICKTIME_MJPA, 4))
265 if(!memcmp(VCODEC, QUICKTIME_H264, 4))
276 in = fopen(in_path, "rb+");
279 perror("open input");
283 fseek(in, STARTING_OFFSET, SEEK_SET);
288 // out = quicktime_open(TEMP_FILE, 0, 1);
291 // perror("open temp");
295 // quicktime_set_audio(out,
300 // quicktime_set_video(out,
307 audio_out = fopen(AUDIO_FILE, "w");
310 perror("open audio output");
314 video_out = quicktime_open(VIDEO_FILE, 0, 1);
318 perror("open video out");
322 quicktime_set_video(video_out,
328 quicktime_set_audio(video_out,
337 quicktime_video_map_t *vtrack = &(video_out->vtracks[0]);
338 quicktime_trak_t *trak = vtrack->track;
339 quicktime_avcc_t *avcc = &trak->mdia.minf.stbl.stsd.table[0].avcc;
340 quicktime_set_avcc_header(avcc,
347 audio_start = (int64_t)0x10;
348 ftell_byte = STARTING_OFFSET;
350 if(fstat(fileno(in), &status))
351 perror("get_file_length fstat:");
352 file_size = status.st_size;
355 NEW_TABLE(start_table, start_size, start_allocation)
356 NEW_TABLE(end_table, end_size, end_allocation)
357 NEW_TABLE(field_table, field_size, field_allocation)
361 audio_frame = BITS * CHANNELS / 8;
363 // Tabulate the start and end of all the JPEG images.
364 // This search is intended to be as simple as possible, reserving more
365 // complicated operations for a table pass.
366 //printf("Pass 1 video only.\n");
367 while(ftell_byte < file_size)
369 current_byte = ftell_byte;
370 fread(search_buffer, SEARCH_FRAGMENT, 1, in);
371 ftell_byte = current_byte + SEARCH_FRAGMENT - SEARCH_PAD;
372 FSEEK(in, ftell_byte, SEEK_SET);
374 for(i = 0; i < SEARCH_FRAGMENT - SEARCH_PAD; i++)
376 // Search for image start
377 if(state == GOT_NOTHING)
382 if(search_buffer[i] == 0x00 &&
383 search_buffer[i + 1] == 0x00 &&
384 search_buffer[i + 4] == 0x06 &&
385 search_buffer[i + 5] == 0x05)
387 //printf("main %d\n", __LINE__);
388 state = GOT_IMAGE_START;
389 image_end = current_byte + i;
390 is_keyframe = next_is_keyframe;
391 next_is_keyframe = 1;
394 if(search_buffer[i] == 0x00 &&
395 search_buffer[i + 1] == 0x00 &&
396 search_buffer[i + 4] == 0x41 &&
397 search_buffer[i + 5] == 0x9a)
399 //printf("main %d\n", __LINE__);
400 state = GOT_IMAGE_START;
401 image_end = current_byte + i;
402 is_keyframe = next_is_keyframe;
403 next_is_keyframe = 0;
406 if(state == GOT_IMAGE_START)
408 // end of previous frame
411 APPEND_TABLE(end_table, end_size, end_allocation, image_end)
413 int frame_size = image_end - image_start;
414 FSEEK(in, image_start, SEEK_SET);
415 fread(frame_buffer, frame_size, 1, in);
416 FSEEK(in, ftell_byte, SEEK_SET);
417 quicktime_write_frame(video_out,
423 quicktime_video_map_t *vtrack = &(video_out->vtracks[0]);
424 quicktime_insert_keyframe(video_out,
425 vtrack->current_position - 1,
428 image_start = image_end;
430 // start of next frame
431 APPEND_TABLE(start_table, start_size, start_allocation, image_start)
438 if(search_buffer[i] == 0x00 &&
439 search_buffer[i + 1] == 0x00 &&
440 search_buffer[i + 2] == 0x00 &&
441 search_buffer[i + 3] == 0x02 &&
442 search_buffer[i + 4] == 0x09)
444 state = GOT_IMAGE_START;
445 image_start = current_byte + i;
446 if(search_buffer[i + 5] == 0x10)
454 if(search_buffer[i] == 0xff &&
455 search_buffer[i + 1] == 0xd8 &&
456 search_buffer[i + 2] == 0xff &&
457 search_buffer[i + 3] == 0xe1 &&
458 search_buffer[i + 10] == 'm' &&
459 search_buffer[i + 11] == 'j' &&
460 search_buffer[i + 12] == 'p' &&
461 search_buffer[i + 13] == 'g')
463 state = GOT_IMAGE_START;
464 image_start = current_byte + i;
466 // Determine the field
469 // Next field offset is nonzero in first field
470 if(search_buffer[i + 22] != 0 ||
471 search_buffer[i + 23] != 0 ||
472 search_buffer[i + 24] != 0 ||
473 search_buffer[i + 25] != 0)
481 APPEND_TABLE(field_table, field_size, field_allocation, field)
485 if(search_buffer[i] == 0xff &&
486 search_buffer[i + 1] == 0xd8 &&
487 search_buffer[i + 2] == 0xff &&
488 search_buffer[i + 3] == 0xe0 &&
489 search_buffer[i + 6] == 'J' &&
490 search_buffer[i + 7] == 'F' &&
491 search_buffer[i + 8] == 'I' &&
492 search_buffer[i + 9] == 'F')
494 state = GOT_IMAGE_START;
495 image_start = current_byte + i;
499 // Search for image end
500 if(state == GOT_IMAGE_START)
504 // got next frame & end of previous frame or previous audio
505 // search 1 byte ahead so the loop doesn't skip the next frame
506 if(search_buffer[i + 1] == 0x00 &&
507 search_buffer[i + 2] == 0x00 &&
508 search_buffer[i + 3] == 0x00 &&
509 search_buffer[i + 4] == 0x02 &&
510 search_buffer[i + 5] == 0x09)
513 image_end = current_byte + i + 1;
515 // Read entire frame & get length from NAL codes
516 if(image_end - image_start <= SEARCH_FRAGMENT)
518 int frame_size = image_end - image_start;
519 FSEEK(in, image_start, SEEK_SET);
520 fread(frame_buffer, frame_size, 1, in);
521 FSEEK(in, ftell_byte, SEEK_SET);
523 int new_frame_size = get_h264_size(frame_buffer, frame_size);
525 * printf("%d: image_start=%lx image_end=%lx new_frame_size=%x\n",
532 image_end = image_start + new_frame_size;
534 //printf("%d: image_start=0x%lx image_size=0x%x\n", __LINE__, image_start, new_frame_size);
538 printf("%d: Possibly lost image between %llx and %llx\n",
545 APPEND_TABLE(start_table, start_size, start_allocation, image_start)
546 APPEND_TABLE(end_table, end_size, end_allocation, image_end)
550 quicktime_write_frame(video_out,
552 image_end - image_start,
556 quicktime_video_map_t *vtrack = &(video_out->vtracks[0]);
557 quicktime_insert_keyframe(video_out,
558 vtrack->current_position - 1,
565 int64_t next_frame_start = start_table[start_size - 1];
566 int64_t prev_frame_end = end_table[start_size - 2];
567 int audio_size = next_frame_start - prev_frame_end;
568 if(audio_size > SEARCH_FRAGMENT)
569 audio_size = SEARCH_FRAGMENT;
570 FSEEK(in, prev_frame_end, SEEK_SET);
571 fread(frame_buffer, audio_size, 1, in);
572 FSEEK(in, ftell_byte, SEEK_SET);
573 // fwrite(frame_buffer, audio_size, 1, audio_out);
575 quicktime_write_vbr_frame(video_out,
585 if(search_buffer[i] == 0xff &&
586 search_buffer[i + 1] == 0xd9)
588 // ffd9 sometimes occurs inside the mjpg tag
589 if(current_byte + i - image_start > 0x2a)
592 // Put it in the table
593 image_end = current_byte + i + 2;
595 // An image may have been lost due to encoding errors but we can't do anything
596 // because the audio may by misaligned. Use the extract utility to get the audio.
597 if(image_end - image_start > audio_chunk * audio_frame)
599 printf("%d: Possibly lost image between %llx and %llx\n",
605 * APPEND_TABLE(start_table, start_size, start_allocation, image_start)
606 * APPEND_TABLE(end_table, end_size, end_allocation, image_start + 1024)
607 * APPEND_TABLE(start_table, start_size, start_allocation, image_end - 1024)
608 * APPEND_TABLE(end_table, end_size, end_allocation, image_end)
612 APPEND_TABLE(start_table, start_size, start_allocation, image_start)
613 APPEND_TABLE(end_table, end_size, end_allocation, image_end)
615 int frame_size = image_end - image_start;
616 FSEEK(in, image_start, SEEK_SET);
617 fread(frame_buffer, frame_size, 1, in);
618 FSEEK(in, ftell_byte, SEEK_SET);
619 quicktime_write_frame(video_out,
621 image_end - image_start,
625 //printf("%d %llx - %llx\n", start_size, image_start, image_end - image_start);
627 if(!(start_size % 100))
629 printf("Got %d frames. %d%%\r",
631 current_byte * (int64_t)100 / file_size);
643 // With the image table complete,
644 // write chunk table from the gaps in the image table
645 // printf("Pass 2 audio table.\n");
646 // total_samples = 0;
647 // for(i = 1; i < start_size; i++)
649 // int64_t next_image_start = start_table[i];
650 // int64_t prev_image_end = end_table[i - 1];
653 // if(next_image_start - prev_image_end >= audio_chunk * audio_frame)
655 // long samples = (next_image_start - prev_image_end) / audio_frame;
656 // quicktime_atom_t chunk_atom;
658 // quicktime_set_position(out, prev_image_end);
659 // quicktime_write_chunk_header(out,
660 // out->atracks[0].track,
662 // quicktime_set_position(out, next_image_start);
663 // quicktime_write_chunk_footer(out,
664 // out->atracks[0].track,
665 // out->atracks[0].current_chunk,
668 // out->atracks[0].current_position += samples;
669 // out->atracks[0].current_chunk++;
670 // total_samples += samples;
678 // // Put image table in movie
679 // printf("Got %d frames %d samples total.\n", start_size, total_samples);
680 // for(i = 0; i < start_size - fields; i += fields)
682 // // Got a field out of order. Skip just 1 image instead of 2.
683 // if(fields == 2 && field_table[i] != 0)
685 // printf("Got field out of order at 0x%llx\n", start_table[i]);
690 // quicktime_atom_t chunk_atom;
691 // quicktime_set_position(out, start_table[i]);
692 // quicktime_write_chunk_header(out,
693 // out->vtracks[0].track,
695 // quicktime_set_position(out, end_table[i + fields - 1]);
696 // quicktime_write_chunk_footer(out,
697 // out->vtracks[0].track,
698 // out->vtracks[0].current_chunk,
701 // out->vtracks[0].current_position++;
702 // out->vtracks[0].current_chunk++;
712 // // Force header out at beginning of temp file
713 // quicktime_set_position(out, 0x10);
714 // quicktime_close(out);
716 // // Transfer header
717 // FSEEK(in, 0x8, SEEK_SET);
719 // data[0] = (ftell_byte & 0xff00000000000000LL) >> 56;
720 // data[1] = (ftell_byte & 0xff000000000000LL) >> 48;
721 // data[2] = (ftell_byte & 0xff0000000000LL) >> 40;
722 // data[3] = (ftell_byte & 0xff00000000LL) >> 32;
723 // data[4] = (ftell_byte & 0xff000000LL) >> 24;
724 // data[5] = (ftell_byte & 0xff0000LL) >> 16;
725 // data[6] = (ftell_byte & 0xff00LL) >> 8;
726 // data[7] = ftell_byte & 0xff;
727 // fwrite(data, 8, 1, in);
729 // FSEEK(in, ftell_byte, SEEK_SET);
730 // stat(TEMP_FILE, &ostat);
732 // temp = fopen(TEMP_FILE, "rb");
733 // FSEEK(temp, 0x10, SEEK_SET);
735 // copy_buffer = calloc(1, ostat.st_size);
736 // fread(copy_buffer, ostat.st_size, 1, temp);
739 // // Enable to alter the original file
740 // printf("%d: writing header to file\n", __LINE__);
741 // fwrite(copy_buffer, ostat.st_size, 1, in);
747 quicktime_close(video_out);