X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=futatabi%2Fvideo_stream.cpp;h=a820411064234bef2d29aa9c7a95f91a1cd0771d;hb=9921f1fe12f5a7dadab7e84069fa8591d5c5663d;hp=66323b88749afc17a7142c51cc7b2e017ee61758;hpb=4a9e97065dade428e373a83618bc973cd93cbe52;p=nageru diff --git a/futatabi/video_stream.cpp b/futatabi/video_stream.cpp index 66323b8..a820411 100644 --- a/futatabi/video_stream.cpp +++ b/futatabi/video_stream.cpp @@ -241,6 +241,30 @@ VideoStream::~VideoStream() if (last_flow_tex != 0) { compute_flow->release_texture(last_flow_tex); } + + for (const unique_ptr &resource : interpolate_resources) { + glUnmapNamedBuffer(resource->pbo); + check_error(); + glDeleteBuffers(1, &resource->pbo); + check_error(); + glDeleteFramebuffers(2, resource->input_fbos); + check_error(); + glDeleteFramebuffers(1, &resource->fade_fbo); + check_error(); + glDeleteTextures(1, &resource->input_tex); + check_error(); + glDeleteTextures(1, &resource->gray_tex); + check_error(); + glDeleteTextures(1, &resource->fade_y_output_tex); + check_error(); + glDeleteTextures(1, &resource->fade_cbcr_output_tex); + check_error(); + glDeleteTextures(1, &resource->cb_tex); + check_error(); + glDeleteTextures(1, &resource->cr_tex); + check_error(); + } + assert(interpolate_resources.size() == num_interpolate_slots); } void VideoStream::start() @@ -264,7 +288,7 @@ void VideoStream::start() size_t width = global_flags.width, height = global_flags.height; // Doesn't matter for MJPEG. mux.reset(new Mux(avctx, width, height, Mux::CODEC_MJPEG, /*video_extradata=*/"", /*audio_codec_parameters=*/nullptr, - AVCOL_SPC_BT709, COARSE_TIMEBASE, /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {})); + AVCOL_SPC_BT709, COARSE_TIMEBASE, /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {}, Mux::WITH_SUBTITLES)); encode_thread = thread(&VideoStream::encode_thread_func, this); } @@ -307,21 +331,18 @@ void VideoStream::clear_queue() void VideoStream::schedule_original_frame(steady_clock::time_point local_pts, int64_t output_pts, function &&display_func, QueueSpotHolder &&queue_spot_holder, - FrameOnDisk frame) + FrameOnDisk frame, const string &subtitle) { fprintf(stderr, "output_pts=%ld original input_pts=%ld\n", output_pts, frame.pts); - // Preload the file from disk, so that the encoder thread does not get stalled. - // TODO: Consider sending it through the queue instead. - (void)frame_reader.read_frame(frame); - QueuedFrame qf; qf.local_pts = local_pts; qf.type = QueuedFrame::ORIGINAL; qf.output_pts = output_pts; - qf.frame1 = frame; qf.display_func = move(display_func); qf.queue_spot_holder = move(queue_spot_holder); + qf.subtitle = subtitle; + qf.encoded_jpeg.reset(new string(frame_reader.read_frame(frame))); lock_guard lock(queue_lock); frame_queue.push_back(move(qf)); @@ -332,7 +353,7 @@ void VideoStream::schedule_faded_frame(steady_clock::time_point local_pts, int64 function &&display_func, QueueSpotHolder &&queue_spot_holder, FrameOnDisk frame1_spec, FrameOnDisk frame2_spec, - float fade_alpha) + float fade_alpha, const string &subtitle) { fprintf(stderr, "output_pts=%ld faded input_pts=%ld,%ld fade_alpha=%.2f\n", output_pts, frame1_spec.pts, frame2_spec.pts, fade_alpha); @@ -365,6 +386,7 @@ void VideoStream::schedule_faded_frame(steady_clock::time_point local_pts, int64 qf.frame1 = frame1_spec; qf.display_func = move(display_func); qf.queue_spot_holder = move(queue_spot_holder); + qf.subtitle = subtitle; qf.secondary_frame = frame2_spec; @@ -400,7 +422,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts int64_t output_pts, function)> &&display_func, QueueSpotHolder &&queue_spot_holder, FrameOnDisk frame1, FrameOnDisk frame2, - float alpha, FrameOnDisk secondary_frame, float fade_alpha) + float alpha, FrameOnDisk secondary_frame, float fade_alpha, const string &subtitle) { if (secondary_frame.pts != -1) { fprintf(stderr, "output_pts=%ld interpolated input_pts1=%ld input_pts2=%ld alpha=%.3f secondary_pts=%ld fade_alpha=%.2f\n", output_pts, frame1.pts, frame2.pts, alpha, secondary_frame.pts, fade_alpha); @@ -426,6 +448,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts qf.display_decoded_func = move(display_func); qf.queue_spot_holder = move(queue_spot_holder); qf.local_pts = local_pts; + qf.subtitle = subtitle; check_error(); @@ -524,13 +547,14 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts void VideoStream::schedule_refresh_frame(steady_clock::time_point local_pts, int64_t output_pts, function &&display_func, - QueueSpotHolder &&queue_spot_holder) + QueueSpotHolder &&queue_spot_holder, const string &subtitle) { QueuedFrame qf; qf.type = QueuedFrame::REFRESH; qf.output_pts = output_pts; qf.display_func = move(display_func); qf.queue_spot_holder = move(queue_spot_holder); + qf.subtitle = subtitle; lock_guard lock(queue_lock); frame_queue.push_back(move(qf)); @@ -611,9 +635,23 @@ void VideoStream::encode_thread_func() frame_queue.pop_front(); } + // Hack: We mux the subtitle packet one time unit before the actual frame, + // so that Nageru is sure to get it first. + if (!qf.subtitle.empty()) { + AVPacket pkt; + av_init_packet(&pkt); + pkt.stream_index = mux->get_subtitle_stream_idx(); + assert(pkt.stream_index != -1); + pkt.data = (uint8_t *)qf.subtitle.data(); + pkt.size = qf.subtitle.size(); + pkt.flags = 0; + pkt.duration = lrint(TIMEBASE / global_flags.output_framerate); // Doesn't really matter for Nageru. + mux->add_packet(pkt, qf.output_pts - 1, qf.output_pts - 1); + } + if (qf.type == QueuedFrame::ORIGINAL) { // Send the JPEG frame on, unchanged. - string jpeg = frame_reader.read_frame(qf.frame1); + string jpeg = move(*qf.encoded_jpeg); AVPacket pkt; av_init_packet(&pkt); pkt.stream_index = 0;