Sunday, May 10, 2009

Subtitle synchronizer

I have written a program to synchronize subtitle's file (srt format). But what i mean when i say synchronize? The reality is that the videos are recorded with differents fps (Frames per second). So if our film has been recorded in 24 fps, but the subtitle is created with a film with 25 fps, our subtitle we'll desynchronize each second. This happen because the film with 24 fps is reproducing faster (less frames in the same time). So to realize that our subtitles are desynchronized with the film, we'll see that the film is more desynchronized as long as time is passing.

The program needs to know when stars and ends the conversation in the video. So you have to look for in the video when is the first and last conversation. It would be more precise to see which are the first and last conversations in the subtitle file, and then you we'll find easier the star/end times in the video.

The way to use the program is the next:

./subsincro desynchronized_file.srt new_start_time new_end_time > synchronized_file.srt

For example:

Suppose that in my_serie.3x05.srt we have this:

1
00:00:18,268 --> 00:00:20,498
Let's keep those hammers working!

(...)

422
01:28:22,428 --> 01:28:26,057
How will you live, John?
- Day by day.

Then, we search this conversations in the video, and make a note of the real times in the video. Finally we execute the program with the new start/end time:

./subsincro my_serie.3x05.srt 00:00:05,520 01:24:31,120 > my_serie.3x05.syn.srt

Program code:
 /*
Copyright 2010 djtalo85@gmail.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/


/*****************************
SUBTITLES SYNCHRONIZER

usage: ./subsincro sub_file.str new_start_time new_end_time
example: ./subsincro Pron.3x01.srt 00:00:01,000 00:54:31,000

To create a new subtitle file, redirect the output.
example: ./subsincro Pron.3x01.srt 00:00:01,000 00:54:31,000 > Pron.3x01.new.srt

*****************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int new_start_time,new_end_time,old_start_time,old_end_time;
int aux_start_time,aux_end_time;
int gap=0, subIndex=1;
float time_desync;


char *timeToString(int time) {
char *outTime;
int hour,min,sec,ms;
div_t d;

outTime = (char *) malloc (13*sizeof(char));

d=div(time,1000*60*60);
hour = d.quot;
time= d.rem;

d=div(time,1000*60);
min= d.quot;
time = d.rem;

d=div(time,1000);
sec= d.quot;
ms = d.rem;

sprintf(outTime,"%02i:%02i:%02i,%03i",hour,min,sec,ms);

return outTime;


}
printTime(int time) {
printf("%s",timeToString(time));
}

int strTimeToInt(char str[13]) {
int hour,min,sec,ms,error;
int time;
char aux[4];

strncpy(aux,str,2);
aux[2]='\0';
hour = atoi(aux);

strncpy(aux,&str[3],2);
aux[2]='\0';
min = atoi(aux);

strncpy(aux,&str[6],2);
aux[2]='\0';
sec = atoi(aux);

strncpy(aux,&str[9],3);
aux[3]='\0';
ms = atoi(aux);

time= ms + 1000*sec + 1000*60*min + 1000*60*60*hour;

//printf("%i ",error);
//printf("( %02i:%02i:%02i,%03i )",hour,min,sec,ms);

return time;
}
getTime(char str[1024]) {
char str_time[13];
char end_time[13];

sscanf(str,"%s --> %s",str_time,end_time);
aux_start_time = strTimeToInt(str_time);
aux_end_time = strTimeToInt(end_time);

}
update_time(char str[1024]) {
getTime(str);

/* Apply sync. */
aux_start_time = abs((float)(aux_start_time * 100) / time_desync);
aux_end_time = abs((float)(aux_end_time * 100) / time_desync);

/* Apply pad */
aux_start_time = aux_start_time - gap;
aux_end_time = aux_end_time - gap;

printTime(aux_start_time);
printf(" --> ");
printTime(aux_end_time);
printf("%s",&str[29]);

}

isInteger(char str[1024]) {
int i=0;
while(i<(strlen(str)-2)) {
if(str[i]>47 && str[i]<58) {
i++;
} else {

return -1;
}
}
if(i==0) {
return -1;
} else {
return 0;
}
}

main(int argc, char *argv[]) {
char line[1024];
FILE *f;
int index,startIndex;
int sync_start_time,sync_end_time;

if (argc < 4) {

fprintf(stderr,"Subsincro 0.3 by Pron\n\n");

fprintf(stderr,"Usage: %s file.srt new_start_time new_end_time\n", argv[0]);

fprintf(stderr,"Time format: hh:mm:ss,mmm\n\n");

return 1;

}

if(strlen(argv[2])!=12 || strlen(argv[3])!=12) {
fprintf(stderr,"Time argument format have to be 00:00:00,000\n");

return 1;
}

new_start_time= strTimeToInt(argv[2]);
new_end_time=strTimeToInt(argv[3]);


if((f = fopen( argv[1], "r" ))==NULL) {
fprintf(stderr,"File '%s' not exist\n",argv[1]);
return 1;
}

while((fgets(line, 1024, f))!=NULL) {
if(isInteger(line)==0) {
index=atoi(line);
fgets(line, 1024, f);
getTime(line);
if(subIndex==1) {
startIndex=index;
old_start_time = aux_start_time;
}
subIndex++;
}
}
old_end_time = aux_start_time;

//time_desync = (old_end_time *100.0)/(old_end_time - ((old_end_time - old_start_time) - (new_end_time - new_start_time)));
time_desync = (((float)old_end_time - (float)old_start_time) * 100.0) / ((float)new_end_time - (float)new_start_time);


/* gap*/
sync_start_time = ((float)(old_start_time * 100) / time_desync);
sync_end_time = ((float)(old_end_time * 100) / time_desync);

gap = sync_start_time - new_start_time;

fprintf(stderr,"Desynchronization: %f\n",time_desync);


fprintf(stderr,"Sub. Index\tOld Time\tSub. Synchronized\n",timeToString(gap));
fprintf(stderr,"%i\t\t%s\t%s\n",startIndex,timeToString(old_start_time),timeToString(sync_start_time - gap));
fprintf(stderr,"[...]\n");
fprintf(stderr,"%i\t\t%s\t%s\n",index,timeToString(old_end_time),timeToString(sync_end_time - gap));
fseek(f,0,0);
subIndex=1;
while((fgets(line, 1024, f))!=NULL) {
if(isInteger(line)==0) {
printf("%s",line);
fgets(line, 1024, f);
update_time(line);
subIndex++;
} else {
printf("%s",line);
}
}
fclose(f);
return 0;
}
}

No comments: