MythTV  master
thumbfinder.cpp
Go to the documentation of this file.
1 /* -*- Mode: c++ -*-
2  * vim: set expandtab tabstop=4 shiftwidth=4:
3  *
4  * Original Project
5  * MythTV http://www.mythtv.org
6  *
7  * Copyright (c) 2004, 2005 John Pullan <john@pullan.org>
8  * Copyright (c) 2009, Janne Grunau <janne-mythtv@grunau.be>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
24  *
25  */
26 
27 // c++
28 #include <cerrno>
29 #include <cmath>
30 #include <cstdlib>
31 #include <iostream>
32 #include <sys/stat.h>
33 
34 // qt
35 #include <QApplication>
36 #include <QDir>
37 #include <QDomDocument>
38 #include <QFile>
39 #include <QPainter>
40 
41 // myth
42 #include <mythconfig.h>
43 #include <libmyth/mythcontext.h>
44 #include <libmythbase/mythdate.h>
45 #include <libmythbase/mythdbcon.h>
46 #include <libmythbase/mythdirs.h>
47 #include <libmythbase/mythmiscutil.h> // for MythFile::copy
50 #include <libmythui/mythimage.h>
52 #include <libmythui/mythuibutton.h>
54 #include <libmythui/mythuihelper.h>
55 #include <libmythui/mythuiimage.h>
56 #include <libmythui/mythuitext.h>
57 
58 extern "C" {
59  #include <libavutil/imgutils.h>
60 }
61 
62 #ifndef INT64_C // Used in FFmpeg headers to define some constants
63 #define INT64_C(v) (v ## LL)
64 #endif
65 
66 // mytharchive
67 #include "thumbfinder.h"
68 
69 // the amount to seek before the required frame
70 static constexpr int8_t PRE_SEEK_AMOUNT { 50 };
71 
72 static const std::array<const SeekAmount,9> kSeekAmounts
73 {{
74  {"frame", -1},
75  {"1 second", 1},
76  {"5 seconds", 5},
77  {"10 seconds", 10},
78  {"30 seconds", 30},
79  {"1 minute", 60},
80  {"5 minutes", 300},
81  {"10 minutes", 600},
82  {"Cut Point", -2},
83 }};
84 
86  const QString &menuTheme)
87  :MythScreenType(parent, "ThumbFinder"),
88  m_archiveItem(archiveItem),
89  m_thumbCount(getChapterCount(menuTheme)),
90  m_thumbDir(createThumbDir())
91 {
92  // copy thumbList so we can abandon changes if required
93  m_thumbList.clear();
94  for (const auto *item : qAsConst(m_archiveItem->thumbList))
95  {
96  auto *thumb = new ThumbImage;
97  *thumb = *item;
98  m_thumbList.append(thumb);
99  }
100 }
101 
103 {
104  getThumbImages();
105 }
106 
108 {
109  while (!m_thumbList.isEmpty())
110  delete m_thumbList.takeFirst();
111  m_thumbList.clear();
112 
113  closeAVCodec();
114 }
115 
117 {
118  // Load the theme for this screen
119  bool foundtheme = LoadWindowFromXML("mythburn-ui.xml", "thumbfinder", this);
120  if (!foundtheme)
121  return false;
122 
123  bool err = false;
124  UIUtilE::Assign(this, m_frameImage, "frameimage", &err);
125  UIUtilE::Assign(this, m_positionImage, "positionimage", &err);
126  UIUtilE::Assign(this, m_imageGrid, "thumblist", &err);
127  UIUtilE::Assign(this, m_saveButton, "save_button", &err);
128  UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
129  UIUtilE::Assign(this, m_frameButton, "frame_button", &err);
130  UIUtilE::Assign(this, m_seekAmountText, "seekamount", &err);
131  UIUtilE::Assign(this, m_currentPosText, "currentpos", &err);
132 
133  if (err)
134  {
135  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'mythburn'");
136  return false;
137  }
138 
141 
144 
146 
148 
149  BuildFocusList();
150 
152 
153  return true;
154 }
155 
156 bool ThumbFinder::keyPressEvent(QKeyEvent *event)
157 {
158  if (GetFocusWidget()->keyPressEvent(event))
159  return true;
160 
161  QStringList actions;
162  bool handled = GetMythMainWindow()->TranslateKeyPress("Archive", event, actions);
163 
164  for (int i = 0; i < actions.size() && !handled; i++)
165  {
166  QString action = actions[i];
167  handled = true;
168 
169  if (action == "MENU")
170  {
171  NextPrevWidgetFocus(true);
172  return true;
173  }
174 
175  if (action == "ESCAPE")
176  {
177  ShowMenu();
178  return true;
179  }
180 
181  if (action == "0" || action == "1" || action == "2" || action == "3" ||
182  action == "4" || action == "5" || action == "6" || action == "7" ||
183  action == "8" || action == "9")
184  {
186  int itemNo = m_imageGrid->GetCurrentPos();
187  ThumbImage *thumb = m_thumbList.at(itemNo);
188  if (thumb)
189  seekToFrame(thumb->frame);
190  return true;
191  }
192 
193  if (GetFocusWidget() == m_frameButton)
194  {
195  if (action == "UP")
196  {
197  changeSeekAmount(true);
198  }
199  else if (action == "DOWN")
200  {
201  changeSeekAmount(false);
202  }
203  else if (action == "LEFT")
204  {
205  seekBackward();
206  }
207  else if (action == "RIGHT")
208  {
209  seekForward();
210  }
211  else if (action == "SELECT")
212  {
213  updateThumb();
214  }
215  else
216  handled = false;
217  }
218  else
219  handled = false;
220  }
221 
222  if (!handled && MythScreenType::keyPressEvent(event))
223  handled = true;
224 
225  return handled;
226 }
227 
228 int ThumbFinder::getChapterCount(const QString &menuTheme)
229 {
230  QString filename = GetShareDir() + "mytharchive/themes/" +
231  menuTheme + "/theme.xml";
232  QDomDocument doc("mydocument");
233  QFile file(filename);
234 
235  if (!file.open(QIODevice::ReadOnly))
236  {
237  LOG(VB_GENERAL, LOG_ERR, "Failed to open theme file: " + filename);
238  return 0; //??
239  }
240  if (!doc.setContent(&file))
241  {
242  file.close();
243  LOG(VB_GENERAL, LOG_ERR, "Failed to parse theme file: " + filename);
244  return 0;
245  }
246  file.close();
247 
248  QDomNodeList chapterNodeList = doc.elementsByTagName("chapter");
249 
250  return chapterNodeList.count();
251 }
252 
254 {
256 
257  if (progInfo && m_archiveItem->hasCutlist)
258  progInfo->QueryCutList(m_deleteMap);
259  delete progInfo;
260 
261  if (m_deleteMap.isEmpty())
262  {
263  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder::loadCutList: Got an empty delete map");
264  return;
265  }
266 
267  // if the first mark is a end mark then add the start mark at the beginning
268  frm_dir_map_t::const_iterator it = m_deleteMap.constBegin();
269  if (it.value() == MARK_CUT_END)
270  m_deleteMap.insert(0, MARK_CUT_START);
271 
272 
273  // if the last mark is a start mark then add the end mark at the end
274  it = m_deleteMap.constEnd();
275  --it;
276  if (it != m_deleteMap.constEnd())
277  {
278  if (it.value() == MARK_CUT_START)
280  }
281 }
282 
284 {
285  // copy the thumb details to the archiveItem
286  while (!m_archiveItem->thumbList.isEmpty())
287  delete m_archiveItem->thumbList.takeFirst();
288  m_archiveItem->thumbList.clear();
289 
290  for (const auto *item : qAsConst(m_thumbList))
291  {
292  auto *thumb = new ThumbImage;
293  *thumb = *item;
294  m_archiveItem->thumbList.append(thumb);
295  }
296 
297  Close();
298 }
299 
301 {
302  Close();
303 }
304 
306 {
307  int64_t pos = m_currentPTS - m_firstIFramePTS;
308  int64_t frame = pos / m_frameTime;
309 
310  if (m_currentPosText)
311  m_currentPosText->SetText(frameToTime(frame, true));
312 
313  updatePositionBar(frame);
314 }
315 
317 {
318  if (up)
319  {
320  m_currentSeek++;
321  if (m_currentSeek >= kSeekAmounts.size())
322  m_currentSeek = 0;
323  }
324  else
325  {
326  if (m_currentSeek == 0)
327  m_currentSeek = kSeekAmounts.size();
328  m_currentSeek--;
329  }
330 
332 }
333 
335 {
336  (void) item;
337 
338  int itemNo = m_imageGrid->GetCurrentPos();
339  ThumbImage *thumb = m_thumbList.at(itemNo);
340  if (thumb)
341  seekToFrame(thumb->frame);
342 }
343 
345 {
346  QString thumbDir = getTempDirectory() + "config/thumbs";
347 
348  // make sure the thumb directory exists
349  QDir dir(thumbDir);
350  if (!dir.exists())
351  {
352  dir.mkdir(thumbDir);
353  if( chmod(qPrintable(thumbDir), 0777) != 0 )
354  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: Failed to change permissions"
355  " on thumb directory: " + ENO);
356  }
357 
358  QString path;
359  for (int x = 1; dir.exists(); x++)
360  {
361  path = thumbDir + QString("/%1").arg(x);
362  dir.setPath(path);
363  }
364 
365  dir.mkdir(path);
366  if( chmod(qPrintable(path), 0777) != 0 )
367  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: Failed to change permissions on "
368  "thumb directory: %1" + ENO);
369 
370  return path;
371 }
372 
374 {
375  int itemNo = m_imageGrid->GetCurrentPos();
377 
378  ThumbImage *thumb = m_thumbList.at(itemNo);
379  if (!thumb)
380  return;
381 
382  // copy current frame image to the selected thumb image
383  QString imageFile = thumb->filename;
384  QFile dst(imageFile);
385  QFile src(m_frameFile);
386  MythFile::copy(dst, src);
387 
388  item->SetImage(imageFile, "", true);
389 
390  // update the image grid item
391  int64_t pos = (m_currentPTS - m_startPTS) / m_frameTime;
392  thumb->frame = pos - m_offset;
393  if (itemNo != 0)
394  {
395  thumb->caption = frameToTime(thumb->frame);
396  item->SetText(thumb->caption);
397  }
398 
400 }
401 
402 QString ThumbFinder::frameToTime(int64_t frame, bool addFrame) const
403 {
404  int sec = (int) (frame / m_fps);
405  frame = frame - (int) (sec * m_fps);
406 
407  QString str = MythDate::formatTime(std::chrono::seconds(sec), "HH:mm:ss");
408  if (addFrame)
409  str += QString(".%1").arg(frame,10,2,QChar('0'));
410  return str;
411 }
412 
414 {
416  {
417  LOG(VB_GENERAL, LOG_ERR,
418  QString("ThumbFinder:: Failed to get file details for %1")
419  .arg(m_archiveItem->filename));
420  return false;
421  }
422 
424  return false;
425 
426  if (m_archiveItem->type == "Recording")
427  loadCutList();
428 
429  // calculate the file duration taking the cut list into account
431 
432  QString origFrameFile = m_frameFile;
433 
434  m_updateFrame = true;
435  getFrameImage();
436 
437  int chapterLen = 0;
438  if (m_thumbCount)
439  chapterLen = m_finalDuration / m_thumbCount;
440  else
441  chapterLen = m_finalDuration;
442 
443  m_updateFrame = false;
444 
445  // add title thumb
446  m_frameFile = m_thumbDir + "/title.jpg";
447  ThumbImage *thumb = nullptr;
448 
449  if (!m_thumbList.empty())
450  {
451  // use the thumb details in the thumbList if already available
452  thumb = m_thumbList.at(0);
453  }
454 
455  if (!thumb)
456  {
457  // no thumb available create a new one
458  thumb = new ThumbImage;
459  thumb->filename = m_frameFile;
460  thumb->frame = (int64_t) 0;
461  thumb->caption = "Title";
462  m_thumbList.append(thumb);
463  }
464  else
465  m_frameFile = thumb->filename;
466 
467  seekToFrame(thumb->frame);
468  getFrameImage();
469 
470  new MythUIButtonListItem(m_imageGrid, thumb->caption, thumb->filename);
471 
472  QCoreApplication::processEvents();
473 
474  for (int x = 1; x <= m_thumbCount; x++)
475  {
476  m_frameFile = m_thumbDir + QString("/chapter-%1.jpg").arg(x);
477 
478  thumb = nullptr;
479 
480  if (m_archiveItem->thumbList.size() > x)
481  {
482  // use the thumb details in the archiveItem if already available
483  thumb = m_archiveItem->thumbList.at(x);
484  }
485 
486  if (!thumb)
487  {
488  int chapter = chapterLen * (x - 1);
489  int hour = chapter / 3600;
490  int min = (chapter % 3600) / 60;
491  int sec = chapter % 60;
492  QString time = QString::asprintf("%02d:%02d:%02d", hour, min, sec);
493 
494  auto frame = (int64_t) (chapter * ceil(m_fps));
495 
496  // no thumb available create a new one
497  thumb = new ThumbImage;
498  thumb->filename = m_frameFile;
499  thumb->frame = frame;
500  thumb->caption = time;
501  m_thumbList.append(thumb);
502  }
503  else
504  m_frameFile = thumb->filename;
505 
506  seekToFrame(thumb->frame);
507  QCoreApplication::processEvents();
508  getFrameImage();
509  QCoreApplication::processEvents();
510  new MythUIButtonListItem(m_imageGrid, thumb->caption, thumb->filename);
511  QCoreApplication::processEvents();
512  }
513 
514  m_frameFile = origFrameFile;
515  seekToFrame(0);
516 
517  m_updateFrame = true;
518 
520 
522 
523  return true;
524 }
525 
526 bool ThumbFinder::initAVCodec(const QString &inFile)
527 {
528  // Open recording
529  LOG(VB_JOBQUEUE, LOG_INFO, QString("ThumbFinder: Opening '%1'")
530  .arg(inFile));
531 
532  if (!m_inputFC.Open(inFile))
533  {
534  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder, Couldn't open input file" + ENO);
535  return false;
536  }
537 
538  // Getting stream information
539  int ret = avformat_find_stream_info(m_inputFC, nullptr);
540  if (ret < 0)
541  {
542  LOG(VB_GENERAL, LOG_ERR,
543  QString("Couldn't get stream info, error #%1").arg(ret));
544  return false;
545  }
546 
547  av_dump_format(m_inputFC, 0, qPrintable(inFile), 0);
548 
549  // find the first video stream
550  m_videostream = -1;
551 
552  for (uint i = 0; i < m_inputFC->nb_streams; i++)
553  {
554  AVStream *st = m_inputFC->streams[i];
555  if (m_inputFC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
556  {
557  m_startTime = -1;
558  if (m_inputFC->streams[i]->start_time != (int) AV_NOPTS_VALUE)
559  m_startTime = m_inputFC->streams[i]->start_time;
560  else
561  {
562  LOG(VB_GENERAL, LOG_ERR,
563  "ThumbFinder: Failed to get start time");
564  return false;
565  }
566 
567  m_videostream = i;
568  m_frameWidth = st->codecpar->width;
569  m_frameHeight = st->codecpar->height;
570  if (st->r_frame_rate.den && st->r_frame_rate.num)
571  m_fps = av_q2d(st->r_frame_rate);
572  else
573  m_fps = 1/av_q2d(st->time_base);
574  break;
575  }
576  }
577 
578  if (m_videostream == -1)
579  {
580  LOG(VB_GENERAL, LOG_ERR, "Couldn't find a video stream");
581  return false;
582  }
583 
584  // get the codec context for the video stream
586  m_codecCtx->debug = 0;
587  m_codecCtx->workaround_bugs = 1;
588  m_codecCtx->lowres = 0;
589  m_codecCtx->idct_algo = FF_IDCT_AUTO;
590  m_codecCtx->skip_frame = AVDISCARD_DEFAULT;
591  m_codecCtx->skip_idct = AVDISCARD_DEFAULT;
592  m_codecCtx->skip_loop_filter = AVDISCARD_DEFAULT;
593  m_codecCtx->err_recognition = AV_EF_CAREFUL;
594  m_codecCtx->error_concealment = 3;
595 
596  // get decoder for video stream
597  m_codec = avcodec_find_decoder(m_codecCtx->codec_id);
598 
599  if (m_codec == nullptr)
600  {
601  LOG(VB_GENERAL, LOG_ERR,
602  "ThumbFinder: Couldn't find codec for video stream");
603  return false;
604  }
605 
606  // open codec
607  if (avcodec_open2(m_codecCtx, m_codec, nullptr) < 0)
608  {
609  LOG(VB_GENERAL, LOG_ERR,
610  "ThumbFinder: Couldn't open codec for video stream");
611  return false;
612  }
613 
614  // allocate temp buffer
615  int bufflen = m_frameWidth * m_frameHeight * 4;
616  m_outputbuf = new unsigned char[bufflen];
617  m_frameFile = getTempDirectory() + "work/frame.jpg";
618  return true;
619 }
620 
622 {
623  if (m_deleteMap.isEmpty() || !m_archiveItem->useCutlist)
624  return frameNumber;
625 
626  int diff = 0;
627  frm_dir_map_t::const_iterator it = m_deleteMap.constFind(frameNumber);
628 
629  for (it = m_deleteMap.constBegin(); it != m_deleteMap.constEnd(); ++it)
630  {
631  int start = it.key();
632 
633  ++it;
634  if (it == m_deleteMap.constEnd())
635  {
636  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: found a start cut but no cut end");
637  break;
638  }
639 
640  int end = it.key();
641 
642  if (start <= frameNumber + diff)
643  diff += end - start;
644  }
645 
646  m_offset = diff;
647  return frameNumber + diff;
648 }
649 
650 bool ThumbFinder::seekToFrame(int frame, bool checkPos)
651 {
652  // make sure the frame is not in a cut point
653  if (checkPos)
654  frame = checkFramePosition(frame);
655 
656  // seek to a position PRE_SEEK_AMOUNT frames before the required frame
657  int64_t timestamp = m_startTime + (frame * m_frameTime) -
659  int64_t requiredPTS = m_startPTS + (frame * m_frameTime);
660 
661  if (timestamp < m_startTime)
662  timestamp = m_startTime;
663 
664  if (av_seek_frame(m_inputFC, m_videostream, timestamp, AVSEEK_FLAG_ANY) < 0)
665  {
666  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder::SeekToFrame: seek failed") ;
667  return false;
668  }
669 
670  avcodec_flush_buffers(m_codecCtx);
671  getFrameImage(true, requiredPTS);
672 
673  return true;
674 }
675 
677 {
678  int64_t currentFrame = (m_currentPTS - m_startPTS) / m_frameTime;
679 
680  int inc = kSeekAmounts[m_currentSeek].amount;
681 
682  if (inc == -1)
683  inc = 1;
684  else if (inc == -2)
685  {
686  int pos = 0;
687  frm_dir_map_t::const_iterator it;
688  for (it = m_deleteMap.constBegin(); it != m_deleteMap.constEnd(); ++it)
689  {
690  if (it.key() > (uint64_t)currentFrame)
691  {
692  pos = it.key();
693  break;
694  }
695  }
696  // seek to next cutpoint
697  m_offset = 0;
698  seekToFrame(pos, false);
699  return true;
700  }
701  else
702  inc = (int) (inc * ceil(m_fps));
703 
704  int64_t newFrame = currentFrame + inc - m_offset;
705  if (newFrame == currentFrame + 1)
706  getFrameImage(false);
707  else
708  seekToFrame(newFrame);
709 
710  return true;
711 }
712 
714 {
715  int64_t currentFrame = (m_currentPTS - m_startPTS) / m_frameTime;
716 
717  int inc = kSeekAmounts[m_currentSeek].amount;
718  if (inc == -1)
719  inc = -1;
720  else if (inc == -2)
721  {
722  // seek to previous cut point
723  frm_dir_map_t::const_iterator it;
724  int pos = 0;
725  for (it = m_deleteMap.constBegin(); it != m_deleteMap.constEnd(); ++it)
726  {
727  if (it.key() >= (uint64_t)currentFrame)
728  break;
729 
730  pos = it.key();
731  }
732 
733  // seek to next cutpoint
734  m_offset = 0;
735  seekToFrame(pos, false);
736  return true;
737  }
738  else
739  inc = (int) (-inc * ceil(m_fps));
740 
741  int64_t newFrame = currentFrame + inc - m_offset;
742  seekToFrame(newFrame);
743 
744  return true;
745 }
746 
747 bool ThumbFinder::getFrameImage(bool needKeyFrame, int64_t requiredPTS)
748 {
749  AVFrame orig;
750  AVFrame retbuf;
751  memset(&orig, 0, sizeof(AVFrame));
752  memset(&retbuf, 0, sizeof(AVFrame));
753 
754  AVPacket *pkt = av_packet_alloc();
755  if (pkt == nullptr)
756  {
757  LOG(VB_GENERAL, LOG_ERR, "packet allocation failed");
758  return false;
759  }
760 
761  bool frameFinished = false;
762  bool gotKeyFrame = false;
763 
764  while (av_read_frame(m_inputFC, pkt) >= 0 && !frameFinished)
765  {
766  if (pkt->stream_index == m_videostream)
767  {
768  int keyFrame = pkt->flags & AV_PKT_FLAG_KEY;
769 
770  if (m_startPTS == -1 && pkt->dts != AV_NOPTS_VALUE)
771  {
772  m_startPTS = pkt->dts;
773  m_frameTime = pkt->duration;
774  }
775 
776  if (keyFrame)
777  gotKeyFrame = true;
778 
779  if (!gotKeyFrame && needKeyFrame)
780  {
781  av_packet_unref(pkt);
782  continue;
783  }
784 
785  if (m_firstIFramePTS == -1)
786  m_firstIFramePTS = pkt->dts;
787 
788  av_frame_unref(m_frame);
789  frameFinished = false;
790  int ret = avcodec_receive_frame(m_codecCtx, m_frame);
791  if (ret == 0)
792  frameFinished = true;
793  if (ret == 0 || ret == AVERROR(EAGAIN))
794  avcodec_send_packet(m_codecCtx, pkt);
795  if (requiredPTS != -1 && pkt->dts != AV_NOPTS_VALUE && pkt->dts < requiredPTS)
796  frameFinished = false;
797 
798  m_currentPTS = pkt->dts;
799  }
800 
801  av_packet_unref(pkt);
802  }
803  av_packet_free(&pkt);
804 
805  if (frameFinished)
806  {
807  av_image_fill_arrays(retbuf.data, retbuf.linesize, m_outputbuf,
808  AV_PIX_FMT_RGB32, m_frameWidth, m_frameHeight, IMAGE_ALIGN);
809  AVFrame *tmp = m_frame;
811  m_copy.Copy(&retbuf, AV_PIX_FMT_RGB32, tmp, m_codecCtx->pix_fmt,
813 
815  QImage::Format_RGB32);
816 
817  QByteArray ffile = m_frameFile.toLocal8Bit();
818  if (!img.save(ffile.constData(), "JPEG"))
819  {
820  LOG(VB_GENERAL, LOG_ERR, "Failed to save thumb: " + m_frameFile);
821  }
822 
823  if (m_updateFrame)
824  {
825  MythImage *mimage =
827  mimage->Assign(img);
828  m_frameImage->SetImage(mimage);
829  mimage->DecrRef();
830  }
831 
833  }
834 
835  return true;
836 }
837 
839 {
840  delete[] m_outputbuf;
841 
842  // close the codec
843  if (m_inputFC.isOpen() && m_inputFC->streams)
845 
846  // close the video file
847  m_inputFC.Close();
848 }
849 
851 {
852  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
853  auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
854 
855  if (menuPopup->Create())
856  popupStack->AddScreen(menuPopup);
857 
858  menuPopup->SetReturnEvent(this, "action");
859 
860  menuPopup->AddButton(tr("Exit, Save Thumbnails"), &ThumbFinder::savePressed);
861  menuPopup->AddButton(tr("Exit, Don't Save Thumbnails"), &ThumbFinder::cancelPressed);
862 }
863 
865 {
866  if (!m_positionImage)
867  return;
868 
869  QSize size = m_positionImage->GetArea().size();
870  auto *pixmap = new QPixmap(size.width(), size.height());
871 
872  QPainter p(pixmap);
873  QBrush brush(Qt::green);
874 
875  p.setBrush(brush);
876  p.setPen(Qt::NoPen);
877  p.fillRect(0, 0, size.width(), size.height(), brush);
878 
879  frm_dir_map_t::const_iterator it;
880 
881  brush.setColor(Qt::red);
882 
883  for (it = m_deleteMap.constBegin(); it != m_deleteMap.constEnd(); ++it)
884  {
885  double startdelta = size.width();
886  if (it.key() != 0)
887  startdelta = (m_archiveItem->duration * m_fps) / it.key();
888 
889  ++it;
890  if (it == m_deleteMap.constEnd())
891  {
892  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: found a start cut but no cut end");
893  break;
894  }
895 
896  double enddelta = size.width();
897  if (it.key() != 0)
898  enddelta = (m_archiveItem->duration * m_fps) / it.key();
899 
900  int start = (int) (size.width() / startdelta);
901  int end = (int) (size.width() / enddelta);
902  p.fillRect(start - 1, 0, end - start, size.height(), brush);
903  }
904 
905  if (frame == 0)
906  frame = 1;
907  brush.setColor(Qt::yellow);
908  int pos = (int) (size.width() / ((m_archiveItem->duration * m_fps) / frame));
909  p.fillRect(pos, 0, 3, size.height(), brush);
910 
912  image->Assign(*pixmap);
913  m_positionImage->SetImage(image);
914 
915  p.end();
916  delete pixmap;
917 }
918 
920 {
921  if (m_archiveItem->type == "Recording")
922  {
924  {
925  frm_dir_map_t::const_iterator it;
926 
927  int cutLen = 0;
928 
929  for (it = m_deleteMap.constBegin(); it != m_deleteMap.constEnd(); ++it)
930  {
931  int start = it.key();
932 
933  ++it;
934  if (it == m_deleteMap.constEnd())
935  {
936  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: found a start cut but no cut end");
937  break;
938  }
939 
940  int end = it.key();
941  cutLen += end - start;
942  }
943  return m_archiveItem->duration - (int) (cutLen / m_fps);
944  }
945  }
946 
947  return m_archiveItem->duration;
948 }
MythUIButton::Clicked
void Clicked()
ThumbFinder::m_frameHeight
int m_frameHeight
Definition: thumbfinder.h:90
ArchiveRemoteAVFormatContext::isOpen
bool isOpen() const
Definition: mytharchive/mytharchive/remoteavformatcontext.h:104
ArchiveItem::useCutlist
bool useCutlist
Definition: archiveutil.h:70
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1587
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:73
ThumbFinder::getThumbImages
bool getThumbImages(void)
Definition: thumbfinder.cpp:413
ThumbFinder::cancelPressed
void cancelPressed(void)
Definition: thumbfinder.cpp:300
ThumbImage::caption
QString caption
Definition: archiveutil.h:45
mythuitext.h
PRE_SEEK_AMOUNT
static constexpr int8_t PRE_SEEK_AMOUNT
Definition: thumbfinder.cpp:70
MythScreenType::NextPrevWidgetFocus
virtual bool NextPrevWidgetFocus(bool up_or_down)
Definition: mythscreentype.cpp:153
ThumbFinder::m_startPTS
int64_t m_startPTS
Definition: thumbfinder.h:94
ThumbFinder::updateCurrentPos
void updateCurrentPos(void)
Definition: thumbfinder.cpp:305
ThumbImage::filename
QString filename
Definition: archiveutil.h:46
MythCodecMap::GetCodecContext
AVCodecContext * GetCodecContext(const AVStream *Stream, const AVCodec *Codec=nullptr, bool NullCodec=false)
Definition: mythavutil.cpp:287
ThumbFinder::seekBackward
bool seekBackward()
Definition: thumbfinder.cpp:713
ThumbFinder::m_videostream
int m_videostream
Definition: thumbfinder.h:91
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:386
MARK_CUT_END
@ MARK_CUT_END
Definition: programtypes.h:56
MythPainter::GetFormatImage
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
Definition: mythpainter.cpp:540
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
MythAVUtil::DeinterlaceAVFrame
static void DeinterlaceAVFrame(AVFrame *Frame)
Deinterlace an AVFrame.
Definition: mythavutil.cpp:134
ThumbFinder::m_firstIFramePTS
int64_t m_firstIFramePTS
Definition: thumbfinder.h:96
mythdialogbox.h
MythScreenStack
Definition: mythscreenstack.h:16
mythdbcon.h
MythDate::formatTime
QString formatTime(std::chrono::milliseconds msecs, QString fmt)
Format a milliseconds time value.
Definition: mythdate.cpp:233
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
ThumbFinder::m_archiveItem
ArchiveItem * m_archiveItem
Definition: thumbfinder.h:103
ThumbFinder::m_codec
const AVCodec * m_codec
Definition: thumbfinder.h:82
ArchiveItem::duration
int duration
Definition: archiveutil.h:62
build_compdb.file
file
Definition: build_compdb.py:55
mythdirs.h
MythMainWindow::GetPainter
MythPainter * GetPainter()
Definition: mythmainwindow.cpp:255
getTempDirectory
QString getTempDirectory(bool showError)
Definition: archiveutil.cpp:46
ThumbFinder::m_startTime
int64_t m_startTime
Definition: thumbfinder.h:93
ThumbFinder::m_frameFile
QString m_frameFile
Definition: thumbfinder.h:88
mythuibuttonlist.h
ThumbFinder::calcFinalDuration
int calcFinalDuration(void)
Definition: thumbfinder.cpp:919
mythuiimage.h
ThumbFinder::m_offset
int m_offset
Definition: thumbfinder.h:101
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
MythScreenType::GetFocusWidget
MythUIType * GetFocusWidget(void) const
Definition: mythscreentype.cpp:113
MythUIType::GetArea
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:884
ArchiveRemoteAVFormatContext::Close
void Close()
Definition: mytharchive/mytharchive/remoteavformatcontext.h:89
MythUIButtonListItem::SetText
void SetText(const QString &text, const QString &name="", const QString &state="")
Definition: mythuibuttonlist.cpp:3268
ThumbFinder::m_currentSeek
size_t m_currentSeek
Definition: thumbfinder.h:92
ThumbFinder::changeSeekAmount
void changeSeekAmount(bool up)
Definition: thumbfinder.cpp:316
ThumbFinder::m_frameButton
MythUIButton * m_frameButton
Definition: thumbfinder.h:109
ThumbFinder::m_frameWidth
int m_frameWidth
Definition: thumbfinder.h:89
MythUIButtonListItem
Definition: mythuibuttonlist.h:41
AVFrame
struct AVFrame AVFrame
Definition: BorderDetector.h:15
ArchiveItem::type
QString type
Definition: archiveutil.h:53
ThumbFinder::Create
bool Create(void) override
Definition: thumbfinder.cpp:116
mythdate.h
ThumbFinder::m_imageGrid
MythUIButtonList * m_imageGrid
Definition: thumbfinder.h:114
programinfo.h
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1104
ThumbFinder::Init
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
Definition: thumbfinder.cpp:102
MythUIButtonList::GetCurrentPos
int GetCurrentPos() const
Definition: mythuibuttonlist.h:238
MythFile::copy
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
Definition: mythmiscutil.cpp:264
hardwareprofile.config.p
p
Definition: config.py:33
ThumbFinder::m_updateFrame
bool m_updateFrame
Definition: thumbfinder.h:98
MythScreenType::SetFocusWidget
bool SetFocusWidget(MythUIType *widget=nullptr)
Definition: mythscreentype.cpp:118
getFileDetails
bool getFileDetails(ArchiveItem *a)
Definition: archiveutil.cpp:199
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:166
ThumbFinder::m_deleteMap
frm_dir_map_t m_deleteMap
Definition: thumbfinder.h:99
ThumbFinder::m_frame
MythAVFrame m_frame
Definition: thumbfinder.h:83
ThumbFinder::getFrameImage
bool getFrameImage(bool needKeyFrame=true, int64_t requiredPTS=-1)
Definition: thumbfinder.cpp:747
GetShareDir
QString GetShareDir(void)
Definition: mythdirs.cpp:222
ThumbFinder::m_saveButton
MythUIButton * m_saveButton
Definition: thumbfinder.h:110
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:206
ThumbImage::frame
qint64 frame
Definition: archiveutil.h:47
ThumbFinder::m_frameTime
int64_t m_frameTime
Definition: thumbfinder.h:97
ProgramInfo::QueryCutList
bool QueryCutList(frm_dir_map_t &delMap, bool loadAutosave=false) const
Definition: programinfo.cpp:3465
MythImage::DecrRef
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:52
ThumbImage
Definition: archiveutil.h:43
MythCodecMap::FreeCodecContext
void FreeCodecContext(const AVStream *Stream)
Definition: mythavutil.cpp:335
ThumbFinder::initAVCodec
bool initAVCodec(const QString &inFile)
Definition: thumbfinder.cpp:526
thumbfinder.h
ThumbFinder::m_positionImage
MythUIImage * m_positionImage
Definition: thumbfinder.h:113
ThumbFinder::ShowMenu
void ShowMenu(void) override
Definition: thumbfinder.cpp:850
uint
unsigned int uint
Definition: compat.h:81
ThumbFinder::ThumbFinder
ThumbFinder(MythScreenStack *parent, ArchiveItem *archiveItem, const QString &menuTheme)
Definition: thumbfinder.cpp:85
ThumbFinder::checkFramePosition
int checkFramePosition(int frameNumber)
Definition: thumbfinder.cpp:621
ThumbFinder::m_cancelButton
MythUIButton * m_cancelButton
Definition: thumbfinder.h:111
getProgramInfoForFile
ProgramInfo * getProgramInfoForFile(const QString &inFile)
Definition: archiveutil.cpp:163
ThumbFinder::~ThumbFinder
~ThumbFinder() override
Definition: thumbfinder.cpp:107
ThumbFinder::createThumbDir
static QString createThumbDir(void)
Definition: thumbfinder.cpp:344
UIUtilDisp::Assign
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
ThumbFinder::loadCutList
void loadCutList(void)
Definition: thumbfinder.cpp:253
ThumbFinder::frameToTime
QString frameToTime(int64_t frame, bool addFrame=false) const
Definition: thumbfinder.cpp:402
ThumbFinder::m_thumbList
QList< ThumbImage * > m_thumbList
Definition: thumbfinder.h:105
mythuihelper.h
ArchiveItem::thumbList
QList< ThumbImage * > thumbList
Definition: archiveutil.h:72
MARK_CUT_START
@ MARK_CUT_START
Definition: programtypes.h:57
mythimage.h
ThumbFinder::m_thumbDir
QString m_thumbDir
Definition: thumbfinder.h:106
ThumbFinder::m_thumbCount
int m_thumbCount
Definition: thumbfinder.h:104
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:67
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:404
ThumbFinder::gridItemChanged
void gridItemChanged(MythUIButtonListItem *item)
Definition: thumbfinder.cpp:334
mythmiscutil.h
ThumbFinder::seekToFrame
bool seekToFrame(int frame, bool checkPos=true)
Definition: thumbfinder.cpp:650
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:695
MythUIButtonListItem::SetImage
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
Definition: mythuibuttonlist.cpp:3429
MythImage
Definition: mythimage.h:36
ArchiveRemoteAVFormatContext::Open
bool Open(const QString &filename)
Definition: mytharchive/mytharchive/remoteavformatcontext.h:29
ThumbFinder::m_inputFC
ArchiveRemoteAVFormatContext m_inputFC
Definition: thumbfinder.h:79
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:132
mythcontext.h
ThumbFinder::m_frameImage
MythUIImage * m_frameImage
Definition: thumbfinder.h:112
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
ThumbFinder::updateThumb
void updateThumb(void)
Definition: thumbfinder.cpp:373
MythUIButtonList::SetItemCurrent
void SetItemCurrent(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1554
build_compdb.action
action
Definition: build_compdb.py:9
ThumbFinder::savePressed
void savePressed(void)
Definition: thumbfinder.cpp:283
ArchiveItem::hasCutlist
bool hasCutlist
Definition: archiveutil.h:69
kSeekAmounts
static const std::array< const SeekAmount, 9 > kSeekAmounts
Definition: thumbfinder.cpp:73
MythUIImage::SetImage
void SetImage(MythImage *img)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
Definition: mythuiimage.cpp:748
mythuibutton.h
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:320
ThumbFinder::m_finalDuration
int m_finalDuration
Definition: thumbfinder.h:100
ThumbFinder::m_codecMap
MythCodecMap m_codecMap
Definition: thumbfinder.h:81
ArchiveItem
Definition: archiveutil.h:50
MythImage::Assign
void Assign(const QImage &img)
Definition: mythimage.cpp:77
MythAVCopy::Copy
int Copy(AVFrame *To, const MythVideoFrame *From, unsigned char *Buffer, AVPixelFormat Fmt=AV_PIX_FMT_YUV420P)
Initialise AVFrame and copy contents of VideoFrame frame into it, performing any required conversion.
Definition: mythavutil.cpp:266
ThumbFinder::m_fps
float m_fps
Definition: thumbfinder.h:86
ThumbFinder::m_copy
MythAVCopy m_copy
Definition: thumbfinder.h:84
ThumbFinder::closeAVCodec
void closeAVCodec()
Definition: thumbfinder.cpp:838
ThumbFinder::m_currentPosText
MythUIText * m_currentPosText
Definition: thumbfinder.h:116
ThumbFinder::updatePositionBar
void updatePositionBar(int64_t frame)
Definition: thumbfinder.cpp:864
build_compdb.filename
filename
Definition: build_compdb.py:21
ThumbFinder::m_outputbuf
unsigned char * m_outputbuf
Definition: thumbfinder.h:87
mythmainwindow.h
ThumbFinder::m_seekAmountText
MythUIText * m_seekAmountText
Definition: thumbfinder.h:115
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:52
MythUIType::SetRedraw
void SetRedraw(void)
Definition: mythuitype.cpp:308
ThumbFinder::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: thumbfinder.cpp:156
ThumbFinder::m_currentPTS
int64_t m_currentPTS
Definition: thumbfinder.h:95
ArchiveItem::filename
QString filename
Definition: archiveutil.h:59
ThumbFinder::getChapterCount
static int getChapterCount(const QString &menuTheme)
Definition: thumbfinder.cpp:228
ThumbFinder::m_codecCtx
AVCodecContext * m_codecCtx
Definition: thumbfinder.h:80
ThumbFinder::seekForward
bool seekForward()
Definition: thumbfinder.cpp:676