1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "DVDDemuxVobsub.h"
10
11 #include "DVDCodecs/DVDCodecs.h"
12 #include "DVDDemuxFFmpeg.h"
13 #include "DVDInputStreams/DVDFactoryInputStream.h"
14 #include "DVDInputStreams/DVDInputStream.h"
15 #include "DVDStreamInfo.h"
16 #include "DVDSubtitles/DVDSubtitleStream.h"
17 #include "cores/VideoPlayer/Interface/DemuxPacket.h"
18 #include "cores/VideoPlayer/Interface/TimingConstants.h"
19
20 #include <string.h>
21
22 CDVDDemuxVobsub::CDVDDemuxVobsub() = default;
23
~CDVDDemuxVobsub()24 CDVDDemuxVobsub::~CDVDDemuxVobsub()
25 {
26 for(unsigned i=0;i<m_Streams.size();i++)
27 {
28 delete m_Streams[i];
29 }
30 m_Streams.clear();
31 }
32
GetStreams() const33 std::vector<CDemuxStream*> CDVDDemuxVobsub::GetStreams() const
34 {
35 std::vector<CDemuxStream*> streams;
36
37 for (auto iter : m_Streams)
38 streams.push_back(iter);
39
40 return streams;
41 }
42
Open(const std::string & filename,int source,const std::string & subfilename)43 bool CDVDDemuxVobsub::Open(const std::string& filename, int source, const std::string& subfilename)
44 {
45 m_Filename = filename;
46 m_source = source;
47
48 std::unique_ptr<CDVDSubtitleStream> pStream(new CDVDSubtitleStream());
49 if(!pStream->Open(filename))
50 return false;
51
52 std::string vobsub = subfilename;
53 if ( vobsub == "")
54 {
55 vobsub = filename;
56 vobsub.erase(vobsub.rfind('.'), vobsub.size());
57 vobsub += ".sub";
58 }
59
60 CFileItem item(vobsub, false);
61 item.SetMimeType("video/x-vobsub");
62 item.SetContentLookup(false);
63 m_Input = CDVDFactoryInputStream::CreateInputStream(NULL, item);
64 if (!m_Input || !m_Input->Open())
65 return false;
66
67 m_Demuxer.reset(new CDVDDemuxFFmpeg());
68 if (!m_Demuxer->Open(m_Input, false))
69 return false;
70
71 CDVDStreamInfo hints;
72 CDVDCodecOptions options;
73 hints.codec = AV_CODEC_ID_DVD_SUBTITLE;
74
75 char line[2048];
76
77 SState state;
78 state.delay = 0;
79 state.id = -1;
80
81 while( pStream->ReadLine(line, sizeof(line)) )
82 {
83 if (*line == 0 || *line == '\r' || *line == '\n' || *line == '#')
84 continue;
85 else if (strncmp("langidx:", line, 8) == 0)
86 ParseLangIdx(state, line + 8);
87 else if (strncmp("delay:", line, 6) == 0)
88 ParseDelay(state, line + 6);
89 else if (strncmp("id:", line, 3) == 0)
90 ParseId(state, line + 3);
91 else if (strncmp("timestamp:", line, 10) == 0)
92 ParseTimestamp(state, line + 10);
93 else if (strncmp("palette:", line, 8) == 0
94 || strncmp("size:", line, 5) == 0
95 || strncmp("org:", line, 4) == 0
96 || strncmp("custom colors:", line, 14) == 0
97 || strncmp("scale:", line, 6) == 0
98 || strncmp("alpha:", line, 6) == 0
99 || strncmp("fadein/out:", line, 11) == 0
100 || strncmp("forced subs:", line, 12) == 0)
101 ParseExtra(state, line);
102 else
103 continue;
104 }
105
106 struct sorter s;
107 sort(m_Timestamps.begin(), m_Timestamps.end(), s);
108 m_Timestamp = m_Timestamps.begin();
109
110 for(unsigned i=0;i<m_Streams.size();i++)
111 {
112 m_Streams[i]->ExtraSize = state.extra.length()+1;
113 m_Streams[i]->ExtraData = new uint8_t[m_Streams[i]->ExtraSize];
114 strcpy((char*)m_Streams[i]->ExtraData, state.extra.c_str());
115 }
116
117 return true;
118 }
119
Reset()120 bool CDVDDemuxVobsub::Reset()
121 {
122 Flush();
123 return true;
124 }
125
Flush()126 void CDVDDemuxVobsub::Flush()
127 {
128 m_Demuxer->Flush();
129 }
130
SeekTime(double time,bool backwards,double * startpts)131 bool CDVDDemuxVobsub::SeekTime(double time, bool backwards, double* startpts)
132 {
133 double pts = DVD_MSEC_TO_TIME(time);
134 m_Timestamp = m_Timestamps.begin();
135 for (;m_Timestamp != m_Timestamps.end();++m_Timestamp)
136 {
137 if(m_Timestamp->pts > pts)
138 break;
139 }
140 for (unsigned i=0;i<m_Streams.size() && m_Timestamps.begin() != m_Timestamp;i++)
141 {
142 --m_Timestamp;
143 }
144 return true;
145 }
146
Read()147 DemuxPacket* CDVDDemuxVobsub::Read()
148 {
149 std::vector<STimestamp>::iterator current;
150 do {
151 if(m_Timestamp == m_Timestamps.end())
152 return NULL;
153
154 current = m_Timestamp++;
155 } while(m_Streams[current->id]->m_discard == true);
156
157 if(!m_Demuxer->SeekByte(current->pos))
158 return NULL;
159
160 DemuxPacket *packet = m_Demuxer->Read();
161 if(!packet)
162 return NULL;
163
164 packet->iStreamId = current->id;
165 packet->pts = current->pts;
166 packet->dts = current->pts;
167
168 return packet;
169 }
170
ParseLangIdx(SState & state,char * line)171 bool CDVDDemuxVobsub::ParseLangIdx(SState& state, char* line)
172 {
173 return true;
174 }
175
ParseDelay(SState & state,char * line)176 bool CDVDDemuxVobsub::ParseDelay(SState& state, char* line)
177 {
178 int h,m,s,ms;
179 bool negative = false;
180
181 while(*line == ' ') line++;
182 if(*line == '-')
183 {
184 line++;
185 negative = true;
186 }
187 if(sscanf(line, "%d:%d:%d:%d", &h, &m, &s, &ms) != 4)
188 return false;
189 state.delay = h*3600.0 + m*60.0 + s + ms*0.001;
190 if(negative)
191 state.delay *= -1;
192 return true;
193 }
194
ParseId(SState & state,char * line)195 bool CDVDDemuxVobsub::ParseId(SState& state, char* line)
196 {
197 std::unique_ptr<CStream> stream(new CStream(this));
198
199 while(*line == ' ') line++;
200 stream->language = std::string(line, 2);
201 line+=2;
202
203 while(*line == ' ' || *line == ',') line++;
204 if (strncmp("index:", line, 6) == 0)
205 {
206 line+=6;
207 while(*line == ' ') line++;
208 stream->uniqueId = atoi(line);
209 }
210 else
211 stream->uniqueId = -1;
212
213 stream->codec = AV_CODEC_ID_DVD_SUBTITLE;
214 stream->uniqueId = m_Streams.size();
215 stream->source = m_source;
216 stream->demuxerId = m_demuxerId;
217
218 state.id = stream->uniqueId;
219 m_Streams.push_back(stream.release());
220 return true;
221 }
222
ParseExtra(SState & state,char * line)223 bool CDVDDemuxVobsub::ParseExtra(SState& state, char* line)
224 {
225 state.extra += line;
226 state.extra += '\n';
227 return true;
228 }
229
ParseTimestamp(SState & state,char * line)230 bool CDVDDemuxVobsub::ParseTimestamp(SState& state, char* line)
231 {
232 if(state.id < 0)
233 return false;
234
235 int h,m,s,ms;
236 STimestamp timestamp;
237
238 while(*line == ' ') line++;
239 if(sscanf(line, "%d:%d:%d:%d, filepos:%" PRIx64, &h, &m, &s, &ms, ×tamp.pos) != 5)
240 return false;
241
242 timestamp.id = state.id;
243 timestamp.pts = DVD_SEC_TO_TIME(state.delay + h*3600.0 + m*60.0 + s + ms*0.001);
244 m_Timestamps.push_back(timestamp);
245 return true;
246 }
247
EnableStream(int id,bool enable)248 void CDVDDemuxVobsub::EnableStream(int id, bool enable)
249 {
250 for (auto &stream : m_Streams)
251 {
252 if (stream->uniqueId == id)
253 {
254 stream->m_discard = !enable;
255 break;
256 }
257 }
258 }
259