MythTV  master
PrePostRollFlagger.cpp
Go to the documentation of this file.
1 #include <chrono> // for milliseconds
2 #include <thread> // for sleep_for
3 
4 #include "PrePostRollFlagger.h"
5 
6 // Qt headers
7 #include <QCoreApplication>
8 
9 // MythTV headers
10 #include "mythcorecontext.h"
11 #include "programinfo.h"
12 #include "mythplayer.h"
13 
15  bool showProgress,bool fullSpeed,
16  MythPlayer* player,
17  const QDateTime& startedAt_in,
18  const QDateTime& stopsAt_in,
19  const QDateTime& recordingStartedAt_in,
20  const QDateTime& recordingStopsAt_in):
21  ClassicCommDetector( commDetectMethod, showProgress, fullSpeed,
22  player, startedAt_in, stopsAt_in,
23  recordingStartedAt_in, recordingStopsAt_in)
24 {
25 }
26 
28 {
30 }
31 
33 {
34  int secsSince = 0;
35  int requiredBuffer = 120;
36  int requiredHeadStart = requiredBuffer;
37  bool wereRecording = m_stillRecording;
38 
39  secsSince = m_startedAt.secsTo(MythDate::current());
40  while (m_stillRecording && (secsSince < requiredHeadStart))
41  {
42  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
43  "Waiting to pass preroll + head start"));
44 
45  emit breathe();
46  if (m_bStop)
47  return false;
48 
49  std::this_thread::sleep_for(std::chrono::seconds(5));
50  secsSince = m_startedAt.secsTo(MythDate::current());
51  }
52 
53  if (m_player->OpenFile() < 0)
54  return false;
55 
56  Init();
57 
58 
59  // Don't bother flagging short ~realtime recordings
60  if ((wereRecording) && (!m_stillRecording) && (secsSince < requiredHeadStart))
61  return false;
62 
64  gCoreContext->GetBoolSetting("AggressiveCommDetect", true);
65 
66  if (!m_player->InitVideo())
67  {
68  LOG(VB_GENERAL, LOG_ERR,
69  "NVP: Unable to initialize video for FlagCommercials.");
70  return false;
71  }
72  m_player->EnableSubtitles(false);
73 
74  emit breathe();
75  if (m_bStop)
76  return false;
77 
78  QTime flagTime;
79  flagTime.start();
80 
83  else
86 
87 
88 
89  if (m_showProgress)
90  {
91  if (m_myTotalFrames)
92  cerr << " 0%/ ";
93  else
94  cerr << " 0/ ";
95  cerr.flush();
96  }
97 
98  float aspect = m_player->GetVideoAspect();
99 
100  SetVideoParams(aspect);
101 
102  emit breathe();
103 
104  long long stopFrame = m_preRoll + m_fps * 120; //look up to 2 minutes past
105  long long framesToProcess = 0;
106  if(m_preRoll)
107  framesToProcess += stopFrame;
108  if(m_postRoll)
109  //guess two minutes before
110  framesToProcess += m_myTotalFrames - m_postRoll + m_fps * 120;
111 
112 
113  long long framesProcessed = 0;
114  if(m_preRoll > 0)
115  {
116  //check from preroll after
117  LOG(VB_COMMFLAG, LOG_INFO,
118  QString("Finding closest after preroll(%1-%2)")
119  .arg(m_preRoll).arg(stopFrame));
120 
121  m_closestAfterPre = findBreakInrange(m_preRoll, stopFrame, framesToProcess,
122  framesProcessed, flagTime, false);
123 
124  LOG(VB_COMMFLAG, LOG_INFO, QString("Closest after preroll: %1")
125  .arg(m_closestAfterPre));
126 
127 
128  //check before preroll
129  long long startFrame = 0;
131  startFrame = m_preRoll - (m_closestAfterPre - m_preRoll) - 1;
132 
133  LOG(VB_COMMFLAG, LOG_INFO, QString("Finding before preroll (%1-%2)")
134  .arg(startFrame).arg(m_preRoll));
136  framesToProcess, framesProcessed,
137  flagTime, true);
138  LOG(VB_COMMFLAG, LOG_INFO, QString("Closest before preroll: %1")
139  .arg(m_closestBeforePre));
140 
143 
144  // for better processing percent
145  framesToProcess -= (stopFrame - framesProcessed);
146 
147  }
148 
149  if(m_stillRecording)
150  {
152  {
153  emit breathe();
154  if (m_bStop)
155  return false;
156  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
157  "Waiting for recording to finish"));
158  std::this_thread::sleep_for(std::chrono::seconds(5));
159  }
160  m_stillRecording = false;
162  }
163 
164  if(m_postRoll > 0)
165  {
166  //check from preroll after
167  long long postRollStartLoc = m_myTotalFrames - m_postRoll;
168  LOG(VB_COMMFLAG, LOG_INFO,
169  QString("Finding closest after postroll(%1-%2)")
170  .arg(postRollStartLoc).arg(m_myTotalFrames));
172  framesToProcess, framesProcessed,
173  flagTime, false);
174  LOG(VB_COMMFLAG, LOG_INFO, QString("Closest after postRoll: %1")
175  .arg(m_closestAfterPost));
176 
177  //check before preroll
178  long long startFrame = 0;
180  startFrame = postRollStartLoc
181  - (m_closestAfterPost - postRollStartLoc) - 1;
182 
183  LOG(VB_COMMFLAG, LOG_INFO,
184  QString("finding closest before preroll(%1-%2)")
185  .arg(startFrame).arg(postRollStartLoc));
186  m_closestBeforePost = findBreakInrange(startFrame, postRollStartLoc,
187  framesToProcess, framesProcessed,
188  flagTime, true);
189  LOG(VB_COMMFLAG, LOG_INFO, QString("Closest before postroll: %1")
190  .arg(m_closestBeforePost));
191 
192  // cppcheck-suppress unreadVariable
193  framesToProcess = framesProcessed;
194  }
195 
196  if (m_showProgress)
197  {
198  //float elapsed = flagTime.elapsed() / 1000.0;
199 
200  //float flagFPS = (elapsed > 0.0F) ? (framesProcessed / elapsed) : 0.0F;
201 
202  if (m_myTotalFrames)
203  cerr << "\b\b\b\b\b\b \b\b\b\b\b\b";
204  else
205  cerr << "\b\b\b\b\b\b\b\b\b\b\b\b\b "
206  "\b\b\b\b\b\b\b\b\b\b\b\b\b";
207  cerr.flush();
208  }
209 
210  return true;
211 }
212 
213 
214 long long PrePostRollFlagger::findBreakInrange(long long startFrame,
215  long long stopFrame,
216  long long totalFrames,
217  long long &framesProcessed,
218  QTime &flagTime, bool findLast)
219 {
220  int requiredBuffer = 30;
221  int prevpercent = -1;
222 
223  if(startFrame > 0)
224  startFrame--;
225  else
226  startFrame = 0;
227 
229 
230  long long tmpStartFrame = startFrame;
231  VideoFrame* f = m_player->GetRawVideoFrame(tmpStartFrame);
232  float aspect = m_player->GetVideoAspect();
233  long long currentFrameNumber = f->frameNumber;
234  LOG(VB_COMMFLAG, LOG_INFO, QString("Starting with frame %1")
235  .arg(currentFrameNumber));
237 
238  long long foundFrame = 0;
239 
240  while (m_player->GetEof() == kEofStateNone)
241  {
242  struct timeval startTime {};
243  if (m_stillRecording)
244  gettimeofday(&startTime, nullptr);
245 
246  VideoFrame* currentFrame = m_player->GetRawVideoFrame();
247  currentFrameNumber = currentFrame->frameNumber;
248 
249  if(currentFrameNumber % 1000 == 0)
250  {
251  LOG(VB_COMMFLAG, LOG_INFO, QString("Processing frame %1")
252  .arg(currentFrameNumber));
253  }
254 
255  if(currentFrameNumber > stopFrame || (!findLast && foundFrame))
256  {
257  m_player->DiscardVideoFrame(currentFrame);
258  break;
259  }
260 
261  float newAspect = currentFrame->aspect;
262  if (newAspect != aspect)
263  {
264  SetVideoParams(aspect);
265  aspect = newAspect;
266  }
267 
268  if (((currentFrameNumber % 500) == 0) ||
269  (((currentFrameNumber % 100) == 0) &&
270  (m_stillRecording)))
271  {
272  emit breathe();
273  if (m_bStop)
274  {
275  m_player->DiscardVideoFrame(currentFrame);
276  return 0;
277  }
278  }
279 
280  while (m_bPaused)
281  {
282  emit breathe();
283  std::this_thread::sleep_for(std::chrono::seconds(1));
284  }
285 
286  // sleep a little so we don't use all cpu even if we're niced
287  if (!m_fullSpeed && !m_stillRecording)
288  std::this_thread::sleep_for(std::chrono::milliseconds(10));
289 
290  if (((currentFrameNumber % 500) == 0) ||
292  ((currentFrameNumber % 100) == 0)))
293  {
294  float elapsed = flagTime.elapsed() / 1000.0;
295 
296  float flagFPS = 0.0F;
297  if (elapsed)
298  flagFPS = framesProcessed / elapsed;
299 
300  int percentage = 0;
301  if (stopFrame)
302  percentage = framesProcessed * 100 / totalFrames;
303  if (percentage > 100)
304  percentage = 100;
305 
306  if (m_showProgress)
307  {
308  if (stopFrame)
309  {
310  QString tmp = QString("\b\b\b\b\b\b\b\b\b\b\b%1%/%2fps")
311  .arg(percentage, 3).arg((int)flagFPS, 3);
312  QByteArray ba = tmp.toLatin1();
313  cerr << ba.constData() << flush;
314  }
315  else
316  {
317  QString tmp = QString("\b\b\b\b\b\b\b\b\b\b\b\b\b%1/%2fps")
318  .arg(currentFrameNumber, 6).arg((int)flagFPS, 3);
319  QByteArray ba = tmp.toLatin1();
320  cerr << ba.constData() << flush;
321  }
322  cerr.flush();
323  }
324 
325  if (stopFrame)
326  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
327  "%1% Completed @ %2 fps.")
328  .arg(percentage).arg(flagFPS));
329  else
330  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
331  "%1 Frames Completed @ %2 fps.")
332  .arg((long)currentFrameNumber).arg(flagFPS));
333 
334  if (percentage % 10 == 0 && prevpercent != percentage)
335  {
336  prevpercent = percentage;
337  LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.")
338  .arg(percentage) .arg(flagFPS));
339  }
340  }
341 
342  ProcessFrame(currentFrame, currentFrameNumber);
343 
344  if(frameInfo[currentFrameNumber].flagMask &
346  {
347  foundFrame = currentFrameNumber;
348  }
349 
350  if (m_stillRecording)
351  {
352  int secondsRecorded =
354  int secondsFlagged = (int)(framesProcessed / m_fps);
355  int secondsBehind = secondsRecorded - secondsFlagged;
356  long usecPerFrame = (long)(1.0F / m_player->GetFrameRate() * 1000000);
357 
358  struct timeval endTime {};
359  gettimeofday(&endTime, nullptr);
360 
361  long long usecSleep =
362  usecPerFrame -
363  (((endTime.tv_sec - startTime.tv_sec) * 1000000) +
364  (endTime.tv_usec - startTime.tv_usec));
365 
366  if (secondsBehind > requiredBuffer)
367  {
368  if (m_fullSpeed)
369  usecSleep = 0;
370  else
371  usecSleep = (long)(usecSleep * 0.25);
372  }
373  else if (secondsBehind < requiredBuffer)
374  usecSleep = (long)(usecPerFrame * 1.5);
375 
376  if (usecSleep > 0)
377  std::this_thread::sleep_for(std::chrono::microseconds(usecSleep));
378  }
379 
380  m_player->DiscardVideoFrame(currentFrame);
381  framesProcessed++;
382  }
383  return foundFrame;
384 }
385 
386 
388 {
389  LOG(VB_COMMFLAG, LOG_INFO, "PrePostRollFlagger::GetCommBreakMap()");
390  marks.clear();
391 
392  long long end = 0;
394  {
395  //choose closest
397  end = m_closestAfterPre;
398  else
399  end = m_closestBeforePre;
400  }
401  else if(m_closestBeforePre)
402  end = m_closestBeforePre;
403  else if(m_closestAfterPre)
404  end = m_closestAfterPre;
405  else
406  end = m_preRoll;
407 
408  if(end)
409  {
410  marks[0] = MARK_COMM_START;
411  marks[end] = MARK_COMM_END;
412  }
413 
414  long long start = 0;
416  {
417  //choose closest
419  start = m_closestAfterPost;
420  else
421  start = m_closestBeforePost;
422  }
423  else if(m_closestBeforePost)
424  start = m_closestBeforePost;
425  else if(m_closestAfterPost)
426  start = m_closestAfterPost;
427  else if(m_postRoll)
428  start = m_myTotalFrames - m_postRoll;
429 
430  if(start)
431  {
432  marks[start] = MARK_COMM_START;
434  }
435 }
float GetVideoAspect(void) const
Definition: mythplayer.h:175
PrePostRollFlagger(SkipType commDetectMethod, bool showProgress, bool fullSpeed, MythPlayer *player, const QDateTime &startedAt_in, const QDateTime &stopsAt_in, const QDateTime &recordingStartedAt_in, const QDateTime &recordingStopsAt_in)
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:81
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static guint32 * tmp
Definition: goom_core.c:35
long long findBreakInrange(long long start, long long stopFrame, long long totalFrames, long long &framesProcessed, QTime &flagTime, bool findLast)
virtual int OpenFile(uint retries=4)
Definition: mythplayer.cpp:829
float aspect
Definition: mythframe.h:43
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
VideoFrame * GetRawVideoFrame(long long frameNumber=-1)
Returns a specific frame from the video.
float GetFrameRate(void) const
Definition: mythplayer.h:176
EofState GetEof(void) const
QMap< long long, FrameInfoEntry > frameInfo
long long frameNumber
Definition: mythframe.h:48
void ProcessFrame(VideoFrame *frame, long long frame_number)
static struct mark marks[16]
void GetCommercialBreakList(frm_dir_map_t &marks) override
void statusUpdate(const QString &a)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void gotNewCommercialBreakList()
bool GetBoolSetting(const QString &key, bool defaultval=false)
void DiscardVideoFrame(VideoFrame *buffer)
Places frame in the available frames queue.
void EnableSubtitles(bool enable)
uint64_t GetTotalFrameCount(void) const
Definition: mythplayer.h:194
bool InitVideo(void)
Definition: mythplayer.cpp:393
void SetVideoParams(float aspect)
enum SkipTypes SkipType
This is used as a bitmask.