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, &timestamp.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