00001 #include "mythcontext.h"
00002 #include "programinfo.h"
00003 #include "playercontext.h"
00004 #include "commbreakmap.h"
00005
00006 #define LOC QString("CommBreakMap: ")
00007
00008 CommBreakMap::CommBreakMap(void)
00009 : commBreakMapLock(QMutex::Recursive),
00010 skipcommercials(0), autocommercialskip(kCommSkipOff),
00011 commrewindamount(0), commnotifyamount(0),
00012 lastCommSkipDirection(0), lastCommSkipTime(0),
00013 lastCommSkipStart(0), lastSkipTime(0 ),
00014 hascommbreaktable(false), maxskip(3600),
00015 maxShortMerge(0), commBreakIter(commBreakMap.end())
00016 {
00017 commrewindamount = gCoreContext->GetNumSetting("CommRewindAmount",0);
00018 commnotifyamount = gCoreContext->GetNumSetting("CommNotifyAmount",0);
00019 lastIgnoredManualSkip = QDateTime::currentDateTime().addSecs(-10);
00020 autocommercialskip = (CommSkipMode)
00021 gCoreContext->GetNumSetting("AutoCommercialSkip", kCommSkipOff);
00022 maxskip = gCoreContext->GetNumSetting("MaximumCommercialSkip", 3600);
00023 maxShortMerge = gCoreContext->GetNumSetting("MergeShortCommBreaks", 0);
00024 }
00025
00026 CommSkipMode CommBreakMap::GetAutoCommercialSkip(void) const
00027 {
00028 QMutexLocker locker(&commBreakMapLock);
00029 return autocommercialskip;
00030 }
00031
00032 void CommBreakMap::ResetLastSkip(void)
00033 {
00034 lastSkipTime = time(NULL);
00035 }
00036
00037 void CommBreakMap::SetAutoCommercialSkip(CommSkipMode autoskip, uint64_t framesplayed)
00038 {
00039 QMutexLocker locker(&commBreakMapLock);
00040 SetTracker(framesplayed);
00041 uint next = (kCommSkipIncr == autoskip) ?
00042 (uint) autocommercialskip + 1 : (uint) autoskip;
00043 autocommercialskip = (CommSkipMode) (next % kCommSkipCount);
00044 }
00045
00046 void CommBreakMap::SkipCommercials(int direction)
00047 {
00048 commBreakMapLock.lock();
00049 if (skipcommercials == 0 && direction != 0)
00050 skipcommercials = direction;
00051 else if (skipcommercials != 0 && direction == 0)
00052 skipcommercials = direction;
00053 commBreakMapLock.unlock();
00054 }
00055
00056 void CommBreakMap::LoadMap(PlayerContext *player_ctx, uint64_t framesPlayed)
00057 {
00058 if (!player_ctx)
00059 return;
00060
00061 QMutexLocker locker(&commBreakMapLock);
00062 player_ctx->LockPlayingInfo(__FILE__, __LINE__);
00063 if (player_ctx->playingInfo)
00064 {
00065 commBreakMap.clear();
00066 player_ctx->playingInfo->QueryCommBreakList(commBreakMap);
00067 hascommbreaktable = !commBreakMap.isEmpty();
00068 SetTracker(framesPlayed);
00069 }
00070 player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00071 }
00072
00073 void CommBreakMap::SetTracker(uint64_t framesPlayed)
00074 {
00075 QMutexLocker locker(&commBreakMapLock);
00076 if (!hascommbreaktable)
00077 return;
00078
00079 commBreakIter = commBreakMap.begin();
00080 while (commBreakIter != commBreakMap.end())
00081 {
00082 if (framesPlayed <= commBreakIter.key())
00083 break;
00084
00085 commBreakIter++;
00086 }
00087
00088 if (commBreakIter != commBreakMap.end())
00089 {
00090 LOG(VB_COMMFLAG, LOG_INFO, LOC +
00091 QString("new commBreakIter = %1 @ frame %2, framesPlayed = %3")
00092 .arg(*commBreakIter).arg(commBreakIter.key())
00093 .arg(framesPlayed));
00094 }
00095 }
00096
00097 void CommBreakMap::GetMap(frm_dir_map_t &map) const
00098 {
00099 QMutexLocker locker(&commBreakMapLock);
00100 map.clear();
00101 map = commBreakMap;
00102 map.detach();
00103 }
00104
00105 bool CommBreakMap::IsInCommBreak(uint64_t frameNumber) const
00106 {
00107 QMutexLocker locker(&commBreakMapLock);
00108 if (commBreakMap.isEmpty())
00109 return false;
00110
00111 frm_dir_map_t::const_iterator it = commBreakMap.find(frameNumber);
00112 if (it != commBreakMap.end())
00113 return true;
00114
00115 int lastType = MARK_UNSET;
00116 for (it = commBreakMap.begin(); it != commBreakMap.end(); ++it)
00117 {
00118 if (it.key() > frameNumber)
00119 {
00120 int type = *it;
00121
00122 if (((type == MARK_COMM_END) ||
00123 (type == MARK_CUT_END)) &&
00124 ((lastType == MARK_COMM_START) ||
00125 (lastType == MARK_CUT_START)))
00126 return true;
00127
00128 if ((type == MARK_COMM_START) ||
00129 (type == MARK_CUT_START))
00130 return false;
00131 }
00132
00133 lastType = *it;
00134 }
00135 return false;
00136 }
00137
00138 void CommBreakMap::SetMap(const frm_dir_map_t &newMap, uint64_t framesPlayed)
00139 {
00140 QMutexLocker locker(&commBreakMapLock);
00141 LOG(VB_COMMFLAG, LOG_INFO, LOC +
00142 QString("Setting New Commercial Break List, old size %1, new %2")
00143 .arg(commBreakMap.size()).arg(newMap.size()));
00144
00145 commBreakMap.clear();
00146 commBreakMap = newMap;
00147 commBreakMap.detach();
00148 hascommbreaktable = !commBreakMap.isEmpty();
00149 SetTracker(framesPlayed);
00150 }
00151
00152 bool CommBreakMap::AutoCommercialSkip(uint64_t &jumpToFrame,
00153 uint64_t framesPlayed,
00154 double video_frame_rate,
00155 uint64_t totalFrames,
00156 QString &comm_msg)
00157 {
00158 QMutexLocker locker(&commBreakMapLock);
00159 if (!hascommbreaktable)
00160 return false;
00161
00162 if (((time(NULL) - lastSkipTime) <= 3) ||
00163 ((time(NULL) - lastCommSkipTime) <= 3))
00164 {
00165 SetTracker(framesPlayed);
00166 return false;
00167 }
00168
00169 if (commBreakIter == commBreakMap.end())
00170 return false;
00171
00172 if (*commBreakIter == MARK_COMM_END)
00173 commBreakIter++;
00174
00175 if (commBreakIter == commBreakMap.end())
00176 return false;
00177
00178 if (!((*commBreakIter == MARK_COMM_START) &&
00179 (((kCommSkipOn == autocommercialskip) &&
00180 (framesPlayed >= commBreakIter.key())) ||
00181 ((kCommSkipNotify == autocommercialskip) &&
00182 (framesPlayed + commnotifyamount * video_frame_rate >=
00183 commBreakIter.key())))))
00184 {
00185 return false;
00186 }
00187
00188 LOG(VB_COMMFLAG, LOG_INFO, LOC +
00189 QString("AutoCommercialSkip(), current framesPlayed %1, commBreakIter "
00190 "frame %2, incrementing commBreakIter")
00191 .arg(framesPlayed).arg(commBreakIter.key()));
00192
00193 ++commBreakIter;
00194
00195 MergeShortCommercials(video_frame_rate);
00196
00197 if (commBreakIter == commBreakMap.end())
00198 {
00199 LOG(VB_COMMFLAG, LOG_INFO, LOC + "AutoCommercialSkip(), at end of "
00200 "commercial break list, will not skip.");
00201 return false;
00202 }
00203
00204 if (*commBreakIter == MARK_COMM_START)
00205 {
00206 LOG(VB_COMMFLAG, LOG_INFO, LOC + "AutoCommercialSkip(), new "
00207 "commBreakIter mark is another start, "
00208 "will not skip.");
00209 return false;
00210 }
00211
00212 if (totalFrames &&
00213 ((commBreakIter.key() + (10 * video_frame_rate)) > totalFrames))
00214 {
00215 LOG(VB_COMMFLAG, LOG_INFO, LOC + "AutoCommercialSkip(), skipping would "
00216 "take us to the end of the file, will "
00217 "not skip.");
00218 return false;
00219 }
00220
00221 LOG(VB_COMMFLAG, LOG_INFO, LOC +
00222 QString("AutoCommercialSkip(), new commBreakIter frame %1")
00223 .arg(commBreakIter.key()));
00224
00225 int skipped_seconds = (int)((commBreakIter.key() -
00226 framesPlayed) / video_frame_rate);
00227 QString skipTime;
00228 skipTime.sprintf("%d:%02d", skipped_seconds / 60,
00229 abs(skipped_seconds) % 60);
00230 if (kCommSkipOn == autocommercialskip)
00231 comm_msg = QString(QObject::tr("Skip %1")).arg(skipTime);
00232 else
00233 comm_msg = QString(QObject::tr("Commercial: %1")).arg(skipTime);
00234
00235 if (kCommSkipOn == autocommercialskip)
00236 {
00237 LOG(VB_COMMFLAG, LOG_INFO, LOC +
00238 QString("AutoCommercialSkip(), auto-skipping to frame %1")
00239 .arg(commBreakIter.key() -
00240 (int)(commrewindamount * video_frame_rate)));
00241
00242 lastCommSkipDirection = 1;
00243 lastCommSkipStart = framesPlayed;
00244 lastCommSkipTime = time(NULL);
00245
00246 jumpToFrame = commBreakIter.key() -
00247 (int)(commrewindamount * video_frame_rate);
00248 return true;
00249 }
00250 ++commBreakIter;
00251 return false;
00252 }
00253
00254 bool CommBreakMap::DoSkipCommercials(uint64_t &jumpToFrame,
00255 uint64_t framesPlayed,
00256 double video_frame_rate,
00257 uint64_t totalFrames, QString &comm_msg)
00258 {
00259 QMutexLocker locker(&commBreakMapLock);
00260 if ((skipcommercials == (0 - lastCommSkipDirection)) &&
00261 ((time(NULL) - lastCommSkipTime) <= 5))
00262 {
00263 comm_msg = QObject::tr("Skipping Back.");
00264
00265 if (lastCommSkipStart > (2.0 * video_frame_rate))
00266 lastCommSkipStart -= (long long) (2.0 * video_frame_rate);
00267 lastCommSkipDirection = 0;
00268 lastCommSkipTime = time(NULL);
00269 jumpToFrame = lastCommSkipStart;
00270 return true;
00271 }
00272 lastCommSkipDirection = skipcommercials;
00273 lastCommSkipStart = framesPlayed;
00274 lastCommSkipTime = time(NULL);
00275
00276 SetTracker(framesPlayed);
00277
00278 if ((commBreakIter == commBreakMap.begin()) &&
00279 (skipcommercials < 0))
00280 {
00281 comm_msg = QObject::tr("Start of program.");
00282 jumpToFrame = 0;
00283 return true;
00284 }
00285
00286 if ((skipcommercials > 0) &&
00287 ((commBreakIter == commBreakMap.end()) ||
00288 ((totalFrames) &&
00289 ((commBreakIter.key() + (10 * video_frame_rate)) > totalFrames))))
00290 {
00291 comm_msg = QObject::tr("At End, cannot Skip.");
00292 return false;
00293 }
00294
00295 if (skipcommercials < 0)
00296 {
00297 commBreakIter--;
00298
00299 int skipped_seconds = (int)(((int64_t)(commBreakIter.key()) -
00300 (int64_t)framesPlayed) / video_frame_rate);
00301
00302
00303 if (skipped_seconds > -3)
00304 {
00305 if (commBreakIter == commBreakMap.begin())
00306 {
00307 comm_msg = QObject::tr("Start of program.");
00308 jumpToFrame = 0;
00309 return true;
00310 }
00311 else
00312 commBreakIter--;
00313 }
00314 }
00315 else
00316 {
00317 int skipped_seconds = (int)(((int64_t)(commBreakIter.key()) -
00318 (int64_t)framesPlayed) / video_frame_rate);
00319
00320
00321
00322
00323
00324 MarkTypes type = *commBreakIter;
00325 if (((type == MARK_COMM_START) && (skipped_seconds < 20)) ||
00326 ((type == MARK_COMM_END) && (skipped_seconds < commrewindamount)))
00327 {
00328 commBreakIter++;
00329
00330 if ((commBreakIter == commBreakMap.end()) ||
00331 ((totalFrames) &&
00332 ((commBreakIter.key() + (10 * video_frame_rate)) >
00333 totalFrames)))
00334 {
00335 comm_msg = QObject::tr("At End, cannot Skip.");
00336 return false;
00337 }
00338 }
00339 }
00340
00341 if (skipcommercials > 0)
00342 MergeShortCommercials(video_frame_rate);
00343 int skipped_seconds = (int)(((int64_t)(commBreakIter.key()) -
00344 (int64_t)framesPlayed) / video_frame_rate);
00345 QString skipTime;
00346 skipTime.sprintf("%d:%02d", skipped_seconds / 60,
00347 abs(skipped_seconds) % 60);
00348
00349 if ((lastIgnoredManualSkip.secsTo(QDateTime::currentDateTime()) > 3) &&
00350 (abs(skipped_seconds) >= maxskip))
00351 {
00352 comm_msg = QObject::tr("Too Far %1").arg(skipTime);
00353 lastIgnoredManualSkip = QDateTime::currentDateTime();
00354 return false;
00355 }
00356 comm_msg = QObject::tr("Skip %1").arg(skipTime);
00357
00358 uint64_t jumpto = (skipcommercials > 0) ?
00359 commBreakIter.key() - (long long)(commrewindamount * video_frame_rate):
00360 commBreakIter.key();
00361 commBreakIter++;
00362 jumpToFrame = jumpto;
00363 return true;
00364 }
00365
00366 void CommBreakMap::MergeShortCommercials(double video_frame_rate)
00367 {
00368 double maxMerge = maxShortMerge * video_frame_rate;
00369 if (maxMerge <= 0.0 || (commBreakIter == commBreakMap.end()))
00370 return;
00371
00372 long long lastFrame = commBreakIter.key();
00373 ++commBreakIter;
00374 while ((commBreakIter != commBreakMap.end()) &&
00375 (commBreakIter.key() - lastFrame < maxMerge))
00376 {
00377 ++commBreakIter;
00378 }
00379 --commBreakIter;
00380 }