From 4a3c888ad7f62a41a86be68c989a0a62913c2b0e Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Fri, 14 Apr 2017 12:19:23 +0200 Subject: [PATCH] Add support for changing the playback rate. --- ffmpeg_capture.cpp | 37 ++++++++++++++++++++++++++++++------- ffmpeg_capture.h | 9 ++++++++- theme.cpp | 10 ++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/ffmpeg_capture.cpp b/ffmpeg_capture.cpp index bb41a35..77af9e6 100644 --- a/ffmpeg_capture.cpp +++ b/ffmpeg_capture.cpp @@ -36,6 +36,16 @@ using namespace std; using namespace std::chrono; using namespace bmusb; +namespace { + +steady_clock::time_point compute_frame_start(int64_t frame_pts, int64_t pts_origin, const AVRational &video_timebase, const steady_clock::time_point &origin, double rate) +{ + const duration pts((frame_pts - pts_origin) * double(video_timebase.num) / double(video_timebase.den)); + return origin + duration_cast(pts / rate); +} + +} // namespace + FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned height) : filename(filename), width(width), height(height) { @@ -179,7 +189,10 @@ bool FFmpegCapture::play_video(const string &pathname) unique_ptr codec_ctx_cleanup( codec_ctx.get(), avcodec_close); + int64_t pts_origin = 0, last_pts = 0; steady_clock::time_point start = steady_clock::now(); + steady_clock::time_point next_frame_start = start; + double rate = 1.0; unique_ptr sws_ctx(nullptr, sws_freeContext); int sws_last_width = -1, sws_last_height = -1; @@ -193,12 +206,20 @@ bool FFmpegCapture::play_video(const string &pathname) swap(commands, command_queue); } for (const QueuedCommand &cmd : commands) { - if (cmd.command == QueuedCommand::REWIND) { + switch (cmd.command) { + case QueuedCommand::REWIND: if (av_seek_frame(format_ctx.get(), /*stream_index=*/-1, /*timestamp=*/0, /*flags=*/0) < 0) { fprintf(stderr, "%s: Rewind failed, stopping play.\n", pathname.c_str()); } - start = steady_clock::now(); - continue; + pts_origin = last_pts = 0; + start = next_frame_start = steady_clock::now(); + break; + + case QueuedCommand::CHANGE_RATE: + start = next_frame_start; + pts_origin = last_pts; + rate = cmd.new_rate; + break; } } @@ -242,6 +263,7 @@ bool FFmpegCapture::play_video(const string &pathname) fprintf(stderr, "%s: Rewind failed, not looping.\n", pathname.c_str()); return true; } + pts_origin = last_pts = 0; start = steady_clock::now(); continue; } @@ -268,8 +290,8 @@ bool FFmpegCapture::play_video(const string &pathname) video_format.has_signal = true; video_format.is_connected = true; - const duration pts(frame->pts * double(video_timebase.num) / double(video_timebase.den)); - const steady_clock::time_point frame_start = start + duration_cast(pts); + next_frame_start = compute_frame_start(frame->pts, pts_origin, video_timebase, start, rate); + last_pts = frame->pts; FrameAllocator::Frame video_frame = video_frame_allocator->alloc_frame(); if (video_frame.data != nullptr) { @@ -277,7 +299,7 @@ bool FFmpegCapture::play_video(const string &pathname) int linesizes[4] = { int(video_format.stride), 0, 0, 0 }; sws_scale(sws_ctx.get(), frame->data, frame->linesize, 0, frame->height, pic_data, linesizes); video_frame.len = video_format.stride * height; - video_frame.received_timestamp = frame_start; + video_frame.received_timestamp = next_frame_start; } FrameAllocator::Frame audio_frame; @@ -285,7 +307,8 @@ bool FFmpegCapture::play_video(const string &pathname) audio_format.bits_per_sample = 32; audio_format.num_channels = 8; - this_thread::sleep_until(frame_start); + // TODO: Make it interruptable somehow. + this_thread::sleep_until(next_frame_start); frame_callback(timecode++, video_frame, 0, video_format, audio_frame, 0, audio_format); diff --git a/ffmpeg_capture.h b/ffmpeg_capture.h index daf353e..3787bd7 100644 --- a/ffmpeg_capture.h +++ b/ffmpeg_capture.h @@ -55,6 +55,12 @@ public: command_queue.push_back(QueuedCommand { QueuedCommand::REWIND }); } + void change_rate(double new_rate) + { + std::lock_guard lock(queue_mu); + command_queue.push_back(QueuedCommand { QueuedCommand::CHANGE_RATE, new_rate }); + } + // CaptureInterface. void set_video_frame_allocator(bmusb::FrameAllocator *allocator) override { @@ -157,7 +163,8 @@ private: std::mutex queue_mu; struct QueuedCommand { - enum Command { REWIND } command; + enum Command { REWIND, CHANGE_RATE } command; + double new_rate; // For CHANGE_RATE. }; std::vector command_queue; // Protected by . }; diff --git a/theme.cpp b/theme.cpp index 7ece56a..1434baa 100644 --- a/theme.cpp +++ b/theme.cpp @@ -360,6 +360,15 @@ int VideoInput_rewind(lua_State* L) return 0; } +int VideoInput_change_rate(lua_State* L) +{ + assert(lua_gettop(L) == 2); + FFmpegCapture **video_input = (FFmpegCapture **)luaL_checkudata(L, 1, "VideoInput"); + double new_rate = luaL_checknumber(L, 2); + (*video_input)->change_rate(new_rate); + return 0; +} + int WhiteBalanceEffect_new(lua_State* L) { assert(lua_gettop(L) == 0); @@ -562,6 +571,7 @@ const luaL_Reg ImageInput_funcs[] = { const luaL_Reg VideoInput_funcs[] = { { "new", VideoInput_new }, { "rewind", VideoInput_rewind }, + { "change_rate", VideoInput_change_rate }, { NULL, NULL } }; -- 2.39.2