Changeset 330
- Timestamp:
- 02/06/07 13:08:02 (2 years ago)
- Files:
-
- trunk/Perian.xcodeproj/project.pbxproj (modified) (4 diffs)
- trunk/SSADocument.h (modified) (3 diffs)
- trunk/SSADocument.m (modified) (6 diffs)
- trunk/SubImport.h (modified) (1 diff)
- trunk/SubImport.mm (moved) (moved from trunk/SubImport.c) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/Perian.xcodeproj/project.pbxproj
r324 r330 226 226 61CB120D0ACE0F8D007994BD /* MatroskaCodecIDs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 61D691500AC8E382000EFC7D /* MatroskaCodecIDs.cpp */; }; 227 227 61CB12260ACE1074007994BD /* MatroskaImport.r in Rez */ = {isa = PBXBuildFile; fileRef = 61D691590AC8E382000EFC7D /* MatroskaImport.r */; }; 228 61D514DE0ADF3DBA00A671E1 /* SubImport. c in Sources */ = {isa = PBXBuildFile; fileRef = 61D514DD0ADF3DBA00A671E1 /* SubImport.c*/; };228 61D514DE0ADF3DBA00A671E1 /* SubImport.mm in Sources */ = {isa = PBXBuildFile; fileRef = 61D514DD0ADF3DBA00A671E1 /* SubImport.mm */; }; 229 229 61D517730AE0402E00A671E1 /* CommonUtils.c in Sources */ = {isa = PBXBuildFile; fileRef = 61D517720AE0402E00A671E1 /* CommonUtils.c */; }; 230 230 61FD41330B4F6F0800BEEFEA /* MatroskaImportPrivate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 61FD41320B4F6F0800BEEFEA /* MatroskaImportPrivate.cpp */; }; … … 582 582 61CB11CB0ACDF50F007994BD /* KaxVersion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = KaxVersion.cpp; path = libmatroska/src/KaxVersion.cpp; sourceTree = "<group>"; }; 583 583 61D514DC0ADF3DBA00A671E1 /* SubImport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SubImport.h; sourceTree = "<group>"; }; 584 61D514DD0ADF3DBA00A671E1 /* SubImport. c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SubImport.c; sourceTree = "<group>"; };584 61D514DD0ADF3DBA00A671E1 /* SubImport.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = SubImport.mm; sourceTree = "<group>"; }; 585 585 61D517710AE0402E00A671E1 /* CommonUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = "<group>"; }; 586 586 61D517720AE0402E00A671E1 /* CommonUtils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CommonUtils.c; sourceTree = "<group>"; }; … … 880 880 6123D6250AD0A3FF003EDE52 /* VobSubCodecDispatch.h */, 881 881 61D514DC0ADF3DBA00A671E1 /* SubImport.h */, 882 61D514DD0ADF3DBA00A671E1 /* SubImport. c*/,882 61D514DD0ADF3DBA00A671E1 /* SubImport.mm */, 883 883 3DB2BB270B6C92F000416863 /* SSARenderCodec.h */, 884 884 3DB2BB280B6C92F000416863 /* SSARenderCodec.m */, … … 1503 1503 6123D6260AD0A3FF003EDE52 /* VobSubCodec.c in Sources */, 1504 1504 613CD51E0AD1FB650098A825 /* TextSubCodec.c in Sources */, 1505 61D514DE0ADF3DBA00A671E1 /* SubImport. cin Sources */,1505 61D514DE0ADF3DBA00A671E1 /* SubImport.mm in Sources */, 1506 1506 61D517730AE0402E00A671E1 /* CommonUtils.c in Sources */, 1507 1507 6116E5510B43C27B0020F1CE /* ACBaseCodec.cpp in Sources */, trunk/SSADocument.h
r329 r330 20 20 21 21 #import <Cocoa/Cocoa.h> 22 #import "SubImport.h" 22 23 23 24 enum {S_LeftAlign = 0, S_CenterAlign, S_RightAlign}; … … 43 44 } ssastyleline; 44 45 45 @interface SSAEvent : NSObject46 {47 @public48 NSString *line;49 unsigned begin_time, end_time;50 }51 @end52 53 46 @interface SSADocument : NSObject { 54 47 NSArray *_lines; … … 68 61 } 69 62 -(NSString *)header; 70 -(S SAEvent*)movPacket:(int)i;63 -(SubLine *)movPacket:(int)i; 71 64 -(void) loadHeader:(NSString*)path width:(float)width height:(float)height; 72 65 -(unsigned)packetCount; trunk/SSADocument.m
r329 r330 22 22 #import "Categories.h" 23 23 #include "SubImport.h" 24 25 @implementation SSAEvent26 -(void)dealloc27 {28 [line release];29 [super dealloc];30 }31 @end32 24 33 25 @implementation SSADocument … … 243 235 } 244 236 245 -(S SAEvent*)movPacket:(int)i237 -(SubLine *)movPacket:(int)i 246 238 { 247 239 return [_lines objectAtIndex:i]; … … 276 268 } 277 269 278 static int cmp_uint(const void *a, const void *b)279 {280 unsigned av = *(unsigned*)a, bv = *(unsigned*)b;281 282 if (av > bv) return 1;283 if (av < bv) return -1;284 return 0;285 }286 287 typedef struct {288 NSString *line;289 unsigned start, end;290 } line_range;291 292 static int cmp_line(const void *a, const void *b)293 {294 const line_range *av = a, *bv = b;295 296 if (av->start > bv->start) return 1;297 if (av->start < bv->start) return -1;298 return 0;299 }300 301 270 -(NSArray *)serializeSubLines:(NSMutableArray*)linesa 302 271 { 303 int num = [linesa count]; 304 unsigned times[num*2]; 305 line_range lines[num]; 306 unsigned i, j; 272 int num = [linesa count], i; 307 273 NSMutableArray *outa = [[NSMutableArray alloc] init]; 274 SubtitleSerializer *serializer = [[SubtitleSerializer alloc] init]; 275 SubLine *sl; 308 276 309 277 for (i = 0; i < num; i++) { 310 NSDictionary *l ;278 NSDictionary *l = [linesa objectAtIndex:i];; 311 279 NSString *s,*e; 312 l = [linesa objectAtIndex:i]; 313 line_range li; 280 SubLine *li; 314 281 315 282 s = [l objectForKey:@"Start"]; 316 283 e = [l objectForKey:@"End"]; 317 284 318 li.line = oneMKVPacket(l); 319 li.start = ParseSSATime(s); 320 li.end = ParseSSATime(e); 285 li = [[[SubLine alloc] initWithLine:oneMKVPacket(l) start:ParseSSATime(s) end:ParseSSATime(e)] autorelease]; 321 286 322 287 if (timescale != 1.) { 323 li.start *= timescale; 324 li.end *= timescale; 325 } 326 327 times[i*2] = li.start; 328 times[i*2+1] = li.end; 329 330 lines[i] = li; 331 } 332 333 qsort(times, num*2, sizeof(unsigned), cmp_uint); 334 qsort(lines, num, sizeof(line_range), cmp_line); 335 336 for (i = 0; i < num*2; i++) { 337 if (i > 0 && times[i-1] == times[i]) continue; 338 NSMutableString *accum = [NSMutableString string]; 339 unsigned start = times[i], end; 340 341 for (j = 0; j < num; j++) { 342 if (isinrange(times[i], lines[j].start, lines[j].end)) { 343 end = lines[j].end; 344 [accum appendString:lines[j].line]; 345 } 346 } 347 348 if ([accum length] > 0) { 349 [accum deleteCharactersInRange:NSMakeRange([accum length] - 1, 1)]; // delete last newline 350 SSAEvent *event = [[[SSAEvent alloc] init] autorelease]; 351 352 event->begin_time = start; 353 event->end_time = end; 354 event->line = [accum retain]; 355 356 [outa addObject:event]; 357 } 358 } 359 288 li->begin_time *= timescale; 289 li->end_time *= timescale; 290 } 291 292 [serializer addLine:li]; 293 } 294 295 [serializer setFinished:YES]; 296 297 while (sl = [serializer getSerializedPacket]) { 298 [outa addObject:sl]; 299 } 300 301 [serializer release]; 360 302 return outa; 361 303 } … … 600 542 601 543 for (i = 0; i < packetCount; i++) { 602 S SAEvent*p = [ssa movPacket:i];544 SubLine *p = [ssa movPacket:i]; 603 545 TimeRecord movieStartTime = {SInt64ToWide(p->begin_time), 100, 0}; 604 546 TimeValue sampleTime; … … 619 561 } 620 562 563 sampleHndl = NULL; 564 621 565 EndMediaEdits(theMedia); 622 566 … … 645 589 646 590 if (headerHndl) DisposeHandle((Handle)headerHndl); 591 if (sampleHndl) DisposeHandle(sampleHndl); 647 592 DisposeHandle((Handle)drefHndl); 648 593 trunk/SubImport.h
r311 r330 30 30 #endif 31 31 32 #ifdef __OBJC__ 33 #import <Cocoa/Cocoa.h> 34 35 @interface SubLine : NSObject 36 { 37 @public 38 NSString *line; 39 unsigned begin_time, end_time; 40 } 41 -(id)initWithLine:(NSString*)l start:(unsigned)s end:(unsigned)e; 42 @end 43 44 @interface SubtitleSerializer : NSObject 45 { 46 NSMutableArray *lines, *outpackets; 47 BOOL finished; 48 } 49 -(void)addLine:(SubLine *)sline; 50 -(void)addLineWithString:(NSString*)string start:(unsigned)start end:(unsigned)end; 51 -(void)setFinished:(BOOL)finished; 52 -(SubLine*)getSerializedPacket; 53 @end 32 54 #endif 55 56 #endif trunk/SubImport.mm
r313 r330 9 9 10 10 #include <QuickTime/QuickTime.h> 11 #include "SubImport.h" 11 #import "SubImport.h" 12 #import "Categories.h" 12 13 #include "CommonUtils.h" 13 14 … … 92 93 } 93 94 94 extern ComponentResult LoadSubStationAlphaSubtitles(const FSRef *theDirectory, CFStringRef filename, Movie theMovie, Track *firstSubTrack);95 extern "C" ComponentResult LoadSubStationAlphaSubtitles(const FSRef *theDirectory, CFStringRef filename, Movie theMovie, Track *firstSubTrack); 95 96 96 97 ComponentResult LoadSubRipSubtitles(const FSRef *theDirectory, CFStringRef filename, Movie theMovie, Track *firstSubTrack) 97 98 { 99 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 100 SubtitleSerializer *serializer = [[SubtitleSerializer alloc] init]; 98 101 ComponentResult err = noErr; 99 102 Handle dataRef = NULL; 100 OSType dataRefType = rAliasType;101 HFSUniStr255 hfsFilename;102 CFRange filenameLen;103 103 Track theTrack = NULL; 104 104 Media theMedia = NULL; 105 105 Rect movieBox; 106 107 ComponentInstance dataHandler = NULL;108 long filePos = 0;109 long fileSize; 110 c har *data = NULL;106 SubLine *sl; 107 TimeScale movieTimeScale = GetMovieTimeScale(theMovie); 108 UInt32 emptyDataRefExtension[2]; 109 110 const char *data = NULL; 111 111 ImageDescriptionHandle textDesc = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription)); 112 113 filenameLen.location = 0; 114 filenameLen.length = hfsFilename.length = CFStringGetLength(filename); 115 CFStringGetCharacters(filename, filenameLen, hfsFilename.unicode); 116 117 err = FSNewAliasMinimalUnicode(theDirectory, hfsFilename.length, hfsFilename.unicode, (AliasHandle *)&dataRef, NULL); 118 if (err) goto bail; 119 120 err = OpenAComponent(GetDataHandler(dataRef, dataRefType, kDataHCanRead), &dataHandler); 121 if (err) goto bail; 122 123 err = DataHSetDataRef(dataHandler, dataRef); 124 if (err) goto bail; 125 126 err = DataHOpenForRead(dataHandler); 127 if (err) goto bail; 128 129 err = DataHGetFileSize(dataHandler, &fileSize); 130 if (err) goto bail; 131 132 // cap subs at 5 megabytes; any more is really insane 133 if (fileSize > 5*1024*1024) 134 goto bail; 135 136 // load the subtitle file 137 data = NewPtr(fileSize + 1); 138 err = DataHScheduleData(dataHandler, data, 0, fileSize, 0, NULL, NULL); 139 if (err) goto bail; 140 data[fileSize] = '\0'; 141 112 unsigned int subNum = 1; 113 UInt8 *path = (UInt8*)malloc(PATH_MAX); 114 NSString *srtfile; 115 NSError *nserr = nil; 116 Handle sampleHndl = NULL; 117 118 FSRefMakePath(theDirectory, path, PATH_MAX); 119 srtfile = [[NSString stringWithUTF8String:(char*)path] stringByAppendingPathComponent:(NSString*)filename]; 120 data = [[[NSString stringWithContentsOfFile:srtfile encoding:NSUTF8StringEncoding error:&nserr] stringByStandardizingNewlines] UTF8String]; 121 free(path); 122 123 dataRef = NewHandleClear(sizeof(Handle) + 1); 124 emptyDataRefExtension[0] = EndianU32_NtoB(sizeof(UInt32)*2); 125 emptyDataRefExtension[1] = EndianU32_NtoB(kDataRefExtensionInitializationData); 126 127 PtrAndHand(&emptyDataRefExtension[0], dataRef, sizeof(emptyDataRefExtension)); 128 142 129 GetMovieBox(theMovie, &movieBox); 143 130 144 // millisecond accuracy 145 theTrack = CreatePlaintextSubTrack(theMovie, textDesc, 1000, dataRef, dataRefType, kSubFormatUTF8, NULL, movieBox); 131 theTrack = CreatePlaintextSubTrack(theMovie, textDesc, 1000, dataRef, HandleDataHandlerSubType, kSubFormatUTF8, NULL, movieBox); 146 132 if (theTrack == NULL) { 147 133 err = GetMoviesError(); … … 155 141 } 156 142 157 unsigned int subNum = 1;158 143 char subNumStr[10]; 159 144 160 s printf(subNumStr, "%u", subNum);145 snprintf(subNumStr, 10, "%u", subNum); 161 146 char *subOffset = strstr(data, subNumStr); 162 147 … … 177 162 // find the next subtitle 178 163 // setting subOffset to point the end of the current subtitle text 179 s printf(subNumStr, "\n%u", ++subNum);164 snprintf(subNumStr, 10, "\n%u", ++subNum); 180 165 subOffset = strstr(subTimecodeOffset, subNumStr); 181 166 … … 185 170 186 171 TimeValue startTime = startmsec + 1000*startsec + 60*1000*startmin + 60*60*1000*starthour; 187 TimeValue duration = endmsec + 1000*endsec + 60*1000*endmin + 60*60*1000*endhour - startTime; 188 TimeValue sampleTime; 189 TimeRecord movieStartTime = { SInt64ToWide(startTime), 1000, 0 }; 172 TimeValue endTime = endmsec + 1000*endsec + 60*1000*endmin + 60*60*1000*endhour; 190 173 191 174 if (subOffset != NULL) { 192 if(subOffset - subTextOffset - 1 > 0 && duration > 0) //Make sure subtitle is not empty175 if(subOffset - subTextOffset - 1 > 0 && endTime > startTime) //Make sure subtitle is not empty 193 176 { 194 err = AddMediaSampleReference(theMedia, subTextOffset - data, 195 subOffset - subTextOffset - 1, 196 duration, (SampleDescriptionHandle) textDesc, 197 1, 0, &sampleTime); 198 if (err) goto bail; 199 200 ConvertTimeScale(&movieStartTime, GetMovieTimeScale(theMovie)); 201 202 err = InsertMediaIntoTrack(theTrack, movieStartTime.value.lo, sampleTime, duration, fixed1); 203 204 if (err) goto bail; 177 NSString *l = [[NSString alloc] initWithBytes:subTextOffset length:subOffset - subTextOffset encoding:NSUTF8StringEncoding]; 178 sl = [[SubLine alloc] initWithLine:l start:startTime end:endTime]; 179 180 [l autorelease]; 181 [sl autorelease]; 182 183 [serializer addLine:sl]; 205 184 } 206 185 … … 211 190 } 212 191 } 192 193 [serializer setFinished:YES]; 194 195 BeginMediaEdits(theMedia); 196 197 while (sl = [serializer getSerializedPacket]) { 198 TimeRecord movieStartTime = {SInt64ToWide(sl->begin_time), 1000, 0}; 199 TimeValue sampleTime; 200 const char *str = [sl->line UTF8String]; 201 size_t sampleLen = strlen(str); 202 203 PtrToHand(str,&sampleHndl,sampleLen); 204 205 err=AddMediaSample(theMedia,sampleHndl,0,sampleLen, sl->end_time - sl->begin_time,(SampleDescriptionHandle)textDesc, 1, 0, &sampleTime); 206 if (err != noErr) goto bail; 207 208 ConvertTimeScale(&movieStartTime, movieTimeScale); 209 210 err = InsertMediaIntoTrack(theTrack, movieStartTime.value.lo, sampleTime, sl->end_time - sl->begin_time, fixed1); 211 if (err != noErr) goto bail; 212 213 DisposeHandle(sampleHndl); 214 } 215 216 sampleHndl = NULL; 213 217 214 218 if (*firstSubTrack == NULL) … … 220 224 221 225 bail: 226 [serializer release]; 227 [pool release]; 222 228 if (err) { 223 229 if (theMedia) … … 231 237 DisposeHandle((Handle) textDesc); 232 238 233 if (data) 234 DisposePtr(data); 235 236 if (dataHandler) 237 CloseComponent(dataHandler); 239 DisposeHandle(dataRef); 240 if (sampleHndl) DisposeHandle(sampleHndl); 241 238 242 239 243 return err; … … 331 335 return err; 332 336 } 337 338 @implementation SubtitleSerializer 339 -(id)init 340 { 341 if (self = [super init]) { 342 lines = [[NSMutableArray alloc] init]; 343 outpackets = [[NSMutableArray alloc] init]; 344 finished = NO; 345 } 346 347 return self; 348 } 349 350 -(void)addLine:(SubLine *)sline 351 { 352 [lines addObject:sline]; 353 } 354 355 -(void)addLineWithString:(NSString*)string start:(unsigned)start end:(unsigned)end 356 { 357 SubLine *sl = [[[SubLine alloc] init] autorelease]; 358 359 sl->line = [string retain]; 360 sl->begin_time = start; 361 sl->end_time = end; 362 363 [lines addObject:sl]; 364 } 365 366 static int cmp_line(id a, id b, void* unused) 367 { 368 SubLine *av = (SubLine*)a, *bv = (SubLine*)b; 369 370 if (av->begin_time > bv->begin_time) return NSOrderedDescbegining; 371 if (av->begin_time < bv->begin_time) return NSOrderedAscbegining; 372 return NSOrderedSame; 373 } 374 375 static int cmp_uint(const void *a, const void *b) 376 { 377 unsigned av = *(unsigned*)a, bv = *(unsigned*)b; 378 379 if (av > bv) return 1; 380 if (av < bv) return -1; 381 return 0; 382 } 383 384 static bool isinrange(unsigned base, unsigned test_s, unsigned test_e) 385 { 386 return (base >= test_s) && (base < test_e); 387 } 388 389 /* FIXME: This algorithm is not nearly good enough. 390 It works for trivial overlaps, but not [ [ ] [ ] ] (where [] are subtitle lines). 391 Many other cases may fail. */ 392 393 -(void)refill 394 { 395 unsigned num = [lines count]; 396 unsigned times[num*2+1]; 397 SubLine *slines[num]; 398 399 [lines sortUsingFunction:cmp_line context:nil]; 400 [lines getObjects:slines]; 401 //NSLog(@"%@",lines); 402 for (int i=0;i < num;i++) { 403 times[i*2] = slines[i]->begin_time; 404 times[i*2+1] = slines[i]->end_time; 405 } 406 407 times[num * 2] = times[num * 2 - 1]; 408 409 qsort(times, num*2, sizeof(unsigned), cmp_uint); 410 411 for (int i=0;i < num*2; i++) { 412 if (i > 0 && times[i-1] == times[i]) continue; 413 NSMutableString *accum = [NSMutableString string]; 414 unsigned start = times[i], end = times[i+1]; 415 bool startedOutput = false, finishedOutput = false; 416 417 // Add on packets until we find one that marks it ending (by starting later) 418 // ...except if we know this is the last input packet from the stream, then we have to explicitly flush it 419 if (finished && i >= (num*2)-2) finishedOutput = true; 420 421 for (int j=0; j < num; j++) { 422 if (isinrange(times[i], slines[j]->begin_time, slines[j]->end_time)) { 423 [accum appendString:slines[j]->line]; 424 startedOutput = true; 425 } else if (startedOutput) {finishedOutput = true; break;} 426 } 427 428 429 if (finishedOutput && startedOutput) { 430 [accum deleteCharactersInRange:NSMakeRange([accum length] - 1, 1)]; // delete last newline 431 SubLine *event = [[[SubLine alloc] initWithLine:accum start:start end:end] autorelease]; 432 433 [outpackets addObject:event]; 434 } 435 } 436 437 //NSLog(@"%@",outpackets); 438 439 [lines removeAllObjects]; 440 if (!finished) [lines addObject:slines[num-1]]; //keep the last packet in the queue 441 } 442 443 -(SubLine*)getSerializedPacket 444 { 445 if ([outpackets count] == 0) [self refill]; 446 if ([outpackets count] == 0) return nil; 447 448 SubLine *sl = [outpackets objectAtIndex:0]; 449 [outpackets removeObjectAtIndex:0]; 450 451 return sl; 452 } 453 454 -(void)setFinished:(BOOL)f 455 { 456 finished = f; 457 } 458 @end 459 460 @implementation SubLine 461 -(id)initWithLine:(NSString*)l start:(unsigned)s end:(unsigned)e 462 { 463 if (self = [super init]) { 464 line = [l retain]; 465 begin_time = s; 466 end_time = e; 467 } 468 469 return self; 470 } 471 472 -(void)dealloc 473 { 474 [line release]; 475 [super dealloc]; 476 } 477 478 -(NSString*)description 479 { 480 return [NSString stringWithFormat:@"\"%@\", %d -> %d",line,begin_time,end_time]; 481 } 482 @end
