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