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