X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=futatabi%2Fvideo_stream.cpp;h=e41304bac948f9180b2f6f108cc121e26e950829;hb=299f7273d2c27e2de40510f8256fb5d4de465014;hp=c4cf52e0779a9093709174466afdf79bc8df5c74;hpb=c768958b335e6a30584d02107667558e454399ae;p=nageru diff --git a/futatabi/video_stream.cpp b/futatabi/video_stream.cpp index c4cf52e..e41304b 100644 --- a/futatabi/video_stream.cpp +++ b/futatabi/video_stream.cpp @@ -129,7 +129,8 @@ vector encode_jpeg(const uint8_t *y_data, const uint8_t *cb_data, const return move(dest.dest); } -VideoStream::VideoStream() +VideoStream::VideoStream(AVFormatContext *file_avctx) + : avctx(file_avctx), output_fast_forward(file_avctx != nullptr) { ycbcr_converter.reset(new YCbCrConverter(YCbCrConverter::OUTPUT_TO_DUAL_YCBCR, /*resource_pool=*/nullptr)); ycbcr_semiplanar_converter.reset(new YCbCrConverter(YCbCrConverter::OUTPUT_TO_SEMIPLANAR, /*resource_pool=*/nullptr)); @@ -207,10 +208,8 @@ VideoStream::VideoStream() check_error(); OperatingPoint op; - if (global_flags.interpolation_quality == 0) { - // Allocate something just for simplicity; we won't be using it. - op = operating_point1; - } else if (global_flags.interpolation_quality == 1) { + if (global_flags.interpolation_quality == 0 || + global_flags.interpolation_quality == 1) { op = operating_point1; } else if (global_flags.interpolation_quality == 2) { op = operating_point2; @@ -219,6 +218,7 @@ VideoStream::VideoStream() } else if (global_flags.interpolation_quality == 4) { op = operating_point4; } else { + // Quality 0 will be changed to 1 in flags.cpp. assert(false); } @@ -240,27 +240,25 @@ VideoStream::~VideoStream() {} void VideoStream::start() { - AVFormatContext *avctx = avformat_alloc_context(); - - // We use Matroska, because it's pretty much the only mux where FFmpeg - // allows writing chroma location to override JFIF's default center placement. - // (Note that at the time of writing, however, FFmpeg does not correctly - // _read_ this information!) - avctx->oformat = av_guess_format("matroska", nullptr, nullptr); + if (avctx == nullptr) { + avctx = avformat_alloc_context(); - uint8_t *buf = (uint8_t *)av_malloc(MUX_BUFFER_SIZE); - avctx->pb = avio_alloc_context(buf, MUX_BUFFER_SIZE, 1, this, nullptr, nullptr, nullptr); - avctx->pb->write_data_type = &VideoStream::write_packet2_thunk; - avctx->pb->ignore_boundary_point = 1; + // We use Matroska, because it's pretty much the only mux where FFmpeg + // allows writing chroma location to override JFIF's default center placement. + // (Note that at the time of writing, however, FFmpeg does not correctly + // _read_ this information!) + avctx->oformat = av_guess_format("matroska", nullptr, nullptr); - Mux::Codec video_codec = Mux::CODEC_MJPEG; + uint8_t *buf = (uint8_t *)av_malloc(MUX_BUFFER_SIZE); + avctx->pb = avio_alloc_context(buf, MUX_BUFFER_SIZE, 1, this, nullptr, nullptr, nullptr); + avctx->pb->write_data_type = &VideoStream::write_packet2_thunk; + avctx->pb->ignore_boundary_point = 1; - avctx->flags = AVFMT_FLAG_CUSTOM_IO; - - string video_extradata; + avctx->flags = AVFMT_FLAG_CUSTOM_IO; + } size_t width = global_flags.width, height = global_flags.height; // Doesn't matter for MJPEG. - stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, /*audio_codec_parameters=*/nullptr, + mux.reset(new Mux(avctx, width, height, Mux::CODEC_MJPEG, /*video_extradata=*/"", /*audio_codec_parameters=*/nullptr, AVCOL_SPC_BT709, Mux::WITHOUT_AUDIO, COARSE_TIMEBASE, /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {})); @@ -269,6 +267,9 @@ void VideoStream::start() void VideoStream::stop() { + should_quit = true; + queue_changed.notify_all(); + clear_queue(); encode_thread.join(); } @@ -552,22 +553,30 @@ void VideoStream::encode_thread_func() exit(1); } - for ( ;; ) { + while (!should_quit) { QueuedFrame qf; { unique_lock lock(queue_lock); // Wait until we have a frame to play. queue_changed.wait(lock, [this]{ - return !frame_queue.empty(); + return !frame_queue.empty() || should_quit; }); + if (should_quit) { + break; + } steady_clock::time_point frame_start = frame_queue.front().local_pts; // Now sleep until the frame is supposed to start (the usual case), // _or_ clear_queue() happened. - bool aborted = queue_changed.wait_until(lock, frame_start, [this, frame_start]{ - return frame_queue.empty() || frame_queue.front().local_pts != frame_start; - }); + bool aborted; + if (output_fast_forward) { + aborted = frame_queue.empty() || frame_queue.front().local_pts != frame_start; + } else { + aborted = queue_changed.wait_until(lock, frame_start, [this, frame_start]{ + return frame_queue.empty() || frame_queue.front().local_pts != frame_start; + }); + } if (aborted) { // clear_queue() happened, so don't play this frame after all. continue; @@ -584,7 +593,7 @@ void VideoStream::encode_thread_func() pkt.stream_index = 0; pkt.data = (uint8_t *)jpeg.data(); pkt.size = jpeg.size(); - stream_mux->add_packet(pkt, qf.output_pts, qf.output_pts); + mux->add_packet(pkt, qf.output_pts, qf.output_pts); last_frame.assign(&jpeg[0], &jpeg[0] + jpeg.size()); } else if (qf.type == QueuedFrame::FADED) { @@ -600,7 +609,7 @@ void VideoStream::encode_thread_func() pkt.stream_index = 0; pkt.data = (uint8_t *)jpeg.data(); pkt.size = jpeg.size(); - stream_mux->add_packet(pkt, qf.output_pts, qf.output_pts); + mux->add_packet(pkt, qf.output_pts, qf.output_pts); last_frame = move(jpeg); } else if (qf.type == QueuedFrame::INTERPOLATED || qf.type == QueuedFrame::FADED_INTERPOLATED) { glClientWaitSync(qf.fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED); @@ -624,7 +633,7 @@ void VideoStream::encode_thread_func() pkt.stream_index = 0; pkt.data = (uint8_t *)jpeg.data(); pkt.size = jpeg.size(); - stream_mux->add_packet(pkt, qf.output_pts, qf.output_pts); + mux->add_packet(pkt, qf.output_pts, qf.output_pts); last_frame = move(jpeg); } else if (qf.type == QueuedFrame::REFRESH) { AVPacket pkt; @@ -632,7 +641,7 @@ void VideoStream::encode_thread_func() pkt.stream_index = 0; pkt.data = (uint8_t *)last_frame.data(); pkt.size = last_frame.size(); - stream_mux->add_packet(pkt, qf.output_pts, qf.output_pts); + mux->add_packet(pkt, qf.output_pts, qf.output_pts); } else { assert(false); }