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