xref: /haiku/src/apps/mediaplayer/supplier/SubTitlesSRT.cpp (revision 843a122fd9a17bfde0f01db2c5660c33524bf40c)
1c8ccdf52SStephan Aßmus /*
2c8ccdf52SStephan Aßmus  * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3c8ccdf52SStephan Aßmus  * Distributed under the terms of the MIT License.
4c8ccdf52SStephan Aßmus  */
5c8ccdf52SStephan Aßmus 
6c8ccdf52SStephan Aßmus 
7c8ccdf52SStephan Aßmus #include "SubTitlesSRT.h"
8c8ccdf52SStephan Aßmus 
9c8ccdf52SStephan Aßmus #include <new>
10c8ccdf52SStephan Aßmus 
11c8ccdf52SStephan Aßmus #include <stdlib.h>
12c8ccdf52SStephan Aßmus 
13c8ccdf52SStephan Aßmus #include <File.h>
14c8ccdf52SStephan Aßmus 
15c8ccdf52SStephan Aßmus #include "FileReadWrite.h"
16c8ccdf52SStephan Aßmus 
17c8ccdf52SStephan Aßmus 
18c8ccdf52SStephan Aßmus SubTitlesSRT::SubTitlesSRT(BFile* file, const char* name)
19c8ccdf52SStephan Aßmus 	:
20c8ccdf52SStephan Aßmus 	SubTitles(),
21c8ccdf52SStephan Aßmus 	fName(name),
22c8ccdf52SStephan Aßmus 	fSubTitles(64)
23c8ccdf52SStephan Aßmus {
24c8ccdf52SStephan Aßmus 	if (file == NULL)
25c8ccdf52SStephan Aßmus 		return;
26c8ccdf52SStephan Aßmus 	if (file->InitCheck() != B_OK)
27c8ccdf52SStephan Aßmus 		return;
28c8ccdf52SStephan Aßmus 
29c8ccdf52SStephan Aßmus 	FileReadWrite lineProvider(file);
30c8ccdf52SStephan Aßmus 	BString line;
31c8ccdf52SStephan Aßmus 	enum {
32c8ccdf52SStephan Aßmus 		EXPECT_SEQUENCE_NUMBER = 0,
33c8ccdf52SStephan Aßmus 		EXPECT_TIME_CODE,
34c8ccdf52SStephan Aßmus 		EXPECT_TEXT
35c8ccdf52SStephan Aßmus 	};
36c8ccdf52SStephan Aßmus 
37c8ccdf52SStephan Aßmus 	SubTitle subTitle;
38c8ccdf52SStephan Aßmus 	int32 lastSequenceNumber = 0;
39c8ccdf52SStephan Aßmus 	int32 currentLine = 0;
40c8ccdf52SStephan Aßmus 
41c8ccdf52SStephan Aßmus 	int32 state = EXPECT_SEQUENCE_NUMBER;
42c8ccdf52SStephan Aßmus 	while (lineProvider.Next(line)) {
43c8ccdf52SStephan Aßmus 		line.RemoveAll("\n");
44c8ccdf52SStephan Aßmus 		line.RemoveAll("\r");
45c8ccdf52SStephan Aßmus 		switch (state) {
46c8ccdf52SStephan Aßmus 			case EXPECT_SEQUENCE_NUMBER:
47c8ccdf52SStephan Aßmus 			{
4882a05afeSAxel Dörfler 				if (line.IsEmpty())
4982a05afeSAxel Dörfler 					continue;
5082a05afeSAxel Dörfler 
51c8ccdf52SStephan Aßmus 				line.Trim();
52c8ccdf52SStephan Aßmus 				int32 sequenceNumber = atoi(line.String());
53c8ccdf52SStephan Aßmus 				if (sequenceNumber != lastSequenceNumber + 1) {
54c8ccdf52SStephan Aßmus 					fprintf(stderr, "Warning: Wrong sequence number in SRT "
55*843a122fSJérôme Duval 						"file: %" B_PRId32 ", expected: %" B_PRId32 ", line %"
56*843a122fSJérôme Duval 						B_PRId32 "\n", sequenceNumber, lastSequenceNumber + 1,
57*843a122fSJérôme Duval 						currentLine);
58c8ccdf52SStephan Aßmus 				}
59c8ccdf52SStephan Aßmus 				state = EXPECT_TIME_CODE;
60c8ccdf52SStephan Aßmus 				lastSequenceNumber = sequenceNumber;
61c8ccdf52SStephan Aßmus 				break;
62c8ccdf52SStephan Aßmus 			}
63c8ccdf52SStephan Aßmus 
64c8ccdf52SStephan Aßmus 			case EXPECT_TIME_CODE:
65c8ccdf52SStephan Aßmus 			{
66c8ccdf52SStephan Aßmus 				line.Trim();
67c8ccdf52SStephan Aßmus 				int32 separatorPos = line.FindFirst(" --> ");
68c8ccdf52SStephan Aßmus 				if (separatorPos < 0) {
69*843a122fSJérôme Duval 					fprintf(stderr, "Error: Time code expected on line %"
70*843a122fSJérôme Duval 						B_PRId32 ", got '%s'\n", currentLine, line.String());
71c8ccdf52SStephan Aßmus 					return;
72c8ccdf52SStephan Aßmus 				}
73c8ccdf52SStephan Aßmus 				BString timeCode(line.String(), separatorPos);
74c8ccdf52SStephan Aßmus 				if (separatorPos != 12) {
75*843a122fSJérôme Duval 					fprintf(stderr, "Warning: Time code broken on line %"
76*843a122fSJérôme Duval 						B_PRId32 " (%s)?\n", currentLine, timeCode.String());
77c8ccdf52SStephan Aßmus 				}
78c8ccdf52SStephan Aßmus 				int hours;
79c8ccdf52SStephan Aßmus 				int minutes;
80c8ccdf52SStephan Aßmus 				int seconds;
81c8ccdf52SStephan Aßmus 				int milliSeconds;
82c8ccdf52SStephan Aßmus 				if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes,
83c8ccdf52SStephan Aßmus 					&seconds, &milliSeconds) != 4) {
84c8ccdf52SStephan Aßmus 					fprintf(stderr, "Error: Failed to parse start time on "
85*843a122fSJérôme Duval 						"line %" B_PRId32 "\n", currentLine);
86c8ccdf52SStephan Aßmus 					return;
87c8ccdf52SStephan Aßmus 				}
88c8ccdf52SStephan Aßmus 				subTitle.startTime = (bigtime_t)hours * 60 * 60 * 1000000LL
89c8ccdf52SStephan Aßmus 					+ (bigtime_t)minutes * 60 * 1000000LL
90c8ccdf52SStephan Aßmus 					+ (bigtime_t)seconds * 1000000LL
91c8ccdf52SStephan Aßmus 					+ (bigtime_t)milliSeconds * 1000;
92c8ccdf52SStephan Aßmus 
93c8ccdf52SStephan Aßmus 				int32 endTimePos = separatorPos + 5;
94c8ccdf52SStephan Aßmus 				timeCode.SetTo(line.String() + endTimePos);
95c8ccdf52SStephan Aßmus 				if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes,
96c8ccdf52SStephan Aßmus 					&seconds, &milliSeconds) != 4) {
97c8ccdf52SStephan Aßmus 					fprintf(stderr, "Error: Failed to parse end time on "
98*843a122fSJérôme Duval 						"line %" B_PRId32 "\n", currentLine);
99c8ccdf52SStephan Aßmus 					return;
100c8ccdf52SStephan Aßmus 				}
101c8ccdf52SStephan Aßmus 				bigtime_t endTime = (bigtime_t)hours * 60 * 60 * 1000000LL
102c8ccdf52SStephan Aßmus 					+ (bigtime_t)minutes * 60 * 1000000LL
103c8ccdf52SStephan Aßmus 					+ (bigtime_t)seconds * 1000000LL
104c8ccdf52SStephan Aßmus 					+ (bigtime_t)milliSeconds * 1000;
105c8ccdf52SStephan Aßmus 
106c8ccdf52SStephan Aßmus 				subTitle.duration = endTime - subTitle.startTime;
107c8ccdf52SStephan Aßmus 
108c8ccdf52SStephan Aßmus 				state = EXPECT_TEXT;
109c8ccdf52SStephan Aßmus 				break;
110c8ccdf52SStephan Aßmus 			}
111c8ccdf52SStephan Aßmus 
112c8ccdf52SStephan Aßmus 			case EXPECT_TEXT:
11382a05afeSAxel Dörfler 				if (line.IsEmpty()) {
114c8ccdf52SStephan Aßmus 					int32 index = _IndexFor(subTitle.startTime);
115c8ccdf52SStephan Aßmus 					SubTitle* clone = new(std::nothrow) SubTitle(subTitle);
116c8ccdf52SStephan Aßmus 					if (clone == NULL || !fSubTitles.AddItem(clone, index)) {
117c8ccdf52SStephan Aßmus 						delete clone;
118c8ccdf52SStephan Aßmus 						return;
119c8ccdf52SStephan Aßmus 					}
120c8ccdf52SStephan Aßmus 					subTitle.text = "";
121c8ccdf52SStephan Aßmus 					subTitle.placement = BPoint(-1, -1);
122c8ccdf52SStephan Aßmus 					subTitle.startTime = 0;
123c8ccdf52SStephan Aßmus 					subTitle.duration = 0;
124c8ccdf52SStephan Aßmus 
125c8ccdf52SStephan Aßmus 					state = EXPECT_SEQUENCE_NUMBER;
126c8ccdf52SStephan Aßmus 				} else
127c8ccdf52SStephan Aßmus 					subTitle.text << line << '\n';
128c8ccdf52SStephan Aßmus 				break;
129c8ccdf52SStephan Aßmus 		}
130c8ccdf52SStephan Aßmus 		line.SetTo("");
131c8ccdf52SStephan Aßmus 		currentLine++;
132c8ccdf52SStephan Aßmus 	}
133c8ccdf52SStephan Aßmus }
134c8ccdf52SStephan Aßmus 
135c8ccdf52SStephan Aßmus 
136c8ccdf52SStephan Aßmus SubTitlesSRT::~SubTitlesSRT()
137c8ccdf52SStephan Aßmus {
138c8ccdf52SStephan Aßmus 	for (int32 i = fSubTitles.CountItems() - 1; i >= 0; i--)
139c8ccdf52SStephan Aßmus 		delete reinterpret_cast<SubTitle*>(fSubTitles.ItemAtFast(i));
140c8ccdf52SStephan Aßmus }
141c8ccdf52SStephan Aßmus 
142c8ccdf52SStephan Aßmus 
143c8ccdf52SStephan Aßmus const char*
144c8ccdf52SStephan Aßmus SubTitlesSRT::Name() const
145c8ccdf52SStephan Aßmus {
146c8ccdf52SStephan Aßmus 	return fName.String();
147c8ccdf52SStephan Aßmus }
148c8ccdf52SStephan Aßmus 
149c8ccdf52SStephan Aßmus 
150c8ccdf52SStephan Aßmus const SubTitle*
151c8ccdf52SStephan Aßmus SubTitlesSRT::SubTitleAt(bigtime_t time) const
152c8ccdf52SStephan Aßmus {
153c8ccdf52SStephan Aßmus 	int32 index = _IndexFor(time);
154c8ccdf52SStephan Aßmus 	SubTitle* subTitle
155c8ccdf52SStephan Aßmus 		= reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index));
156c8ccdf52SStephan Aßmus 	if (subTitle != NULL && subTitle->startTime > time)
157c8ccdf52SStephan Aßmus 		subTitle = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index - 1));
158c8ccdf52SStephan Aßmus 	if (subTitle != NULL && subTitle->startTime <= time
159c8ccdf52SStephan Aßmus 		&& subTitle->startTime + subTitle->duration > time) {
160c8ccdf52SStephan Aßmus 		return subTitle;
161c8ccdf52SStephan Aßmus 	}
162c8ccdf52SStephan Aßmus 	return NULL;
163c8ccdf52SStephan Aßmus }
164c8ccdf52SStephan Aßmus 
165c8ccdf52SStephan Aßmus 
166c8ccdf52SStephan Aßmus int32
167c8ccdf52SStephan Aßmus SubTitlesSRT::_IndexFor(bigtime_t startTime) const
168c8ccdf52SStephan Aßmus {
169c8ccdf52SStephan Aßmus 	// binary search index
170c8ccdf52SStephan Aßmus 	int32 lower = 0;
171c8ccdf52SStephan Aßmus 	int32 upper = fSubTitles.CountItems();
172c8ccdf52SStephan Aßmus 	while (lower < upper) {
173c8ccdf52SStephan Aßmus 		int32 mid = (lower + upper) / 2;
174c8ccdf52SStephan Aßmus 		SubTitle* subTitle = reinterpret_cast<SubTitle*>(
175c8ccdf52SStephan Aßmus 			fSubTitles.ItemAtFast(mid));
176c8ccdf52SStephan Aßmus 		if (startTime < subTitle->startTime)
177c8ccdf52SStephan Aßmus 			upper = mid;
178c8ccdf52SStephan Aßmus 		else
179c8ccdf52SStephan Aßmus 			lower = mid + 1;
180c8ccdf52SStephan Aßmus 	}
181c8ccdf52SStephan Aßmus 	return lower;
182c8ccdf52SStephan Aßmus }
183c8ccdf52SStephan Aßmus 
184c8ccdf52SStephan Aßmus 
185c8ccdf52SStephan Aßmus 
186