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 <stdio.h> 12 #include <stdlib.h> 13 14 #include <AutoDeleter.h> 15 #include <File.h> 16 #include <StringList.h> 17 #include <TextEncoding.h> 18 19 20 static void 21 ReadLines(BFile* file, BStringList& lines) 22 { 23 if (file == NULL) 24 return; 25 if (file->InitCheck() != B_OK) 26 return; 27 28 off_t size; 29 if (file->GetSize(&size) != B_OK || size == 0) 30 return; 31 32 ArrayDeleter<char> buffer(new(std::nothrow) char[size + 1]); 33 if (!buffer.IsSet()) 34 return; 35 36 if (file->Read(buffer.Get(), size) < size) 37 return; 38 buffer[size] = '\0'; 39 40 BPrivate::BTextEncoding decoder(buffer.Get(), size); 41 size_t decodedLength = size * 4; 42 BString content; 43 char* decoded = content.LockBuffer(decodedLength); 44 size_t consumed = size; 45 decoder.Decode(buffer.Get(), consumed, decoded, decodedLength); 46 content.UnlockBuffer(decodedLength); 47 48 content.Split("\n", false, lines); 49 } 50 51 52 SubTitlesSRT::SubTitlesSRT(BFile* file, const char* name) 53 : 54 SubTitles(), 55 fName(name), 56 fSubTitles(64) 57 { 58 BStringList lines; 59 ReadLines(file, lines); 60 int32 totalLines = lines.CountStrings(); 61 62 enum { 63 EXPECT_SEQUENCE_NUMBER = 0, 64 EXPECT_TIME_CODE, 65 EXPECT_TEXT 66 }; 67 68 SubTitle subTitle; 69 int32 lastSequenceNumber = 0; 70 71 int32 state = EXPECT_SEQUENCE_NUMBER; 72 for (int32 currentLine = 0; currentLine < totalLines; currentLine++) { 73 BString line(lines.StringAt(currentLine)); 74 line.RemoveAll("\r"); 75 switch (state) { 76 case EXPECT_SEQUENCE_NUMBER: 77 { 78 if (line.IsEmpty()) 79 continue; 80 81 line.Trim(); 82 int32 sequenceNumber = atoi(line.String()); 83 if (sequenceNumber != lastSequenceNumber + 1) { 84 fprintf(stderr, "Warning: Wrong sequence number in SRT " 85 "file: %" B_PRId32 ", expected: %" B_PRId32 ", line %" 86 B_PRId32 "\n", sequenceNumber, lastSequenceNumber + 1, 87 currentLine); 88 } 89 state = EXPECT_TIME_CODE; 90 lastSequenceNumber = sequenceNumber; 91 break; 92 } 93 94 case EXPECT_TIME_CODE: 95 { 96 line.Trim(); 97 int32 separatorPos = line.FindFirst(" --> "); 98 if (separatorPos < 0) { 99 fprintf(stderr, "Error: Time code expected on line %" 100 B_PRId32 ", got '%s'\n", currentLine, line.String()); 101 return; 102 } 103 BString timeCode(line.String(), separatorPos); 104 if (separatorPos != 12) { 105 fprintf(stderr, "Warning: Time code broken on line %" 106 B_PRId32 " (%s)?\n", currentLine, timeCode.String()); 107 } 108 int hours; 109 int minutes; 110 int seconds; 111 int milliSeconds; 112 if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, 113 &seconds, &milliSeconds) != 4) { 114 fprintf(stderr, "Error: Failed to parse start time on " 115 "line %" B_PRId32 "\n", currentLine); 116 return; 117 } 118 subTitle.startTime = (bigtime_t)hours * 60 * 60 * 1000000LL 119 + (bigtime_t)minutes * 60 * 1000000LL 120 + (bigtime_t)seconds * 1000000LL 121 + (bigtime_t)milliSeconds * 1000; 122 123 int32 endTimePos = separatorPos + 5; 124 timeCode.SetTo(line.String() + endTimePos); 125 if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, 126 &seconds, &milliSeconds) != 4) { 127 fprintf(stderr, "Error: Failed to parse end time on " 128 "line %" B_PRId32 "\n", currentLine); 129 return; 130 } 131 bigtime_t endTime = (bigtime_t)hours * 60 * 60 * 1000000LL 132 + (bigtime_t)minutes * 60 * 1000000LL 133 + (bigtime_t)seconds * 1000000LL 134 + (bigtime_t)milliSeconds * 1000; 135 136 subTitle.duration = endTime - subTitle.startTime; 137 138 state = EXPECT_TEXT; 139 break; 140 } 141 142 case EXPECT_TEXT: 143 if (line.IsEmpty()) { 144 int32 index = _IndexFor(subTitle.startTime); 145 SubTitle* clone = new(std::nothrow) SubTitle(subTitle); 146 if (clone == NULL || !fSubTitles.AddItem(clone, index)) { 147 delete clone; 148 return; 149 } 150 subTitle.text = ""; 151 subTitle.placement = BPoint(-1, -1); 152 subTitle.startTime = 0; 153 subTitle.duration = 0; 154 155 state = EXPECT_SEQUENCE_NUMBER; 156 } else { 157 subTitle.text << line << '\n'; 158 } 159 break; 160 } 161 } 162 } 163 164 165 SubTitlesSRT::~SubTitlesSRT() 166 { 167 for (int32 i = fSubTitles.CountItems() - 1; i >= 0; i--) 168 delete reinterpret_cast<SubTitle*>(fSubTitles.ItemAtFast(i)); 169 } 170 171 172 const char* 173 SubTitlesSRT::Name() const 174 { 175 return fName.String(); 176 } 177 178 179 const SubTitle* 180 SubTitlesSRT::SubTitleAt(bigtime_t time) const 181 { 182 int32 index = _IndexFor(time) - 1; 183 SubTitle* subTitle = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index)); 184 if (subTitle != NULL && subTitle->startTime + subTitle->duration > time) 185 return subTitle; 186 return NULL; 187 } 188 189 190 int32 191 SubTitlesSRT::_IndexFor(bigtime_t startTime) const 192 { 193 // binary search index 194 int32 lower = 0; 195 int32 upper = fSubTitles.CountItems(); 196 while (lower < upper) { 197 int32 mid = (lower + upper) / 2; 198 SubTitle* subTitle = reinterpret_cast<SubTitle*>( 199 fSubTitles.ItemAtFast(mid)); 200 if (startTime < subTitle->startTime) 201 upper = mid; 202 else 203 lower = mid + 1; 204 } 205 return lower; 206 } 207