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.
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"
19 #define MAXERR DVD_MSEC_TO_TIME(2.5)
21 CPtsTracker::CPtsTracker()
27 void CPtsTracker::ResetVFRDetection(void)
29 m_minframeduration
= DVD_NOPTS_VALUE
;
30 m_maxframeduration
= DVD_NOPTS_VALUE
;
33 m_lastPattern
.clear();
36 void CPtsTracker::Flush()
40 m_prevpts
= DVD_NOPTS_VALUE
;
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
)
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
;
64 if (m_ringfill
< DIFFRINGSIZE
)
67 //only search for patterns if we have full ringbuffer
68 if (m_ringfill
< DIFFRINGSIZE
)
71 //get the current pattern in the ringbuffer
72 std::vector
<double> 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
))
82 m_lastPattern
= m_pattern
;
83 CLog::Log(LOGDEBUG
, "CPtsTracker: pattern lost on diff {:f}, number of losses {}", GetDiff(0),
88 //no pattern detected or current pattern broke/changed
89 //save detected pattern so we can check it with the next iteration
99 m_patternlength
= m_pattern
.size();
101 if (!m_lastPattern
.empty() && !CheckPattern(m_lastPattern
))
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
;
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
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
]))
146 //if we don't have a match with a saved difftype, we add it as a new one
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
;
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
184 if (!MatchDifftype(difftypesbuff
, difftypesbuff
+ j
* length
, nrdiffs
))
193 checkexisting
= false;
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
);
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
])
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())
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
]))
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
)
285 m_minframeduration
= tempduration
;
289 if (standard
&& (tempduration
< m_minframeduration
))
290 m_minframeduration
= tempduration
;
293 tempduration
= CDVDCodecUtils::NormalizeFrameduration(currentmax
, &standard
);
294 if (m_maxframeduration
== DVD_NOPTS_VALUE
)
297 m_maxframeduration
= tempduration
;
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
);