Changeset 49dbed5be in mythtv


Ignore:
Timestamp:
Jan 1, 2013, 3:23:42 AM (12 years ago)
Author:
Jim Stichnoth <jstichnoth@…>
Branches:
devel/2020-player, devel/ffmpeg-resync, fixes/0.27, fixes/0.28, fixes/29, fixes/30, fixes/31, github-templates, master
Children:
88573b7466
Parents:
1eaecea6dd
Message:

Provide accurate position/duration/seeking with non-constant framerates.

The recordedseek and filemarkup tables are enhanced to hold timestamp
data in addition to the existing file offset data. The millisecond
timestamps are produced by all recorders that subclass DTVRecorder, as
well as mythtranscode and mythcommflag --rebuild. These timestamps
are relative to the start of the recording/video.

A new command is added to the myth protocol, "QUERY_RECORDER
FILL_DURATION_MAP", modeled after FILL_POSITION_MAP, to send updated
timestamp info for playback of an in-progress recording.

The timestamp markup is used during playback to give accurate position
and duration information in the OSD wherever possible, and to provide
accurate time-based seeking, such as "skip forward 30 seconds" or
"jump to the 5-minute mark". Timestamps are linearly interpolated
from a frame's nearest neighbors in the map, and extrapolated based on
the current frame rate when the map is missing (e.g. legacy
recordings) or incomplete (e.g. in-progress recordings). Other than
that, the frame rate is not used for seeking or duration calculations.
(With the exception of a handful of areas that still need some
attention, including seeking based on commskipmap, seeking across
program boundaries during Live TV, and the watched flag calculation.)

The cutlist continues to be taken into account for seeking and
displaying timestamps, for the most part making cutlists
indistinguishable from the result of lossless transcoding.

Note that to get the benefit of these changes for preexisting
recordings, it may be necessary to run "mythcommflag --rebuild" on
such recordings.

Bumps the ABI and protocol versions. "make distclean" is recommended.

Fixes #10104.

Location:
mythtv
Files:
33 edited

Legend:

