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 if (line.IsEmpty()) 49 continue; 50 51 line.Trim(); 52 int32 sequenceNumber = atoi(line.String()); 53 if (sequenceNumber != lastSequenceNumber + 1) { 54 fprintf(stderr, "Warning: Wrong sequence number in SRT " 55 "file: %" B_PRId32 ", expected: %" B_PRId32 ", line %" 56 B_PRId32 "\n", sequenceNumber, lastSequenceNumber + 1, 57 currentLine); 58 } 59 state = EXPECT_TIME_CODE; 60 lastSequenceNumber = sequenceNumber; 61 break; 62 } 63 64 case EXPECT_TIME_CODE: 65 { 66 line.Trim(); 67 int32 separatorPos = line.FindFirst(" --> "); 68 if (separatorPos < 0) { 69 fprintf(stderr, "Error: Time code expected on line %" 70 B_PRId32 ", got '%s'\n", currentLine, line.String()); 71 return; 72 } 73 BString timeCode(line.String(), separatorPos); 74 if (separatorPos != 12) { 75 fprintf(stderr, "Warning: Time code broken on line %" 76 B_PRId32 " (%s)?\n", currentLine, timeCode.String()); 77 } 78 int hours; 79 int minutes; 80 int seconds; 81 int milliSeconds; 82 if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, 83 &seconds, &milliSeconds) != 4) { 84 fprintf(stderr, "Error: Failed to parse start time on " 85 "line %" B_PRId32 "\n", currentLine); 86 return; 87 } 88 subTitle.startTime = (bigtime_t)hours * 60 * 60 * 1000000LL 89 + (bigtime_t)minutes * 60 * 1000000LL 90 + (bigtime_t)seconds * 1000000LL 91 + (bigtime_t)milliSeconds * 1000; 92 93 int32 endTimePos = separatorPos + 5; 94 timeCode.SetTo(line.String() + endTimePos); 95 if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes, 96 &seconds, &milliSeconds) != 4) { 97 fprintf(stderr, "Error: Failed to parse end time on " 98 "line %" B_PRId32 "\n", currentLine); 99 return; 100 } 101 bigtime_t endTime = (bigtime_t)hours * 60 * 60 * 1000000LL 102 + (bigtime_t)minutes * 60 * 1000000LL 103 + (bigtime_t)seconds * 1000000LL 104 + (bigtime_t)milliSeconds * 1000; 105 106 subTitle.duration = endTime - subTitle.startTime; 107 108 state = EXPECT_TEXT; 109 break; 110 } 111 112 case EXPECT_TEXT: 113 if (line.IsEmpty()) { 114 int32 index = _IndexFor(subTitle.startTime); 115 SubTitle* clone = new(std::nothrow) SubTitle(subTitle); 116 if (clone == NULL || !fSubTitles.AddItem(clone, index)) { 117 delete clone; 118 return; 119 } 120 subTitle.text = ""; 121 subTitle.placement = BPoint(-1, -1); 122 subTitle.startTime = 0; 123 subTitle.duration = 0; 124 125 state = EXPECT_SEQUENCE_NUMBER; 126 } else 127 subTitle.text << line << '\n'; 128 break; 129 } 130 line.SetTo(""); 131 currentLine++; 132 } 133 } 134 135 136 SubTitlesSRT::~SubTitlesSRT() 137 { 138 for (int32 i = fSubTitles.CountItems() - 1; i >= 0; i--) 139 delete reinterpret_cast<SubTitle*>(fSubTitles.ItemAtFast(i)); 140 } 141 142 143 const char* 144 SubTitlesSRT::Name() const 145 { 146 return fName.String(); 147 } 148 149 150 const SubTitle* 151 SubTitlesSRT::SubTitleAt(bigtime_t time) const 152 { 153 int32 index = _IndexFor(time); 154 SubTitle* subTitle 155 = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index)); 156 if (subTitle != NULL && subTitle->startTime > time) 157 subTitle = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index - 1)); 158 if (subTitle != NULL && subTitle->startTime <= time 159 && subTitle->startTime + subTitle->duration > time) { 160 return subTitle; 161 } 162 return NULL; 163 } 164 165 166 int32 167 SubTitlesSRT::_IndexFor(bigtime_t startTime) const 168 { 169 // binary search index 170 int32 lower = 0; 171 int32 upper = fSubTitles.CountItems(); 172 while (lower < upper) { 173 int32 mid = (lower + upper) / 2; 174 SubTitle* subTitle = reinterpret_cast<SubTitle*>( 175 fSubTitles.ItemAtFast(mid)); 176 if (startTime < subTitle->startTime) 177 upper = mid; 178 else 179 lower = mid + 1; 180 } 181 return lower; 182 } 183 184 185 186