00001 #include <math.h>
00002 #include <cstdlib>
00003
00004
00005 #include <qdir.h>
00006 #include <qapplication.h>
00007 #include <qfileinfo.h>
00008 #include <qsqldatabase.h>
00009 #include <qprocess.h>
00010
00011
00012 #include <mythtv/mythcontext.h>
00013 #include <mythtv/mythdbcon.h>
00014 #include <mythtv/libmythtv/programinfo.h>
00015
00016
00017 #include "thumbfinder.h"
00018
00019
00020 #define PRE_SEEK_AMOUNT 50
00021
00022 struct SeekAmount SeekAmounts[] =
00023 {
00024 {"frame", -1},
00025 {"1 second", 1},
00026 {"5 seconds", 5},
00027 {"10 seconds", 10},
00028 {"30 seconds", 30},
00029 {"1 minute", 60},
00030 {"5 minutes", 300},
00031 {"10 minutes", 600},
00032 {"Cut Point", -2},
00033 };
00034
00035 int SeekAmountsCount = sizeof(SeekAmounts) / sizeof(SeekAmounts[0]);
00036
00037 ThumbFinder::ThumbFinder(ArchiveItem *archiveItem, const QString &menuTheme,
00038 MythMainWindow *parent, const QString &window_name,
00039 const QString &theme_filename, const char *name)
00040 :MythThemedDialog(parent, window_name, theme_filename, name)
00041 {
00042 m_archiveItem = archiveItem;
00043 m_thumbList.setAutoDelete(true);
00044
00045 m_thumbDir = createThumbDir();
00046
00047
00048 m_thumbList.clear();
00049 for (uint x = 0; x < m_archiveItem->thumbList.count(); x++)
00050 {
00051 ThumbImage *thumb = new ThumbImage;
00052 *thumb = *m_archiveItem->thumbList.at(x);
00053 m_thumbList.append(thumb);
00054 }
00055
00056 m_thumbCount = getChapterCount(menuTheme);
00057
00058 wireUpTheme();
00059 assignFirstFocus();
00060
00061 m_currentSeek = 0;
00062 m_offset = 0;
00063 m_startTime = -1;
00064 m_startPTS = -1;
00065 m_currentPTS = -1;
00066 m_firstIFramePTS = -1;
00067 m_popupMenu = NULL;
00068
00069 QTimer::singleShot(500, this, SLOT(getThumbImages()));
00070 }
00071
00072 ThumbFinder::~ThumbFinder()
00073 {
00074 m_thumbList.clear();
00075 closeAVCodec();
00076 }
00077
00078 int ThumbFinder::getChapterCount(const QString &menuTheme)
00079 {
00080 QString filename = gContext->GetShareDir() + "mytharchive/themes/" +
00081 menuTheme + "/theme.xml";
00082 QDomDocument doc("mydocument");
00083 QFile file(filename);
00084 if (!file.open(IO_ReadOnly))
00085 return 0;
00086
00087 if (!doc.setContent(&file))
00088 {
00089 file.close();
00090 return 0;
00091 }
00092 file.close();
00093
00094 QDomNodeList chapterNodeList = doc.elementsByTagName("chapter");
00095 return chapterNodeList.count();
00096 }
00097
00098 void ThumbFinder::loadCutList()
00099 {
00100 ProgramInfo *progInfo = getProgramInfoForFile(m_archiveItem->filename);
00101
00102 if (progInfo && m_archiveItem->hasCutlist)
00103 {
00104 progInfo->GetCutList(m_deleteMap);
00105 delete progInfo;
00106 }
00107 }
00108
00109 void ThumbFinder::keyPressEvent(QKeyEvent *e)
00110 {
00111 bool handled = false;
00112
00113 QStringList actions;
00114 gContext->GetMainWindow()->TranslateKeyPress("Global", e, actions);
00115
00116 for (unsigned int i = 0; i < actions.size() && !handled; i++)
00117 {
00118 QString action = actions[i];
00119 handled = true;
00120
00121
00122 if (action == "MENU")
00123 {
00124 nextPrevWidgetFocus(true);
00125 return;
00126 }
00127
00128 if (action == "ESCAPE")
00129 {
00130 showMenu();
00131 return;
00132 }
00133
00134 if (action == "0" || action == "1" || action == "2" || action == "3" ||
00135 action == "4" || action == "5" || action == "6" || action == "7" ||
00136 action == "8" || action == "9")
00137 {
00138 m_imageGrid->setCurrentPos(action.toInt());
00139 int itemNo = m_imageGrid->getCurrentPos();
00140 ThumbImage *thumb = m_thumbList.at(itemNo);
00141 if (thumb)
00142 seekToFrame(thumb->frame);
00143 return;
00144 }
00145
00146 if (getCurrentFocusWidget() == m_imageGrid)
00147 {
00148
00149 if (m_imageGrid->handleKeyPress(action))
00150 return;
00151 }
00152
00153 if (getCurrentFocusWidget() == m_frameButton)
00154 {
00155 if (action == "UP")
00156 {
00157 changeSeekAmount(true);
00158 }
00159 else if (action == "DOWN")
00160 {
00161 changeSeekAmount(false);
00162 }
00163 else if (action == "LEFT")
00164 {
00165 seekBackward();
00166 }
00167 else if (action == "RIGHT")
00168 {
00169 seekForward();
00170 }
00171 else if (action == "SELECT")
00172 {
00173 updateThumb();
00174 }
00175 else if (action == "ESCAPE")
00176 {
00177 }
00178 }
00179 else
00180 {
00181 if (action == "SELECT")
00182 {
00183 activateCurrent();
00184 }
00185 else if (action == "UP")
00186 {
00187 nextPrevWidgetFocus(false);
00188 }
00189 else if (action == "DOWN")
00190 {
00191 nextPrevWidgetFocus(true);
00192 }
00193 else if (action == "LEFT")
00194 {
00195 nextPrevWidgetFocus(false);
00196 }
00197 else if (action == "RIGHT")
00198 {
00199 nextPrevWidgetFocus(true);
00200 }
00201 else if (action == "ESCAPE")
00202 {
00203 handled = false;
00204 }
00205 else
00206 handled = false;
00207 }
00208 }
00209
00210 if (!handled)
00211 MythThemedDialog::keyPressEvent(e);
00212 }
00213
00214 void ThumbFinder::wireUpTheme()
00215 {
00216 m_frameImage = getUIImageType("frameimage");
00217 m_positionImage = getUIImageType("positionimage");
00218
00219 m_imageGrid = getUIImageGridType("imagegrid");
00220 if (!m_imageGrid)
00221 {
00222 VERBOSE(VB_IMPORTANT, "ThumbFinder: Failed to get image grid.");
00223 exit(-1);
00224 }
00225 connect(m_imageGrid, SIGNAL(itemChanged(ImageGridItem *)),
00226 this, SLOT(gridItemChanged(ImageGridItem *)));
00227
00228
00229 m_saveButton = getUITextButtonType("save_button");
00230 if (m_saveButton)
00231 {
00232 m_saveButton->setText(tr("Save"));
00233 connect(m_saveButton, SIGNAL(pushed()), this, SLOT(savePressed()));
00234 }
00235
00236
00237 m_cancelButton = getUITextButtonType("cancel_button");
00238 if (m_cancelButton)
00239 {
00240 m_cancelButton->setText(tr("Cancel"));
00241 connect(m_cancelButton, SIGNAL(pushed()), this, SLOT(cancelPressed()));
00242 }
00243
00244
00245 m_frameButton = getUITextButtonType("frame_button");
00246
00247 m_seekAmountText = getUITextType("seekamount");
00248 m_currentPosText = getUITextType("currentpos");
00249
00250 buildFocusList();
00251 }
00252
00253 void ThumbFinder::savePressed()
00254 {
00255
00256 m_archiveItem->thumbList.clear();
00257 for (uint x = 0; x < m_thumbList.count(); x++)
00258 {
00259 ThumbImage *thumb = new ThumbImage;
00260 *thumb = *m_thumbList.at(x);
00261 m_archiveItem->thumbList.append(thumb);
00262 }
00263 done(Accepted);
00264 }
00265
00266 void ThumbFinder::cancelPressed()
00267 {
00268 done(Rejected);
00269 }
00270
00271 void ThumbFinder::updateCurrentPos()
00272 {
00273 int64_t pos = m_currentPTS - m_firstIFramePTS;
00274 int64_t frame = pos / m_frameTime;
00275
00276 if (m_currentPosText)
00277 m_currentPosText->SetText(frameToTime(frame, true));
00278
00279 updatePositionBar(frame);
00280 }
00281
00282 void ThumbFinder::changeSeekAmount(bool up)
00283 {
00284 if (up)
00285 {
00286 m_currentSeek++;
00287 if (m_currentSeek >= SeekAmountsCount)
00288 m_currentSeek = 0;
00289 }
00290 else
00291 {
00292 m_currentSeek--;
00293 if (m_currentSeek < 0)
00294 m_currentSeek = SeekAmountsCount - 1;
00295 }
00296
00297 m_seekAmountText->SetText(SeekAmounts[m_currentSeek].name);
00298 }
00299
00300 void ThumbFinder::gridItemChanged(ImageGridItem *item)
00301 {
00302 (void) item;
00303
00304 int itemNo = m_imageGrid->getCurrentPos();
00305 ThumbImage *thumb = m_thumbList.at(itemNo);
00306 if (thumb)
00307 seekToFrame(thumb->frame);
00308 }
00309
00310 static bool copyFile(const QString &src, const QString &dst)
00311 {
00312 const int bufferSize = 16*1024;
00313
00314 QFile s(src.ascii());
00315 QFile d(dst.ascii());
00316 char buffer[bufferSize];
00317 int len;
00318
00319 if (!s.open(IO_ReadOnly))
00320 return false;
00321
00322 if (!d.open(IO_WriteOnly))
00323 {
00324 s.close();
00325 return false;
00326 }
00327
00328 len = s.readBlock(buffer, bufferSize);
00329 do
00330 {
00331 d.writeBlock(buffer, len);
00332 len = s.readBlock(buffer, bufferSize);
00333 } while (len > 0);
00334
00335 s.close();
00336 d.close();
00337
00338 return true;
00339 }
00340
00341 QString ThumbFinder::createThumbDir(void)
00342 {
00343 QString thumbDir = getTempDirectory() + "config/thumbs";
00344
00345
00346 QDir dir(thumbDir);
00347 if (!dir.exists())
00348 {
00349 dir.mkdir(thumbDir);
00350 system("chmod 777 " + thumbDir);
00351 }
00352
00353 int x = 0;
00354 QString res;
00355 do
00356 {
00357 x++;
00358 res = QString(thumbDir + "/%1").arg(x);
00359 dir.setPath(res);
00360 } while (dir.exists());
00361
00362 dir.mkdir(res);
00363 system("chmod 777 " + res);
00364
00365 return res;
00366 }
00367
00368 void ThumbFinder::updateThumb(void)
00369 {
00370 int itemNo = m_imageGrid->getCurrentPos();
00371 ImageGridItem *item = m_imageGrid->getCurrentItem();
00372
00373 ThumbImage *thumb = m_thumbList.at(itemNo);
00374 if (!thumb)
00375 return;
00376
00377
00378 QString imageFile = thumb->filename;
00379 copyFile(m_frameFile, imageFile);
00380
00381
00382 QSize size = m_imageGrid->getImageItemSize();
00383
00384 if (item->pixmap)
00385 delete item->pixmap;
00386 item->pixmap = createScaledPixmap(imageFile, size.width(), size.height(),
00387 QImage::ScaleFree);
00388 int64_t pos = (int) ((m_currentPTS - m_startPTS) / m_frameTime);
00389 thumb->frame = pos - m_offset;
00390 if (itemNo != 0)
00391 {
00392 thumb->caption = frameToTime(thumb->frame);
00393 item->text = thumb->caption;
00394 }
00395
00396 m_imageGrid->refresh();
00397 }
00398
00399 QString ThumbFinder::frameToTime(int64_t frame, bool addFrame)
00400 {
00401 int hour, min, sec;
00402 QString str;
00403
00404 sec = (int) (frame / m_fps);
00405 frame = frame - (int) (sec * m_fps);
00406 min = sec / 60;
00407 sec %= 60;
00408 hour = min / 60;
00409 min %= 60;
00410
00411 if (addFrame)
00412 str = str.sprintf("%01d:%02d:%02d.%02d", hour, min, sec, (int) frame);
00413 else
00414 str = str.sprintf("%02d:%02d:%02d", hour, min, sec);
00415 return str;
00416 }
00417
00418 bool ThumbFinder::getThumbImages()
00419 {
00420 if (!getFileDetails(m_archiveItem))
00421 {
00422 VERBOSE(VB_IMPORTANT, QString("ThumbFinder:: Failed to get file details for %1")
00423 .arg(m_archiveItem->filename));
00424 return false;
00425 }
00426
00427 if (m_archiveItem->type == "Recording")
00428 loadCutList();
00429
00430 if (!initAVCodec(m_archiveItem->filename))
00431 return false;
00432
00433
00434 m_finalDuration = calcFinalDuration();
00435
00436 QString origFrameFile = m_frameFile;
00437
00438 m_updateFrame = true;
00439 getFrameImage();
00440
00441 int chapterLen = m_finalDuration / m_thumbCount;
00442 QString thumbList = "";
00443 QSize size = m_imageGrid->getImageItemSize();
00444 m_updateFrame = false;
00445
00446
00447 m_frameFile = m_thumbDir + "/title.jpg";
00448
00449
00450 ThumbImage *thumb = m_thumbList.at(0);
00451
00452 if (!thumb)
00453 {
00454
00455 thumb = new ThumbImage;
00456 thumb->filename = m_frameFile;
00457 thumb->frame = (int64_t) 0;
00458 thumb->caption = "Title";
00459 m_thumbList.append(thumb);
00460 }
00461 else
00462 m_frameFile = thumb->filename;
00463
00464 seekToFrame(thumb->frame);
00465 getFrameImage();
00466
00467 QPixmap *pixmap = createScaledPixmap(m_frameFile,
00468 size.width(), size.height(),
00469 QImage::ScaleFree);
00470
00471 ImageGridItem *item = new ImageGridItem(thumb->caption, pixmap, false, NULL);
00472 m_imageGrid->appendItem(item);
00473 m_imageGrid->refresh();
00474 qApp->processEvents();
00475
00476 for (int x = 1; x <= m_thumbCount; x++)
00477 {
00478 m_frameFile = QString(m_thumbDir + "/chapter-%1.jpg").arg(x);
00479
00480
00481 thumb = m_archiveItem->thumbList.at(x);
00482
00483 if (!thumb)
00484 {
00485 QString time;
00486 int chapter, hour, min, sec;
00487
00488 chapter = chapterLen * (x - 1);
00489 hour = chapter / 3600;
00490 min = (chapter % 3600) / 60;
00491 sec = chapter % 60;
00492 time = time.sprintf("%02d:%02d:%02d", hour, min, sec);
00493
00494 int64_t frame = (int64_t) (chapter * ceil(m_fps));
00495
00496
00497 thumb = new ThumbImage;
00498 thumb->filename = m_frameFile;
00499 thumb->frame = frame;
00500 thumb->caption = time;
00501 m_thumbList.append(thumb);
00502 }
00503 else
00504 m_frameFile = thumb->filename;
00505
00506 seekToFrame(thumb->frame);
00507 qApp->processEvents();
00508 getFrameImage();
00509 qApp->processEvents();
00510
00511 QPixmap *pixmap = createScaledPixmap(m_frameFile,
00512 size.width(), size.height(),
00513 QImage::ScaleFree);
00514
00515 ImageGridItem *item = new ImageGridItem(thumb->caption, pixmap, false, NULL);
00516 m_imageGrid->appendItem(item);
00517 m_imageGrid->refresh();
00518 qApp->processEvents();
00519 }
00520
00521 m_frameFile = origFrameFile;
00522 seekToFrame(0);
00523
00524 m_updateFrame = true;
00525
00526 m_imageGrid->setItemCount(m_thumbCount+1);
00527 m_imageGrid->recalculateLayout();
00528 m_imageGrid->refresh();
00529
00530 return true;
00531 }
00532
00533 QPixmap *ThumbFinder::createScaledPixmap(QString filename,
00534 int width, int height, QImage::ScaleMode mode)
00535 {
00536 QPixmap *pixmap = NULL;
00537
00538 if (filename != "")
00539 {
00540 QImage *img = gContext->LoadScaleImage(filename);
00541 if (!img)
00542 {
00543 VERBOSE(VB_IMPORTANT, QString("ThumbFinder: Failed to load image %1").arg(filename));
00544 return NULL;
00545 }
00546 else
00547 {
00548 pixmap = new QPixmap(img->smoothScale(width, height, mode));
00549 delete img;
00550 }
00551 }
00552
00553 return pixmap;
00554 }
00555
00556 bool ThumbFinder::initAVCodec(const QString &inFile)
00557 {
00558 int ret;
00559
00560 av_register_all();
00561
00562 m_inputFC = NULL;
00563
00564
00565 VERBOSE(VB_JOBQUEUE, QString("Opening %1").arg(inFile));
00566
00567 if ((ret = av_open_input_file(&m_inputFC, inFile.ascii(), NULL, 0, NULL)) != 0)
00568 {
00569 VERBOSE(VB_IMPORTANT,
00570 QString("Couldn't open input file, error #%1").arg(ret));
00571 return false;
00572 }
00573
00574
00575 if ((ret = av_find_stream_info(m_inputFC)) < 0)
00576 {
00577 VERBOSE(VB_IMPORTANT,
00578 QString("Couldn't get stream info, error #%1").arg(ret));
00579 av_close_input_file(m_inputFC);
00580 m_inputFC = NULL;
00581 return false;
00582 }
00583 av_estimate_timings(m_inputFC, 0);
00584 dump_format(m_inputFC, 0, inFile.ascii(), 0);
00585
00586
00587 m_videostream = -1;
00588
00589 for (uint i = 0; i < m_inputFC->nb_streams; i++)
00590 {
00591 AVStream *st = m_inputFC->streams[i];
00592 if (m_inputFC->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
00593 {
00594 m_startTime = -1;
00595 if (m_inputFC->streams[i]->start_time != (int) AV_NOPTS_VALUE)
00596 m_startTime = m_inputFC->streams[i]->start_time;
00597 else
00598 {
00599 VERBOSE(VB_IMPORTANT, "ThumbFinder: Failed to get start time");
00600 return false;
00601 }
00602
00603 m_videostream = i;
00604 m_frameWidth = st->codec->width;
00605 m_frameHeight = st->codec->height;
00606 if (st->r_frame_rate.den && st->r_frame_rate.num)
00607 m_fps = av_q2d(st->r_frame_rate);
00608 else
00609 m_fps = 1/av_q2d(st->time_base);
00610 break;
00611 }
00612 }
00613
00614 if (m_videostream == -1)
00615 {
00616 VERBOSE(VB_IMPORTANT, "Couldn't find a video stream");
00617 return false;
00618 }
00619
00620
00621 m_codecCtx = m_inputFC->streams[m_videostream]->codec;
00622 m_codecCtx->debug_mv = 0;
00623 m_codecCtx->debug = 0;
00624 m_codecCtx->workaround_bugs = 1;
00625 m_codecCtx->lowres = 0;
00626 m_codecCtx->idct_algo = FF_IDCT_AUTO;
00627 m_codecCtx->skip_frame = AVDISCARD_DEFAULT;
00628 m_codecCtx->skip_idct = AVDISCARD_DEFAULT;
00629 m_codecCtx->skip_loop_filter = AVDISCARD_DEFAULT;
00630 m_codecCtx->error_resilience = FF_ER_CAREFUL;
00631 m_codecCtx->error_concealment = 3;
00632
00633
00634 m_codec = avcodec_find_decoder(m_codecCtx->codec_id);
00635
00636 if (m_codec == NULL)
00637 {
00638 VERBOSE(VB_IMPORTANT, "ThumbFinder: Couldn't find codec for video stream");
00639 return false;
00640 }
00641
00642
00643 if (avcodec_open(m_codecCtx, m_codec) < 0)
00644 {
00645 VERBOSE(VB_IMPORTANT, "ThumbFinder: Couldn't open codec for video stream");
00646 return false;
00647 }
00648
00649
00650 int bufflen = m_frameWidth * m_frameHeight * 4;
00651 m_outputbuf = new unsigned char[bufflen];
00652
00653 m_frame = avcodec_alloc_frame();
00654
00655 m_frameFile = getTempDirectory() + "work/frame.jpg";
00656
00657 return true;
00658 }
00659
00660 int ThumbFinder::checkFramePosition(int frameNumber)
00661 {
00662 if (m_deleteMap.isEmpty() || !m_archiveItem->useCutlist)
00663 return frameNumber;
00664
00665 int diff = 0;
00666 QMap<long long, int>::Iterator it = m_deleteMap.find(frameNumber);
00667
00668 for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
00669 {
00670 int start = it.key();
00671 ++it;
00672 int end = it.key();
00673
00674 if (start <= frameNumber + diff)
00675 diff += end - start;
00676 }
00677
00678 m_offset = diff;
00679 return frameNumber + diff;
00680 }
00681
00682 bool ThumbFinder::seekToFrame(int frame, bool checkPos)
00683 {
00684
00685 if (checkPos)
00686 frame = checkFramePosition(frame);
00687
00688
00689 int64_t timestamp = m_startTime + (frame * m_frameTime) - (PRE_SEEK_AMOUNT * m_frameTime);
00690 int64_t requiredPTS = m_startPTS + (frame * m_frameTime);
00691
00692 if (timestamp < m_startTime)
00693 timestamp = m_startTime;
00694
00695 if (av_seek_frame(m_inputFC, m_videostream, timestamp, AVSEEK_FLAG_ANY) < 0)
00696 {
00697 VERBOSE(VB_IMPORTANT, "ThumbFinder::SeekToFrame: seek failed") ;
00698 return false;
00699 }
00700
00701 avcodec_flush_buffers(m_codecCtx);
00702 getFrameImage(true, requiredPTS);
00703
00704 return true;
00705 }
00706
00707 bool ThumbFinder::seekForward()
00708 {
00709 int inc;
00710 int64_t currentFrame = (m_currentPTS - m_startPTS) / m_frameTime;
00711 int64_t newFrame;
00712
00713 inc = SeekAmounts[m_currentSeek].amount;
00714
00715 if (inc == -1)
00716 inc = 1;
00717 else if (inc == -2)
00718 {
00719 int pos = 0;
00720 QMap<long long, int>::Iterator it;
00721 for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
00722 {
00723 if (it.key() > currentFrame)
00724 {
00725 pos = it.key();
00726 break;
00727 }
00728 }
00729
00730 m_offset = 0;
00731 seekToFrame(pos, false);
00732 return true;
00733 }
00734 else
00735 inc = (int) (inc * ceil(m_fps));
00736
00737 newFrame = currentFrame + inc - m_offset;
00738 if (newFrame == currentFrame + 1)
00739 getFrameImage(false);
00740 else
00741 seekToFrame(newFrame);
00742
00743 return true;
00744 }
00745
00746 bool ThumbFinder::seekBackward()
00747 {
00748 int inc;
00749 int64_t newFrame;
00750 int64_t currentFrame = (m_currentPTS - m_startPTS) / m_frameTime;
00751
00752 inc = SeekAmounts[m_currentSeek].amount;
00753 if (inc == -1)
00754 inc = -1;
00755 else if (inc == -2)
00756 {
00757
00758 QMap<long long, int>::Iterator it;
00759 int pos = 0;
00760 for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
00761 {
00762 if (it.key() >= currentFrame)
00763 break;
00764
00765 pos = it.key();
00766 }
00767
00768
00769 m_offset = 0;
00770 seekToFrame(pos, false);
00771 return true;
00772 }
00773 else
00774 inc = (int) (-inc * ceil(m_fps));
00775
00776 newFrame = currentFrame + inc - m_offset;
00777 seekToFrame(newFrame);
00778
00779 return true;
00780 }
00781
00782 bool ThumbFinder::getFrameImage(bool needKeyFrame, int64_t requiredPTS)
00783 {
00784 AVPacket pkt;
00785 AVPicture orig;
00786 AVPicture retbuf;
00787 bzero(&orig, sizeof(AVPicture));
00788 bzero(&retbuf, sizeof(AVPicture));
00789
00790 av_init_packet(&pkt);
00791
00792 int frameFinished = 0;
00793 int keyFrame;
00794 int frameCount = 0;
00795 bool gotKeyFrame = false;
00796
00797 while (av_read_frame(m_inputFC, &pkt) >= 0 && !frameFinished)
00798 {
00799 if (pkt.stream_index == m_videostream)
00800 {
00801 frameCount++;
00802
00803 keyFrame = pkt.flags & PKT_FLAG_KEY;
00804
00805 if (m_startPTS == -1 && pkt.dts != (int64_t)AV_NOPTS_VALUE)
00806 {
00807 m_startPTS = pkt.dts;
00808 m_frameTime = pkt.duration;
00809 }
00810
00811 if (keyFrame)
00812 gotKeyFrame = true;
00813
00814 if (!gotKeyFrame && needKeyFrame)
00815 {
00816 av_free_packet(&pkt);
00817 continue;
00818 }
00819
00820 if (m_firstIFramePTS == -1)
00821 m_firstIFramePTS = pkt.dts;
00822
00823 avcodec_decode_video(m_codecCtx, m_frame, &frameFinished, pkt.data, pkt.size);
00824
00825 if (requiredPTS != -1 && pkt.dts != (int64_t)AV_NOPTS_VALUE && pkt.dts < requiredPTS)
00826 frameFinished = false;
00827
00828 m_currentPTS = pkt.dts;
00829 }
00830
00831 av_free_packet(&pkt);
00832 }
00833
00834 if (frameFinished)
00835 {
00836 avpicture_fill(&retbuf, m_outputbuf, PIX_FMT_RGBA32, m_frameWidth, m_frameHeight);
00837
00838 avpicture_deinterlace((AVPicture*)m_frame, (AVPicture*)m_frame,
00839 m_codecCtx->pix_fmt, m_frameWidth, m_frameHeight);
00840
00841 img_convert(&retbuf, PIX_FMT_RGBA32,
00842 (AVPicture*) m_frame, m_codecCtx->pix_fmt, m_frameWidth, m_frameHeight);
00843
00844 QImage img(m_outputbuf, m_frameWidth, m_frameHeight, 32, NULL,
00845 65536 * 65536, QImage::LittleEndian);
00846
00847 if (!img.save(m_frameFile.ascii(), "JPEG"))
00848 {
00849 VERBOSE(VB_IMPORTANT, "Failed to save thumb: " + m_frameFile);
00850 }
00851 if (m_updateFrame)
00852 {
00853 m_frameImage->SetImage(m_frameFile);
00854 m_frameImage->LoadImage();
00855 }
00856
00857 updateCurrentPos();
00858 }
00859
00860 return true;
00861 }
00862
00863 void ThumbFinder::closeAVCodec()
00864 {
00865 if (m_outputbuf)
00866 delete[] m_outputbuf;
00867
00868
00869 av_free(m_frame);
00870
00871
00872 avcodec_close(m_codecCtx);
00873
00874
00875 av_close_input_file(m_inputFC);
00876 }
00877
00878 void ThumbFinder::showMenu()
00879 {
00880 if (m_popupMenu)
00881 return;
00882
00883 m_popupMenu = new MythPopupBox(gContext->GetMainWindow(),
00884 "popupMenu");
00885
00886 QButton *button;
00887 button = m_popupMenu->addButton(tr("Exit, Save Thumbnails"), this, SLOT(menuSavePressed()));
00888 button->setFocus();
00889
00890 m_popupMenu->addButton(tr("Exit, Don't Save Thumbnails"), this, SLOT(menuCancelPressed()));
00891 m_popupMenu->addButton(tr("Cancel"), this, SLOT(closePopupMenu()));
00892
00893 m_popupMenu->ShowPopup(this, SLOT(closePopupMenu()));
00894 }
00895
00896 void ThumbFinder::closePopupMenu(void)
00897 {
00898 if (m_popupMenu)
00899 {
00900 m_popupMenu->deleteLater();
00901 m_popupMenu = NULL;
00902 }
00903 }
00904
00905 void ThumbFinder::menuSavePressed()
00906 {
00907 closePopupMenu();
00908 savePressed();
00909 }
00910
00911 void ThumbFinder::menuCancelPressed()
00912 {
00913 closePopupMenu();
00914 cancelPressed();
00915 }
00916
00917 void ThumbFinder::updatePositionBar(int64_t frame)
00918 {
00919 if (!m_positionImage)
00920 return;
00921
00922 QSize size = m_positionImage->GetSize();
00923 QPixmap *pixmap = new QPixmap(size.width(), size.height(), -1);
00924
00925 QPainter p(pixmap);
00926 QBrush brush(green);
00927
00928 p.setBrush(brush);
00929 p.setPen(NoPen);
00930 p.fillRect(0, 0, size.width(), size.height(), brush);
00931
00932 QMap<long long, int>::Iterator it;
00933
00934 brush.setColor(red);
00935 double startdelta, enddelta;
00936
00937 for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
00938 {
00939 if (it.key() != 0)
00940 startdelta = (m_archiveItem->duration * m_fps) / it.key();
00941 else
00942 startdelta = size.width();
00943
00944 ++it;
00945 if (it.key() != 0)
00946 enddelta = (m_archiveItem->duration * m_fps) / it.key();
00947 else
00948 enddelta = size.width();
00949 int start = (int) (size.width() / startdelta);
00950 int end = (int) (size.width() / enddelta);
00951 p.fillRect(start - 1, 0, end - start, size.height(), brush);
00952 }
00953
00954 if (frame == 0)
00955 frame = 1;
00956 brush.setColor(yellow);
00957 int pos = (int) (size.width() / ((m_archiveItem->duration * m_fps) / frame));
00958 p.fillRect(pos, 0, 3, size.height(), brush);
00959
00960 m_positionImage->SetImage(*pixmap);
00961 m_positionImage->refresh();
00962
00963 p.end();
00964 delete pixmap;
00965 }
00966
00967 int ThumbFinder::calcFinalDuration()
00968 {
00969 if (m_archiveItem->type == "Recording")
00970 {
00971 if (m_archiveItem->useCutlist)
00972 {
00973 QMap<long long, int>::Iterator it;
00974
00975 int start, end, cutLen = 0;
00976
00977 for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it)
00978 {
00979 start = it.key();
00980 ++it;
00981 end = it.key();
00982 cutLen += end - start;
00983 }
00984 return m_archiveItem->duration - (int) (cutLen / m_fps);
00985 }
00986 }
00987
00988 return m_archiveItem->duration;
00989 }
00990