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