MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
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 <sys/stat.h>
29 #include <math.h>
30 #include <errno.h>
31 
32 // c++
33 #include <cstdlib>
34 #include <iostream>
35 
36 // qt
37 #include <QApplication>
38 #include <QDomDocument>
39 #include <QFile>
40 #include <QDir>
41 #include <QPainter>
42 
43 // myth
44 #include <mythcontext.h>
45 #include <mythdbcon.h>
46 #include <programinfo.h>
47 #include <mythuihelper.h>
48 #include <mythmainwindow.h>
49 #include <mythdialogbox.h>
50 #include <mythdirs.h>
51 #include <mythmiscutil.h>
52 #include <mythuitext.h>
53 #include <mythuibutton.h>
54 #include <mythuiimage.h>
55 #include <mythuibuttonlist.h>
56 #include <mythimage.h>
57 #include <mythconfig.h>
58 extern "C" {
59 #include <swscale.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 #define PRE_SEEK_AMOUNT 50
71 
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 
85 int SeekAmountsCount = sizeof(SeekAmounts) / sizeof(SeekAmounts[0]);
86 
88  const QString &menuTheme)
89  :MythScreenType(parent, "ThumbFinder"),
90  m_inputFC(NULL), m_codecCtx(NULL),
91  m_codec(NULL), m_frame(NULL),
92  m_fps(0.0), m_outputbuf(NULL),
93  m_frameWidth(0), m_frameHeight(0),
94  m_videostream(0), m_currentSeek(0),
95  m_startTime(-1), m_startPTS(-1),
96  m_currentPTS(-1), m_firstIFramePTS(-1),
97  m_frameTime(0), m_updateFrame(false),
98  m_finalDuration(0), m_offset(0),
99  m_archiveItem(archiveItem),
100  m_thumbCount(getChapterCount(menuTheme)),
101  m_thumbDir(createThumbDir()),
102  m_frameButton(NULL), m_saveButton(NULL),
103  m_cancelButton(NULL), m_frameImage(NULL),
104  m_positionImage(NULL), m_imageGrid(NULL),
105  m_seekAmountText(NULL), m_currentPosText(NULL)
106 {
107  // copy thumbList so we can abandon changes if required
108  m_thumbList.clear();
109  for (int x = 0; x < m_archiveItem->thumbList.size(); x++)
110  {
111  ThumbImage *thumb = new ThumbImage;
112  *thumb = *m_archiveItem->thumbList.at(x);
113  m_thumbList.append(thumb);
114  }
115 }
116 
118 {
119  getThumbImages();
120 }
121 
123 {
124  while (!m_thumbList.isEmpty())
125  delete m_thumbList.takeFirst();
126  m_thumbList.clear();
127 
128  closeAVCodec();
129 }
130 
132 {
133  bool foundtheme = false;
134 
135  // Load the theme for this screen
136  foundtheme = LoadWindowFromXML("mythburn-ui.xml", "thumbfinder", this);
137 
138  if (!foundtheme)
139  return false;
140 
141  bool err = false;
142  UIUtilE::Assign(this, m_frameImage, "frameimage", &err);
143  UIUtilE::Assign(this, m_positionImage, "positionimage", &err);
144  UIUtilE::Assign(this, m_imageGrid, "thumblist", &err);
145  UIUtilE::Assign(this, m_saveButton, "save_button", &err);
146  UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
147  UIUtilE::Assign(this, m_frameButton, "frame_button", &err);
148  UIUtilE::Assign(this, m_seekAmountText, "seekamount", &err);
149  UIUtilE::Assign(this, m_currentPosText, "currentpos", &err);
150 
151  if (err)
152  {
153  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'mythburn'");
154  return false;
155  }
156 
157  connect(m_imageGrid, SIGNAL(itemSelected(MythUIButtonListItem *)),
158  this, SLOT(gridItemChanged(MythUIButtonListItem *)));
159 
160  connect(m_saveButton, SIGNAL(Clicked()), this, SLOT(savePressed()));
161  connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(cancelPressed()));
162 
163  connect(m_frameButton, SIGNAL(Clicked()), this, SLOT(updateThumb()));
164 
165  BuildFocusList();
166 
168 
169  return true;
170 }
171 
172 bool ThumbFinder::keyPressEvent(QKeyEvent *event)
173 {
174  if (GetFocusWidget()->keyPressEvent(event))
175  return true;
176 
177  bool handled = false;
178  QStringList actions;
179  handled = GetMythMainWindow()->TranslateKeyPress("Archive", event, actions);
180 
181  for (int i = 0; i < actions.size() && !handled; i++)
182  {
183  QString action = actions[i];
184  handled = true;
185 
186  if (action == "MENU")
187  {
188  NextPrevWidgetFocus(true);
189  return true;
190  }
191 
192  if (action == "ESCAPE")
193  {
194  showMenu();
195  return true;
196  }
197 
198  if (action == "0" || action == "1" || action == "2" || action == "3" ||
199  action == "4" || action == "5" || action == "6" || action == "7" ||
200  action == "8" || action == "9")
201  {
202  m_imageGrid->SetItemCurrent(action.toInt());
203  int itemNo = m_imageGrid->GetCurrentPos();
204  ThumbImage *thumb = m_thumbList.at(itemNo);
205  if (thumb)
206  seekToFrame(thumb->frame);
207  return true;
208  }
209 
210  if (GetFocusWidget() == m_frameButton)
211  {
212  if (action == "UP")
213  {
214  changeSeekAmount(true);
215  }
216  else if (action == "DOWN")
217  {
218  changeSeekAmount(false);
219  }
220  else if (action == "LEFT")
221  {
222  seekBackward();
223  }
224  else if (action == "RIGHT")
225  {
226  seekForward();
227  }
228  else if (action == "SELECT")
229  {
230  updateThumb();
231  }
232  else
233  handled = false;
234  }
235  else
236  handled = false;
237  }
238 
239  if (!handled && MythScreenType::keyPressEvent(event))
240  handled = true;
241 
242  return handled;
243 }
244 
245 int ThumbFinder::getChapterCount(const QString &menuTheme)
246 {
247  QString filename = GetShareDir() + "mytharchive/themes/" +
248  menuTheme + "/theme.xml";
249  QDomDocument doc("mydocument");
250  QFile file(filename);
251 
252  if (!file.open(QIODevice::ReadOnly))
253  {
254  LOG(VB_GENERAL, LOG_ERR, "Failed to open theme file: " + filename);
255  return 0; //??
256  }
257  if (!doc.setContent(&file))
258  {
259  file.close();
260  LOG(VB_GENERAL, LOG_ERR, "Failed to parse theme file: " + filename);
261  return 0;
262  }
263  file.close();
264 
265  QDomNodeList chapterNodeList = doc.elementsByTagName("chapter");
266 
267  return chapterNodeList.count();
268 }
269 
271 {
273 
274  if (progInfo && m_archiveItem->hasCutlist)
275  {
276  progInfo->QueryCutList(m_deleteMap);
277  delete progInfo;
278  }
279 
280  // if the first mark is a end mark then add the start mark at the beginning
281  frm_dir_map_t::const_iterator it = m_deleteMap.begin();
282  if (it.value() == MARK_CUT_END)
283  m_deleteMap.insert(0, MARK_CUT_START);
284 
285 
286  // if the last mark is a start mark then add the end mark at the end
287  it = m_deleteMap.end();
288  --it;
289  if (it != m_deleteMap.end())
290  {
291  if (it.value() == MARK_CUT_START)
293  }
294 }
295 
297 {
298  // copy the thumb details to the archiveItem
299  while (!m_archiveItem->thumbList.isEmpty())
300  delete m_archiveItem->thumbList.takeFirst();
301  m_archiveItem->thumbList.clear();
302 
303  for (int x = 0; x < m_thumbList.size(); x++)
304  {
305  ThumbImage *thumb = new ThumbImage;
306  *thumb = *m_thumbList.at(x);
307  m_archiveItem->thumbList.append(thumb);
308  }
309 
310  Close();
311 }
312 
314 {
315  Close();
316 }
317 
319 {
320  int64_t pos = m_currentPTS - m_firstIFramePTS;
321  int64_t frame = pos / m_frameTime;
322 
323  if (m_currentPosText)
324  m_currentPosText->SetText(frameToTime(frame, true));
325 
326  updatePositionBar(frame);
327 }
328 
330 {
331  if (up)
332  {
333  m_currentSeek++;
335  m_currentSeek = 0;
336  }
337  else
338  {
339  m_currentSeek--;
340  if (m_currentSeek < 0)
342  }
343 
345 }
346 
348 {
349  (void) item;
350 
351  int itemNo = m_imageGrid->GetCurrentPos();
352  ThumbImage *thumb = m_thumbList.at(itemNo);
353  if (thumb)
354  seekToFrame(thumb->frame);
355 }
356 
358 {
359  QString thumbDir = getTempDirectory() + "config/thumbs";
360 
361  // make sure the thumb directory exists
362  QDir dir(thumbDir);
363  if (!dir.exists())
364  {
365  dir.mkdir(thumbDir);
366  if( chmod(qPrintable(thumbDir), 0777) )
367  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: Failed to change permissions"
368  " on thumb directory: " + ENO);
369  }
370 
371  QString path;
372  for (int x = 1; dir.exists(); x++)
373  {
374  path = QString(thumbDir + "/%1").arg(x);
375  dir.setPath(path);
376  }
377 
378  dir.mkdir(path);
379  if( chmod(qPrintable(path), 0777) )
380  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: Failed to change permissions on "
381  "thumb directory: %1" + ENO);
382 
383  return path;
384 }
385 
387 {
388  int itemNo = m_imageGrid->GetCurrentPos();
390 
391  ThumbImage *thumb = m_thumbList.at(itemNo);
392  if (!thumb)
393  return;
394 
395  // copy current frame image to the selected thumb image
396  QString imageFile = thumb->filename;
397  QFile dst(imageFile);
398  QFile src(m_frameFile);
399  copy(dst, src);
400 
401  item->SetImage(imageFile, "", true);
402 
403  // update the image grid item
404  int64_t pos = (int) ((m_currentPTS - m_startPTS) / m_frameTime);
405  thumb->frame = pos - m_offset;
406  if (itemNo != 0)
407  {
408  thumb->caption = frameToTime(thumb->frame);
409  item->SetText(thumb->caption);
410  }
411 
413 }
414 
415 QString ThumbFinder::frameToTime(int64_t frame, bool addFrame)
416 {
417  int hour, min, sec;
418  QString str;
419 
420  sec = (int) (frame / m_fps);
421  frame = frame - (int) (sec * m_fps);
422  min = sec / 60;
423  sec %= 60;
424  hour = min / 60;
425  min %= 60;
426 
427  if (addFrame)
428  str = str.sprintf("%01d:%02d:%02d.%02d", hour, min, sec, (int) frame);
429  else
430  str = str.sprintf("%02d:%02d:%02d", hour, min, sec);
431  return str;
432 }
433 
435 {
437  {
438  LOG(VB_GENERAL, LOG_ERR,
439  QString("ThumbFinder:: Failed to get file details for %1")
440  .arg(m_archiveItem->filename));
441  return false;
442  }
443 
445  return false;
446 
447  if (m_archiveItem->type == "Recording")
448  loadCutList();
449 
450  // calculate the file duration taking the cut list into account
452 
453  QString origFrameFile = m_frameFile;
454 
455  m_updateFrame = true;
456  getFrameImage();
457 
458  int chapterLen;
459  if (m_thumbCount)
460  chapterLen = m_finalDuration / m_thumbCount;
461  else
462  chapterLen = m_finalDuration;
463 
464  QString thumbList = "";
465  m_updateFrame = false;
466 
467  // add title thumb
468  m_frameFile = m_thumbDir + "/title.jpg";
469  ThumbImage *thumb = NULL;
470 
471  if (m_thumbList.size() > 0)
472  {
473  // use the thumb details in the thumbList if already available
474  thumb = m_thumbList.at(0);
475  }
476 
477  if (!thumb)
478  {
479  // no thumb available create a new one
480  thumb = new ThumbImage;
481  thumb->filename = m_frameFile;
482  thumb->frame = (int64_t) 0;
483  thumb->caption = "Title";
484  m_thumbList.append(thumb);
485  }
486  else
487  m_frameFile = thumb->filename;
488 
489  seekToFrame(thumb->frame);
490  getFrameImage();
491 
492  new MythUIButtonListItem(m_imageGrid, thumb->caption, thumb->filename);
493 
494  qApp->processEvents();
495 
496  for (int x = 1; x <= m_thumbCount; x++)
497  {
498  m_frameFile = QString(m_thumbDir + "/chapter-%1.jpg").arg(x);
499 
500  thumb = NULL;
501 
502  if (m_archiveItem->thumbList.size() > x)
503  {
504  // use the thumb details in the archiveItem if already available
505  thumb = m_archiveItem->thumbList.at(x);
506  }
507 
508  if (!thumb)
509  {
510  QString time;
511  int chapter, hour, min, sec;
512 
513  chapter = chapterLen * (x - 1);
514  hour = chapter / 3600;
515  min = (chapter % 3600) / 60;
516  sec = chapter % 60;
517  time = time.sprintf("%02d:%02d:%02d", hour, min, sec);
518 
519  int64_t frame = (int64_t) (chapter * ceil(m_fps));
520 
521  // no thumb available create a new one
522  thumb = new ThumbImage;
523  thumb->filename = m_frameFile;
524  thumb->frame = frame;
525  thumb->caption = time;
526  m_thumbList.append(thumb);
527  }
528  else
529  m_frameFile = thumb->filename;
530 
531  seekToFrame(thumb->frame);
532  qApp->processEvents();
533  getFrameImage();
534  qApp->processEvents();
535  new MythUIButtonListItem(m_imageGrid, thumb->caption, thumb->filename);
536  qApp->processEvents();
537  }
538 
539  m_frameFile = origFrameFile;
540  seekToFrame(0);
541 
542  m_updateFrame = true;
543 
545 
547 
548  return true;
549 }
550 
551 bool ThumbFinder::initAVCodec(const QString &inFile)
552 {
553  av_register_all();
554 
555  // Open recording
556  LOG(VB_JOBQUEUE, LOG_INFO, QString("ThumbFinder: Opening '%1'")
557  .arg(inFile));
558 
559  if (!m_inputFC.Open(inFile))
560  {
561  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder, Couldn't open input file" + ENO);
562  return false;
563  }
564 
565  // Getting stream information
566  int ret = avformat_find_stream_info(m_inputFC, NULL);
567  if (ret < 0)
568  {
569  LOG(VB_GENERAL, LOG_ERR,
570  QString("Couldn't get stream info, error #%1").arg(ret));
571  return false;
572  }
573 
574  av_dump_format(m_inputFC, 0, qPrintable(inFile), 0);
575 
576  // find the first video stream
577  m_videostream = -1;
578 
579  for (uint i = 0; i < m_inputFC->nb_streams; i++)
580  {
581  AVStream *st = m_inputFC->streams[i];
582  if (m_inputFC->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
583  {
584  m_startTime = -1;
585  if (m_inputFC->streams[i]->start_time != (int) AV_NOPTS_VALUE)
586  m_startTime = m_inputFC->streams[i]->start_time;
587  else
588  {
589  LOG(VB_GENERAL, LOG_ERR,
590  "ThumbFinder: Failed to get start time");
591  return false;
592  }
593 
594  m_videostream = i;
595  m_frameWidth = st->codec->width;
596  m_frameHeight = st->codec->height;
597  if (st->r_frame_rate.den && st->r_frame_rate.num)
598  m_fps = av_q2d(st->r_frame_rate);
599  else
600  m_fps = 1/av_q2d(st->time_base);
601  break;
602  }
603  }
604 
605  if (m_videostream == -1)
606  {
607  LOG(VB_GENERAL, LOG_ERR, "Couldn't find a video stream");
608  return false;
609  }
610 
611  // get the codec context for the video stream
612  m_codecCtx = m_inputFC->streams[m_videostream]->codec;
613  m_codecCtx->debug_mv = 0;
614  m_codecCtx->debug = 0;
615  m_codecCtx->workaround_bugs = 1;
616  m_codecCtx->lowres = 0;
617  m_codecCtx->idct_algo = FF_IDCT_AUTO;
618  m_codecCtx->skip_frame = AVDISCARD_DEFAULT;
619  m_codecCtx->skip_idct = AVDISCARD_DEFAULT;
620  m_codecCtx->skip_loop_filter = AVDISCARD_DEFAULT;
621  m_codecCtx->err_recognition = AV_EF_CAREFUL;
622  m_codecCtx->error_concealment = 3;
623 
624  // get decoder for video stream
625  m_codec = avcodec_find_decoder(m_codecCtx->codec_id);
626 
627  if (m_codec == NULL)
628  {
629  LOG(VB_GENERAL, LOG_ERR,
630  "ThumbFinder: Couldn't find codec for video stream");
631  return false;
632  }
633 
634  // open codec
635  if (avcodec_open2(m_codecCtx, m_codec, NULL) < 0)
636  {
637  LOG(VB_GENERAL, LOG_ERR,
638  "ThumbFinder: Couldn't open codec for video stream");
639  return false;
640  }
641 
642  // allocate temp buffer
643  int bufflen = m_frameWidth * m_frameHeight * 4;
644  m_outputbuf = new unsigned char[bufflen];
645 
646  m_frame = avcodec_alloc_frame();
647 
648  m_frameFile = getTempDirectory() + "work/frame.jpg";
649 
650  return true;
651 }
652 
654 {
655  if (m_deleteMap.isEmpty() || !m_archiveItem->useCutlist)
656  return frameNumber;
657 
658  int diff = 0;
659  frm_dir_map_t::const_iterator it = m_deleteMap.find(frameNumber);
660 
661  for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
662  {
663  int start = it.key();
664 
665  ++it;
666  if (it == m_deleteMap.end())
667  {
668  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: found a start cut but no cut end");
669  break;
670  }
671 
672  int end = it.key();
673 
674  if (start <= frameNumber + diff)
675  diff += end - start;
676  }
677 
678  m_offset = diff;
679  return frameNumber + diff;
680 }
681 
682 bool ThumbFinder::seekToFrame(int frame, bool checkPos)
683 {
684  // make sure the frame is not in a cut point
685  if (checkPos)
686  frame = checkFramePosition(frame);
687 
688  // seek to a position PRE_SEEK_AMOUNT frames before the required frame
689  int64_t timestamp = m_startTime + (frame * m_frameTime) -
690  (PRE_SEEK_AMOUNT * m_frameTime);
691  int64_t requiredPTS = m_startPTS + (frame * m_frameTime);
692 
693  if (timestamp < m_startTime)
694  timestamp = m_startTime;
695 
696  if (av_seek_frame(m_inputFC, m_videostream, timestamp, AVSEEK_FLAG_ANY) < 0)
697  {
698  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder::SeekToFrame: seek failed") ;
699  return false;
700  }
701 
702  avcodec_flush_buffers(m_codecCtx);
703  getFrameImage(true, requiredPTS);
704 
705  return true;
706 }
707 
709 {
710  int inc;
711  int64_t currentFrame = (m_currentPTS - m_startPTS) / m_frameTime;
712  int64_t newFrame;
713 
714  inc = SeekAmounts[m_currentSeek].amount;
715 
716  if (inc == -1)
717  inc = 1;
718  else if (inc == -2)
719  {
720  int pos = 0;
721  frm_dir_map_t::const_iterator it;
722  for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
723  {
724  if (it.key() > (uint64_t)currentFrame)
725  {
726  pos = it.key();
727  break;
728  }
729  }
730  // seek to next cutpoint
731  m_offset = 0;
732  seekToFrame(pos, false);
733  return true;
734  }
735  else
736  inc = (int) (inc * ceil(m_fps));
737 
738  newFrame = currentFrame + inc - m_offset;
739  if (newFrame == currentFrame + 1)
740  getFrameImage(false);
741  else
742  seekToFrame(newFrame);
743 
744  return true;
745 }
746 
748 {
749  int inc;
750  int64_t newFrame;
751  int64_t currentFrame = (m_currentPTS - m_startPTS) / m_frameTime;
752 
753  inc = SeekAmounts[m_currentSeek].amount;
754  if (inc == -1)
755  inc = -1;
756  else if (inc == -2)
757  {
758  // seek to previous cut point
759  frm_dir_map_t::const_iterator it;
760  int pos = 0;
761  for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
762  {
763  if (it.key() >= (uint64_t)currentFrame)
764  break;
765 
766  pos = it.key();
767  }
768 
769  // seek to next cutpoint
770  m_offset = 0;
771  seekToFrame(pos, false);
772  return true;
773  }
774  else
775  inc = (int) (-inc * ceil(m_fps));
776 
777  newFrame = currentFrame + inc - m_offset;
778  seekToFrame(newFrame);
779 
780  return true;
781 }
782 
783 // Note: copied this function from myth_imgconvert.cpp -- dtk 2009-08-17
785  AVPicture *dst, PixelFormat dst_pix_fmt, AVPicture *src,
786  PixelFormat pix_fmt, int width, int height)
787 {
788  static QMutex lock;
789  QMutexLocker locker(&lock);
790 
791  static struct SwsContext *convert_ctx;
792 
793  convert_ctx = sws_getCachedContext(convert_ctx, width, height, pix_fmt,
794  width, height, dst_pix_fmt,
795  SWS_FAST_BILINEAR, NULL, NULL, NULL);
796  if (!convert_ctx)
797  {
798  LOG(VB_GENERAL, LOG_ERR, "myth_sws_img_convert: Cannot initialize "
799  "the image conversion context");
800  return -1;
801  }
802 
803  sws_scale(convert_ctx, src->data, src->linesize,
804  0, height, dst->data, dst->linesize);
805 
806  return 0;
807 }
808 
809 bool ThumbFinder::getFrameImage(bool needKeyFrame, int64_t requiredPTS)
810 {
811  AVPacket pkt;
812  AVPicture orig;
813  AVPicture retbuf;
814  memset(&orig, 0, sizeof(AVPicture));
815  memset(&retbuf, 0, sizeof(AVPicture));
816 
817  av_init_packet(&pkt);
818 
819  int frameFinished = 0;
820  int keyFrame;
821  int frameCount = 0;
822  bool gotKeyFrame = false;
823 
824  while (av_read_frame(m_inputFC, &pkt) >= 0 && !frameFinished)
825  {
826  if (pkt.stream_index == m_videostream)
827  {
828  frameCount++;
829 
830  keyFrame = pkt.flags & AV_PKT_FLAG_KEY;
831 
832  if (m_startPTS == -1 && pkt.dts != (int64_t)AV_NOPTS_VALUE)
833  {
834  m_startPTS = pkt.dts;
835  m_frameTime = pkt.duration;
836  }
837 
838  if (keyFrame)
839  gotKeyFrame = true;
840 
841  if (!gotKeyFrame && needKeyFrame)
842  {
843  av_free_packet(&pkt);
844  continue;
845  }
846 
847  if (m_firstIFramePTS == -1)
848  m_firstIFramePTS = pkt.dts;
849 
850  avcodec_decode_video2(m_codecCtx, m_frame, &frameFinished, &pkt);
851 
852  if (requiredPTS != -1 && pkt.dts != (int64_t)AV_NOPTS_VALUE && pkt.dts < requiredPTS)
853  frameFinished = false;
854 
855  m_currentPTS = pkt.dts;
856  }
857 
858  av_free_packet(&pkt);
859  }
860 
861  if (frameFinished)
862  {
863  avpicture_fill(&retbuf, m_outputbuf, PIX_FMT_RGB32, m_frameWidth, m_frameHeight);
864 
865  avpicture_deinterlace((AVPicture*)m_frame, (AVPicture*)m_frame,
867 
869  &retbuf, PIX_FMT_RGB32,
871 
873  QImage::Format_RGB32);
874 
875  QByteArray ffile = m_frameFile.toLocal8Bit();
876  if (!img.save(ffile.constData(), "JPEG"))
877  {
878  LOG(VB_GENERAL, LOG_ERR, "Failed to save thumb: " + m_frameFile);
879  }
880 
881  if (m_updateFrame)
882  {
883  MythImage *mimage =
885  mimage->Assign(img);
886  m_frameImage->SetImage(mimage);
887  mimage->DecrRef();
888  }
889 
891  }
892 
893  return true;
894 }
895 
897 {
898  if (m_outputbuf)
899  delete[] m_outputbuf;
900 
901  // free the frame
902  av_free(m_frame);
903 
904  // close the codec
905  if (m_codecCtx)
906  avcodec_close(m_codecCtx);
907 
908  // close the video file
909  m_inputFC.Close();
910 }
911 
913 {
914  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
915  MythDialogBox *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
916 
917  if (menuPopup->Create())
918  popupStack->AddScreen(menuPopup);
919 
920  menuPopup->SetReturnEvent(this, "action");
921 
922  menuPopup->AddButton(tr("Exit, Save Thumbnails"), SLOT(savePressed()));
923  menuPopup->AddButton(tr("Exit, Don't Save Thumbnails"), SLOT(cancelPressed()));
924 }
925 
927 {
928  if (!m_positionImage)
929  return;
930 
931  QSize size = m_positionImage->GetArea().size();
932  QPixmap *pixmap = new QPixmap(size.width(), size.height());
933 
934  QPainter p(pixmap);
935  QBrush brush(Qt::green);
936 
937  p.setBrush(brush);
938  p.setPen(Qt::NoPen);
939  p.fillRect(0, 0, size.width(), size.height(), brush);
940 
941  frm_dir_map_t::const_iterator it;
942 
943  brush.setColor(Qt::red);
944  double startdelta, enddelta;
945 
946  for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
947  {
948  if (it.key() != 0)
949  startdelta = (m_archiveItem->duration * m_fps) / it.key();
950  else
951  startdelta = size.width();
952 
953  ++it;
954  if (it == m_deleteMap.end())
955  {
956  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: found a start cut but no cut end");
957  break;
958  }
959 
960  if (it.key() != 0)
961  enddelta = (m_archiveItem->duration * m_fps) / it.key();
962  else
963  enddelta = size.width();
964  int start = (int) (size.width() / startdelta);
965  int end = (int) (size.width() / enddelta);
966  p.fillRect(start - 1, 0, end - start, size.height(), brush);
967  }
968 
969  if (frame == 0)
970  frame = 1;
971  brush.setColor(Qt::yellow);
972  int pos = (int) (size.width() / ((m_archiveItem->duration * m_fps) / frame));
973  p.fillRect(pos, 0, 3, size.height(), brush);
974 
976  image->Assign(*pixmap);
977  m_positionImage->SetImage(image);
978 
979  p.end();
980  delete pixmap;
981 }
982 
984 {
985  if (m_archiveItem->type == "Recording")
986  {
988  {
989  frm_dir_map_t::const_iterator it;
990 
991  int start, end, cutLen = 0;
992 
993  for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
994  {
995  start = it.key();
996 
997  ++it;
998  if (it == m_deleteMap.end())
999  {
1000  LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: found a start cut but no cut end");
1001  break;
1002  }
1003 
1004  end = it.key();
1005  cutLen += end - start;
1006  }
1007  return m_archiveItem->duration - (int) (cutLen / m_fps);
1008  }
1009  }
1010 
1011  return m_archiveItem->duration;
1012 }
1013 
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=NULL)
Definition: mythuiutils.h:26
void updateCurrentPos(void)
QString filename
Definition: archiveutil.h:47
struct SeekAmount SeekAmounts[]
Definition: thumbfinder.cpp:72
int m_videostream
Definition: thumbfinder.h:89
bool keyPressEvent(QKeyEvent *)
Key event handler.
typedef void(__LZO_CDECL *lzo_free_func_t)(lzo_callback_p self
void SetImage(MythImage *img)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
QString caption
Definition: archiveutil.h:46
bool getThumbImages(void)
QString frameToTime(int64_t frame, bool addFrame=false)
void cancelPressed(void)
int64_t m_currentPTS
Definition: thumbfinder.h:93
bool QueryCutList(frm_dir_map_t &, bool loadAutosave=false) const
QString getTempDirectory(bool showError)
Definition: archiveutil.cpp:75
AVCodecContext * m_codecCtx
Definition: thumbfinder.h:80
QString m_frameFile
Definition: thumbfinder.h:86
bool hasCutlist
Definition: archiveutil.h:69
void SetRedraw(void)
Definition: mythuitype.cpp:306
bool useCutlist
Definition: archiveutil.h:70
Basic menu dialog, message and a list of options.
int m_frameWidth
Definition: thumbfinder.h:87
ArchiveItem * m_archiveItem
Definition: thumbfinder.h:101
struct ThumbImage ThumbImage
virtual bool Create(void)
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:166
AllMusic * parent
MythScreenStack * GetStack(const QString &stackname)
int64_t m_startTime
Definition: thumbfinder.h:91
const char * filename
Definition: ioapi.h:135
unsigned int uint
Definition: compat.h:135
MythUIButtonListItem * GetItemCurrent() const
bool getFileDetails(ArchiveItem *a)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
long long copy(QFile &dst, QFile &src, uint block_size)
Copies src file to dst file.
void changeSeekAmount(bool up)
frm_dir_map_t m_deleteMap
Definition: thumbfinder.h:97
static int myth_sws_img_convert(AVPicture *dst, PixelFormat dst_pix_fmt, AVPicture *src, PixelFormat pix_fmt, int width, int height)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:72
void BuildFocusList(void)
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache...
qint64 frame
Definition: archiveutil.h:48
int64_t m_firstIFramePTS
Definition: thumbfinder.h:94
return memset(s, c, len)
AVFrame * m_frame
Definition: thumbfinder.h:82
QString filename
Definition: archiveutil.h:60
virtual void Close()
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
Holds information on recordings and videos.
Definition: programinfo.h:72
MythUIButton * m_frameButton
Definition: thumbfinder.h:107
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
int m_currentSeek
Definition: thumbfinder.h:90
int checkFramePosition(int frameNumber)
MythUIButtonList * m_imageGrid
Definition: thumbfinder.h:112
bool Create(void)
int SeekAmountsCount
Definition: thumbfinder.cpp:85
int calcFinalDuration(void)
bool initAVCodec(const QString &inFile)
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area...
Definition: mythuitype.cpp:871
virtual bool NextPrevWidgetFocus(bool up_or_down)
int GetCurrentPos() const
QString GetShareDir(void)
Definition: mythdirs.cpp:145
MythUIButton * m_saveButton
Definition: thumbfinder.h:108
bool getFrameImage(bool needKeyFrame=true, int64_t requiredPTS=-1)
RemoteAVFormatContext m_inputFC
Definition: thumbfinder.h:79
void gridItemChanged(MythUIButtonListItem *item)
bool seekToFrame(int frame, bool checkPos=true)
void SetText(const QString &text, const QString &name="", const QString &state="")
MythPainter * GetCurrentPainter()
bool m_updateFrame
Definition: thumbfinder.h:96
const char * name
Definition: ParseText.cpp:338
MythUIType * GetFocusWidget(void) const
void showMenu(void)
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
lzo_uintptr_t p
Definition: minilzo.cpp:2207
QString m_thumbDir
Definition: thumbfinder.h:104
QString type
Definition: archiveutil.h:54
ThumbFinder(MythScreenStack *parent, ArchiveItem *archiveItem, const QString &menuTheme)
Definition: thumbfinder.cpp:87
MythMainWindow * GetMythMainWindow(void)
void Init(void)
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
void savePressed(void)
line height
Definition: MythXMLTest.css:26
GLint GLenum GLsizei width
void loadCutList(void)
MythUIImage * m_positionImage
Definition: thumbfinder.h:111
virtual bool keyPressEvent(QKeyEvent *)
Key event handler.
float m_fps
Definition: thumbfinder.h:84
void closeAVCodec()
MythUIButton * m_cancelButton
Definition: thumbfinder.h:109
void SetReturnEvent(QObject *retobject, const QString &resultid)
MythUIText * m_currentPosText
Definition: thumbfinder.h:114
void updateThumb(void)
QString createThumbDir(void)
bool SetFocusWidget(MythUIType *widget=NULL)
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true) MUNUSED_RESULT
Get a list of actions for a keypress in the given context.
bool Open(const QString &filename)
GLuint GLfloat x
lzo_uint lzo_bytep dst
Definition: lzoconf.h:237
int getChapterCount(const QString &menuTheme)
QList< ThumbImage * > m_thumbList
Definition: thumbfinder.h:103
void SetItemCurrent(MythUIButtonListItem *item)
MythUIImage * m_frameImage
Definition: thumbfinder.h:110
void av_free(void *ptr)
int m_thumbCount
Definition: thumbfinder.h:102
typedef int
Definition: lzoconf.h:279
ProgramInfo * getProgramInfoForFile(const QString &inFile)
MythUIButton * m_saveButton
unsigned char * m_outputbuf
Definition: thumbfinder.h:85
Screen in which all other widgets are contained and rendered.
void Assign(const QImage &img)
Definition: mythimage.cpp:97
int m_frameHeight
Definition: thumbfinder.h:88
lzo_uint lzo_uint size
Definition: lzoconf.h:273
AVCodec * m_codec
Definition: thumbfinder.h:81
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
struct AVPicture AVPicture
int64_t m_startPTS
Definition: thumbfinder.h:92
QList< ThumbImage * > thumbList
Definition: archiveutil.h:72
bool seekForward()
src
Definition: minilzo.cpp:2074
MythUIButton * m_cancelButton
bool seekBackward()
void updatePositionBar(int64_t frame)
MythUIText * m_seekAmountText
Definition: thumbfinder.h:113
int m_finalDuration
Definition: thumbfinder.h:98
int m_frameTime
Definition: thumbfinder.h:95