Unmodified
Added
Removed
  • mythtv/bindings/perl/MythTV.pm

    r1eaecea6dd r49dbed5be  
    108108# versions of the form "58a".  This will get used if protocol versions are
    109109# changed on a fixes branch ongoing.
    110     our $PROTO_VERSION = "76";
    111     our $PROTO_TOKEN = "FireWilde";
     110    our $PROTO_VERSION = "77";
     111    our $PROTO_TOKEN = "WindMark";
    112112
    113113# currentDatabaseVersion is defined in libmythtv in
  • mythtv/bindings/php/MythBackend.php

    r1eaecea6dd r49dbed5be  
    1212// MYTH_PROTO_VERSION is defined in libmyth in mythtv/libs/libmyth/mythcontext.h
    1313// and should be the current MythTV protocol version.
    14     static $protocol_version        = '76';
    15     static $protocol_token          = 'FireWilde';
     14    static $protocol_version        = '77';
     15    static $protocol_token          = 'WindMark';
    1616
    1717// The character string used by the backend to separate records
  • mythtv/bindings/python/MythTV/static.py

    r1eaecea6dd r49dbed5be  
    99NVSCHEMA_VERSION = 1007
    1010MUSICSCHEMA_VERSION = 1018
    11 PROTO_VERSION = '76'
    12 PROTO_TOKEN = 'FireWilde'
     11PROTO_VERSION = '77'
     12PROTO_TOKEN = 'WindMark'
    1313BACKEND_SEP = '[]:[]'
    1414INSTALL_PREFIX = '/usr/local'
     
    3535    MARK_VIDEO_WIDTH    = 30
    3636    MARK_VIDEO_HEIGHT   = 31
     37    MARK_VIDEO_RATE     = 32
     38    MARK_DURATION_MS    = 33
    3739
    3840class RECTYPE( object ):
  • mythtv/libs/libmythbase/mythversion.h

    r1eaecea6dd r49dbed5be  
    1313/// Including changes in the libmythbase, libmyth, libmythtv, libmythav* and
    1414/// libmythui class methods used by plug-ins.
    15 #define MYTH_BINARY_VERSION "0.27.20121227-2"
     15#define MYTH_BINARY_VERSION "0.27.20121231-1"
    1616
    1717/** \brief Increment this whenever the MythTV network protocol changes.
     
    3535 *       mythtv/bindings/python/MythTV/static.py (version number)
    3636 *       mythtv/bindings/python/MythTV/mythproto.py (layout)
     37 *
     38 *   Be kind and update the wiki as well.
     39 *       http://www.mythtv.org/wiki/Category:Myth_Protocol_Commands
     40 *       http://www.mythtv.org/wiki/Category:Myth_Protocol
    3741 */
    38 #define MYTH_PROTO_VERSION "76"
    39 #define MYTH_PROTO_TOKEN "FireWilde"
     42#define MYTH_PROTO_VERSION "77"
     43#define MYTH_PROTO_TOKEN "WindMark"
    4044
    4145/** \brief Increment this whenever the MythTV core database schema changes.
  • mythtv/libs/libmythtv/DVD/mythdvdplayer.cpp

    r1eaecea6dd r49dbed5be  
    391391}
    392392
    393 int64_t MythDVDPlayer::GetSecondsPlayed(void)
    394 {
    395     if (!player_ctx->buffer->IsDVD())
    396         return 0;
    397 
    398     return (m_stillFrameLength > 0) ?
    399                 (m_stillFrameTimer.elapsed() / 1000) :
    400                 (player_ctx->buffer->DVD()->GetCurrentTime());
    401 
    402 }
    403 
    404393int64_t MythDVDPlayer::GetTotalSeconds(void) const
    405394{
  • mythtv/libs/libmythtv/DVD/mythdvdplayer.h

    r1eaecea6dd r49dbed5be  
    2020    // Gets
    2121    virtual uint64_t GetBookmark(void);
    22     virtual  int64_t GetSecondsPlayed(void);
    2322    virtual  int64_t GetTotalSeconds(void) const;
    2423
  • mythtv/libs/libmythtv/avformatdecoder.cpp

    r1eaecea6dd r49dbed5be  
    29462946
    29472947            QMutexLocker locker(&m_positionMapLock);
     2948            // Create a dummy positionmap entry for frame 0 so that
     2949            // seeking will work properly.  (See
     2950            // DecoderBase::FindPosition() which subtracts
     2951            // DecoderBase::indexOffset from each frame number.)
     2952            if (m_positionMap.empty())
     2953            {
     2954                PosMapEntry dur = {0, 0, 0};
     2955                m_positionMap.push_back(dur);
     2956            }
    29482957            m_positionMap.push_back(entry);
     2958            m_frameToDurMap[framesRead] = totalDuration / 1000;
     2959            m_durToFrameMap[m_frameToDurMap[framesRead]] = framesRead;
    29492960        }
    29502961
  • mythtv/libs/libmythtv/decoderbase.cpp

    r1eaecea6dd r49dbed5be  
    120120
    121121    // Overwrites current positionmap with entire contents of database
    122     frm_pos_map_t posMap;
     122    frm_pos_map_t posMap, durMap;
    123123
    124124    if (ringBuffer && ringBuffer->IsDVD())
     
    192192        return false; // no position map in recording
    193193
     194    m_playbackinfo->QueryPositionMap(durMap, MARK_DURATION_MS);
     195
    194196    QMutexLocker locker(&m_positionMapLock);
    195197    m_positionMap.clear();
     
    213215    }
    214216
     217    uint64_t last = 0;
     218    for (frm_pos_map_t::const_iterator it = durMap.begin();
     219         it != durMap.end(); ++it)
     220    {
     221        m_frameToDurMap[it.key()] = it.value();
     222        m_durToFrameMap[it.value()] = it.key();
     223        last = it.key();
     224    }
     225
     226    if (!m_durToFrameMap.empty())
     227    {
     228        LOG(VB_PLAYBACK, LOG_INFO, LOC +
     229            QString("Duration map filled from DB to: %1").arg(last));
     230    }
     231
    215232    return true;
    216233}
     
    236253    }
    237254
    238     QMap<long long, long long> posMap;
    239     if (!m_parent->PosMapFromEnc(start, posMap))
     255    frm_pos_map_t posMap, durMap;
     256    if (!m_parent->PosMapFromEnc(start, posMap, durMap))
    240257        return false;
    241258
     
    244261    // append this new position map to class's
    245262    m_positionMap.reserve(m_positionMap.size() + posMap.size());
    246     long long last_index = m_positionMap.back().index;
    247     for (QMap<long long,long long>::const_iterator it = posMap.begin();
     263    uint64_t last_index = m_positionMap.back().index;
     264    for (frm_pos_map_t::const_iterator it = posMap.begin();
    248265         it != posMap.end(); ++it)
    249266    {
    250267        if (it.key() <= last_index)
    251             continue; // we released the m_positionMapLock for a few ms...
     268            continue;
    252269
    253270        PosMapEntry e = {it.key(), it.key() * keyframedist, *it};
     
    263280            QString("Position map filled from Encoder to: %1")
    264281                .arg(m_positionMap.back().index));
     282    }
     283
     284    bool isEmpty = m_frameToDurMap.empty();
     285    if (!isEmpty)
     286    {
     287        frm_pos_map_t::const_iterator it = m_frameToDurMap.end();
     288        --it;
     289        last_index = it.key();
     290    }
     291    for (frm_pos_map_t::const_iterator it = durMap.begin();
     292         it != durMap.end(); ++it)
     293    {
     294        if (!isEmpty && it.key() <= last_index)
     295            continue; // we released the m_positionMapLock for a few ms...
     296        m_frameToDurMap[it.key()] = it.value();
     297        m_durToFrameMap[it.value()] = it.key();
     298    }
     299
     300    if (!m_frameToDurMap.empty())
     301    {
     302        frm_pos_map_t::const_iterator it = m_frameToDurMap.end();
     303        --it;
     304        LOG(VB_PLAYBACK, LOG_INFO, LOC +
     305            QString("Duration map filled from Encoder to: %1").arg(it.key()));
    265306    }
    266307
     
    492533    }
    493534
     535    frm_pos_map_t durMap;
     536    for (frm_pos_map_t::const_iterator it = m_frameToDurMap.begin();
     537         it != m_frameToDurMap.end(); ++it)
     538    {
     539        if (it.key() < first)
     540            continue;
     541        if (it.key() > last)
     542            break;
     543        durMap[it.key()] = it.value();
     544    }
     545
    494546    locker.unlock();
    495547
    496548    stm.start();
    497549    m_playbackinfo->SavePositionMapDelta(posMap, type);
     550    m_playbackinfo->SavePositionMapDelta(durMap, MARK_DURATION_MS);
    498551
    499552#if 0
     
    11551208}
    11561209
     1210// Linearly interpolate the value for a given key in the map.  If the
     1211// key is outside the range of keys in the map, linearly extrapolate
     1212// using the fallback ratio.
     1213uint64_t DecoderBase::TranslatePosition(const frm_pos_map_t &map,
     1214                                        uint64_t key,
     1215                                        float fallback_ratio)
     1216{
     1217    uint64_t key1, key2;
     1218    uint64_t val1, val2;
     1219
     1220    frm_pos_map_t::const_iterator lower = map.lowerBound(key);
     1221    // QMap::lowerBound() finds a key >= the given key.  We want one
     1222    // <= the given key, so back up one element upon > condition.
     1223    if (lower != map.begin() && (lower == map.end() || lower.key() > key))
     1224        --lower;
     1225    if (lower == map.end())
     1226    {
     1227        key1 = 0;
     1228        val1 = 0;
     1229        LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
     1230            QString("TranslatePosition(key=%1): extrapolating to (0,0)")
     1231            .arg(key));
     1232    }
     1233    else
     1234    {
     1235        key1 = lower.key();
     1236        val1 = lower.value();
     1237    }
     1238    // Find the next key >= the given key.  QMap::lowerBound() is
     1239    // precisely correct in this case.
     1240    frm_pos_map_t::const_iterator upper = map.lowerBound(key);
     1241    if (upper == map.end())
     1242    {
     1243        // Extrapolate from (key1,val1) based on fallback_ratio
     1244        key2 = key;
     1245        val2 = val1 + fallback_ratio * (key2 - key1) + 0.5;
     1246        LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
     1247            QString("TranslatePosition(key=%1, ratio=%2): "
     1248                    "extrapolating to (%3,%4)")
     1249            .arg(key).arg(fallback_ratio).arg(key2).arg(val2));
     1250        return val2;
     1251    }
     1252    else
     1253    {
     1254        key2 = upper.key();
     1255        val2 = upper.value();
     1256    }
     1257    if (key1 == key2) // this happens for an exact keyframe match
     1258        return val2; // can also set key2 = key1 + 1 avoid dividing by zero
     1259
     1260    return val1 + (double) (key - key1) * (val2 - val1) / (key2 - key1) + 0.5;
     1261}
     1262
     1263// Convert from an absolute frame number (not cutlist adjusted) to its
     1264// cutlist-adjusted position in milliseconds.
     1265uint64_t DecoderBase::TranslatePositionFrameToMs(uint64_t position,
     1266                                                 float fallback_framerate,
     1267                                                 const frm_dir_map_t &cutlist)
     1268    const
     1269{
     1270    QMutexLocker locker(&m_positionMapLock);
     1271    return TranslatePositionAbsToRel(cutlist, position, m_frameToDurMap,
     1272                                     1000 / fallback_framerate);
     1273}
     1274
     1275// Convert from a cutlist-adjusted position in milliseconds to its
     1276// absolute frame number (not cutlist-adjusted).
     1277uint64_t DecoderBase::TranslatePositionMsToFrame(uint64_t dur_ms,
     1278                                                 float fallback_framerate,
     1279                                                 const frm_dir_map_t &cutlist)
     1280    const
     1281{
     1282    QMutexLocker locker(&m_positionMapLock);
     1283    // Convert relative position in milliseconds (cutlist-adjusted) to
     1284    // its absolute position in milliseconds (not cutlist-adjusted).
     1285    uint64_t ms = TranslatePositionRelToAbs(cutlist, dur_ms, m_frameToDurMap,
     1286                                            1000 / fallback_framerate);
     1287    // Convert absolute position in milliseconds to its absolute frame
     1288    // number.
     1289    return TranslatePosition(m_durToFrameMap, ms, fallback_framerate / 1000);
     1290}
     1291
     1292// Convert from an "absolute" (not cutlist-adjusted) value to its
     1293// "relative" (cutlist-adjusted) mapped value.  Usually the position
     1294// argument is a frame number, the map argument maps frames to
     1295// milliseconds, the fallback_ratio is 1000/framerate_fps, and the
     1296// return value is in milliseconds.
     1297//
     1298// If the map and fallback_ratio arguments are omitted, it simply
     1299// converts from an absolute frame number to a relative
     1300// (cutlist-adjusted) frame number.
     1301uint64_t
     1302DecoderBase::TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap,
     1303                                       uint64_t absPosition, // frames
     1304                                       const frm_pos_map_t &map, // frame->ms
     1305                                       float fallback_ratio)
     1306{
     1307    uint64_t subtraction = 0;
     1308    uint64_t startOfCutRegion = 0;
     1309    bool withinCut = false;
     1310    bool first = true;
     1311    for (frm_dir_map_t::const_iterator i = deleteMap.begin();
     1312         i != deleteMap.end(); ++i)
     1313    {
     1314        if (first)
     1315            withinCut = (i.value() == MARK_CUT_END);
     1316        first = false;
     1317        if (i.key() > absPosition)
     1318            break;
     1319        uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio);
     1320        if (i.value() == MARK_CUT_START && !withinCut)
     1321        {
     1322            withinCut = true;
     1323            startOfCutRegion = mappedKey;
     1324        }
     1325        else if (i.value() == MARK_CUT_END && withinCut)
     1326        {
     1327            withinCut = false;
     1328            subtraction += (mappedKey - startOfCutRegion);
     1329        }
     1330    }
     1331    uint64_t mappedPos = TranslatePosition(map, absPosition, fallback_ratio);
     1332    if (withinCut)
     1333        subtraction += (mappedPos - startOfCutRegion);
     1334    return mappedPos - subtraction;
     1335}
     1336
     1337// Convert from a "relative" (cutlist-adjusted) value to its
     1338// "absolute" (not cutlist-adjusted) mapped value.  Usually the
     1339// position argument is in milliseconds, the map argument maps frames
     1340// to milliseconds, the fallback_ratio is 1000/framerate_fps, and the
     1341// return value is also in milliseconds.  Upon return, if necessary,
     1342// the result may need a separate, non-cutlist adjusted conversion
     1343// from milliseconds to frame number, using the inverse
     1344// millisecond-to-frame map and the inverse fallback_ratio; see for
     1345// example TranslatePositionMsToFrame().
     1346//
     1347// If the map and fallback_ratio arguments are omitted, it simply
     1348// converts from a relatve (cutlist-adjusted) frame number to an
     1349// absolute frame number.
     1350uint64_t
     1351DecoderBase::TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap,
     1352                                       uint64_t relPosition, // ms
     1353                                       const frm_pos_map_t &map, // frame->ms
     1354                                       float fallback_ratio)
     1355{
     1356    uint64_t addition = 0;
     1357    uint64_t startOfCutRegion = 0;
     1358    bool withinCut = false;
     1359    bool first = true;
     1360    for (frm_dir_map_t::const_iterator i = deleteMap.begin();
     1361         i != deleteMap.end(); ++i)
     1362    {
     1363        if (first)
     1364            withinCut = (i.value() == MARK_CUT_END);
     1365        first = false;
     1366        uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio);
     1367        if (i.value() == MARK_CUT_START && !withinCut)
     1368        {
     1369            withinCut = true;
     1370            startOfCutRegion = mappedKey;
     1371            if (relPosition + addition <= startOfCutRegion)
     1372                break;
     1373        }
     1374        else if (i.value() == MARK_CUT_END && withinCut)
     1375        {
     1376            withinCut = false;
     1377            addition += (mappedKey - startOfCutRegion);
     1378        }
     1379    }
     1380    return relPosition + addition;
     1381}
     1382
    11571383
    11581384/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • mythtv/libs/libmythtv/decoderbase.h

    r1eaecea6dd r49dbed5be  
    142142    virtual bool DoFastForward(long long desiredFrame, bool doflush = true);
    143143
     144    static uint64_t
     145        TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap,
     146                                  uint64_t absPosition,
     147                                  const frm_pos_map_t &map = frm_pos_map_t(),
     148                                  float fallback_ratio = 1.0);
     149    static uint64_t
     150        TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap,
     151                                  uint64_t relPosition,
     152                                  const frm_pos_map_t &map = frm_pos_map_t(),
     153                                  float fallback_ratio = 1.0);
     154    static uint64_t TranslatePosition(const frm_pos_map_t &map,
     155                                      uint64_t key,
     156                                      float fallback_ratio);
     157    uint64_t TranslatePositionFrameToMs(uint64_t position,
     158                                        float fallback_framerate,
     159                                        const frm_dir_map_t &cutlist) const;
     160    uint64_t TranslatePositionMsToFrame(uint64_t dur_ms,
     161                                        float fallback_framerate,
     162                                        const frm_dir_map_t &cutlist) const;
     163
    144164    float GetVideoAspect(void) const { return current_aspect; }
    145165
     
    275295    mutable QMutex m_positionMapLock;
    276296    vector<PosMapEntry> m_positionMap;
     297    frm_pos_map_t m_frameToDurMap; // guarded by m_positionMapLock
     298    frm_pos_map_t m_durToFrameMap; // guarded by m_positionMapLock
    277299    bool dontSyncPositionMap;
    278300
  • mythtv/libs/libmythtv/deletemap.cpp

    r1eaecea6dd r49dbed5be  
    3434    m_undoStack.append(entry);
    3535    m_undoStackPointer ++;
    36     SaveMap(0, true);
     36    SaveMap(true);
    3737}
    3838
     
    4444    m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
    4545    m_changed = true;
    46     SaveMap(0, true);
     46    SaveMap(true);
    4747    return true;
    4848}
     
    5555    m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
    5656    m_changed = true;
    57     SaveMap(0, true);
     57    SaveMap(true);
    5858    return true;
    5959}
     
    7171}
    7272
    73 bool DeleteMap::HandleAction(QString &action, uint64_t frame,
    74                              uint64_t played, uint64_t total, double rate)
     73bool DeleteMap::HandleAction(QString &action, uint64_t frame, uint64_t played)
    7574{
    7675    bool handled = true;
    7776    if (action == ACTION_UP)
    78         UpdateSeekAmount(1, rate);
     77        UpdateSeekAmount(1);
    7978    else if (action == ACTION_DOWN)
    80         UpdateSeekAmount(-1, rate);
     79        UpdateSeekAmount(-1);
    8180    else if (action == ACTION_CLEARMAP)
    8281        Clear(tr("Clear Cuts"));
    8382    else if (action == ACTION_INVERTMAP)
    84         ReverseAll(total);
     83        ReverseAll();
    8584    else if (action == "MOVEPREV")
    86         MoveRelative(frame, total, false);
     85        MoveRelative(frame, false);
    8786    else if (action == "MOVENEXT")
    88         MoveRelative(frame, total, true);
     87        MoveRelative(frame, true);
    8988    else if (action == "CUTTOBEGINNING")
    90         Add(frame, total, MARK_CUT_END, tr("Cut to Beginning"));
     89        Add(frame, MARK_CUT_END, tr("Cut to Beginning"));
    9190    else if (action == "CUTTOEND")
    9291    {
    93         Add(frame, total, MARK_CUT_START, tr("Cut to End"));
     92        Add(frame, MARK_CUT_START, tr("Cut to End"));
    9493        // If the recording is still in progress, add an explicit end
    9594        // mark at the end.
    9695        if (m_ctx->player && m_ctx->player->IsWatchingInprogress())
    97             Add(total - 1, total, MARK_CUT_END, "");
     96            Add(m_ctx->player->GetTotalFrameCount() - 1, MARK_CUT_END, "");
    9897    }
    9998    else if (action == "NEWCUT")
    100         NewCut(frame, total);
     99        NewCut(frame);
    101100    else if (action == "DELETE")
    102101        //: Delete the current cut or preserved region
    103         Delete(frame, total, tr("Delete"));
     102        Delete(frame, tr("Delete"));
    104103    else if (action == "UNDO")
    105104        Undo();
     
    111110}
    112111
    113 void DeleteMap::UpdateSeekAmount(int change, double framerate)
     112void DeleteMap::UpdateSeekAmount(int change)
    114113{
    115114    m_seekamountpos += change;
     
    124123        case 0: m_seekText = tr("cut point"); m_seekamount = -2; break;
    125124        case 1: m_seekText = tr("keyframe"); m_seekamount = -1; break;
    126         case 2: m_seekText = tr("1 frame"); m_seekamount = 1; break;
    127         case 3: m_seekText = tr("0.5 seconds"); m_seekamount = (int)roundf(framerate / 2); break;
    128         case 4: m_seekText = tr("%n second(s)", "", 1); m_seekamount = (int)roundf(framerate); break;
    129         case 5: m_seekText = tr("%n second(s)", "", 5); m_seekamount = (int)roundf(framerate * 5); break;
    130         case 6: m_seekText = tr("%n second(s)", "", 20); m_seekamount = (int)roundf(framerate * 20); break;
    131         case 7: m_seekText = tr("%n minute(s)", "", 1); m_seekamount = (int)roundf(framerate * 60); break;
    132         case 8: m_seekText = tr("%n minute(s)", "", 5); m_seekamount = (int)roundf(framerate * 300); break;
    133         case 9: m_seekText = tr("%n minute(s)", "", 10); m_seekamount = (int)roundf(framerate * 600); break;
    134         default: m_seekText = tr("error"); m_seekamount = (int)roundf(framerate); break;
    135     }
    136 }
    137 
    138 static QString createTimeString(uint64_t frame, uint64_t total,
    139                                 double frame_rate, bool full_resolution)
    140 {
    141     int secs   = (int)(frame / frame_rate);
    142     int frames = frame - (int)(secs * frame_rate);
    143     int totalSecs = (int)(total / frame_rate);
     125        case 2: m_seekText = tr("1 frame"); m_seekamount = 0; break;
     126        case 3: m_seekText = tr("0.5 seconds"); m_seekamount = 0.5; break;
     127        case 4: m_seekText = tr("%n second(s)", "", 1); m_seekamount = 1; break;
     128        case 5: m_seekText = tr("%n second(s)", "", 5); m_seekamount = 5; break;
     129        case 6: m_seekText = tr("%n second(s)", "", 20); m_seekamount = 20; break;
     130        case 7: m_seekText = tr("%n minute(s)", "", 1); m_seekamount = 60; break;
     131        case 8: m_seekText = tr("%n minute(s)", "", 5); m_seekamount = 300; break;
     132        case 9: m_seekText = tr("%n minute(s)", "", 10); m_seekamount = 600; break;
     133        default: m_seekText = tr("error"); m_seekamount = 1; break;
     134    }
     135}
     136
     137QString DeleteMap::CreateTimeString(uint64_t frame, bool use_cutlist,
     138                                    double frame_rate, bool full_resolution)
     139    const
     140{
     141    uint64_t ms = TranslatePositionFrameToMs(frame, frame_rate, use_cutlist);
     142    int secs = (int)(ms / 1000);
     143    int remainder = (int)(ms % 1000);
     144    int totalSecs = (int)
     145        (TranslatePositionFrameToMs(frame, frame_rate, use_cutlist) / 1000);
    144146    QString timestr;
    145147    if (totalSecs >= 3600)
     
    148150        QString(":%1").arg(secs % 60, 2, 10, QChar(48));
    149151    if (full_resolution)
    150         timestr += QString(".%1").arg(frames, 2, 10, QChar(48));
     152        timestr += QString(".%1").arg(remainder, 3, 10, QChar(48));
    151153    return timestr;
    152154}
     
    156158 *        are only refreshed if the deleteMap has been updated.
    157159 */
    158 void DeleteMap::UpdateOSD(uint64_t frame, uint64_t total, double frame_rate,
    159                           OSD *osd)
     160void DeleteMap::UpdateOSD(uint64_t frame, double frame_rate, OSD *osd)
    160161{
    161162    if (!osd || !m_ctx)
    162163        return;
    163     CleanMap(total);
     164    CleanMap();
    164165
    165166    InfoMap infoMap;
     
    174175        cutmarker = tr("cut");
    175176
    176     QString timestr = createTimeString(frame, total, frame_rate, true);
    177     uint64_t relTotal = TranslatePositionAbsToRel(total);
    178     QString relTimeDisplay = createTimeString(TranslatePositionAbsToRel(frame),
    179                                               relTotal, frame_rate, false);
    180     QString relLengthDisplay = createTimeString(relTotal,
    181                                                 relTotal, frame_rate, false);
     177    uint64_t total = m_ctx->player->GetTotalFrameCount();
     178    QString timestr = CreateTimeString(frame, false, frame_rate, true);
     179    QString relTimeDisplay;
     180    relTimeDisplay = CreateTimeString(frame, true, frame_rate, false);
     181    QString relLengthDisplay;
     182    relLengthDisplay = CreateTimeString(total, true, frame_rate, false);
    182183    infoMap["timedisplay"]  = timestr;
    183184    infoMap["framedisplay"] = QString::number(frame);
     
    250251
    251252/// Reverses the direction of each mark in the map.
    252 void DeleteMap::ReverseAll(uint64_t total)
     253void DeleteMap::ReverseAll(void)
    253254{
    254255    EDIT_CHECK;
     
    257258        Add(it.key(), it.value() == MARK_CUT_END ? MARK_CUT_START :
    258259                                                   MARK_CUT_END);
    259     CleanMap(total);
     260    CleanMap();
    260261    Push(tr("Reverse Cuts"));
    261262}
     
    266267 *        the cleanup code.
    267268 */
    268 void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type,
    269                     QString undoMessage)
     269void DeleteMap::Add(uint64_t frame, MarkTypes type, QString undoMessage)
    270270{
    271271    EDIT_CHECK;
     
    281281            // Delete the temporary mark before putting a real mark at its
    282282            // location
    283             Delete(frame, total);
     283            Delete(frame, "");
    284284        }
    285285        else // Don't add a mark on top of a mark
     
    335335        Delete((uint64_t)remove);
    336336    Add(frame, type);
    337     CleanMap(total);
     337    CleanMap();
    338338    if (!undoMessage.isEmpty())
    339339        Push(undoMessage);
     
    341341
    342342/// Remove the mark at the given frame.
    343 void DeleteMap::Delete(uint64_t frame, uint64_t total, QString undoMessage)
     343void DeleteMap::Delete(uint64_t frame, QString undoMessage)
    344344{
    345345    EDIT_CHECK;
     
    347347        return;
    348348
    349     uint64_t prev = GetNearestMark(frame, total, false);
    350     uint64_t next = GetNearestMark(frame, total, true);
     349    uint64_t prev = GetNearestMark(frame, false);
     350    uint64_t next = GetNearestMark(frame, true);
    351351
    352352    // If frame is a cut point, GetNearestMark() would return the previous/next
     
    367367    if (prev != next)
    368368        Delete(next);
    369     CleanMap(total);
     369    CleanMap();
    370370    if (!undoMessage.isEmpty())
    371371        Push(undoMessage);
     
    373373
    374374/// Add a new cut marker (to start or end a cut region)
    375 void DeleteMap::NewCut(uint64_t frame, uint64_t total)
     375void DeleteMap::NewCut(uint64_t frame)
    376376{
    377377    EDIT_CHECK;
     
    391391    if (existing > -1)
    392392    {
     393        uint64_t total = m_ctx->player->GetTotalFrameCount();
    393394        uint64_t otherframe = static_cast<uint64_t>(existing);
    394395        if (otherframe == frame)
     
    403404            {
    404405                MarkTypes type = MARK_UNSET;
    405                 cut_start = GetNearestMark(frame, total, false);
    406                 cut_end = GetNearestMark(frame, total, true);
     406                cut_start = GetNearestMark(frame, false);
     407                cut_end = GetNearestMark(frame, true);
    407408                frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
    408409                if (it != m_deleteMap.end())
     
    467468        Add(frame, MARK_PLACEHOLDER);
    468469
    469     CleanMap(total);
     470    CleanMap();
    470471    Push(tr("New Cut"));
    471472}
    472473
    473474/// Move the previous (!right) or next (right) cut to frame.
    474 void DeleteMap::MoveRelative(uint64_t frame, uint64_t total, bool right)
     475void DeleteMap::MoveRelative(uint64_t frame, bool right)
    475476{
    476477    frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
     
    491492            // instead, delete the region
    492493            //: Delete the current cut or preserved region
    493             Delete(frame, total, tr("Delete"));
     494            Delete(frame, tr("Delete"));
    494495            return;
    495496        }
     
    498499            // Delete the temporary mark before putting a real mark at its
    499500            // location
    500             Delete(frame, total);
    501         }
    502     }
    503 
    504     uint64_t from = GetNearestMark(frame, total, right);
    505     Move(from, frame, total);
     501            Delete(frame, "");
     502        }
     503    }
     504
     505    uint64_t from = GetNearestMark(frame, right);
     506    Move(from, frame);
    506507}
    507508
    508509/// Move an existing mark to a new frame.
    509 void DeleteMap::Move(uint64_t frame, uint64_t to, uint64_t total)
     510void DeleteMap::Move(uint64_t frame, uint64_t to)
    510511{
    511512    EDIT_CHECK;
     
    515516        if (frame == 0)
    516517            type = MARK_CUT_START;
    517         else if (frame == total)
     518        else if (frame == m_ctx->player->GetTotalFrameCount())
    518519            type = MARK_CUT_END;
    519520    }
    520     Add(to, total, type, tr("Move Mark"));
     521    Add(to, type, tr("Move Mark"));
    521522}
    522523
     
    585586 *        next/previous mark exists, and false otherwise.
    586587 */
    587 uint64_t DeleteMap::GetNearestMark(
    588     uint64_t frame, uint64_t total, bool right,
    589     bool *hasMark) const
     588uint64_t DeleteMap::GetNearestMark(uint64_t frame, bool right, bool *hasMark)
     589    const
    590590{
    591591    uint64_t result;
     
    595595    if (right)
    596596    {
    597         result = total;
     597        result = m_ctx->player->GetTotalFrameCount();
    598598        for (; it != m_deleteMap.end(); ++it)
    599599            if (it.key() > frame)
     
    636636 *        not include the first or last frames.
    637637 */
    638 void DeleteMap::CleanMap(uint64_t total)
     638void DeleteMap::CleanMap(void)
    639639{
    640640    if (IsEmpty())
    641641        return;
    642642
     643    uint64_t total = m_ctx->player->GetTotalFrameCount();
    643644    Delete(0);
    644645    Delete(total);
     
    694695
    695696/// Loads the given commercial break map into the deleteMap.
    696 void DeleteMap::LoadCommBreakMap(uint64_t total, frm_dir_map_t &map)
     697void DeleteMap::LoadCommBreakMap(frm_dir_map_t &map)
    697698{
    698699    Clear();
     
    701702        Add(it.key(), it.value() == MARK_COMM_START ?
    702703                MARK_CUT_START : MARK_CUT_END);
    703     CleanMap(total);
     704    CleanMap();
    704705    Push(tr("Load Detected Commercials"));
    705706}
    706707
    707708/// Loads the delete map from the database.
    708 void DeleteMap::LoadMap(uint64_t total, QString undoMessage)
     709void DeleteMap::LoadMap(QString undoMessage)
    709710{
    710711    if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
     
    715716    m_ctx->playingInfo->QueryCutList(m_deleteMap);
    716717    m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    717     CleanMap(total);
     718    CleanMap();
    718719    if (!undoMessage.isEmpty())
    719720        Push(undoMessage);
     
    722723/// Returns true if an auto-save map was loaded.
    723724/// Does nothing and returns false if not.
    724 bool DeleteMap::LoadAutoSaveMap(uint64_t total)
     725bool DeleteMap::LoadAutoSaveMap(void)
    725726{
    726727    if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
     
    732733    bool result = m_ctx->playingInfo->QueryCutList(m_deleteMap, true);
    733734    m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    734     CleanMap(total);
     735    CleanMap();
    735736    if (result)
    736737        Push(tr("Load Auto-saved Cuts"));
     
    742743
    743744/// Saves the delete map to the database.
    744 void DeleteMap::SaveMap(uint64_t total, bool isAutoSave)
     745void DeleteMap::SaveMap(bool isAutoSave)
    745746{
    746747    if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
     
    761762        }
    762763
    763         CleanMap(total);
     764        CleanMap();
    764765    }
    765766    m_ctx->LockPlayingInfo(__FILE__, __LINE__);
     
    775776 *        many times per second.
    776777 */
    777 void DeleteMap::TrackerReset(uint64_t frame, uint64_t total)
     778void DeleteMap::TrackerReset(uint64_t frame)
    778779{
    779780    m_nextCutStart = 0;
     
    794795            ++cutpoint;
    795796            m_nextCutStartIsValid = (cutpoint != m_deleteMap.end());
    796             m_nextCutStart = m_nextCutStartIsValid ? cutpoint.key() : total;
     797            m_nextCutStart = m_nextCutStartIsValid ? cutpoint.key() :
     798                m_ctx->player->GetTotalFrameCount();
    797799        }
    798800    }
    799801    else
    800         m_nextCutStart = GetNearestMark(frame, total, !IsInDelete(frame),
     802        m_nextCutStart = GetNearestMark(frame, !IsInDelete(frame),
    801803                                        &m_nextCutStartIsValid);
    802804    LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Tracker next CUT_START: %1")
     
    808810 *        and provides the frame number of the next jump.
    809811 */
    810 bool DeleteMap::TrackerWantsToJump(uint64_t frame, uint64_t total, uint64_t &to)
     812bool DeleteMap::TrackerWantsToJump(uint64_t frame, uint64_t &to)
    811813{
    812814    if (IsEmpty() || !m_nextCutStartIsValid || frame < m_nextCutStart)
    813815        return false;
    814816
    815     to = GetNearestMark(m_nextCutStart, total, true);
     817    to = GetNearestMark(m_nextCutStart, true);
    816818    LOG(VB_PLAYBACK, LOG_INFO, LOC +
    817819        QString("Tracker wants to jump to: %1").arg(to));
     
    823825 *        cut sequence.
    824826 */
    825 uint64_t DeleteMap::GetLastFrame(uint64_t total) const
    826 {
    827     uint64_t result = total;
     827uint64_t DeleteMap::GetLastFrame(void) const
     828{
     829    uint64_t result = m_ctx->player->GetTotalFrameCount();
    828830    if (IsEmpty())
    829831        return result;
     
    863865}
    864866
    865 uint64_t DeleteMap::TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap,
    866                                               uint64_t absPosition)
    867 {
    868     uint64_t subtraction = 0;
    869     uint64_t startOfCutRegion = 0;
    870     frm_dir_map_t::const_iterator i;
    871     bool withinCut = false;
    872     bool first = true;
    873     for (i = deleteMap.constBegin(); i != deleteMap.constEnd(); ++i)
    874     {
    875         if (first)
    876             withinCut = (i.value() == MARK_CUT_END);
    877         first = false;
    878         if (i.key() > absPosition)
    879             break;
    880         if (i.value() == MARK_CUT_START && !withinCut)
    881         {
    882             withinCut = true;
    883             startOfCutRegion = i.key();
    884         }
    885         else if (i.value() == MARK_CUT_END && withinCut)
    886         {
    887             withinCut = false;
    888             subtraction += (i.key() - startOfCutRegion);
    889         }
    890     }
    891     if (withinCut)
    892         subtraction += (absPosition - startOfCutRegion);
    893     return absPosition - subtraction;
    894 }
    895 
    896 uint64_t DeleteMap::TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap,
    897                                               uint64_t relPosition)
    898 {
    899     uint64_t addition = 0;
    900     uint64_t startOfCutRegion = 0;
    901     frm_dir_map_t::const_iterator i;
    902     bool withinCut = false;
    903     bool first = true;
    904     for (i = deleteMap.constBegin(); i != deleteMap.constEnd(); ++i)
    905     {
    906         if (first)
    907             withinCut = (i.value() == MARK_CUT_END);
    908         first = false;
    909         if (i.value() == MARK_CUT_START && !withinCut)
    910         {
    911             withinCut = true;
    912             startOfCutRegion = i.key();
    913             if (relPosition + addition <= startOfCutRegion)
    914                 break;
    915         }
    916         else if (i.value() == MARK_CUT_END && withinCut)
    917         {
    918             withinCut = false;
    919             addition += (i.key() - startOfCutRegion);
    920         }
    921     }
    922     return relPosition + addition;
    923 }
     867uint64_t DeleteMap::TranslatePositionFrameToMs(uint64_t position,
     868                                               float fallback_framerate,
     869                                               bool use_cutlist) const
     870{
     871    return m_ctx->player->GetDecoder()
     872        ->TranslatePositionFrameToMs(position, fallback_framerate,
     873                                     use_cutlist ? m_deleteMap :
     874                                     frm_dir_map_t());
     875}
     876uint64_t DeleteMap::TranslatePositionMsToFrame(uint64_t dur_ms,
     877                                               float fallback_framerate,
     878                                               bool use_cutlist) const
     879{
     880    return m_ctx->player->GetDecoder()
     881        ->TranslatePositionMsToFrame(dur_ms, fallback_framerate,
     882                                     use_cutlist ? m_deleteMap :
     883                                     frm_dir_map_t());
     884}
     885
     886uint64_t DeleteMap::TranslatePositionAbsToRel(uint64_t position) const
     887{
     888    return DecoderBase::TranslatePositionAbsToRel(m_deleteMap, position);
     889}
     890
     891uint64_t DeleteMap::TranslatePositionRelToAbs(uint64_t position) const
     892{
     893    return DecoderBase::TranslatePositionRelToAbs(m_deleteMap, position);
     894}
  • mythtv/libs/libmythtv/deletemap.h

    r1eaecea6dd r49dbed5be  
    2626                 m_nextCutStartIsValid(false),
    2727                 m_nextCutStart(0), m_changed(true),
    28                  m_seekamountpos(4), m_seekamount(30),
    29                  m_ctx(0), m_cachedTotalForOSD(0), m_undoStackPointer(-1)
     28                 m_seekamountpos(4), m_seekamount(1.0),
     29                 m_ctx(NULL), m_cachedTotalForOSD(0), m_undoStackPointer(-1)
    3030    {
    3131        Push("");
     
    3333
    3434    void SetPlayerContext(PlayerContext *ctx) { m_ctx = ctx; }
    35     bool HandleAction(QString &action, uint64_t frame, uint64_t played,
    36                       uint64_t total, double rate);
    37     int  GetSeekAmount(void) const { return m_seekamount; }
    38     void UpdateSeekAmount(int change, double framerate);
    39     void SetSeekAmount(int amount) { m_seekamount = amount; }
     35    bool HandleAction(QString &action, uint64_t frame, uint64_t played);
     36    float GetSeekAmount(void) const { return m_seekamount; }
     37    void UpdateSeekAmount(int change);
     38    void SetSeekAmount(float amount) { m_seekamount = amount; }
    4039
    41     void UpdateOSD(uint64_t frame, uint64_t total, double frame_rate, OSD *osd);
     40    void UpdateOSD(uint64_t frame, double frame_rate, OSD *osd);
    4241
    4342    bool IsEditing(void) const { return m_editing; }
     
    4948
    5049    void SetMap(const frm_dir_map_t &map);
    51     void LoadCommBreakMap(uint64_t total, frm_dir_map_t &map);
    52     void SaveMap(uint64_t total, bool isAutoSave = false);
    53     void LoadMap(uint64_t total, QString undoMessage = "");
    54     bool LoadAutoSaveMap(uint64_t total);
    55     void CleanMap(uint64_t total);
     50    void LoadCommBreakMap(frm_dir_map_t &map);
     51    void SaveMap(bool isAutoSave = false);
     52    void LoadMap(QString undoMessage = "");
     53    bool LoadAutoSaveMap(void);
     54    void CleanMap(void);
    5655
    5756    void Clear(QString undoMessage = "");
    58     void ReverseAll(uint64_t total);
    59     void Add(uint64_t frame, uint64_t total, MarkTypes type,
    60              QString undoMessage);
    61     void NewCut(uint64_t frame, uint64_t total);
    62     void Delete(uint64_t frame, uint64_t total, QString undoMessage = "");
    63     void MoveRelative(uint64_t frame, uint64_t total, bool right);
    64     void Move(uint64_t frame, uint64_t to, uint64_t total);
     57    void ReverseAll(void);
     58    void Add(uint64_t frame, MarkTypes type, QString undoMessage);
     59    void NewCut(uint64_t frame);
     60    void Delete(uint64_t frame, QString undoMessage);
     61    void MoveRelative(uint64_t frame, bool right);
     62    void Move(uint64_t frame, uint64_t to);
    6563
    6664    bool     IsInDelete(uint64_t frame) const;
    67     uint64_t GetNearestMark(uint64_t frame, uint64_t total, bool right,
    68                             bool *hasMark = 0) const;
     65    uint64_t GetNearestMark(uint64_t frame, bool right,
     66                            bool *hasMark = NULL) const;
    6967    bool     IsTemporaryMark(uint64_t frame) const;
    7068    bool     HasTemporaryMark(void) const;
    71     uint64_t GetLastFrame(uint64_t total) const;
    72     uint64_t TranslatePositionAbsToRel(uint64_t absPosition) const {
    73         return TranslatePositionAbsToRel(m_deleteMap, absPosition);
    74     }
    75     uint64_t TranslatePositionRelToAbs(uint64_t relPosition) const {
    76         return TranslatePositionRelToAbs(m_deleteMap, relPosition);
    77     }
    78     static uint64_t TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap,
    79                                               uint64_t absPosition);
    80     static uint64_t TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap,
    81                                               uint64_t relPosition);
     69    uint64_t GetLastFrame(void) const;
    8270
    83     void TrackerReset(uint64_t frame, uint64_t total);
    84     bool TrackerWantsToJump(uint64_t frame, uint64_t total, uint64_t &to);
     71    // Provide translations between frame numbers and millisecond
     72    // durations, optionally taking the custlist into account.
     73    uint64_t TranslatePositionFrameToMs(uint64_t position,
     74                                        float fallback_framerate,
     75                                        bool use_cutlist) const;
     76    uint64_t TranslatePositionMsToFrame(uint64_t dur_ms,
     77                                        float fallback_framerate,
     78                                        bool use_cutlist) const;
     79    uint64_t TranslatePositionAbsToRel(uint64_t position) const;
     80    uint64_t TranslatePositionRelToAbs(uint64_t position) const;
     81
     82    void TrackerReset(uint64_t frame);
     83    bool TrackerWantsToJump(uint64_t frame, uint64_t &to);
    8584
    8685    bool Undo(void);
     
    9897    void Push(QString undoMessage);
    9998
     99    QString CreateTimeString(uint64_t frame, bool use_cutlist,
     100                             double frame_rate, bool full_resolution) const;
     101
    100102    bool          m_editing;
    101103    bool          m_nextCutStartIsValid;
     
    105107    bool          m_changed;
    106108    int           m_seekamountpos;
    107     int           m_seekamount;
     109    float         m_seekamount;
    108110    PlayerContext *m_ctx;
    109111    uint64_t      m_cachedTotalForOSD;
  • mythtv/libs/libmythtv/mythcommflagplayer.cpp

    r1eaecea6dd r49dbed5be  
    7575        player_ctx->playingInfo->ClearPositionMap(MARK_GOP_START);
    7676        player_ctx->playingInfo->ClearPositionMap(MARK_GOP_BYFRAME);
     77        player_ctx->playingInfo->ClearPositionMap(MARK_DURATION_MS);
    7778    }
    7879    player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
  • mythtv/libs/libmythtv/mythplayer.cpp

    r1eaecea6dd r49dbed5be  
    1616#include <sys/time.h>
    1717#include <assert.h>
     18#include <math.h>
    1819
    1920// C++ headers
     
    992993    {
    993994        hasFullPositionMap = true;
    994         deleteMap.LoadMap(totalFrames);
    995         deleteMap.TrackerReset(0, totalFrames);
     995        deleteMap.LoadMap();
     996        deleteMap.TrackerReset(0);
    996997    }
    997998
    998999    // Determine the initial bookmark and update it for the cutlist
    9991000    bookmarkseek = GetBookmark();
    1000     deleteMap.TrackerReset(bookmarkseek, totalFrames);
    1001     deleteMap.TrackerWantsToJump(bookmarkseek, totalFrames, bookmarkseek);
     1001    deleteMap.TrackerReset(bookmarkseek);
     1002    deleteMap.TrackerWantsToJump(bookmarkseek, bookmarkseek);
    10021003
    10031004    if (!gCoreContext->IsDatabaseIgnored() &&
     
    23962397
    23972398    if (fftime <= 0)
    2398         fftime = (long long)(seconds * video_frame_rate + 0.5);
     2399    {
     2400        float current = ComputeSecs(framesPlayed, true);
     2401        float dest = current + seconds;
     2402        uint64_t target = FindFrame(dest, true);
     2403        fftime = target - framesPlayed;
     2404    }
    23992405    return fftime > CalcMaxFFTime(fftime, false);
    24002406}
     
    24062412
    24072413    if (rewindtime <= 0)
    2408         rewindtime = (long long)(seconds * video_frame_rate + 0.5);
     2414    {
     2415        float current = ComputeSecs(framesPlayed, true);
     2416        float dest = current + seconds;
     2417        uint64_t target = FindFrame(dest, true);
     2418        rewindtime = target - framesPlayed;
     2419    }
    24092420    return (uint64_t)rewindtime >= framesPlayed;
    24102421}
     
    27672778void MythPlayer::EventLoop(void)
    27682779{
     2780    uint64_t frameCount = GetCurrentFrameCount();
    27692781    // recreate the osd if a reinit was triggered by another thread
    27702782    if (reinit_osd)
     
    27952807            forcePositionMapSync = true;
    27962808            osdLock.lock();
    2797             deleteMap.UpdateOSD(framesPlayed, totalFrames, video_frame_rate,
    2798                                 osd);
     2809            deleteMap.UpdateOSD(framesPlayed, video_frame_rate, osd);
    27992810            osdLock.unlock();
    28002811            editUpdateTimer.start();
     
    29432954            QString msg;
    29442955            uint64_t jumpto = 0;
     2956            // XXX CommBreakMap should use duration map not video_frame_rate
    29452957            bool jump = commBreakMap.DoSkipCommercials(jumpto, framesPlayed,
    2946                                             video_frame_rate, totalFrames, msg);
     2958                                                       video_frame_rate,
     2959                                                       frameCount, msg);
    29472960            if (!msg.isEmpty())
    29482961                SetOSDStatus(msg, kOSDTimeout_Med);
     
    29602973    {
    29612974        QString msg;
     2975        // XXX CommBreakMap should use duration map not video_frame_rate
    29622976        bool jump = commBreakMap.AutoCommercialSkip(jumpto, framesPlayed,
    29632977                                                    video_frame_rate,
    2964                                                     totalFrames, msg);
     2978                                                    frameCount, msg);
    29652979        if (!msg.isEmpty())
    29662980            SetOSDStatus(msg, kOSDTimeout_Med);
     
    29712985    // Handle cutlist skipping
    29722986    if (!allpaused && (ffrew_skip == 1) &&
    2973         deleteMap.TrackerWantsToJump(framesPlayed, totalFrames, jumpto))
     2987        deleteMap.TrackerWantsToJump(framesPlayed, jumpto))
    29742988    {
    29752989        if (jumpto == totalFrames)
     
    33703384    }
    33713385
    3372     long long numFrames = totalFrames;
     3386    uint64_t numFrames = GetCurrentFrameCount();
    33733387
    33743388    // For recordings we want to ignore the post-roll and account for
     
    35523566}
    35533567
     3568bool MythPlayer::DoRewindSecs(float secs, double inaccuracy, bool use_cutlist)
     3569{
     3570    float current = ComputeSecs(framesPlayed, use_cutlist);
     3571    float target = current - secs;
     3572    if (target < 0)
     3573        target = 0;
     3574    uint64_t targetFrame = FindFrame(target, use_cutlist);
     3575    return DoRewind(framesPlayed - targetFrame, inaccuracy);
     3576}
     3577
    35543578long long MythPlayer::CalcRWTime(long long rw) const
    35553579{
     
    35603584        return rw;
    35613585
    3562     player_ctx->tvchain->JumpToNext(false, (int)(-15.0 * video_frame_rate));
     3586    player_ctx->tvchain->JumpToNext(false, (int)(-15.0 * video_frame_rate)); // XXX use seconds instead of assumed framerate
    35633587    return -1;
    35643588}
    35653589
    3566 long long MythPlayer::CalcMaxFFTime(long long ff, bool setjump) const
    3567 {
    3568     long long maxtime = (long long)(1.0 * video_frame_rate);
     3590long long MythPlayer::CalcMaxFFTime(long long ffframes, bool setjump) const
     3591{
     3592    float maxtime = 1.0;
    35693593    bool islivetvcur = (livetv && player_ctx->tvchain &&
    35703594                        !player_ctx->tvchain->HasNext());
    35713595
    35723596    if (livetv || IsWatchingInprogress())
    3573         maxtime = (long long)(3.0 * video_frame_rate);
    3574 
    3575     long long ret = ff;
     3597        maxtime = 3.0;
     3598
     3599    long long ret = ffframes;
     3600    float ff = ComputeSecs(ffframes, true);
     3601    float secsPlayed = ComputeSecs(framesPlayed, true);
    35763602
    35773603    limitKeyRepeat = false;
     
    35813607        if (totalFrames > 0)
    35823608        {
    3583             long long behind = totalFrames - framesPlayed;
     3609            float behind = ComputeSecs(totalFrames, true) - secsPlayed;
    35843610            if (behind < maxtime || behind - ff <= maxtime * 2)
    35853611            {
     
    35923618    else if (islivetvcur || IsWatchingInprogress())
    35933619    {
    3594         long long behind = player_ctx->recorder->GetFramesWritten() -
    3595             framesPlayed;
     3620        float secsWritten =
     3621            ComputeSecs(player_ctx->recorder->GetFramesWritten(), true);
     3622        float behind = secsWritten - secsPlayed;
    35963623
    35973624        if (behind < maxtime) // if we're close, do nothing
    35983625            ret = 0;
    35993626        else if (behind - ff <= maxtime)
    3600             ret = behind - maxtime;
     3627            ret = TranslatePositionMsToFrame(1000 * (secsWritten - maxtime),
     3628                                             true) - framesPlayed;
    36013629
    36023630        if (behind < maxtime * 3)
     
    36073635        if (totalFrames > 0)
    36083636        {
    3609             long long behind = totalFrames - framesPlayed;
     3637            float behind = ComputeSecs(totalFrames, true) - secsPlayed;
    36103638            if (behind < maxtime)
    36113639                ret = 0;
    36123640            else if (behind - ff <= maxtime * 2)
    3613                 ret = behind - maxtime * 2;
     3641            {
     3642                uint64_t ms = 1000 *
     3643                    (ComputeSecs(totalFrames, true) - maxtime * 2);
     3644                ret = TranslatePositionMsToFrame(ms, true) - framesPlayed;
     3645            }
    36143646        }
    36153647    }
     
    36603692        player_ctx->GetState() == kState_WatchingPreRecorded)
    36613693    {
    3662         if (framesRead >= deleteMap.GetLastFrame(totalFrames))
    3663             return true;
    3664         framesLeft = (totalFrames > framesRead) ? totalFrames - framesRead : 0;
     3694        uint64_t frameCount = GetCurrentFrameCount();
     3695        framesLeft = (frameCount > framesRead) ? frameCount - framesRead : 0;
    36653696        return (framesLeft < (uint64_t)margin);
    36663697    }
     
    36953726    if (!deleteMap.IsEditing() && IsInDelete(desiredFrame))
    36963727    {
    3697         uint64_t endcheck = deleteMap.GetLastFrame(totalFrames);
     3728        uint64_t endcheck = deleteMap.GetLastFrame();
    36983729        if (desiredFrame > endcheck)
    36993730            desiredFrame = endcheck;
     
    37093740}
    37103741
     3742bool MythPlayer::DoFastForwardSecs(float secs, double inaccuracy,
     3743                                   bool use_cutlist)
     3744{
     3745    float current = ComputeSecs(framesPlayed, use_cutlist);
     3746    float target = current + secs;
     3747    uint64_t targetFrame = FindFrame(target, use_cutlist);
     3748    return DoFastForward(targetFrame - framesPlayed, inaccuracy);
     3749}
     3750
    37113751void MythPlayer::DoJumpToFrame(uint64_t frame, double inaccuracy)
    37123752{
     
    37273767                        !player_ctx->tvchain->HasNext());
    37283768
    3729     uint64_t max = totalFrames;
     3769    uint64_t max = GetCurrentFrameCount();
    37303770    if (islivetvcur || IsWatchingInprogress())
    37313771    {
     
    37943834    audio.Reset();
    37953835    ResetCaptions();
    3796     deleteMap.TrackerReset(framesPlayed, totalFrames);
     3836    deleteMap.TrackerReset(framesPlayed);
    37973837    commBreakMap.SetTracker(framesPlayed);
    37983838    commBreakMap.ResetLastSkip();
     
    38353875    osd->HideAll();
    38363876
    3837     bool loadedAutoSave = deleteMap.LoadAutoSaveMap(totalFrames);
     3877    bool loadedAutoSave = deleteMap.LoadAutoSaveMap();
    38383878    if (loadedAutoSave)
    38393879    {
     
    38423882    }
    38433883
    3844     deleteMap.UpdateSeekAmount(0, video_frame_rate);
    3845     deleteMap.UpdateOSD(framesPlayed, totalFrames, video_frame_rate, osd);
     3884    deleteMap.UpdateSeekAmount(0);
     3885    deleteMap.UpdateOSD(framesPlayed, video_frame_rate, osd);
    38463886    deleteMap.SetFileEditing(true);
    38473887    player_ctx->LockPlayingInfo(__FILE__, __LINE__);
     
    38693909    deleteMap.SetEditing(false, osd);
    38703910    if (howToSave == 0)
    3871         deleteMap.LoadMap(totalFrames);
     3911        deleteMap.LoadMap();
    38723912    // Unconditionally save to remove temporary marks from the DB.
    38733913    if (howToSave >= 0)
    3874         deleteMap.SaveMap(totalFrames);
    3875     deleteMap.TrackerReset(framesPlayed, totalFrames);
     3914        deleteMap.SaveMap();
     3915    deleteMap.TrackerReset(framesPlayed);
    38763916    deleteMap.SetFileEditing(false);
    38773917    player_ctx->LockPlayingInfo(__FILE__, __LINE__);
     
    38953935        QString action = actions[i];
    38963936        handled = true;
    3897         int seekamount = deleteMap.GetSeekAmount();
     3937        float seekamount = deleteMap.GetSeekAmount();
    38983938        if (action == ACTION_LEFT)
    38993939        {
    3900             if (deleteMap.GetSeekAmount() > 0)
    3901             {
    3902                 DoRewind(seekamount, seekamount > 1 ?
    3903                          kInaccuracyEditor : kInaccuracyNone);
    3904             }
     3940            if (seekamount == 0) // 1 frame
     3941                DoRewind(1, kInaccuracyNone);
     3942            else if (seekamount > 0)
     3943                DoRewindSecs(seekamount, kInaccuracyEditor, false);
    39053944            else
    39063945                HandleArbSeek(false);
     
    39083947        else if (action == ACTION_RIGHT)
    39093948        {
    3910             if (deleteMap.GetSeekAmount() > 0)
    3911             {
    3912                 DoFastForward(seekamount, seekamount > 1 ?
    3913                               kInaccuracyEditor : kInaccuracyNone);
    3914             }
     3949            if (seekamount == 0) // 1 frame
     3950                DoFastForward(1, kInaccuracyNone);
     3951            else if (seekamount > 0)
     3952                DoFastForwardSecs(seekamount, kInaccuracyEditor, false);
    39153953            else
    39163954                HandleArbSeek(true);
     
    39223960                frm_dir_map_t map;
    39233961                commBreakMap.GetMap(map);
    3924                 deleteMap.LoadCommBreakMap(totalFrames, map);
     3962                deleteMap.LoadCommBreakMap(map);
    39253963            }
    39263964        }
    39273965        else if (action == ACTION_PREVCUT)
    39283966        {
    3929             int old_seekamount = deleteMap.GetSeekAmount();
     3967            float old_seekamount = deleteMap.GetSeekAmount();
    39303968            deleteMap.SetSeekAmount(-2);
    39313969            HandleArbSeek(false);
     
    39343972        else if (action == ACTION_NEXTCUT)
    39353973        {
    3936             int old_seekamount = deleteMap.GetSeekAmount();
     3974            float old_seekamount = deleteMap.GetSeekAmount();
    39373975            deleteMap.SetSeekAmount(-2);
    39383976            HandleArbSeek(true);
     
    39423980        else if (action == ACTION_BIGJUMPREW)
    39433981        {
    3944             if (seekamount > 0)
    3945                 DoRewind(seekamount * FFREW_MULTICOUNT, seekamount > 1 ?
    3946                          kInaccuracyEditor : kInaccuracyNone);
     3982            if (seekamount == 0)
     3983                DoRewind(FFREW_MULTICOUNT, kInaccuracyNone);
     3984            else if (seekamount > 0)
     3985                DoRewindSecs(seekamount * FFREW_MULTICOUNT,
     3986                             kInaccuracyEditor, false);
    39473987            else
    3948             {
    3949                 int fps = (int)ceil(video_frame_rate);
    3950                 DoRewind(fps * FFREW_MULTICOUNT / 2, kInaccuracyNone);
    3951             }
     3988                DoRewindSecs(FFREW_MULTICOUNT / 2,
     3989                             kInaccuracyNone, false);
    39523990        }
    39533991        else if (action == ACTION_BIGJUMPFWD)
    39543992        {
    3955             if (seekamount > 0)
    3956                 DoFastForward(seekamount * FFREW_MULTICOUNT, seekamount > 1 ?
    3957                               kInaccuracyEditor : kInaccuracyNone);
     3993            if (seekamount == 0)
     3994                DoFastForward(FFREW_MULTICOUNT, kInaccuracyNone);
     3995            else if (seekamount > 0)
     3996                DoFastForwardSecs(seekamount * FFREW_MULTICOUNT,
     3997                                  kInaccuracyEditor, false);
    39583998            else
    3959             {
    3960                 int fps = (int)ceil(video_frame_rate);
    3961                 DoFastForward(fps * FFREW_MULTICOUNT / 2,
    3962                               kInaccuracyNone);
    3963             }
     3999                DoFastForwardSecs(FFREW_MULTICOUNT / 2,
     4000                                  kInaccuracyNone, false);
    39644001        }
    39654002        else if (action == ACTION_SELECT)
    39664003        {
    3967             deleteMap.NewCut(frame, totalFrames);
     4004            deleteMap.NewCut(frame);
    39684005            SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
    39694006            refresh = true;
     
    39714008        else if (action == "DELETE")
    39724009        {
    3973             deleteMap.Delete(frame, totalFrames, tr("Delete"));
     4010            deleteMap.Delete(frame, tr("Delete"));
    39744011            refresh = true;
    39754012        }
    39764013        else if (action == "REVERT")
    39774014        {
    3978             deleteMap.LoadMap(totalFrames, tr("Undo Changes"));
     4015            deleteMap.LoadMap(tr("Undo Changes"));
    39794016            refresh = true;
    39804017        }
     
    39864023        else if (action == ACTION_SAVEMAP)
    39874024        {
    3988             deleteMap.SaveMap(totalFrames);
     4025            deleteMap.SaveMap();
    39894026            refresh = true;
    39904027        }
     
    39984035            QString undoMessage = deleteMap.GetUndoMessage();
    39994036            QString redoMessage = deleteMap.GetRedoMessage();
    4000             handled = deleteMap.HandleAction(action, frame, framesPlayed,
    4001                                              totalFrames, video_frame_rate);
     4037            handled = deleteMap.HandleAction(action, frame, framesPlayed);
    40024038            if (handled && (action == "CUTTOBEGINNING" ||
    40034039                action == "CUTTOEND" || action == "NEWCUT"))
     
    40254061        if (osd)
    40264062        {
    4027             deleteMap.UpdateOSD(framesPlayed, totalFrames, video_frame_rate,
    4028                                 osd);
     4063            deleteMap.UpdateOSD(framesPlayed, video_frame_rate, osd);
    40294064        }
    40304065        osdLock.unlock();
     
    40414076uint64_t MythPlayer::GetNearestMark(uint64_t frame, bool right)
    40424077{
    4043     return deleteMap.GetNearestMark(frame, totalFrames, right);
     4078    return deleteMap.GetNearestMark(frame, right);
    40444079}
    40454080
     
    40584093    if (deleteMap.GetSeekAmount() == -2)
    40594094    {
    4060         long long framenum = deleteMap.GetNearestMark(framesPlayed,
    4061                                                       totalFrames, right);
     4095        uint64_t framenum = deleteMap.GetNearestMark(framesPlayed, right);
    40624096        if (right && (framenum > (int64_t)framesPlayed))
    40634097            DoFastForward(framenum - framesPlayed, kInaccuracyNone);
     
    40684102    {
    40694103        if (right)
    4070         {
    40714104            DoFastForward(2, kInaccuracyFull);
    4072         }
    40734105        else
    4074         {
    40754106            DoRewind(2, kInaccuracyFull);
    4076         }
    40774107    }
    40784108}
     
    43054335        {
    43064336            uint64_t oldnumber = number;
    4307             deleteMap.LoadMap(totalFrames);
     4337            deleteMap.LoadMap();
    43084338            commBreakMap.LoadMap(player_ctx, framesPlayed);
    43094339
     
    44654495
    44664496    if ((lastDecodedFrameNumber == 0) && honorCutList)
    4467         deleteMap.TrackerReset(0, 0);
     4497        deleteMap.TrackerReset(0);
    44684498
    44694499    if (!decoderThread)
     
    44854515
    44864516        uint64_t jumpto = 0;
    4487         if (deleteMap.TrackerWantsToJump(lastDecodedFrameNumber, totalFrames,
    4488                                          jumpto))
     4517        if (deleteMap.TrackerWantsToJump(lastDecodedFrameNumber, jumpto))
    44894518        {
    44904519            LOG(VB_GENERAL, LOG_INFO, LOC +
     
    46134642}
    46144643
    4615 int64_t MythPlayer::GetSecondsPlayed(void)
    4616 {
    4617 #if 0
    4618     return decoder->IsCodecMPEG() ?
    4619                 (disp_timecode / 1000.f) :
    4620                 (framesPlayed / video_frame_rate);
    4621 #else
    4622     return framesPlayed / video_frame_rate;
    4623 #endif
    4624 }
    4625 
    46264644int64_t MythPlayer::GetTotalSeconds(void) const
    46274645{
    46284646    return totalDuration;
     4647}
     4648
     4649// Returns the total frame count, as totalFrames for a completed
     4650// recording, or the most recent frame count from the recorder for
     4651// live TV or an in-progress recording.
     4652uint64_t MythPlayer::GetCurrentFrameCount(void) const
     4653{
     4654    uint64_t result = totalFrames;
     4655    if (IsWatchingInprogress())
     4656        result = player_ctx->recorder->GetFramesWritten();
     4657    return result;
     4658}
     4659
     4660// Finds the frame number associated with the given time offset.  A
     4661// positive offset or +0.0f indicate offset from the beginning.  A
     4662// negative offset or -0.0f indicate offset from the end.  Limit the
     4663// result to within bounds of the video.
     4664uint64_t MythPlayer::FindFrame(float offset, bool use_cutlist) const
     4665{
     4666    uint64_t length_ms = TranslatePositionFrameToMs(totalFrames, use_cutlist);
     4667    uint64_t offset_ms = offset * 1000 + 0.5;
     4668    if (signbit(offset))
     4669        offset_ms += length_ms;
     4670    if (offset_ms < 0)
     4671        offset_ms = 0;
     4672    if (offset_ms > length_ms)
     4673        offset_ms = length_ms;
     4674    return TranslatePositionMsToFrame(offset_ms, use_cutlist);
    46294675}
    46304676
     
    46454691    info.values.insert("progafter",  0);
    46464692
    4647     int playbackLen = GetTotalSeconds();
    4648     float secsplayed = (float)GetSecondsPlayed();
    4649 
    4650     if (totalDuration == 0 || decoder->GetCodecDecoderName() == "nuppel")
     4693    uint64_t frames_played = framesPlayed;
     4694    uint64_t total_frames = totalFrames;
     4695    int playbackLen = 0;
     4696    bool fixed_playbacklen = false;
     4697
     4698    if (decoder->GetCodecDecoderName() == "nuppel")
     4699    {
    46514700        playbackLen = totalLength;
     4701        fixed_playbacklen = true;
     4702    }
    46524703
    46534704    if (livetv && player_ctx->tvchain)
     
    46574708        playbackLen = player_ctx->tvchain->GetLengthAtCurPos();
    46584709        islive = true;
     4710        fixed_playbacklen = true;
    46594711    }
    46604712    else if (IsWatchingInprogress())
    46614713    {
    4662         playbackLen =
    4663             (int)(((float)player_ctx->recorder->GetFramesWritten() /
    4664                    video_frame_rate));
     4714        total_frames = player_ctx->recorder->GetFramesWritten();
    46654715        islive = true;
    46664716    }
     
    46924742    }
    46934743
    4694     playbackLen = max(playbackLen, 1);
    4695     secsplayed  = min((float)playbackLen, max(secsplayed, 0.0f));
    4696 
    46974744    // Set the raw values, followed by the translated values.
    46984745    for (int i = 0; i < 2 ; ++i)
    46994746    {
    47004747        QString relPrefix = (i == 0 ? "" : "rel");
    4701         if (i > 0)
    4702         {
    4703             playbackLen = deleteMap.TranslatePositionAbsToRel(playbackLen * video_frame_rate) /
    4704                 video_frame_rate;
    4705             secsplayed = deleteMap.TranslatePositionAbsToRel(secsplayed * video_frame_rate) /
    4706                 video_frame_rate;
    4707         }
     4748        if (!fixed_playbacklen)
     4749            playbackLen =
     4750                TranslatePositionFrameToMs(total_frames, (i > 0))
     4751            / 1000;
     4752        playbackLen = max(playbackLen, 1);
     4753        float secsplayed =
     4754            TranslatePositionFrameToMs(frames_played, (i > 0))
     4755            / 1000;
     4756        secsplayed = min((float)playbackLen, max(secsplayed, 0.0f));
    47084757
    47094758        info.values.insert(relPrefix + "secondsplayed", (int)secsplayed);
     
    50585107}
    50595108
    5060 bool MythPlayer::PosMapFromEnc(unsigned long long start,
    5061                                QMap<long long, long long> &posMap)
     5109bool MythPlayer::PosMapFromEnc(uint64_t start,
     5110                               frm_pos_map_t &posMap,
     5111                               frm_pos_map_t &durMap)
    50625112{
    50635113    // Reads only new positionmap entries from encoder
     
    50745124
    50755125    player_ctx->recorder->FillPositionMap(start, -1, posMap);
     5126    player_ctx->recorder->FillDurationMap(start, -1, durMap);
    50765127
    50775128    return true;
  • mythtv/libs/libmythtv/mythplayer.h

    r1eaecea6dd r49dbed5be  
    181181    int     GetLength(void) const             { return totalLength; }
    182182    uint64_t GetTotalFrameCount(void) const   { return totalFrames; }
     183    uint64_t GetCurrentFrameCount(void) const;
    183184    uint64_t GetFramesPlayed(void) const      { return framesPlayed; }
    184     virtual  int64_t GetSecondsPlayed(void);
    185185    virtual  int64_t GetTotalSeconds(void) const;
    186186    virtual  uint64_t GetBookmark(void);
     
    322322
    323323    // Position Map Stuff
    324     bool PosMapFromEnc(unsigned long long          start,
    325                        QMap<long long, long long> &posMap);
     324    bool PosMapFromEnc(uint64_t start,
     325                       frm_pos_map_t &posMap,
     326                       frm_pos_map_t &durMap);
    326327
    327328    // OSD locking for TV class
     
    392393    long long CalcRWTime(long long rw) const;
    393394    virtual void calcSliderPos(osdInfo &info, bool paddedFields = false);
    394     uint64_t TranslatePositionAbsToRel(uint64_t absPosition) const {
    395         return deleteMap.TranslatePositionAbsToRel(absPosition);
     395    uint64_t TranslatePositionFrameToMs(uint64_t position,
     396                                        bool use_cutlist) const {
     397        return deleteMap.TranslatePositionFrameToMs(position,
     398                                                    GetFrameRate(),
     399                                                    use_cutlist);
    396400    }
    397     uint64_t TranslatePositionRelToAbs(uint64_t relPosition) const {
    398         return deleteMap.TranslatePositionRelToAbs(relPosition);
     401    uint64_t TranslatePositionMsToFrame(uint64_t position,
     402                                        bool use_cutlist) const {
     403        return deleteMap.TranslatePositionMsToFrame(position,
     404                                                    GetFrameRate(),
     405                                                    use_cutlist);
    399406    }
     407    // TranslatePositionAbsToRel and TranslatePositionRelToAbs are
     408    // used for frame calculations when seeking relative to a number
     409    // of frames rather than by time.
     410    uint64_t TranslatePositionAbsToRel(uint64_t position) const {
     411        return deleteMap.TranslatePositionAbsToRel(position);
     412    }
     413    uint64_t TranslatePositionRelToAbs(uint64_t position) const {
     414        return deleteMap.TranslatePositionRelToAbs(position);
     415    }
     416    float ComputeSecs(uint64_t position, bool use_cutlist) const {
     417        return TranslatePositionFrameToMs(position, use_cutlist) / 1000.0;
     418    }
     419    uint64_t FindFrame(float offset, bool use_cutlist) const;
    400420
    401421    // Commercial stuff
     
    556576    bool DoFastForward(uint64_t frames, double inaccuracy);
    557577    bool DoRewind(uint64_t frames, double inaccuracy);
     578    bool DoFastForwardSecs(float secs, double inaccuracy, bool use_cutlist);
     579    bool DoRewindSecs(float secs, double inaccuracy, bool use_cutlist);
    558580    void DoJumpToFrame(uint64_t frame, double inaccuracy);
    559581
  • mythtv/libs/libmythtv/nuppeldecoder.cpp

    r1eaecea6dd r49dbed5be  
    379379                                     ste.file_offset};
    380380                    m_positionMap.push_back(e);
     381                    uint64_t frame_num = ste.keyframe_number * keyframedist;
     382                    m_frameToDurMap[frame_num] =
     383                        frame_num * 1000 / video_frame_rate;
     384                    m_durToFrameMap[m_frameToDurMap[frame_num]] = frame_num;
    381385                }
    382386                hasFullPositionMap = true;
     
    11531157                        PosMapEntry e = {this_index, lastKey, currentposition};
    11541158                        m_positionMap.push_back(e);
     1159                        m_frameToDurMap[lastKey] =
     1160                            lastKey * 1000 / video_frame_rate;
     1161                        m_durToFrameMap[m_frameToDurMap[lastKey]] = lastKey;
    11551162                    }
    11561163                }
  • mythtv/libs/libmythtv/recorders/dtvrecorder.cpp

    r1eaecea6dd r49dbed5be  
    8282    _packet_count(0),
    8383    _continuity_error_count(0),
    84     _frames_seen_count(0),          _frames_written_count(0)
     84    _frames_seen_count(0),          _frames_written_count(0),
     85    _frame_interval(0),             _frame_duration(0),
     86    _total_duration(0)
    8587{
    8688    SetPositionMapType(MARK_GOP_BYFRAME);
     
    160162            curRecording->SaveFilesize(ringBuffer->GetRealFileSize());
    161163        SavePositionMap(true);
     164        curRecording->SaveTotalDuration((int64_t)_total_duration);
     165        curRecording->SaveTotalFrames(_frames_written_count);
    162166    }
    163167}
     
    230234
    231235    if (curRecording)
     236    {
    232237        curRecording->ClearPositionMap(MARK_GOP_BYFRAME);
     238        curRecording->ClearPositionMap(MARK_DURATION_MS);
     239    }
    233240}
    234241
     
    496503                                }
    497504                            }
     505                            // The _repeat_pict code above matches
     506                            // mpegvideo_extract_headers(), but the
     507                            // code in mpeg_field_start() computes a
     508                            // value one less, which seems correct.
     509                            --_repeat_pict;
    498510                        }
    499511                        break;
     
    534546        _frames_seen_count++;
    535547        if (!_wait_for_keyframe_option || _first_keyframe>=0)
    536             _frames_written_count++;
     548            UpdateFramesWritten();
    537549    }
    538550
     
    627639}
    628640
     641void DTVRecorder::UpdateFramesWritten(void)
     642{
     643    _frames_written_count++;
     644    if (m_frameRate > 0)
     645    {
     646        // m_frameRate is frames per 1000 seconds, e.g. 29970 for
     647        // 29.97 fps.  Calculate usec values.
     648        _frame_interval = 1000000000.0 / m_frameRate;
     649        _frame_duration = _frame_interval +
     650            (_repeat_pict * _frame_interval * 0.5f);
     651        _total_duration += _frame_duration;
     652    }
     653}
     654
    629655bool DTVRecorder::FindAudioKeyframes(const TSPacket*)
    630656{
     
    657683
    658684        if (!_wait_for_keyframe_option || _first_keyframe>=0)
    659             _frames_written_count++;
     685            UpdateFramesWritten();
    660686    }
    661687
     
    677703
    678704    _frames_seen_count++;
    679     _frames_written_count++;
     705    UpdateFramesWritten();
    680706    _last_keyframe_seen = _frames_seen_count;
    681707
     
    717743            positionMapDelta[frameNum] = startpos;
    718744            positionMap[frameNum]      = startpos;
     745            durationMap[frameNum]      = _total_duration / 1000;
     746            durationMapDelta[frameNum] = _total_duration / 1000;
    719747        }
    720748    }
     
    861889        _frames_seen_count++;
    862890        if (!_wait_for_keyframe_option || _first_keyframe >= 0)
    863             _frames_written_count++;
     891            UpdateFramesWritten();
    864892    }
    865893
     
    908936        positionMapDelta[frameNum] = m_h264_parser.keyframeAUstreamOffset();
    909937        positionMap[frameNum]      = m_h264_parser.keyframeAUstreamOffset();
     938        durationMap[frameNum]      = _total_duration / 1000;
     939        durationMapDelta[frameNum] = _total_duration / 1000;
    910940    }
    911941    positionMapLock.unlock();
     
    10271057            _frames_seen_count++;
    10281058            if (!_wait_for_keyframe_option || _first_keyframe >= 0)
    1029                 _frames_written_count++;
     1059                UpdateFramesWritten();
    10301060        }
    10311061
  • mythtv/libs/libmythtv/recorders/dtvrecorder.h

    r1eaecea6dd r49dbed5be  
    9696    void HandleKeyframe(uint64_t frameNum, int64_t extra = 0);
    9797    void HandleTimestamps(int stream_id, int64_t pts, int64_t dts);
     98    void UpdateFramesWritten(void);
    9899
    99100    void BufferedWrite(const TSPacket &tspacket);
     
    183184    unsigned long long _frames_seen_count;
    184185    unsigned long long _frames_written_count;
     186    double _frame_interval; // usec
     187    double _frame_duration; // usec
     188    double _total_duration; // usec
    185189
    186190    // constants
  • mythtv/libs/libmythtv/recorders/recorderbase.cpp

    r1eaecea6dd r49dbed5be  
    438438}
    439439
     440bool RecorderBase::GetKeyframeDurations(
     441    int64_t start, int64_t end, frm_pos_map_t &map) const
     442{
     443    map.clear();
     444
     445    QMutexLocker locker(&positionMapLock);
     446    if (durationMap.empty())
     447        return true;
     448
     449    frm_pos_map_t::const_iterator it = durationMap.lowerBound(start);
     450    end = (end < 0) ? INT64_MAX : end;
     451    for (; (it != durationMap.end()) &&
     452             (it.key() <= (uint64_t)end); ++it)
     453        map[it.key()] = *it;
     454
     455    LOG(VB_GENERAL, LOG_INFO, LOC +
     456        QString("GetKeyframeDurations(%1,%2,#%3) out of %4")
     457            .arg(start).arg(end).arg(map.size()).arg(durationMap.size()));
     458
     459    return true;
     460}
     461
    440462/** \fn RecorderBase::SavePositionMap(bool)
    441463 *  \brief This saves the postition map delta to the database if force
     
    457479    // save every 10 seconds later on
    458480    needToSave |= (delta_size >= 1) && (pm_elapsed >= 10000);
     481    // Assume that durationMapDelta is the same size as
     482    // positionMapDelta and implicitly use the same logic about when
     483    // to same durationMapDelta.
    459484
    460485    if (curRecording && needToSave)
     
    468493            frm_pos_map_t deltaCopy(positionMapDelta);
    469494            positionMapDelta.clear();
     495            frm_pos_map_t durationDeltaCopy(durationMapDelta);
     496            durationMapDelta.clear();
    470497            positionMapLock.unlock();
    471498
    472499            curRecording->SavePositionMapDelta(deltaCopy, positionMapType);
     500            curRecording->SavePositionMapDelta(durationDeltaCopy,
     501                                               MARK_DURATION_MS);
    473502        }
    474503        else
  • mythtv/libs/libmythtv/recorders/recorderbase.h

    r1eaecea6dd r49dbed5be  
    174174    bool GetKeyframePositions(
    175175        int64_t start, int64_t end, frm_pos_map_t&) const;
     176    bool GetKeyframeDurations(
     177        int64_t start, int64_t end, frm_pos_map_t&) const;
    176178
    177179    virtual void StopRecording(void);
     
    302304    frm_pos_map_t  positionMap;
    303305    frm_pos_map_t  positionMapDelta;
     306    frm_pos_map_t  durationMap;
     307    frm_pos_map_t  durationMapDelta;
    304308    MythTimer      positionMapTimer;
    305309
  • mythtv/libs/libmythtv/remoteencoder.cpp

    r1eaecea6dd r49dbed5be  
    265265}
    266266
    267 void RemoteEncoder::FillPositionMap(long long start, long long end,
    268                                     QMap<long long, long long> &positionMap)
     267void RemoteEncoder::FillPositionMap(int64_t start, int64_t end,
     268                                    frm_pos_map_t &positionMap)
    269269{
    270270    QStringList strlist( QString("QUERY_RECORDER %1").arg(recordernum));
     
    280280    {
    281281        bool ok;
    282         long long index = (*it).toLongLong(&ok);
     282        uint64_t index = (*it).toLongLong(&ok);
    283283        if (++it == strlist.end() || !ok)
    284284            break;
    285285
    286         long long pos = (*it).toLongLong(&ok);
     286        uint64_t pos = (*it).toLongLong(&ok);
    287287        if (!ok)
    288288            break;
    289289
    290290        positionMap[index] = pos;
     291    }
     292}
     293
     294void RemoteEncoder::FillDurationMap(int64_t start, int64_t end,
     295                                    frm_pos_map_t &durationMap)
     296{
     297    QStringList strlist( QString("QUERY_RECORDER %1").arg(recordernum));
     298    strlist << "FILL_DURATION_MAP";
     299    strlist << QString::number(start);
     300    strlist << QString::number(end);
     301
     302    if (!SendReceiveStringList(strlist))
     303        return;
     304
     305    QStringList::const_iterator it = strlist.begin();
     306    for (; it != strlist.end(); ++it)
     307    {
     308        bool ok;
     309        uint64_t index = (*it).toLongLong(&ok);
     310        if (++it == strlist.end() || !ok)
     311            break;
     312
     313        uint64_t pos = (*it).toLongLong(&ok);
     314        if (!ok)
     315            break;
     316
     317        durationMap[index] = pos;
    291318    }
    292319}
  • mythtv/libs/libmythtv/remoteencoder.h

    r1eaecea6dd r49dbed5be  
    1212#include "videoouttypes.h"
    1313#include "tv.h"
     14#include "programtypes.h"
    1415
    1516class QStringList;
     
    3738    long long GetMaxBitrate();
    3839    int64_t GetKeyframePosition(uint64_t desired);
    39     void FillPositionMap(long long start, long long end,
    40                          QMap<long long, long long> &positionMap);
     40    void FillPositionMap(int64_t start, int64_t end,
     41                         frm_pos_map_t &positionMap);
     42    void FillDurationMap(int64_t start, int64_t end,
     43                         frm_pos_map_t &durationMap);
    4144    void StopPlaying(void);
    4245    void SpawnLiveTV(QString chainid, bool pip, QString startchan);
  • mythtv/libs/libmythtv/tv_play.cpp

    r1eaecea6dd r49dbed5be  
    42154215        ctx->LockDeletePlayer(__FILE__, __LINE__);
    42164216        uint64_t bookmark  = ctx->player->GetBookmark();
    4217         float     rate     = ctx->player->GetFrameRate();
    4218         float seekloc = ctx->player->TranslatePositionAbsToRel(bookmark) / rate;
    42194217        ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    42204218
    4221         if (bookmark > rate)
    4222             DoSeek(ctx, seekloc, tr("Jump to Bookmark"),
    4223                    /*timeIsOffset*/false,
    4224                    /*honorCutlist*/true);
     4219        if (bookmark)
     4220        {
     4221            DoPlayerSeekToFrame(ctx, bookmark);
     4222            ctx->LockDeletePlayer(__FILE__, __LINE__);
     4223            UpdateOSDSeekMessage(ctx, tr("Jump to Bookmark"), kOSDTimeout_Med);
     4224            ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     4225        }
    42254226    }
    42264227    else if (has_action(ACTION_JUMPSTART,actions))
     
    50475048            {
    50485049                fplay = ctx->player->GetFramesPlayed();
    5049                 rate  = ctx->player->GetFrameRate();
     5050                rate  = ctx->player->GetFrameRate(); // for display only
    50505051            }
    50515052            ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     
    61266127    else if (time < 0.0)
    61276128        res = ctx->player->Rewind(-time);
     6129    ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     6130
     6131    return res;
     6132}
     6133
     6134bool TV::DoPlayerSeekToFrame(PlayerContext *ctx, uint64_t target)
     6135{
     6136    if (!ctx || !ctx->buffer)
     6137        return false;
     6138
     6139    LOG(VB_PLAYBACK, LOG_INFO, LOC +
     6140        QString("DoPlayerSeekToFrame %1").arg(target));
     6141
     6142    ctx->LockDeletePlayer(__FILE__, __LINE__);
     6143    if (!ctx->player)
     6144    {
     6145        ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     6146        return false;
     6147    }
     6148
     6149    if (!ctx->buffer->IsSeekingAllowed())
     6150    {
     6151        ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     6152        return false;
     6153    }
     6154
     6155    if (ctx == GetPlayer(ctx, 0))
     6156        PauseAudioUntilBuffered(ctx);
     6157
     6158    bool res = ctx->player->JumpToFrame(target);
     6159
    61286160    ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    61296161
     
    61636195        if (!isDVD)
    61646196        {
    6165             float rate = 30.0f;
    6166             actx->LockDeletePlayer(__FILE__, __LINE__);
    6167             if (actx->player)
    6168                 rate = actx->player->GetFrameRate();
    6169             actx->UnlockDeletePlayer(__FILE__, __LINE__);
    6170             float time = (flags & kAbsolute) ?  direction :
    6171                              direction * (1.001 / rate);
    61726197            QString message = (flags & kRewind) ? tr("Rewind") :
    61736198                                                  tr("Forward");
    6174             DoSeek(actx, time, message,
    6175                    /*timeIsOffset*/true,
    6176                    /*honorCutlist*/!(flags & kIgnoreCutlist));
     6199            actx->LockDeletePlayer(__FILE__, __LINE__);
     6200            uint64_t frameAbs = actx->player->GetFramesPlayed();
     6201            uint64_t frameRel =
     6202                actx->player->TranslatePositionAbsToRel(frameAbs);
     6203            uint64_t targetRel = frameRel + direction;
     6204            if (frameRel == 0 && direction < 0)
     6205                targetRel = 0;
     6206            uint64_t maxAbs = actx->player->GetCurrentFrameCount();
     6207            uint64_t maxRel = actx->player->TranslatePositionAbsToRel(maxAbs);
     6208            if (targetRel > maxRel)
     6209                targetRel = maxRel;
     6210            uint64_t targetAbs =
     6211                actx->player->TranslatePositionRelToAbs(targetRel);
     6212            actx->UnlockDeletePlayer(__FILE__, __LINE__);
     6213            DoPlayerSeekToFrame(actx, targetAbs);
     6214            UpdateOSDSeekMessage(actx, message, kOSDTimeout_Med);
    61776215        }
    61786216    }
     
    62146252    if (ctx->player->GetLimitKeyRepeat())
    62156253        limitkeys = true;
    6216     ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    62176254
    62186255    if (!limitkeys || (keyRepeatTimer.elapsed() > (int)kKeyRepeatTimeout))
     
    62216258        NormalSpeed(ctx);
    62226259        time += StopFFRew(ctx);
    6223         float framerate = ctx->player->GetFrameRate();
    62246260        uint64_t currentFrameAbs = ctx->player->GetFramesPlayed();
    6225         uint64_t currentFrameRel = honorCutlist ?
    6226             ctx->player->TranslatePositionAbsToRel(currentFrameAbs) :
    6227             currentFrameAbs;
    6228         int64_t desiredFrameRel = (timeIsOffset ? currentFrameRel : 0) +
    6229             time * framerate + 0.5;
    6230         if (desiredFrameRel < 0)
    6231             desiredFrameRel = 0;
    6232         uint64_t desiredFrameAbs = honorCutlist ?
    6233             ctx->player->TranslatePositionRelToAbs(desiredFrameRel) :
    6234             desiredFrameRel;
    6235         time = ((int64_t)desiredFrameAbs - (int64_t)currentFrameAbs) /
    6236             framerate;
    6237         DoPlayerSeek(ctx, time);
     6261        if (timeIsOffset)
     6262            time +=
     6263                ctx->player->TranslatePositionFrameToMs(currentFrameAbs,
     6264                                                        honorCutlist) / 1000.0;
     6265        if (time < 0)
     6266            time = 0;
     6267        uint64_t desiredFrameRel =
     6268            ctx->player->TranslatePositionMsToFrame(time * 1000, honorCutlist);
     6269        ctx->UnlockDeletePlayer(__FILE__, __LINE__);
     6270        DoPlayerSeekToFrame(ctx, desiredFrameRel);
    62386271        UpdateOSDSeekMessage(ctx, mesg, kOSDTimeout_Med);
    62396272    }
     6273    else
     6274        ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    62406275}
    62416276
     
    62826317            return;
    62836318        }
    6284         time = (ctx->player->CalcMaxFFTime(LONG_MAX, false) /
    6285                 ctx->player->GetFrameRate()) - time;
     6319        uint64_t total_frames = ctx->player->GetCurrentFrameCount();
     6320        float dur = ctx->player->ComputeSecs(total_frames, honorCutlist);
     6321        time = max(0.0f, dur - time);
    62866322        ctx->UnlockDeletePlayer(__FILE__, __LINE__);
    6287         DoSeek(ctx, time, tr("Jump To"),
    6288                /*timeIsOffset*/(whence != ARBSEEK_SET), honorCutlist);
     6323        DoSeek(ctx, time, tr("Jump To"), /*timeIsOffset*/false, honorCutlist);
    62896324    }
    62906325    else
  • mythtv/libs/libmythtv/tv_play.h

    r1eaecea6dd r49dbed5be  
    427427                bool timeIsOffset, bool honorCutlist);
    428428    bool DoPlayerSeek(PlayerContext*, float time);
     429    bool DoPlayerSeekToFrame(PlayerContext *ctx, uint64_t target);
    429430    enum ArbSeekWhence {
    430431        ARBSEEK_SET = 0,
  • mythtv/libs/libmythtv/tv_rec.cpp

    r1eaecea6dd r49dbed5be  
    25582558}
    25592559
     2560bool TVRec::GetKeyframeDurations(
     2561    int64_t start, int64_t end, frm_pos_map_t &map) const
     2562{
     2563    QMutexLocker lock(&stateChangeLock);
     2564
     2565    if (recorder)
     2566        return recorder->GetKeyframeDurations(start, end, map);
     2567
     2568    return false;
     2569}
     2570
    25602571/** \fn TVRec::GetMaxBitrate(void) const
    25612572 *  \brief Returns the maximum bits per second this recorder can produce.
  • mythtv/libs/libmythtv/tv_rec.h

    r1eaecea6dd r49dbed5be  
    183183    int64_t GetKeyframePosition(uint64_t desired) const;
    184184    bool GetKeyframePositions(int64_t start, int64_t end, frm_pos_map_t&) const;
     185    bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t&) const;
    185186    void SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan);
    186187    QString GetChainID(void);
  • mythtv/programs/mythbackend/encoderlink.cpp

    r1eaecea6dd r49dbed5be  
    573573}
    574574
     575bool EncoderLink::GetKeyframeDurations(
     576    int64_t start, int64_t end, frm_pos_map_t &map)
     577{
     578    if (!local)
     579    {
     580        LOG(VB_GENERAL, LOG_ERR,
     581            "Should be local only query: GetKeyframeDurations");
     582        return false;
     583    }
     584
     585    return tv->GetKeyframeDurations(start, end, map);
     586}
     587
    575588/** \fn EncoderLink::FrontendReady()
    576589 *  \brief Tells TVRec that the frontend is ready for data.
  • mythtv/programs/mythbackend/encoderlink.h

    r1eaecea6dd r49dbed5be  
    103103    int64_t GetKeyframePosition(uint64_t desired);
    104104    bool GetKeyframePositions(int64_t start, int64_t end, frm_pos_map_t&);
     105    bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t&);
    105106    void SpawnLiveTV(LiveTVChain *chain, bool pip, QString startchan);
    106107    QString GetChainID(void);
  • mythtv/programs/mythbackend/mainserver.cpp

    r1eaecea6dd r49dbed5be  
    38663866    else if (command == "FILL_POSITION_MAP")
    38673867    {
    3868         long long start = slist[2].toLongLong();
    3869         long long end   = slist[3].toLongLong();
     3868        int64_t start = slist[2].toLongLong();
     3869        int64_t end   = slist[3].toLongLong();
    38703870        frm_pos_map_t map;
    38713871
    38723872        if (!enc->GetKeyframePositions(start, end, map))
     3873        {
     3874            retlist << "error";
     3875        }
     3876        else
     3877        {
     3878            frm_pos_map_t::const_iterator it = map.begin();
     3879            for (; it != map.end(); ++it)
     3880            {
     3881                retlist += QString::number(it.key());
     3882                retlist += QString::number(*it);
     3883            }
     3884            if (retlist.empty())
     3885                retlist << "OK";
     3886        }
     3887    }
     3888    else if (command == "FILL_DURATION_MAP")
     3889    {
     3890        int64_t start = slist[2].toLongLong();
     3891        int64_t end   = slist[3].toLongLong();
     3892        frm_pos_map_t map;
     3893
     3894        if (!enc->GetKeyframeDurations(start, end, map))
    38733895        {
    38743896            retlist << "error";
  • mythtv/programs/mythtranscode/cutter.cpp

    r1eaecea6dd r49dbed5be  
    33#include "cutter.h"
    44
    5 void Cutter::SetCutList(frm_dir_map_t &deleteMap)
     5void Cutter::SetCutList(frm_dir_map_t &deleteMap, PlayerContext *ctx)
    66{
    77    // Break each cut into two parts, the first for
     
    1212    int64_t                 leadinLength;
    1313
     14    tracker.SetPlayerContext(ctx);
    1415    foreshortenedCutList.clear();
    1516
     
    8384    videoFramesToCut = 0;
    8485    audioFramesToCut = 0;
    85     tracker.TrackerReset(0, totalFrames);
     86    tracker.TrackerReset(0);
    8687}
    8788
     
    9495            uint64_t jumpTo = 0;
    9596
    96             if (tracker.TrackerWantsToJump(currentFrame, totalFrames,
    97                                            jumpTo))
     97            if (tracker.TrackerWantsToJump(currentFrame, jumpTo))
    9898            {
    9999                // Reset the tracker and work out how much video and audio
    100100                // to drop
    101                 tracker.TrackerReset(jumpTo, totalFrames);
     101                tracker.TrackerReset(jumpTo);
    102102                videoFramesToCut = jumpTo - currentFrame;
    103103                audioFramesToCut += (int64_t)(videoFramesToCut *
  • mythtv/programs/mythtranscode/cutter.h

    r1eaecea6dd r49dbed5be  
    1616        audioFramesPerVideoFrame(0.0) {};
    1717
    18     void          SetCutList(frm_dir_map_t &deleteMap);
     18    void          SetCutList(frm_dir_map_t &deleteMap, PlayerContext *ctx);
    1919    frm_dir_map_t AdjustedCutList() const;
    2020    void          Activate(float v2a, int64_t total);
  • mythtv/programs/mythtranscode/main.cpp

    r1eaecea6dd r49dbed5be  
    4444        pginfo->ClearPositionMap(MARK_GOP_START);
    4545        pginfo->SavePositionMap(posMap, MARK_GOP_BYFRAME);
     46        pginfo->SavePositionMap(posMap, MARK_DURATION_MS);
    4647    }
    4748    else if (!mapfile.isEmpty())
    4849    {
     50        MarkTypes keyType = MARK_GOP_BYFRAME;
    4951        FILE *mapfh = fopen(mapfile.toLocal8Bit().constData(), "w");
    5052        if (!mapfh)
     
    5557        }
    5658        frm_pos_map_t::const_iterator it;
    57         fprintf (mapfh, "Type: %d\n", MARK_GOP_BYFRAME);
     59        fprintf (mapfh, "Type: %d\n", keyType);
    5860        for (it = posMap.begin(); it != posMap.end(); ++it)
    59             fprintf(mapfh, "%lld %lld\n",
    60                     (unsigned long long)it.key(), (unsigned long long)*it);
     61            if (it.key() == keyType)
     62                fprintf(mapfh, "%lld %lld\n",
     63                        it.key(), *it);
    6164        fclose(mapfh);
    6265    }
  • mythtv/programs/mythtranscode/mpeg2fix.cpp

    r1eaecea6dd r49dbed5be  
    26612661    av_init_packet(&pkt);
    26622662
     2663    uint64_t totalDuration = 0;
    26632664    while (av_read_frame(inputFC, &pkt) >= 0)
    26642665    {
     
    26672668            if (pkt.flags & AV_PKT_FLAG_KEY)
    26682669                posMap[count] = pkt.pos;
     2670
     2671            // XXX totalDuration untested.  Results should be the same
     2672            // as from mythcommflag --rebuild.
     2673
     2674            // totalDuration calculation based on
     2675            // AvFormatDecoder::PreProcessVideoPacket()
     2676            totalDuration +=
     2677                av_q2d(inputFC->streams[pkt.stream_index]->time_base) *
     2678                pkt.duration * 1000000; // usec
     2679            posMap.insertMulti(count, totalDuration);
    26692680            count++;
    26702681        }
  • mythtv/programs/mythtranscode/transcode.cpp

    • Property mode changed from 100755 to 100644
    r1eaecea6dd r49dbed5be  
    800800            // discard the rest
    801801            cutter = new Cutter();
    802             cutter->SetCutList(deleteMap);
    803 
     802            cutter->SetCutList(deleteMap, ctx);
    804803            GetPlayer()->SetCutList(cutter->AdjustedCutList());
    805804        }
     
    14741473            m_proginfo->ClearPositionMap(MARK_GOP_START);
    14751474            m_proginfo->ClearPositionMap(MARK_GOP_BYFRAME);
     1475            m_proginfo->ClearPositionMap(MARK_DURATION_MS);
    14761476        }
    14771477
Note: See TracChangeset for help on using the changeset viewer.