From da48061910db0ee78396237bc78d6f2e321ea5eb Mon Sep 17 00:00:00 2001 From: Peter Rekdal Sunde Date: Wed, 3 Feb 2016 01:00:43 +0100 Subject: [PATCH] Add interrupt callback with default timeout of 30s. Fixes #5730. Fix docs build failure by re-indenting with spaces instead of tabs. Trailing whitespace fixes. http://pullrequest.opencv.org/buildbot/builders/precommit_docs/builds/5152/steps/whitespace/logs/stdio mingw build fix. --- modules/videoio/src/cap_ffmpeg_impl.hpp | 180 ++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 9da8de5cd..d8388e996 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -129,12 +129,22 @@ extern "C" { #if defined WIN32 || defined _WIN32 #include + #if defined _MSC_VER && _MSC_VER < 1900 + struct timespec + { + time_t tv_sec; + long tv_nsec; + }; + #endif #elif defined __linux__ || defined __APPLE__ #include #include #include + #include #if defined __APPLE__ #include + #include + #include #endif #endif @@ -184,6 +194,116 @@ extern "C" { #endif #endif + +#define LIBAVFORMAT_INTERRUPT_TIMEOUT_MS 30000 + +#ifdef WIN32 +// http://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows + +inline LARGE_INTEGER get_filetime_offset() +{ + SYSTEMTIME s; + FILETIME f; + LARGE_INTEGER t; + + s.wYear = 1970; + s.wMonth = 1; + s.wDay = 1; + s.wHour = 0; + s.wMinute = 0; + s.wSecond = 0; + s.wMilliseconds = 0; + SystemTimeToFileTime(&s, &f); + t.QuadPart = f.dwHighDateTime; + t.QuadPart <<= 32; + t.QuadPart |= f.dwLowDateTime; + return t; +} + +inline void get_monotonic_time(timespec *tv) +{ + LARGE_INTEGER t; + FILETIME f; + double microseconds; + static LARGE_INTEGER offset; + static double frequencyToMicroseconds; + static int initialized = 0; + static BOOL usePerformanceCounter = 0; + + if (!initialized) + { + LARGE_INTEGER performanceFrequency; + initialized = 1; + usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); + if (usePerformanceCounter) + { + QueryPerformanceCounter(&offset); + frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; + } + else + { + offset = get_filetime_offset(); + frequencyToMicroseconds = 10.; + } + } + + if (usePerformanceCounter) + { + QueryPerformanceCounter(&t); + } else { + GetSystemTimeAsFileTime(&f); + t.QuadPart = f.dwHighDateTime; + t.QuadPart <<= 32; + t.QuadPart |= f.dwLowDateTime; + } + + t.QuadPart -= offset.QuadPart; + microseconds = (double)t.QuadPart / frequencyToMicroseconds; + t.QuadPart = microseconds; + tv->tv_sec = t.QuadPart / 1000000; + tv->tv_nsec = (t.QuadPart % 1000000) * 1000; +} +#else +inline void get_monotonic_time(timespec *time) +{ +#if defined(__APPLE__) && defined(__MACH__) + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + time->tv_sec = mts.tv_sec; + time->tv_nsec = mts.tv_nsec; +#else + clock_gettime(CLOCK_MONOTONIC, time); +#endif +} +#endif + +inline timespec get_monotonic_time_diff(timespec start, timespec end) +{ + timespec temp; + if (end.tv_nsec - start.tv_nsec < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +inline double get_monotonic_time_diff_ms(timespec time1, timespec time2) +{ + timespec delta = get_monotonic_time_diff(time1, time2); + double milliseconds = delta.tv_sec * 1000 + (double)delta.tv_nsec / 1000000.0; + + return milliseconds; +} + static int get_number_of_cpus(void) { #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(52, 111, 0) @@ -233,6 +353,14 @@ struct Image_FFMPEG }; +struct AVInterruptCallbackMetadata +{ + timespec value; + unsigned int timeout_after_ms; + int timeout; +}; + + inline void _opencv_ffmpeg_free(void** ptr) { if(*ptr) free(*ptr); @@ -240,6 +368,20 @@ inline void _opencv_ffmpeg_free(void** ptr) } +inline int _opencv_ffmpeg_interrupt_callback(void *ptr) +{ + AVInterruptCallbackMetadata* metadata = (AVInterruptCallbackMetadata*)ptr; + assert(metadata); + + timespec now; + get_monotonic_time(&now); + + metadata->timeout = get_monotonic_time_diff_ms(metadata->value, now) > metadata->timeout_after_ms; + + return metadata->timeout ? -1 : 0; +} + + struct CvCapture_FFMPEG { bool open( const char* filename ); @@ -293,6 +435,8 @@ struct CvCapture_FFMPEG #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) AVDictionary *dict; #endif + + AVInterruptCallbackMetadata interrupt_metadata; }; void CvCapture_FFMPEG::init() @@ -591,6 +735,14 @@ bool CvCapture_FFMPEG::open( const char* _filename ) close(); + /* interrupt callback */ + interrupt_metadata.timeout_after_ms = LIBAVFORMAT_INTERRUPT_TIMEOUT_MS; + get_monotonic_time(&interrupt_metadata.value); + + ic = avformat_alloc_context(); + ic->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback; + ic->interrupt_callback.opaque = &interrupt_metadata; + #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) av_dict_set(&dict, "rtsp_transport", "tcp", 0); int err = avformat_open_input(&ic, _filename, NULL, &dict); @@ -703,6 +855,13 @@ bool CvCapture_FFMPEG::grabFrame() { av_free_packet (&packet); + + if (interrupt_metadata.timeout) + { + valid = false; + break; + } + int ret = av_read_frame(ic, &packet); if (ret == AVERROR(EAGAIN)) continue; @@ -738,6 +897,9 @@ bool CvCapture_FFMPEG::grabFrame() picture_pts = packet.pts != AV_NOPTS_VALUE_ && packet.pts != 0 ? packet.pts : packet.dts; frame_number++; valid = true; + + // update interrupt value + get_monotonic_time(&interrupt_metadata.value); } else { @@ -2323,6 +2485,8 @@ private: AVFormatContext* ctx_; int video_stream_id_; AVPacket pkt_; + + AVInterruptCallbackMetadata interrupt_metadata; }; bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma_format, int* width, int* height) @@ -2333,6 +2497,14 @@ bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma video_stream_id_ = -1; memset(&pkt_, 0, sizeof(AVPacket)); + /* interrupt callback */ + interrupt_metadata.timeout_after_ms = LIBAVFORMAT_INTERRUPT_TIMEOUT_MS; + get_monotonic_time(&interrupt_metadata.value); + + ctx_ = avformat_alloc_context(); + ctx_->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback; + ctx_->interrupt_callback.opaque = &interrupt_metadata; + #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 13, 0) avformat_network_init(); #endif @@ -2449,11 +2621,19 @@ bool InputMediaStream_FFMPEG::read(unsigned char** data, int* size, int* endOfFi // get the next frame for (;;) { + if(interrupt_metadata.timeout) + { + break; + } + int ret = av_read_frame(ctx_, &pkt_); if (ret == AVERROR(EAGAIN)) continue; + // update interrupt value + get_monotonic_time(&interrupt_metadata.value); + if (ret < 0) { if (ret == (int)AVERROR_EOF)