X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffmpeg_capture.cpp;h=ab35c5c5ef1c5f18e66f338fa1cad20fef860a9d;hb=72afc695d201f9d2a0dcb316ec62f1610db5fa74;hp=5474c52fd94f511491206e4f8a92b2545bc87475;hpb=f07adb19f0e2571bf4894ec57e6fcfe4a3e5fd95;p=nageru diff --git a/ffmpeg_capture.cpp b/ffmpeg_capture.cpp index 5474c52..ab35c5c 100644 --- a/ffmpeg_capture.cpp +++ b/ffmpeg_capture.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include extern "C" { @@ -27,6 +28,7 @@ extern "C" { #include "bmusb/bmusb.h" #include "ffmpeg_raii.h" +#include "ffmpeg_util.h" #include "flags.h" #include "image_input.h" @@ -44,6 +46,19 @@ steady_clock::time_point compute_frame_start(int64_t frame_pts, int64_t pts_orig return origin + duration_cast(pts / rate); } +bool changed_since(const std::string &pathname, const timespec &ts) +{ + if (ts.tv_sec < 0) { + return false; + } + struct stat buf; + if (stat(pathname.c_str(), &buf) != 0) { + fprintf(stderr, "%s: Couldn't check for new version, leaving the old in place.\n", pathname.c_str()); + return false; + } + return (buf.st_mtim.tv_sec != ts.tv_sec || buf.st_mtim.tv_nsec != ts.tv_nsec); +} + } // namespace FFmpegCapture::FFmpegCapture(const string &filename, unsigned width, unsigned height) @@ -142,6 +157,18 @@ void FFmpegCapture::producer_thread_func() bool FFmpegCapture::play_video(const string &pathname) { + // Note: Call before open, not after; otherwise, there's a race. + // (There is now, too, but it tips the correct way. We could use fstat() + // if we had the file descriptor.) + timespec last_modified; + struct stat buf; + if (stat(pathname.c_str(), &buf) != 0) { + // Probably some sort of protocol, so can't stat. + last_modified.tv_sec = -1; + } else { + last_modified = buf.st_mtim; + } + auto format_ctx = avformat_open_input_unique(pathname.c_str(), nullptr, nullptr); if (format_ctx == nullptr) { fprintf(stderr, "%s: Error opening file\n", pathname.c_str()); @@ -153,25 +180,14 @@ bool FFmpegCapture::play_video(const string &pathname) return false; } - int video_stream_index = -1, audio_stream_index = -1; - AVRational video_timebase{ 1, 1 }; - for (unsigned i = 0; i < format_ctx->nb_streams; ++i) { - if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - video_stream_index == -1) { - video_stream_index = i; - video_timebase = format_ctx->streams[i]->time_base; - } - if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && - audio_stream_index == -1) { - audio_stream_index = i; - } - } + int video_stream_index = find_stream_index(format_ctx.get(), AVMEDIA_TYPE_VIDEO); if (video_stream_index == -1) { fprintf(stderr, "%s: No video stream found\n", pathname.c_str()); return false; } const AVCodecParameters *codecpar = format_ctx->streams[video_stream_index]->codecpar; + AVRational video_timebase = format_ctx->streams[video_stream_index]->time_base; AVCodecContextWithDeleter codec_ctx = avcodec_alloc_context3_unique(nullptr); if (avcodec_parameters_to_context(codec_ctx.get(), codecpar) < 0) { fprintf(stderr, "%s: Cannot fill codec parameters\n", pathname.c_str()); @@ -189,9 +205,7 @@ 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; + internal_rewind(); double rate = 1.0; unique_ptr sws_ctx(nullptr, sws_freeContext); @@ -211,8 +225,14 @@ bool FFmpegCapture::play_video(const string &pathname) 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(); + // If the file has changed since last time, return to get it reloaded. + // Note that depending on how you move the file into place, you might + // end up corrupting the one you're already playing, so this path + // might not trigger. + if (changed_since(pathname, last_modified)) { + return true; + } + internal_rewind(); break; case QueuedCommand::CHANGE_RATE: @@ -263,15 +283,21 @@ 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(); + // If the file has changed since last time, return to get it reloaded. + // Note that depending on how you move the file into place, you might + // end up corrupting the one you're already playing, so this path + // might not trigger. + if (changed_since(pathname, last_modified)) { + return true; + } + internal_rewind(); continue; } if (sws_ctx == nullptr || sws_last_width != frame->width || sws_last_height != frame->height) { sws_ctx.reset( sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format, - width, height, AV_PIX_FMT_RGBA, + width, height, AV_PIX_FMT_BGRA, SWS_BICUBIC, nullptr, nullptr, nullptr)); sws_last_width = frame->width; sws_last_height = frame->height; @@ -287,6 +313,11 @@ bool FFmpegCapture::play_video(const string &pathname) video_format.stride = width * 4; video_format.frame_rate_nom = video_timebase.den; video_format.frame_rate_den = av_frame_get_pkt_duration(frame.get()) * video_timebase.num; + if (video_format.frame_rate_nom == 0 || video_format.frame_rate_den == 0) { + // Invalid frame rate. + video_format.frame_rate_nom = 60; + video_format.frame_rate_den = 1; + } video_format.has_signal = true; video_format.is_connected = true; @@ -314,3 +345,9 @@ bool FFmpegCapture::play_video(const string &pathname) } return true; } + +void FFmpegCapture::internal_rewind() +{ + pts_origin = last_pts = 0; + start = next_frame_start = steady_clock::now(); +}