1 /* 2 * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "SubTitlesSRT.h" 8 9 #include <new> 10 11 #include <stdlib.h> 12 13 #include <File.h> 14 15 #include "FileReadWrite.h" 16 17 18 SubTitlesSRT::SubTitlesSRT(BFile* file, const char* name) 19 : 20 SubTitles(), 21 fName(name), 22 fSubTitles(64) 23 { 24 if (file == NULL) 25 return; 26 if (file->InitCheck() != B_OK) 27 return; 28 29 FileReadWrite lineProvider(file); 30 BString line; 31 enum { 32 EXPECT_SEQUENCE_NUMBER = 0, 33 EXPECT_TIME_CODE, 34 EXPECT_TEXT 35 }; 36 37 SubTitle subTitle; 38 int32 lastSequenceNumber = 0; 39 int32 currentLine = 0; 40 41 int32 state = EXPECT_SEQUENCE_NUMBER; 42 while (lineProvider.Next(line)) { 43 line.RemoveAll("\n"); 44 line.RemoveAll("\r"); 45 switch (state) { 46 case EXPECT_SEQUENCE_NUMBER: 47 { 48 line.Trim(); 49 int32 sequenceNumber = atoi(line.String()); 50 if (sequenceNumber != lastSequenceNumber + 1) { 51 fprintf(stderr, "Warning: Wrong sequence number in SRT " 52 "file: %ld, expected: %ld, line %ld\n", sequenceNumber, 53 lastSequenceNumber + 1, currentLine); 54 } 55 state = EXPECT_TIME_CODE; 56 lastSequenceNumber = sequenceNumber; 57 break; 58 } 59 60 case EXPECT_TIME_CODE: 61 { 62 line.Trim(); 63 int32 separatorPos = line.FindFirst(" --> "); 64 if (separatorPos < 0) { 65 fprintf(stderr, "Error: Time code expected on line %ld, " 66 "got '%s'\n", currentLine, line.String()); 67 return; 68 } 69 BString timeCode(line.String(), separatorPos); 70 if (separatorPos != 12) { 71 fprintf(stderr, "Warning: Time code broken on line %ld " 72 "(%s)?\n", currentLine, timeCode.String()); 73 } 74 int hours; 75 int minutes; 76 int seconds; 77 int milliSeconds; 78 if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, 79 &seconds, &milliSeconds) != 4) { 80 fprintf(stderr, "Error: Failed to parse start time on " 81 "line %ld\n", currentLine); 82 return; 83 } 84 subTitle.startTime = (bigtime_t)hours * 60 * 60 * 1000000LL 85 + (bigtime_t)minutes * 60 * 1000000LL 86 + (bigtime_t)seconds * 1000000LL 87 + (bigtime_t)milliSeconds * 1000; 88 89 int32 endTimePos = separatorPos + 5; 90 timeCode.SetTo(line.String() + endTimePos); 91 if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, 92 &seconds, &milliSeconds) != 4) { 93 fprintf(stderr, "Error: Failed to parse end time on " 94 "line %ld\n", currentLine); 95 return; 96 } 97 bigtime_t endTime = (bigtime_t)hours * 60 * 60 * 1000000LL 98 + (bigtime_t)minutes * 60 * 1000000LL 99 + (bigtime_t)seconds * 1000000LL 100 + (bigtime_t)milliSeconds * 1000; 101 102 subTitle.duration = endTime - subTitle.startTime; 103 104 state = EXPECT_TEXT; 105 break; 106 } 107 108 case EXPECT_TEXT: 109 if (line.Length() == 0) { 110 int32 index = _IndexFor(subTitle.startTime); 111 SubTitle* clone = new(std::nothrow) SubTitle(subTitle); 112 if (clone == NULL || !fSubTitles.AddItem(clone, index)) { 113 delete clone; 114 return; 115 } 116 subTitle.text = ""; 117 subTitle.placement = BPoint(-1, -1); 118 subTitle.startTime = 0; 119 subTitle.duration = 0; 120 121 state = EXPECT_SEQUENCE_NUMBER; 122 } else 123 subTitle.text << line << '\n'; 124 break; 125 } 126 line.SetTo(""); 127 currentLine++; 128 } 129 } 130 131 132 SubTitlesSRT::~SubTitlesSRT() 133 { 134 for (int32 i = fSubTitles.CountItems() - 1; i >= 0; i--) 135 delete reinterpret_cast<SubTitle*>(fSubTitles.ItemAtFast(i)); 136 } 137 138 139 const char* 140 SubTitlesSRT::Name() const 141 { 142 return fName.String(); 143 } 144 145 146 const SubTitle* 147 SubTitlesSRT::SubTitleAt(bigtime_t time) const 148 { 149 int32 index = _IndexFor(time); 150 SubTitle* subTitle 151 = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index)); 152 if (subTitle != NULL && subTitle->startTime > time) 153 subTitle = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index - 1)); 154 if (subTitle != NULL && subTitle->startTime <= time 155 && subTitle->startTime + subTitle->duration > time) { 156 return subTitle; 157 } 158 return NULL; 159 } 160 161 162 int32 163 SubTitlesSRT::_IndexFor(bigtime_t startTime) const 164 { 165 // binary search index 166 int32 lower = 0; 167 int32 upper = fSubTitles.CountItems(); 168 while (lower < upper) { 169 int32 mid = (lower + upper) / 2; 170 SubTitle* subTitle = reinterpret_cast<SubTitle*>( 171 fSubTitles.ItemAtFast(mid)); 172 if (startTime < subTitle->startTime) 173 upper = mid; 174 else 175 lower = mid + 1; 176 } 177 return lower; 178 } 179 180 181 182