/* * avisync.c * * Copyright (C) Thomas Oestreich - June 2001 * * This file is part of transcode, a video stream processing tool * * transcode is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * transcode is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "transcode.h" #include #include #include #include #include "buffer.h" #include "avilib/avilib.h" #include "aud_scan.h" #include "aud_scan_avi.h" #define EXE "avisync" /* AVI_info is no longer in avilib */ void AVI_info(avi_t *avifile); void version(void) { printf("%s (%s v%s) (C) 2001-2003 Thomas Oestreich," " 2003-2010 Transcode Team\n", EXE, PACKAGE, VERSION); } static void usage(int status) { version(); printf("\nUsage: %s [options]\n", EXE); printf(" -o file output file\n"); printf(" -i file input file\n"); printf(" -q be quiet\n"); printf(" -a num audio track number [0]\n"); printf(" -b n handle vbr audio [1]\n"); printf(" -f FILE read AVI comments from FILE [off]\n"); //printf(" -N enocde a real silent frame [off]\n"); printf(" -n count shift audio by count frames [0]\n"); printf(" count>0: audio starts with frame 'count'\n"); printf(" count<0: prepend 'count' padding audio frames\n"); exit(status); } // buffer static char data[SIZE_RGB_FRAME]; static char ptrdata[SIZE_RGB_FRAME]; static int ptrlen=0; static char *comfile = NULL; int is_vbr = 1; int main(int argc, char *argv[]) { avi_t *avifile1=NULL; avi_t *avifile2=NULL; avi_t *avifile3=NULL; char *in_file=NULL, *out_file=NULL; long frames, bytes; double fps; char *codec; int track_num=0, aud_tracks; int encode_null=0; int i, j, n, key, shift=0; int ch, preload=0; long rate, mp3rate; int width, height, format, chan, bits; int be_quiet = 0; FILE *status_fd = stderr; /* for null frame encoding */ char nulls[32000]; long nullbytes=0; char tmp0[] = "/tmp/nullfile.00.avi"; /* XXX: use mktemp*() */ buffer_list_t *ptr; double vid_ms = 0.0, shift_ms = 0.0, one_vid_ms = 0.0; double aud_ms [ AVI_MAX_TRACKS ]; int aud_bitrate = 0; int aud_chunks = 0; ac_init(AC_ALL); if(argc==1) usage(EXIT_FAILURE); while ((ch = getopt(argc, argv, "a:b:vi:o:n:Nq?h")) != -1) { switch (ch) { case 'i': if(optarg[0]=='-') usage(EXIT_FAILURE); in_file=optarg; break; case 'a': if(optarg[0]=='-') usage(EXIT_FAILURE); track_num = atoi(optarg); if(track_num<0) usage(EXIT_FAILURE); break; case 'b': if(optarg[0]=='-') usage(EXIT_FAILURE); is_vbr = atoi(optarg); if(is_vbr<0) usage(EXIT_FAILURE); break; case 'o': if(optarg[0]=='-') usage(EXIT_FAILURE); out_file=optarg; break; case 'f': if(optarg[0]=='-') usage(EXIT_FAILURE); comfile = optarg; break; case 'n': if(sscanf(optarg,"%d", &shift)!=1) { fprintf(stderr, "invalid parameter for option -n\n"); usage(EXIT_FAILURE); } break; case 'N': encode_null=1; break; case 'q': be_quiet = 1; break; case 'v': version(); exit(0); break; case 'h': usage(EXIT_SUCCESS); default: usage(EXIT_FAILURE); } } // check if(in_file==NULL || out_file == NULL) usage(EXIT_FAILURE); if(shift == 0) fprintf(stderr, "no sync requested - exit"); memset (nulls, 0, sizeof(nulls)); // open file if(NULL == (avifile1 = AVI_open_input_file(in_file,1))) { AVI_print_error("AVI open"); exit(1); } if(strcmp(in_file, out_file)==0) { printf("error: output filename conflicts with input filename\n"); exit(1); } if(NULL == (avifile2 = AVI_open_output_file(out_file))) { AVI_print_error("AVI open"); exit(1); } if (be_quiet) { if (!(status_fd = fopen("/dev/null", "w"))) { fprintf(stderr, "Can't open /dev/null\n"); exit(1); } } // read video info; AVI_info(avifile1); // read video info; frames = AVI_video_frames(avifile1); width = AVI_video_width(avifile1); height = AVI_video_height(avifile1); fps = AVI_frame_rate(avifile1); codec = AVI_video_compressor(avifile1); //set video in outputfile AVI_set_video(avifile2, width, height, fps, codec); if (comfile!=NULL) AVI_set_comment_fd(avifile2, open(comfile, O_RDONLY)); aud_tracks = AVI_audio_tracks(avifile1); for(j=0; j0) { // for n < shift, shift audio frames are discarded if(!preload) { if (tc_format_ms_supported(format)) { for(i=0;i=frames-2*shift) { // save audio frame for later ptr = buffer_register(n); if(ptr==NULL) { fprintf(stderr,"buffer allocation failed\n"); break; } ac_memcpy(ptr->data, data, bytes); ptr->size = bytes; ptr->status = BUFFER_READY; } if ( !aud_bitrate && tc_get_audio_header(data, bytes, format, NULL, NULL, &aud_bitrate)<0) { if (n == frames-1) continue; aud_ms[track_num] = vid_ms + shift_ms; } else aud_ms[track_num] += (bytes*8.0)/(format==0x1?((double)(rate*chan*bits)/1000.0): (format==0x2000?(double)(mp3rate):aud_bitrate)); } } else { // fallback bytes = AVI_audio_size(avifile1, n+shift-1); do { if( (bytes = AVI_read_audio_chunk(avifile1, data)) < 0) { AVI_print_error("AVI audio read frame"); return(-1); } if(AVI_write_audio(avifile2, data, bytes) < 0) { AVI_print_error("AVI write audio frame"); return(-1); } fprintf(status_fd, "V [%05d] | A [%05d] [%05ld]\r", n, n+shift, bytes); if(n>=frames-2*shift) { // save audio frame for later ptr = buffer_register(n); if(ptr==NULL) { fprintf(stderr,"buffer allocation failed\n"); break; } ac_memcpy(ptr->data, data, bytes); ptr->size = bytes; ptr->status = BUFFER_READY; } } while (AVI_can_read_audio(avifile1)); } } // padding at the end if(n>=frames-shift) { if (!ptrlen) { ptr = buffer_retrieve(); ac_memcpy (ptrdata, ptr->data, ptr->size); ptrlen = ptr->size; } if (tc_format_ms_supported(format)) { while (aud_ms[track_num] < vid_ms + shift_ms) { aud_bitrate = (format==0x1||format==0x2000)?1:0; // mute this -- check if can mute (valid A header)! if (tc_probe_audio_header(ptrdata, ptrlen) > 0) tc_format_mute(ptrdata, ptrlen, format); if(AVI_write_audio(avifile2, ptrdata, ptrlen) < 0) { AVI_print_error("AVI write audio frame"); return(-1); } fprintf(status_fd, " V [%05d][%08.2f] | A [%05d][%08.2f] [%05ld]\r", n, vid_ms, n+shift, aud_ms[track_num], bytes); if ( !aud_bitrate && tc_get_audio_header(ptrdata, ptrlen, format, NULL, NULL, &aud_bitrate)<0) { //if (n == frames-1) continue; aud_ms[track_num] = vid_ms + shift_ms; } else aud_ms[track_num] += (ptrlen*8.0)/(format==0x1?((double)(rate*chan*bits)/1000.0): (format==0x2000?(double)(mp3rate):aud_bitrate)); } } else { // fallback // get next audio frame ptr = buffer_retrieve(); while (1) { printf("ptr->id (%d) ptr->size (%d)\n", ptr->id, ptr->size); if(ptr==NULL) { fprintf(stderr,"no buffer found\n"); break; } if (encode_null) { if(AVI_write_audio(avifile2, nulls, nullbytes)<0) { AVI_print_error("AVI write audio frame"); return(-1); } } else { // simple keep old frames to force exact time delay if(AVI_write_audio(avifile2, ptr->data, ptr->size)<0) { AVI_print_error("AVI write audio frame"); return(-1); } } fprintf(status_fd, "V [%05d] | padding\r", n); if (ptr->next && ptr->next->id == ptr->id) { buffer_remove(ptr); ptr = buffer_retrieve(); continue; } buffer_remove(ptr); break; } // 1 } } // ************************************* // negative shift (pad audio at start) // ************************************* } else { if (tc_format_ms_supported(format)) { /* fprintf(status_fd, "n(%d) -shift(%d) shift_ms (%.2lf) vid_ms(%.2lf) aud_ms[%d](%.2lf) v-s(%.2lf)\n", n, -shift, shift_ms, vid_ms, track_num, aud_ms[track_num], vid_ms + shift_ms); */ // shift<0 -> shift_ms<0 ! while (aud_ms[track_num] < vid_ms) { /* fprintf(stderr, " 1 (%02d) %s frame_read len=%4ld (A/V) (%8.2f/%8.2f)\n", n, format==0x55?"MP3":"AC3", bytes, aud_ms[track_num], vid_ms); */ aud_bitrate = (format==0x1||format==0x2000)?1:0; if( (bytes = AVI_read_audio_chunk(avifile1, data)) < 0) { AVI_print_error("AVI 2 audio read frame"); aud_ms[track_num] = vid_ms; break; //return(-1); } // save audio frame for later ptr = buffer_register(n); if(ptr==NULL) { fprintf(stderr,"buffer allocation failed\n"); break; } ac_memcpy(ptr->data, data, bytes); ptr->size = bytes; ptr->status = BUFFER_READY; if(n<-shift) { // mute this -- check if can mute! if (tc_probe_audio_header(data, bytes) > 0) tc_format_mute(data, bytes, format); // simple keep old frames to force exact time delay if(AVI_write_audio(avifile2, data, bytes)<0) { AVI_print_error("AVI write audio frame"); return(-1); } fprintf(status_fd, "V [%05d] | padding\r", n); } else { if (n==-shift) fprintf(status_fd, "\n"); // get next audio frame ptr = buffer_retrieve(); if(ptr==NULL) { fprintf(stderr,"no buffer found\n"); break; } if(AVI_write_audio(avifile2, ptr->data, ptr->size)<0) { AVI_print_error("AVI write audio frame"); return(-1); } bytes = ptr->size; ac_memcpy (data, ptr->data, bytes); fprintf(status_fd, "V [%05d] | A [%05d]\r", n, ptr->id); buffer_remove(ptr); } if ( !aud_bitrate && tc_get_audio_header(data, bytes, format, NULL, NULL, &aud_bitrate)<0) { if (n == frames-1) continue; aud_ms[track_num] = vid_ms; } else aud_ms[track_num] += (bytes*8.0)/(format==0x1?((double)(rate*chan*bits)/1000.0): (format==0x2000?(double)(mp3rate):aud_bitrate)); /* fprintf(stderr, " 1 (%02d) %s frame_read len=%4ld (A/V) (%8.2f/%8.2f)\n", n, format==0x55?"MP3":"AC3", bytes, aud_ms[track_num], vid_ms); */ } } else { // no supported format bytes = AVI_audio_size(avifile1, n); if(bytes > SIZE_RGB_FRAME) { fprintf(stderr, "invalid frame size\n"); return(-1); } if(AVI_read_audio(avifile1, data, bytes) < 0) { AVI_print_error("AVI audio read frame"); return(-1); } // save audio frame for later ptr = buffer_register(n); if(ptr==NULL) { fprintf(stderr,"buffer allocation failed\n"); break; } ac_memcpy(ptr->data, data, bytes); ptr->size = bytes; ptr->status = BUFFER_READY; if(n<-shift) { if (encode_null) { if(AVI_write_audio(avifile2, nulls, nullbytes)<0) { AVI_print_error("AVI write audio frame"); return(-1); } } else { // simple keep old frames to force exact time delay if(AVI_write_audio(avifile2, data, bytes)<0) { AVI_print_error("AVI write audio frame"); return(-1); } } fprintf(status_fd, "V [%05d] | padding\r", n); } else { // get next audio frame ptr = buffer_retrieve(); if(ptr==NULL) { fprintf(stderr,"no buffer found\n"); break; } if(AVI_write_audio(avifile2, ptr->data, ptr->size)<0) { AVI_print_error("AVI write audio frame"); return(-1); } fprintf(status_fd, "V [%05d] | A [%05d]\r", n, ptr->id); buffer_remove(ptr); } } } } fprintf(status_fd, "\n"); if (be_quiet) { fclose(status_fd); } AVI_close(avifile1); AVI_close(avifile2); if (avifile3) { memset(nulls, 0, sizeof(nulls)); tc_snprintf(nulls, sizeof(nulls), "rm -f %s", tmp0); system(nulls); AVI_close(avifile3); } return(0); } /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */