xref: /haiku/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp (revision 1026b0a1a76dc88927bb8175c470f638dc5464ee)
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