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