Skip to content

Commit

Permalink
ReadFFmpeg: fix issues with desiredFrame vs frame
Browse files Browse the repository at this point in the history
  • Loading branch information
devernay committed May 8, 2018
1 parent ce708a7 commit 83cd498
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 29 deletions.
69 changes: 40 additions & 29 deletions FFmpeg/FFmpegFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@
* OFX ffmpeg Reader plugin.
* Reads a video input file using the libav library.
* Synced with mov64Reader 11.1v3
*
* BUGS:
* - The last frames from long-GOP mp4 don't read, see:
* - https://github.com/NatronGitHub/Natron/issues/241
* - https://github.com/NatronGitHub/Natron/issues/231
* - MPEG1 files cannot be read, for example
* - https://github.com/NatronGitHub/Natron-Tests/raw/master/TestReadMPEG1/input.mpg
* - http://devernay.free.fr/vision/diffprop/herve3d.mpg
* This was already true before the 11.1v3 sync (e.g. at commit 4d0d3a5).
*/
//#define TRACE_DECODE_PROCESS 1

#if (defined(_STDINT_H) || defined(_STDINT_H_) || defined(_MSC_STDINT_H_ ) ) && !defined(UINT64_C)
#warning "__STDC_CONSTANT_MACROS has to be defined before including <stdint.h>, this file will probably not compile."
Expand Down Expand Up @@ -1196,32 +1206,37 @@ FFmpegFile::decode(const ImageEffect* plugin,
AutoMutex guard(_lock);
#endif

if (_streams.empty()) {
return false;
}

assert(_selectedStream && "Null _selectedStream");
if (!_selectedStream) {
return false;
}
Stream* stream = _selectedStream;

// Translate from the 1-based frames expected to 0-based frame offsets for use in the rest of this code.
int desiredFrame = frame - 1;
int originalFrame = frame;
frame = frame - 1;

// Early-out if out-of-range frame requested.
if (desiredFrame < 0) {
if (frame < 0) {
if (loadNearest) {
desiredFrame = 0;
frame = 0;
} else {
throw std::runtime_error("Missing frame");
}
} else if (desiredFrame >= stream->_frames) {
} else if (frame >= stream->_frames) {
if (loadNearest) {
desiredFrame = (int)stream->_frames - 1;
frame = (int)stream->_frames - 1;
} else {
throw std::runtime_error("Missing frame");
}
}

#if TRACE_DECODE_PROCESS
std::cout << "FFmpeg Reader=" << this << "::decode(): frame=" << desiredFrame << /*", _viewIndex = " << _viewIndex <<*/ ", videoStream=" << streamIdx << ", streamIdx=" << stream->_idx << std::endl;
std::cout << "FFmpeg Reader=" << this << "::decode(): frame=" << frame << /*", _viewIndex = " << _viewIndex <<*/ ", stream->_idx=" << stream->_idx << std::endl;
#endif

// Number of read retries remaining when decode stall is detected before we give up (in the case of post-seek stalls,
Expand Down Expand Up @@ -1258,18 +1273,18 @@ FFmpegFile::decode(const ImageEffect* plugin,
int lastSeekedFrame = -1; // 0-based index of the last frame to which we seeked when seek in progress / negative when no
// seek in progress,

if (desiredFrame != stream->_decodeNextFrameOut) {
if (frame != stream->_decodeNextFrameOut) {
#if TRACE_DECODE_PROCESS
std::cout << " Next frame expected out=" << stream->_decodeNextFrameOut << ", Seeking to desired frame" << std::endl;
#endif

lastSeekedFrame = desiredFrame;
lastSeekedFrame = frame;
stream->_decodeNextFrameIn = -1;
stream->_decodeNextFrameOut = -1;
stream->_accumDecodeLatency = 0;
awaitingFirstDecodeAfterSeek = true;

if ( !seekFrame(desiredFrame, stream) ) {
if ( !seekFrame(frame, stream) ) {
return false;
}
}
Expand Down Expand Up @@ -1322,14 +1337,14 @@ FFmpegFile::decode(const ImageEffect* plugin,
stream->_frames = stream->_decodeNextFrameIn;
if (loadNearest) {
// try again
desiredFrame = (int)stream->_frames - 1;
lastSeekedFrame = desiredFrame;
frame = (int)stream->_frames - 1;
lastSeekedFrame = frame;
stream->_decodeNextFrameIn = -1;
stream->_decodeNextFrameOut = -1;
stream->_accumDecodeLatency = 0;
awaitingFirstDecodeAfterSeek = true;

if ( !seekFrame(desiredFrame, stream) ) {
if ( !seekFrame(frame, stream) ) {
return false;
}
}
Expand Down Expand Up @@ -1378,13 +1393,13 @@ FFmpegFile::decode(const ImageEffect* plugin,
// timestamp. Likewise, if the landing frame is after the seek target frame (this can happen, presumably a bug
// in FFmpeg seeking), we need to seek back to an earlier frame so that we can start decoding at or before the
// desired frame.
int landingFrame = ( _avPacket.*stream->_timestampField == int64_t(AV_NOPTS_VALUE) ? -1 :
stream->ptsToFrame(_avPacket.*stream->_timestampField) );
bool noTimestamp = ( _avPacket.*stream->_timestampField == int64_t(AV_NOPTS_VALUE) );
int landingFrame = noTimestamp ? -1 : stream->ptsToFrame(_avPacket.*stream->_timestampField);

if ( ( _avPacket.*stream->_timestampField == int64_t(AV_NOPTS_VALUE) ) || (landingFrame > lastSeekedFrame) ) {
if ( noTimestamp || (landingFrame > lastSeekedFrame) ) {
#if TRACE_DECODE_PROCESS
std::cout << ", landing frame not found";
if ( _avPacket.*stream->_timestampField == int64_t(AV_NOPTS_VALUE) ) {
if ( noTimestamp ) {
std::cout << " (no timestamp)";
} else {
std::cout << " (landed after target at " << landingFrame << ")";
Expand All @@ -1402,7 +1417,7 @@ FFmpegFile::decode(const ImageEffect* plugin,
// frame from this stream, switch to using DTSs and retry the read from the initial desired frame.
if ( (stream->_timestampField == &AVPacket::pts) && !stream->_ptsSeen ) {
stream->_timestampField = &AVPacket::dts;
lastSeekedFrame = desiredFrame;
lastSeekedFrame = frame;
#if TRACE_DECODE_PROCESS
std::cout << ", PTSs absent, switching to use DTSs";
#endif
Expand Down Expand Up @@ -1548,8 +1563,8 @@ FFmpegFile::decode(const ImageEffect* plugin,
}
#endif
} //stream->_decodeNextFrameIn < stream->_frames
// If the next frame to decode is out of frame range, there's nothing more to read and the decoder will be fed
// null input frames in order to obtain any remaining output.
// If the next frame to decode is out of frame range, there's nothing more to read and the decoder will be fed
// null input frames in order to obtain any remaining output.
else {
#if TRACE_DECODE_PROCESS
std::cout << " No more frames to read, pumping remaining decoder output" << std::endl;
Expand Down Expand Up @@ -1611,7 +1626,7 @@ FFmpegFile::decode(const ImageEffect* plugin,

// If the frame just output from decode is the desired one, get the decoded picture from it and set that we
// have a picture.
if (stream->_decodeNextFrameOut == desiredFrame) {
if (stream->_decodeNextFrameOut == frame) {
#if TRACE_DECODE_PROCESS
std::cout << ", is desired frame" << std::endl;
#endif
Expand Down Expand Up @@ -1659,7 +1674,7 @@ FFmpegFile::decode(const ImageEffect* plugin,
}
#if TRACE_DECODE_PROCESS
else {
std::cout << ", is not desired frame (" << desiredFrame << ")" << std::endl;
std::cout << ", is not desired frame (" << frame << ")" << std::endl;
}
#endif

Expand Down Expand Up @@ -1695,7 +1710,7 @@ FFmpegFile::decode(const ImageEffect* plugin,
// starting from the desired frame.
else if (retriesRemaining > 0) {
--retriesRemaining;
seekTargetFrame = desiredFrame;
seekTargetFrame = frame;
#if TRACE_DECODE_PROCESS
std::cout << " Post-seek stall detected, at start of file, retrying from desired frame " << seekTargetFrame << std::endl;
#endif
Expand All @@ -1714,7 +1729,7 @@ FFmpegFile::decode(const ImageEffect* plugin,
// If we have any retries remaining, use one to attempt the read again, starting from the desired frame.
if (retriesRemaining > 0) {
--retriesRemaining;
seekTargetFrame = desiredFrame;
seekTargetFrame = frame;
#if TRACE_DECODE_PROCESS
std::cout << " Mid-decode stall detected, retrying from desired frame " << seekTargetFrame << std::endl;
#endif
Expand All @@ -1741,14 +1756,10 @@ FFmpegFile::decode(const ImageEffect* plugin,
}
} // if (decodeAttempted)
} // if (stream->_decodeNextFrameIn < stream->_frames)
_avPacket.FreePacket();
if ( plugin->abort() ) {
return false;
}
} while (!hasPicture);
} while (!hasPicture && !plugin->abort());

#if TRACE_DECODE_PROCESS
std::cout << "<-validPicture=" << hasPicture << " for frame " << desiredFrame << std::endl;
std::cout << "<-validPicture=" << hasPicture << " for frame " << frame << std::endl;
#endif

// If read failed, reset the next frame expected out so that we seek and restart decode on next read attempt. Also free
Expand Down
10 changes: 10 additions & 0 deletions FFmpeg/ReadFFmpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@
/*
* OFX ffmpegReader plugin.
* Reads a video input file using the libav library.
*
* Synced with mov64Reader 11.1v3
*
* BUGS:
* - The last frames from long-GOP mp4 don't read, see:
* - https://github.com/NatronGitHub/Natron/issues/241
* - https://github.com/NatronGitHub/Natron/issues/231
* - MPEG1 files cannot be read, for example
* - https://github.com/NatronGitHub/Natron-Tests/raw/master/TestReadMPEG1/input.mpg
* - http://devernay.free.fr/vision/diffprop/herve3d.mpg
* This was already true before the 11.1v3 sync (e.g. at commit 4d0d3a5).
*/

#if (defined(_STDINT_H) || defined(_STDINT_H_) || defined(_MSC_STDINT_H_ ) ) && !defined(UINT64_C)
Expand Down

0 comments on commit 83cd498

Please sign in to comment.