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
84  m_myTotalFrames = (long long)(m_player->GetFrameRate() *
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  float flagFPS;
221  int requiredBuffer = 30;
222  long long currentFrameNumber;
223  int prevpercent = -1;
224 
225  if(startFrame > 0)
226  startFrame--;
227  else
228  startFrame = 0;
229 
231 
232  long long tmpStartFrame = startFrame;
233  VideoFrame* f = m_player->GetRawVideoFrame(tmpStartFrame);
234  float aspect = m_player->GetVideoAspect();
235  currentFrameNumber = f->frameNumber;
236  LOG(VB_COMMFLAG, LOG_INFO, QString("Starting with frame %1")
237  .arg(currentFrameNumber));
239 
240  long long foundFrame = 0;
241 
242  while (m_player->GetEof() == kEofStateNone)
243  {
244  struct timeval startTime;
245  if (m_stillRecording)
246  gettimeofday(&startTime, nullptr);
247 
248  VideoFrame* currentFrame = m_player->GetRawVideoFrame();
249  currentFrameNumber = currentFrame->frameNumber;
250 
251  if(currentFrameNumber % 1000 == 0)
252  {
253  LOG(VB_COMMFLAG, LOG_INFO, QString("Processing frame %1")
254  .arg(currentFrameNumber));
255  }
256 
257  if(currentFrameNumber > stopFrame || (!findLast && foundFrame))
258  {
259  m_player->DiscardVideoFrame(currentFrame);
260  break;
261  }
262 
263  float newAspect = currentFrame->aspect;
264  if (newAspect != aspect)
265  {
266  SetVideoParams(aspect);
267  aspect = newAspect;
268  }
269 
270  if (((currentFrameNumber % 500) == 0) ||
271  (((currentFrameNumber % 100) == 0) &&
272  (m_stillRecording)))
273  {
274  emit breathe();
275  if (m_bStop)
276  {
277  m_player->DiscardVideoFrame(currentFrame);
278  return 0;
279  }
280  }
281 
282  while (m_bPaused)
283  {
284  emit breathe();
285  std::this_thread::sleep_for(std::chrono::seconds(1));
286  }
287 
288  // sleep a little so we don't use all cpu even if we're niced
289  if (!m_fullSpeed && !m_stillRecording)
290  std::this_thread::sleep_for(std::chrono::milliseconds(10));
291 
292  if (((currentFrameNumber % 500) == 0) ||
294  ((currentFrameNumber % 100) == 0)))
295  {
296  float elapsed = flagTime.elapsed() / 1000.0;
297 
298  if (elapsed)
299  flagFPS = framesProcessed / elapsed;
300  else
301  flagFPS = 0.0;
302 
303  int percentage;
304  if (stopFrame)
305  percentage = framesProcessed * 100 / totalFrames;
306  else
307  percentage = 0;
308 
309  if (percentage > 100)
310  percentage = 100;
311 
312  if (m_showProgress)
313  {
314  if (stopFrame)
315  {
316  QString tmp = QString("\b\b\b\b\b\b\b\b\b\b\b%1%/%2fps")
317  .arg(percentage, 3).arg((int)flagFPS, 3);
318  QByteArray ba = tmp.toLatin1();
319  cerr << ba.constData() << flush;
320  }
321  else
322  {
323  QString tmp = QString("\b\b\b\b\b\b\b\b\b\b\b\b\b%1/%2fps")
324  .arg(currentFrameNumber, 6).arg((int)flagFPS, 3);
325  QByteArray ba = tmp.toLatin1();
326  cerr << ba.constData() << flush;
327  }
328  cerr.flush();
329  }
330 
331  if (stopFrame)
332  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
333  "%1% Completed @ %2 fps.")
334  .arg(percentage).arg(flagFPS));
335  else
336  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
337  "%1 Frames Completed @ %2 fps.")
338  .arg((long)currentFrameNumber).arg(flagFPS));
339 
340  if (percentage % 10 == 0 && prevpercent != percentage)
341  {
342  prevpercent = percentage;
343  LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.")
344  .arg(percentage) .arg(flagFPS));
345  }
346  }
347 
348  ProcessFrame(currentFrame, currentFrameNumber);
349 
350  if(frameInfo[currentFrameNumber].flagMask &
352  {
353  foundFrame = currentFrameNumber;
354  }
355 
356  if (m_stillRecording)
357  {
358  int secondsRecorded =
360  int secondsFlagged = (int)(framesProcessed / m_fps);
361  int secondsBehind = secondsRecorded - secondsFlagged;
362  long usecPerFrame = (long)(1.0F / m_player->GetFrameRate() * 1000000);
363 
364  struct timeval endTime;
365  gettimeofday(&endTime, nullptr);
366 
367  long long usecSleep =
368  usecPerFrame -
369  (((endTime.tv_sec - startTime.tv_sec) * 1000000) +
370  (endTime.tv_usec - startTime.tv_usec));
371 
372  if (secondsBehind > requiredBuffer)
373  {
374  if (m_fullSpeed)
375  usecSleep = 0;
376  else
377  usecSleep = (long)(usecSleep * 0.25);
378  }
379  else if (secondsBehind < requiredBuffer)
380  usecSleep = (long)(usecPerFrame * 1.5);
381 
382  if (usecSleep > 0)
383  std::this_thread::sleep_for(std::chrono::microseconds(usecSleep));
384  }
385 
386  m_player->DiscardVideoFrame(currentFrame);
387  framesProcessed++;
388  }
389  return foundFrame;
390 }
391 
392 
394 {
395  LOG(VB_COMMFLAG, LOG_INFO, "PrePostRollFlagger::GetCommBreakMap()");
396  marks.clear();
397 
398  long long end = 0;
400  {
401  //choose closest
403  end = m_closestAfterPre;
404  else
405  end = m_closestBeforePre;
406  }
407  else if(m_closestBeforePre)
408  end = m_closestBeforePre;
409  else if(m_closestAfterPre)
410  end = m_closestAfterPre;
411  else
412  end = m_preRoll;
413 
414  if(end)
415  {
416  marks[0] = MARK_COMM_START;
417  marks[end] = MARK_COMM_END;
418  }
419 
420  long long start = 0;
422  {
423  //choose closest
425  start = m_closestAfterPost;
426  else
427  start = m_closestBeforePost;
428  }
429  else if(m_closestBeforePost)
430  start = m_closestBeforePost;
431  else if(m_closestAfterPost)
432  start = m_closestAfterPost;
433  else if(m_postRoll)
434  start = m_myTotalFrames - m_postRoll;
435 
436  if(start)
437  {
438  marks[start] = MARK_COMM_START;
440  }
441 }
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:933
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:497
void SetVideoParams(float aspect)
enum SkipTypes SkipType
This is used as a bitmask.