X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffmpeg_capture.cpp;h=5474c52fd94f511491206e4f8a92b2545bc87475;hb=f07adb19f0e2571bf4894ec57e6fcfe4a3e5fd95;hp=647b7cbea4b4a96e25b1496b66a0b2b5d476d335;hpb=bcdbb7bdfcfc45b34ccdf36d3950e7e0af6444bd;p=nageru diff --git a/ffmpeg_capture.cpp b/ffmpeg_capture.cpp index 647b7cb..5474c52 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) { @@ -68,7 +78,7 @@ void FFmpegCapture::start_bm_capture() return; } running = true; - producer_thread_should_quit = false; + producer_thread_should_quit.unquit(); producer_thread = thread(&FFmpegCapture::producer_thread_func, this); } @@ -78,7 +88,7 @@ void FFmpegCapture::stop_dequeue_thread() return; } running = false; - producer_thread_should_quit = true; + producer_thread_should_quit.quit(); producer_thread.join(); } @@ -107,17 +117,17 @@ void FFmpegCapture::producer_thread_func() snprintf(thread_name, sizeof(thread_name), "FFmpeg_C_%d", card_index); pthread_setname_np(pthread_self(), thread_name); - while (!producer_thread_should_quit) { + while (!producer_thread_should_quit.should_quit()) { string pathname = search_for_file(filename); if (filename.empty()) { fprintf(stderr, "%s not found, sleeping one second and trying again...\n", filename.c_str()); - sleep(1); + producer_thread_should_quit.sleep_for(seconds(1)); continue; } if (!play_video(pathname)) { // Error. fprintf(stderr, "Error when playing %s, sleeping one second and trying again...\n", pathname.c_str()); - sleep(1); + producer_thread_should_quit.sleep_for(seconds(1)); continue; } @@ -179,13 +189,40 @@ 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; // Main loop. - while (!producer_thread_should_quit) { + while (!producer_thread_should_quit.should_quit()) { + // Process any queued commands from other threads. + vector commands; + { + lock_guard lock(queue_mu); + swap(commands, command_queue); + } + for (const QueuedCommand &cmd : commands) { + 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()); + } + 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; + } + } + // Read packets until we have a frame or there are none left. int frame_finished = 0; AVFrameWithDeleter frame = av_frame_alloc_unique(); @@ -226,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; } @@ -252,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) { @@ -261,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; @@ -269,7 +307,7 @@ bool FFmpegCapture::play_video(const string &pathname) audio_format.bits_per_sample = 32; audio_format.num_channels = 8; - this_thread::sleep_until(frame_start); + producer_thread_should_quit.sleep_until(next_frame_start); frame_callback(timecode++, video_frame, 0, video_format, audio_frame, 0, audio_format);