[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / cores / VideoPlayer / PTSTracker.cpp
blobc5fdbf1a769bbb566d5cef49d645b4eeb931d2ab
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "PTSTracker.h"
11 #include "DVDCodecs/DVDCodecUtils.h"
12 #include "cores/VideoPlayer/Interface/TimingConstants.h"
13 #include "utils/StringUtils.h"
14 #include "utils/log.h"
16 #include <algorithm>
17 #include <cmath>
19 #define MAXERR DVD_MSEC_TO_TIME(2.5)
21 CPtsTracker::CPtsTracker()
23 ResetVFRDetection();
24 Flush();
27 void CPtsTracker::ResetVFRDetection(void)
29 m_minframeduration = DVD_NOPTS_VALUE;
30 m_maxframeduration = DVD_NOPTS_VALUE;
31 m_VFRCounter = 0;
32 m_patternCounter = 0;
33 m_lastPattern.clear();
36 void CPtsTracker::Flush()
38 m_pattern.clear();
39 m_ringpos = 0;
40 m_prevpts = DVD_NOPTS_VALUE;
41 m_ringfill = 0;
42 m_haspattern = false;
43 m_patternlength = 0;
44 m_frameduration = DVD_NOPTS_VALUE;
45 memset(m_diffring, 0, sizeof(m_diffring));
48 void CPtsTracker::Add(double pts)
50 //can't get a diff with just one pts
51 if (m_prevpts == DVD_NOPTS_VALUE)
53 m_prevpts = pts;
54 return;
57 //increase the ringbuffer position
58 m_ringpos = (m_ringpos + 1) % DIFFRINGSIZE;
59 //add the current diff to the ringbuffer
60 m_diffring[m_ringpos] = pts - m_prevpts;
61 //save the pts
62 m_prevpts = pts;
64 if (m_ringfill < DIFFRINGSIZE)
65 m_ringfill++;
67 //only search for patterns if we have full ringbuffer
68 if (m_ringfill < DIFFRINGSIZE)
69 return;
71 //get the current pattern in the ringbuffer
72 std::vector<double> pattern;
73 GetPattern(pattern);
75 //check if the pattern is the same as the saved pattern
76 //and if it is actually a pattern
77 if (!CheckPattern(pattern))
79 if (m_haspattern)
81 m_VFRCounter++;
82 m_lastPattern = m_pattern;
83 CLog::Log(LOGDEBUG, "CPtsTracker: pattern lost on diff {:f}, number of losses {}", GetDiff(0),
84 m_VFRCounter);
85 Flush();
88 //no pattern detected or current pattern broke/changed
89 //save detected pattern so we can check it with the next iteration
90 m_pattern = pattern;
92 return;
94 else
96 if (!m_haspattern)
98 m_haspattern = true;
99 m_patternlength = m_pattern.size();
101 if (!m_lastPattern.empty() && !CheckPattern(m_lastPattern))
103 m_patternCounter++;
106 double frameduration = CalcFrameDuration();
107 CLog::Log(LOGDEBUG, "CPtsTracker: detected pattern of length {}: {}, frameduration: {:f}",
108 (int)pattern.size(), GetPatternStr(), frameduration);
112 m_frameduration = CalcFrameDuration();
115 //gets a diff diffnr into the past
116 inline double CPtsTracker::GetDiff(int diffnr)
118 //m_ringpos is the last added diff, so if we want to go in the past we have to move back in the ringbuffer
119 int pos = m_ringpos - diffnr;
120 if (pos < 0)
121 pos += DIFFRINGSIZE;
123 return m_diffring[pos];
126 //calculate the current pattern in the ringbuffer
127 void CPtsTracker::GetPattern(std::vector<double>& pattern)
129 int difftypesbuff[DIFFRINGSIZE]; //difftypes of the diffs, difftypesbuff[0] is the last added diff,
130 //difftypesbuff[1] the one added before that etc
132 //get the difftypes
133 std::vector<double> difftypes;
134 for (int i = 0; i < m_ringfill; i++)
136 bool hasmatch = false;
137 for (unsigned int j = 0; j < difftypes.size(); j++)
139 if (MatchDiff(GetDiff(i), difftypes[j]))
141 hasmatch = true;
142 break;
146 //if we don't have a match with a saved difftype, we add it as a new one
147 if (!hasmatch)
148 difftypes.push_back(GetDiff(i));
151 //mark each diff with what difftype it is
152 for (int i = 0; i < m_ringfill; i++)
154 for (unsigned int j = 0; j < difftypes.size(); j++)
156 if (MatchDiff(GetDiff(i), difftypes[j]))
158 difftypesbuff[i] = j;
159 break;
164 bool checkexisting = !m_pattern.empty();
166 //we check for patterns to the length of DIFFRINGSIZE / 2
167 for (int i = 1; i <= m_ringfill / 2; i++)
169 //check the existing pattern length first
170 int length = checkexisting ? m_pattern.size() : i;
172 bool hasmatch = true;
173 for (int j = 1; j <= m_ringfill / length; j++)
175 int nrdiffs = length;
176 //we want to check the full buffer to see if the pattern repeats
177 //but we can't go beyond the buffer
178 if (j * length + length > m_ringfill)
179 nrdiffs = m_ringfill - j * length;
181 if (nrdiffs < 1) //if the buffersize can be cleanly divided by i we're done here
182 break;
184 if (!MatchDifftype(difftypesbuff, difftypesbuff + j * length, nrdiffs))
186 hasmatch = false;
187 break;
191 if (checkexisting)
193 checkexisting = false;
194 i--;
197 if (hasmatch)
199 for (int i = 0; i < length; i++)
201 double avgdiff = 0.0;
202 for (int j = 0; j < m_ringfill / length; j++)
203 avgdiff += GetDiff(j * length + i);
205 avgdiff /= m_ringfill / length;
206 pattern.push_back(avgdiff);
208 break;
211 std::sort(pattern.begin(), pattern.end());
214 inline bool CPtsTracker::MatchDiff(double diff1, double diff2)
216 return fabs(diff1 - diff2) < MAXERR;
219 //check if diffs1 is the same as diffs2
220 inline bool CPtsTracker::MatchDifftype(int diffs1[], int diffs2[], int nrdiffs)
222 for (int i = 0; i < nrdiffs; i++)
224 if (diffs1[i] != diffs2[i])
225 return false;
227 return true;
230 //check if our current detected pattern is the same as the one we saved
231 bool CPtsTracker::CheckPattern(std::vector<double>& pattern)
233 //if no pattern was detected or if the size of the patterns differ we don't have a match
234 if (pattern.empty() || pattern.size() != m_pattern.size())
235 return false;
237 if (pattern.size() == 1)
239 if (pattern[0] < MAXERR)
240 return false; //all diffs are too close to 0, can't use this
243 //check if the current pattern matches the saved pattern, with an offset of 1
244 for (unsigned int i = 0; i < m_pattern.size(); i++)
246 double diff = pattern[i];
248 if (!MatchDiff(diff, m_pattern[i]))
249 return false;
252 return true;
255 //calculate how long each frame should last from the saved pattern
256 //also retrieve information of max and min frame rate duration, for VFR files case
257 double CPtsTracker::CalcFrameDuration()
259 if (!m_pattern.empty())
261 //take the average of all diffs in the pattern
262 double frameduration;
263 double current, currentmin, currentmax;
265 currentmin = m_pattern[0];
266 currentmax = currentmin;
267 frameduration = currentmin;
268 for (unsigned int i = 1; i < m_pattern.size(); i++)
270 current = m_pattern[i];
271 if (current>currentmax)
272 currentmax = current;
273 if (current<currentmin)
274 currentmin = current;
275 frameduration += current;
277 frameduration /= m_pattern.size();
279 // Update min and max frame duration, only if data is valid
280 bool standard = false;
281 double tempduration = CDVDCodecUtils::NormalizeFrameduration(currentmin, &standard);
282 if (m_minframeduration == DVD_NOPTS_VALUE)
284 if (standard)
285 m_minframeduration = tempduration;
287 else
289 if (standard && (tempduration < m_minframeduration))
290 m_minframeduration = tempduration;
293 tempduration = CDVDCodecUtils::NormalizeFrameduration(currentmax, &standard);
294 if (m_maxframeduration == DVD_NOPTS_VALUE)
296 if (standard)
297 m_maxframeduration = tempduration;
299 else
301 if (standard && (tempduration > m_maxframeduration))
302 m_maxframeduration = tempduration;
305 //frameduration is not completely correct, use a common one if it's close
306 return CDVDCodecUtils::NormalizeFrameduration(frameduration);
309 return DVD_NOPTS_VALUE;
312 //looks pretty in the log
313 std::string CPtsTracker::GetPatternStr()
315 std::string patternstr;
317 for (unsigned int i = 0; i < m_pattern.size(); i++)
318 patternstr += StringUtils::Format("{:.2f} ", m_pattern[i]);
320 StringUtils::Trim(patternstr);
322 return patternstr;