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