00001 #include <cmath>
00002 #include <stdint.h>
00003
00004 #include "mythlogging.h"
00005 #include "mythcontext.h"
00006 #include "osd.h"
00007 #include "deletemap.h"
00008 #include "mythplayer.h"
00009
00010 #define LOC QString("DelMap: ")
00011 #define EDIT_CHECK do { \
00012 if(!m_editing) { \
00013 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot edit outside edit mode."); \
00014 return; \
00015 } \
00016 } while(0)
00017
00018 DeleteMapUndoEntry::DeleteMapUndoEntry(frm_dir_map_t dm, QString msg) :
00019 deleteMap(dm), message(msg) { }
00020
00021 DeleteMapUndoEntry::DeleteMapUndoEntry(void)
00022 {
00023 frm_dir_map_t dm;
00024 deleteMap = dm;
00025 message = "";
00026 }
00027
00028 void DeleteMap::Push(QString undoMessage)
00029 {
00030 DeleteMapUndoEntry entry(m_deleteMap, undoMessage);
00031
00032 while (m_undoStack.size() > m_undoStackPointer + 1)
00033 m_undoStack.pop_back();
00034 m_undoStack.append(entry);
00035 m_undoStackPointer ++;
00036 SaveMap(0, true);
00037 }
00038
00039 bool DeleteMap::Undo(void)
00040 {
00041 if (!HasUndo())
00042 return false;
00043 m_undoStackPointer --;
00044 m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
00045 m_changed = true;
00046 SaveMap(0, true);
00047 return true;
00048 }
00049
00050 bool DeleteMap::Redo(void)
00051 {
00052 if (!HasRedo())
00053 return false;
00054 m_undoStackPointer ++;
00055 m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
00056 m_changed = true;
00057 SaveMap(0, true);
00058 return true;
00059 }
00060
00061 QString DeleteMap::GetUndoMessage(void) const
00062 {
00063 return (HasUndo() ? m_undoStack[m_undoStackPointer].message :
00064 QObject::tr("(Nothing to undo)"));
00065 }
00066
00067 QString DeleteMap::GetRedoMessage(void) const
00068 {
00069 return (HasRedo() ? m_undoStack[m_undoStackPointer + 1].message :
00070 QObject::tr("(Nothing to redo)"));
00071 }
00072
00073 bool DeleteMap::HandleAction(QString &action, uint64_t frame,
00074 uint64_t played, uint64_t total, double rate)
00075 {
00076 bool handled = true;
00077 if (action == ACTION_UP)
00078 UpdateSeekAmount(1, rate);
00079 else if (action == ACTION_DOWN)
00080 UpdateSeekAmount(-1, rate);
00081 else if (action == ACTION_CLEARMAP)
00082 Clear(QObject::tr("Clear Cuts"));
00083 else if (action == ACTION_INVERTMAP)
00084 ReverseAll(total);
00085 else if (action == "MOVEPREV")
00086 MoveRelative(frame, total, false);
00087 else if (action == "MOVENEXT")
00088 MoveRelative(frame, total, true);
00089 else if (action == "CUTTOBEGINNING")
00090 Add(frame, total, MARK_CUT_END, QObject::tr("Cut to Beginning"));
00091 else if (action == "CUTTOEND")
00092 {
00093 Add(frame, total, MARK_CUT_START, QObject::tr("Cut to End"));
00094
00095
00096 if (m_ctx->player && m_ctx->player->IsWatchingInprogress())
00097 Add(total - 1, total, MARK_CUT_END, "");
00098 }
00099 else if (action == "NEWCUT")
00100 NewCut(frame, total);
00101 else if (action == "DELETE")
00102 Delete(frame, total, QObject::tr("Delete"));
00103 else if (action == "UNDO")
00104 Undo();
00105 else if (action == "REDO")
00106 Redo();
00107 else
00108 handled = false;
00109 return handled;
00110 }
00111
00112 void DeleteMap::UpdateSeekAmount(int change, double framerate)
00113 {
00114 m_seekamountpos += change;
00115 if (m_seekamountpos > 9)
00116 m_seekamountpos = 9;
00117 if (m_seekamountpos < 0)
00118 m_seekamountpos = 0;
00119
00120 m_seekText = "";
00121 switch (m_seekamountpos)
00122 {
00123 case 0: m_seekText = QObject::tr("cut point"); m_seekamount = -2; break;
00124 case 1: m_seekText = QObject::tr("keyframe"); m_seekamount = -1; break;
00125 case 2: m_seekText = QObject::tr("1 frame"); m_seekamount = 1; break;
00126 case 3: m_seekText = QObject::tr("0.5 seconds"); m_seekamount = (int)roundf(framerate / 2); break;
00127 case 4: m_seekText = QObject::tr("%n second(s)", "", 1); m_seekamount = (int)roundf(framerate); break;
00128 case 5: m_seekText = QObject::tr("%n second(s)", "", 5); m_seekamount = (int)roundf(framerate * 5); break;
00129 case 6: m_seekText = QObject::tr("%n second(s)", "", 20); m_seekamount = (int)roundf(framerate * 20); break;
00130 case 7: m_seekText = QObject::tr("%n minute(s)", "", 1); m_seekamount = (int)roundf(framerate * 60); break;
00131 case 8: m_seekText = QObject::tr("%n minute(s)", "", 5); m_seekamount = (int)roundf(framerate * 300); break;
00132 case 9: m_seekText = QObject::tr("%n minute(s)", "", 10); m_seekamount = (int)roundf(framerate * 600); break;
00133 default: m_seekText = QObject::tr("error"); m_seekamount = (int)roundf(framerate); break;
00134 }
00135 }
00136
00137 static QString createTimeString(uint64_t frame, uint64_t total,
00138 double frame_rate, bool full_resolution)
00139 {
00140 int secs = (int)(frame / frame_rate);
00141 int frames = frame - (int)(secs * frame_rate);
00142 int totalSecs = (int)(total / frame_rate);
00143 QString timestr;
00144 if (totalSecs >= 3600)
00145 timestr = QString::number(secs / 3600) + ":";
00146 timestr += QString("%1").arg((secs / 60) % 60, 2, 10, QChar(48)) +
00147 QString(":%1").arg(secs % 60, 2, 10, QChar(48));
00148 if (full_resolution)
00149 timestr += QString(".%1").arg(frames, 2, 10, QChar(48));
00150 return timestr;
00151 }
00152
00157 void DeleteMap::UpdateOSD(uint64_t frame, uint64_t total, double frame_rate,
00158 OSD *osd)
00159 {
00160 if (!osd || !m_ctx)
00161 return;
00162 CleanMap(total);
00163
00164 InfoMap infoMap;
00165 m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00166 if (m_ctx->playingInfo)
00167 m_ctx->playingInfo->ToMap(infoMap);
00168 infoMap.detach();
00169 m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00170
00171 QString cutmarker = " ";
00172 if (IsInDelete(frame))
00173 cutmarker = QObject::tr("cut");
00174
00175 QString timestr = createTimeString(frame, total, frame_rate, true);
00176 uint64_t relTotal = TranslatePositionAbsToRel(total);
00177 QString relTimeDisplay = createTimeString(TranslatePositionAbsToRel(frame),
00178 relTotal, frame_rate, false);
00179 QString relLengthDisplay = createTimeString(relTotal,
00180 relTotal, frame_rate, false);
00181 infoMap["timedisplay"] = timestr;
00182 infoMap["framedisplay"] = QString::number(frame);
00183 infoMap["cutindicator"] = cutmarker;
00184 infoMap["title"] = QObject::tr("Edit");
00185 infoMap["seekamount"] = m_seekText;;
00186 infoMap["reltimedisplay"] = relTimeDisplay;
00187 infoMap["rellengthdisplay"] = relLengthDisplay;
00188 infoMap["fulltimedisplay"] = timestr + " (" +
00189 QObject::tr("%1 of %2").arg(relTimeDisplay).arg(relLengthDisplay) + ")";
00190
00191 QHash<QString,float> posMap;
00192 posMap.insert("position", (float)((double)frame/(double)total));
00193 osd->SetValues("osd_program_editor", posMap, kOSDTimeout_None);
00194 osd->SetText("osd_program_editor", infoMap, kOSDTimeout_None);
00195 if (m_changed || total != m_cachedTotalForOSD)
00196 osd->SetRegions("osd_program_editor", m_deleteMap, total);
00197 m_changed = false;
00198 m_cachedTotalForOSD = total;
00199 }
00200
00202 void DeleteMap::SetEditing(bool edit, OSD *osd)
00203 {
00204 if (osd && !edit)
00205 osd->HideWindow("osd_program_editor");
00206 m_editing = edit;
00207 }
00208
00210 void DeleteMap::SetFileEditing(bool edit)
00211 {
00212 if (m_ctx)
00213 {
00214 m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00215 if (m_ctx->playingInfo)
00216 m_ctx->playingInfo->SetEditing(edit);
00217 m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00218 }
00219 }
00220
00222 bool DeleteMap::IsFileEditing(void)
00223 {
00224 bool result = false;
00225 if (m_ctx)
00226 {
00227 m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00228 if (m_ctx->playingInfo)
00229 result = m_ctx->playingInfo->QueryIsEditing();
00230 m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00231 }
00232 return result;
00233 }
00234
00235 bool DeleteMap::IsEmpty(void) const
00236 {
00237 return m_deleteMap.empty();
00238 }
00239
00241 void DeleteMap::Clear(QString undoMessage)
00242 {
00243 m_deleteMap.clear();
00244 m_changed = true;
00245 if (!undoMessage.isEmpty())
00246 Push(undoMessage);
00247 }
00248
00250 void DeleteMap::ReverseAll(uint64_t total)
00251 {
00252 EDIT_CHECK;
00253 frm_dir_map_t::Iterator it = m_deleteMap.begin();
00254 for ( ; it != m_deleteMap.end(); ++it)
00255 Add(it.key(), it.value() == MARK_CUT_END ? MARK_CUT_START :
00256 MARK_CUT_END);
00257 CleanMap(total);
00258 Push(QObject::tr("Reverse Cuts"));
00259 }
00260
00266 void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type,
00267 QString undoMessage)
00268 {
00269 EDIT_CHECK;
00270 if ((MARK_CUT_START != type) && (MARK_CUT_END != type) &&
00271 (MARK_PLACEHOLDER != type))
00272 return;
00273
00274 frm_dir_map_t::Iterator find_temporary = m_deleteMap.find(frame);
00275 if (find_temporary != m_deleteMap.end())
00276 {
00277 if (MARK_PLACEHOLDER == find_temporary.value())
00278 {
00279
00280
00281 Delete(frame, total);
00282 }
00283 else
00284 return;
00285 }
00286
00287 int lasttype = MARK_UNSET;
00288 long long lastframe = -1;
00289 long long remove = -1;
00290 QMutableMapIterator<uint64_t, MarkTypes> it(m_deleteMap);
00291
00292 if (type == MARK_CUT_END)
00293 {
00294
00295 while (it.hasNext())
00296 {
00297 it.next();
00298 if (it.key() > frame)
00299 {
00300 if ((lasttype == MARK_CUT_END) && (lastframe > -1))
00301 remove = lastframe;
00302 break;
00303 }
00304 lasttype = it.value();
00305 lastframe = it.key();
00306 }
00307 if ((remove < 0) && (lasttype == MARK_CUT_END) &&
00308 (lastframe > -1) && (lastframe < (int64_t)frame))
00309 remove = lastframe;
00310 }
00311 else if (type == MARK_CUT_START)
00312 {
00313
00314 it.toBack();
00315 while (it.hasPrevious())
00316 {
00317 it.previous();
00318 if (it.key() <= frame)
00319 {
00320 if (lasttype == MARK_CUT_START && (lastframe > -1))
00321 remove = lastframe;
00322 break;
00323 }
00324 lasttype = it.value();
00325 lastframe = it.key();
00326 }
00327 if ((remove < 0) && (lasttype == MARK_CUT_START) &&
00328 (lastframe > -1) && (lastframe > (int64_t)frame))
00329 remove = lastframe;
00330 }
00331
00332 if (remove > -1)
00333 Delete((uint64_t)remove);
00334 Add(frame, type);
00335 CleanMap(total);
00336 if (!undoMessage.isEmpty())
00337 Push(undoMessage);
00338 }
00339
00341 void DeleteMap::Delete(uint64_t frame, uint64_t total, QString undoMessage)
00342 {
00343 EDIT_CHECK;
00344 if (m_deleteMap.isEmpty())
00345 return;
00346
00347 uint64_t prev = GetNearestMark(frame, total, false);
00348 uint64_t next = GetNearestMark(frame, total, true);
00349
00350
00351
00352 frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
00353 if (it != m_deleteMap.end())
00354 {
00355 int type = it.value();
00356 if (MARK_PLACEHOLDER == type)
00357 next = prev = frame;
00358 else if (MARK_CUT_END == type)
00359 next = frame;
00360 else if (MARK_CUT_START == type)
00361 prev = frame;
00362 }
00363
00364 Delete(prev);
00365 if (prev != next)
00366 Delete(next);
00367 CleanMap(total);
00368 if (!undoMessage.isEmpty())
00369 Push(undoMessage);
00370 }
00371
00373 void DeleteMap::NewCut(uint64_t frame, uint64_t total)
00374 {
00375 EDIT_CHECK;
00376
00377
00378 int64_t existing = -1;
00379 frm_dir_map_t::Iterator it;
00380 for (it = m_deleteMap.begin() ; it != m_deleteMap.end(); ++it)
00381 {
00382 if (MARK_PLACEHOLDER == it.value())
00383 {
00384 existing = it.key();
00385 break;
00386 }
00387 }
00388
00389 if (existing > -1)
00390 {
00391 uint64_t otherframe = static_cast<uint64_t>(existing);
00392 if (otherframe == frame)
00393 Delete(otherframe);
00394 else
00395 {
00396 uint64_t startframe;
00397 uint64_t endframe;
00398 int64_t cut_start = -1;
00399 int64_t cut_end = -1;
00400 if (IsInDelete(frame))
00401 {
00402 MarkTypes type = MARK_UNSET;
00403 cut_start = GetNearestMark(frame, total, false);
00404 cut_end = GetNearestMark(frame, total, true);
00405 frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
00406 if (it != m_deleteMap.end())
00407 type = it.value();
00408 if (MARK_CUT_START == type)
00409 {
00410 cut_start = frame;
00411 }
00412 else if (MARK_CUT_END == type)
00413 {
00414 cut_end = frame;
00415 }
00416 }
00417
00418 if (otherframe < frame)
00419 {
00420 startframe = otherframe;
00421 endframe = cut_end != -1 ? static_cast<uint64_t>(cut_end)
00422 : frame;
00423 }
00424 else
00425 {
00426 startframe = cut_start != -1 ? static_cast<uint64_t>(cut_start)
00427 : frame;
00428 endframe = otherframe;
00429 }
00430
00431
00432
00433 if (startframe == 1)
00434 startframe = 0;
00435 if (endframe >= total - 1)
00436 endframe = total;
00437
00438
00439 if ((startframe == 0) && (endframe == total))
00440 {
00441 LOG(VB_GENERAL, LOG_CRIT, LOC +
00442 "Refusing to cut entire recording.");
00443 return;
00444 }
00445
00446 Delete(otherframe);
00447 Add(startframe, MARK_CUT_START);
00448 Add(endframe, MARK_CUT_END);
00449
00450 otherframe = 0;
00451 frm_dir_map_t::Iterator it = m_deleteMap.find(startframe);
00452 for ( ; it != m_deleteMap.end() && otherframe < endframe; ++it)
00453 {
00454 otherframe = it.key();
00455 if ((startframe < otherframe) && (endframe > otherframe))
00456 {
00457 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00458 QString("Deleting bounded marker: %1").arg(otherframe));
00459 Delete(otherframe);
00460 }
00461 }
00462 }
00463 }
00464 else
00465 Add(frame, MARK_PLACEHOLDER);
00466
00467 CleanMap(total);
00468 Push(QObject::tr("New Cut"));
00469 }
00470
00472 void DeleteMap::MoveRelative(uint64_t frame, uint64_t total, bool right)
00473 {
00474 frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
00475 if (it != m_deleteMap.end())
00476 {
00477 int type = it.value();
00478 if (((MARK_CUT_START == type) && !right) ||
00479 ((MARK_CUT_END == type) && right))
00480 {
00481
00482
00483 return;
00484 }
00485 else if (((MARK_CUT_START == type) && right) ||
00486 ((MARK_CUT_END == type) && !right))
00487 {
00488
00489
00490 Delete(frame, total, QObject::tr("Delete"));
00491 return;
00492 }
00493 else if (MARK_PLACEHOLDER == type)
00494 {
00495
00496
00497 Delete(frame, total);
00498 }
00499 }
00500
00501 uint64_t from = GetNearestMark(frame, total, right);
00502 Move(from, frame, total);
00503 }
00504
00506 void DeleteMap::Move(uint64_t frame, uint64_t to, uint64_t total)
00507 {
00508 EDIT_CHECK;
00509 MarkTypes type = Delete(frame);
00510 if (MARK_UNSET == type)
00511 {
00512 if (frame == 0)
00513 type = MARK_CUT_START;
00514 else if (frame == total)
00515 type = MARK_CUT_END;
00516 }
00517 Add(to, total, type, QObject::tr("Move Mark"));
00518 }
00519
00521 void DeleteMap::Add(uint64_t frame, MarkTypes type)
00522 {
00523 m_deleteMap[frame] = type;
00524 m_changed = true;
00525 }
00526
00528 MarkTypes DeleteMap::Delete(uint64_t frame)
00529 {
00530 if (m_deleteMap.contains(frame))
00531 {
00532 m_changed = true;
00533 return m_deleteMap.take(frame);
00534 }
00535 return MARK_UNSET;
00536 }
00537
00542 bool DeleteMap::IsInDelete(uint64_t frame) const
00543 {
00544 if (m_deleteMap.isEmpty())
00545 return false;
00546
00547 frm_dir_map_t::const_iterator it = m_deleteMap.find(frame);
00548 if (it != m_deleteMap.end())
00549 return true;
00550
00551 int lasttype = MARK_UNSET;
00552 uint64_t lastframe = -1;
00553 for (it = m_deleteMap.begin() ; it != m_deleteMap.end(); ++it)
00554 {
00555 if (it.key() > frame)
00556 return MARK_CUT_END == it.value();
00557 lasttype = it.value();
00558 lastframe = it.key();
00559 }
00560
00561 if (lasttype == MARK_CUT_START && lastframe <= frame)
00562 return true;
00563 return false;
00564 }
00565
00569 bool DeleteMap::IsTemporaryMark(uint64_t frame) const
00570 {
00571 if (m_deleteMap.isEmpty())
00572 return false;
00573
00574 frm_dir_map_t::const_iterator it = m_deleteMap.find(frame);
00575 return (it != m_deleteMap.end()) && (MARK_PLACEHOLDER == it.value());
00576 }
00577
00584 uint64_t DeleteMap::GetNearestMark(
00585 uint64_t frame, uint64_t total, bool right,
00586 bool *hasMark) const
00587 {
00588 uint64_t result;
00589 if (hasMark)
00590 *hasMark = true;
00591 frm_dir_map_t::const_iterator it = m_deleteMap.begin();
00592 if (right)
00593 {
00594 result = total;
00595 for (; it != m_deleteMap.end(); ++it)
00596 if (it.key() > frame)
00597 return it.key();
00598 if (hasMark)
00599 *hasMark = false;
00600 }
00601 else
00602 {
00603 result = 0;
00604 for (; it != m_deleteMap.end(); ++it)
00605 {
00606 if (it.key() >= frame)
00607 return result;
00608 result = it.key();
00609 }
00610 }
00611 return result;
00612 }
00613
00617 bool DeleteMap::HasTemporaryMark(void) const
00618 {
00619 if (!m_deleteMap.isEmpty())
00620 {
00621 frm_dir_map_t::const_iterator it = m_deleteMap.begin();
00622 for ( ; it != m_deleteMap.end(); ++it)
00623 if (MARK_PLACEHOLDER == it.value())
00624 return true;
00625 }
00626
00627 return false;
00628 }
00629
00635 void DeleteMap::CleanMap(uint64_t total)
00636 {
00637 if (IsEmpty())
00638 return;
00639
00640 Delete(0);
00641 Delete(total);
00642
00643 bool clear = false;
00644 while (!IsEmpty() && !clear)
00645 {
00646 clear = true;
00647 int lasttype = MARK_UNSET;
00648 int64_t lastframe = -1;
00649 int64_t tempframe = -1;
00650 frm_dir_map_t::iterator it = m_deleteMap.begin();
00651 for ( ; it != m_deleteMap.end(); ++it)
00652 {
00653 int thistype = it.value();
00654 uint64_t thisframe = it.key();
00655 if (thisframe >= total)
00656 {
00657 Delete(thisframe);
00658 }
00659 else if (lasttype == thistype)
00660 {
00661 Delete(thistype == MARK_CUT_END ? thisframe :
00662 (uint64_t)lastframe);
00663 clear = false;
00664 break;
00665 }
00666 if (MARK_PLACEHOLDER == thistype)
00667 {
00668 if (tempframe > 0)
00669 Delete(tempframe);
00670 tempframe = thisframe;
00671 }
00672 else
00673 {
00674 lasttype = thistype;
00675 lastframe = thisframe;
00676 }
00677 }
00678 }
00679 }
00680
00682 void DeleteMap::SetMap(const frm_dir_map_t &map)
00683 {
00684 Clear();
00685 m_deleteMap = map;
00686 m_deleteMap.detach();
00687
00688
00689
00690 }
00691
00693 void DeleteMap::LoadCommBreakMap(uint64_t total, frm_dir_map_t &map)
00694 {
00695 Clear();
00696 frm_dir_map_t::Iterator it = map.begin();
00697 for ( ; it != map.end(); ++it)
00698 Add(it.key(), it.value() == MARK_COMM_START ?
00699 MARK_CUT_START : MARK_CUT_END);
00700 CleanMap(total);
00701 Push(QObject::tr("Load Detected Commercials"));
00702 }
00703
00705 void DeleteMap::LoadMap(uint64_t total, QString undoMessage)
00706 {
00707 if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00708 return;
00709
00710 Clear();
00711 m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00712 m_ctx->playingInfo->QueryCutList(m_deleteMap);
00713 m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00714 CleanMap(total);
00715 if (!undoMessage.isEmpty())
00716 Push(undoMessage);
00717 }
00718
00721 bool DeleteMap::LoadAutoSaveMap(uint64_t total)
00722 {
00723 if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00724 return false;
00725
00726 frm_dir_map_t tmpDeleteMap = m_deleteMap;
00727 Clear();
00728 m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00729 bool result = m_ctx->playingInfo->QueryCutList(m_deleteMap, true);
00730 m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00731 CleanMap(total);
00732 if (result)
00733 Push(QObject::tr("Load Auto-saved Cuts"));
00734 else
00735 m_deleteMap = tmpDeleteMap;
00736
00737 return result;
00738 }
00739
00741 void DeleteMap::SaveMap(uint64_t total, bool isAutoSave)
00742 {
00743 if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00744 return;
00745
00746 if (!isAutoSave)
00747 {
00748
00749 QMutableMapIterator<uint64_t, MarkTypes> it(m_deleteMap);
00750 while (it.hasNext())
00751 {
00752 it.next();
00753 if (MARK_PLACEHOLDER == it.value())
00754 {
00755 it.remove();
00756 m_changed = true;
00757 }
00758 }
00759
00760 CleanMap(total);
00761 }
00762 m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00763 m_ctx->playingInfo->SaveMarkupFlag(MARK_UPDATED_CUT);
00764 m_ctx->playingInfo->SaveCutList(m_deleteMap, isAutoSave);
00765 m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00766 }
00767
00774 void DeleteMap::TrackerReset(uint64_t frame, uint64_t total)
00775 {
00776 m_nextCutStart = 0;
00777 m_nextCutStartIsValid = false;
00778 if (IsEmpty())
00779 return;
00780
00781 frm_dir_map_t::iterator cutpoint = m_deleteMap.find(frame);
00782 if (cutpoint != m_deleteMap.end())
00783 {
00784 if (cutpoint.value() == MARK_CUT_START)
00785 {
00786 m_nextCutStartIsValid = true;
00787 m_nextCutStart = cutpoint.key();
00788 }
00789 else
00790 {
00791 ++cutpoint;
00792 m_nextCutStartIsValid = (cutpoint != m_deleteMap.end());
00793 m_nextCutStart = m_nextCutStartIsValid ? cutpoint.key() : total;
00794 }
00795 }
00796 else
00797 m_nextCutStart = GetNearestMark(frame, total, !IsInDelete(frame),
00798 &m_nextCutStartIsValid);
00799 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Tracker next CUT_START: %1")
00800 .arg(m_nextCutStart));
00801 }
00802
00807 bool DeleteMap::TrackerWantsToJump(uint64_t frame, uint64_t total, uint64_t &to)
00808 {
00809 if (IsEmpty() || !m_nextCutStartIsValid || frame < m_nextCutStart)
00810 return false;
00811
00812 to = GetNearestMark(m_nextCutStart, total, true);
00813 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00814 QString("Tracker wants to jump to: %1").arg(to));
00815 return true;
00816 }
00817
00822 uint64_t DeleteMap::GetLastFrame(uint64_t total) const
00823 {
00824 uint64_t result = total;
00825 if (IsEmpty())
00826 return result;
00827
00828 frm_dir_map_t::const_iterator it = m_deleteMap.end();
00829 --it;
00830
00831 if (it.value() == MARK_CUT_START)
00832 result = it.key();
00833 return result;
00834 }
00835
00839 bool DeleteMap::IsSaved(void) const
00840 {
00841 if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00842 return true;
00843
00844 frm_dir_map_t currentMap(m_deleteMap);
00845 frm_dir_map_t savedMap;
00846 m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00847 m_ctx->playingInfo->QueryCutList(savedMap);
00848 m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00849
00850
00851 QMutableMapIterator<uint64_t, MarkTypes> it(currentMap);
00852 while (it.hasNext())
00853 {
00854 it.next();
00855 if (MARK_PLACEHOLDER == it.value())
00856 it.remove();
00857 }
00858
00859 return currentMap == savedMap;
00860 }
00861
00862 uint64_t DeleteMap::TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap,
00863 uint64_t absPosition)
00864 {
00865 uint64_t subtraction = 0;
00866 uint64_t startOfCutRegion = 0;
00867 frm_dir_map_t::const_iterator i;
00868 bool withinCut = false;
00869 bool first = true;
00870 for (i = deleteMap.constBegin(); i != deleteMap.constEnd(); ++i)
00871 {
00872 if (first)
00873 withinCut = (i.value() == MARK_CUT_END);
00874 first = false;
00875 if (i.key() > absPosition)
00876 break;
00877 if (i.value() == MARK_CUT_START && !withinCut)
00878 {
00879 withinCut = true;
00880 startOfCutRegion = i.key();
00881 }
00882 else if (i.value() == MARK_CUT_END && withinCut)
00883 {
00884 withinCut = false;
00885 subtraction += (i.key() - startOfCutRegion);
00886 }
00887 }
00888 if (withinCut)
00889 subtraction += (absPosition - startOfCutRegion);
00890 return absPosition - subtraction;
00891 }
00892
00893 uint64_t DeleteMap::TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap,
00894 uint64_t relPosition)
00895 {
00896 uint64_t addition = 0;
00897 uint64_t startOfCutRegion = 0;
00898 frm_dir_map_t::const_iterator i;
00899 bool withinCut = false;
00900 bool first = true;
00901 for (i = deleteMap.constBegin(); i != deleteMap.constEnd(); ++i)
00902 {
00903 if (first)
00904 withinCut = (i.value() == MARK_CUT_END);
00905 first = false;
00906 if (i.value() == MARK_CUT_START && !withinCut)
00907 {
00908 withinCut = true;
00909 startOfCutRegion = i.key();
00910 if (relPosition + addition <= startOfCutRegion)
00911 break;
00912 }
00913 else if (i.value() == MARK_CUT_END && withinCut)
00914 {
00915 withinCut = false;
00916 addition += (i.key() - startOfCutRegion);
00917 }
00918 }
00919 return relPosition + addition;
00920 }