X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=futatabi%2Fvideo_stream.cpp;h=591ee7e219883e5f4eb9209cbb17e5100108ea83;hb=adc0df09f7a9dc88a3c0dbad47a21a805e728862;hp=5a36801b5e6ebf787cc5f84874650d63db2862fb;hpb=02ea864dc5a6dde7450c497581ff18d784ab832c;p=nageru diff --git a/futatabi/video_stream.cpp b/futatabi/video_stream.cpp index 5a36801..591ee7e 100644 --- a/futatabi/video_stream.cpp +++ b/futatabi/video_stream.cpp @@ -11,6 +11,7 @@ extern "C" { #include "flow.h" #include "jpeg_frame_view.h" #include "movit/util.h" +#include "pbo_pool.h" #include "player.h" #include "shared/context.h" #include "shared/httpd.h" @@ -37,6 +38,14 @@ Summary metric_interpolation_latency_seconds; Summary metric_fade_fence_wait_time_seconds; Summary metric_interpolation_fence_wait_time_seconds; +void wait_for_upload(shared_ptr &frame) +{ + if (frame->uploaded_interpolation != nullptr) { + glWaitSync(frame->uploaded_interpolation.get(), /*flags=*/0, GL_TIMEOUT_IGNORED); + frame->uploaded_interpolation.reset(); + } +} + } // namespace extern HTTPD *global_httpd; @@ -152,6 +161,16 @@ string encode_jpeg(const uint8_t *y_data, const uint8_t *cb_data, const uint8_t return move(dest.dest); } +string encode_jpeg_from_pbo(void *contents, unsigned width, unsigned height, const string exif_data) +{ + unsigned chroma_width = width / 2; + + const uint8_t *y = (const uint8_t *)contents; + const uint8_t *cb = (const uint8_t *)contents + width * height; + const uint8_t *cr = (const uint8_t *)contents + width * height + chroma_width * height; + return encode_jpeg(y, cb, cr, width, height, move(exif_data)); +} + VideoStream::VideoStream(AVFormatContext *file_avctx) : avctx(file_avctx), output_fast_forward(file_avctx != nullptr) { @@ -430,6 +449,8 @@ void VideoStream::schedule_faded_frame(steady_clock::time_point local_pts, int64 shared_ptr frame1 = decode_jpeg_with_cache(frame1_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode); shared_ptr frame2 = decode_jpeg_with_cache(frame2_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode); + wait_for_upload(frame1); + wait_for_upload(frame2); ycbcr_semiplanar_converter->prepare_chain_for_fade(frame1, frame2, fade_alpha)->render_to_fbo(resources->fade_fbo, global_flags.width, global_flags.height); @@ -517,6 +538,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts FrameOnDisk frame_spec = frame_no == 1 ? frame2 : frame1; bool did_decode; shared_ptr frame = decode_jpeg_with_cache(frame_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode); + wait_for_upload(frame); ycbcr_converter->prepare_chain_for_conversion(frame)->render_to_fbo(resources->input_fbos[frame_no], global_flags.width, global_flags.height); if (frame_no == 1) { qf.exif_data = frame->exif_data; // Use the white point from the last frame. @@ -557,6 +579,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts // Now decode the image we are fading against. bool did_decode; shared_ptr frame2 = decode_jpeg_with_cache(secondary_frame, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode); + wait_for_upload(frame2); // Then fade against it, putting it into the fade Y' and CbCr textures. RGBTriplet neutral_color = get_neutral_color(qf.exif_data); @@ -645,31 +668,27 @@ void VideoStream::schedule_silence(steady_clock::time_point local_pts, int64_t o namespace { -shared_ptr frame_from_pbo(void *contents, size_t width, size_t height) +RefCountedTexture clone_r8_texture(GLuint src_tex, unsigned width, unsigned height) { - size_t chroma_width = width / 2; - - const uint8_t *y = (const uint8_t *)contents; - const uint8_t *cb = (const uint8_t *)contents + width * height; - const uint8_t *cr = (const uint8_t *)contents + width * height + chroma_width * height; + GLuint tex; + glCreateTextures(GL_TEXTURE_2D, 1, &tex); + check_error(); + glTextureStorage2D(tex, 1, GL_R8, width, height); + check_error(); + glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, 0, 0, 0, + tex, GL_TEXTURE_2D, 0, 0, 0, 0, + width, height, 1); + check_error(); + glTextureParameteri(tex, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + check_error(); + glTextureParameteri(tex, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + check_error(); + glTextureParameteri(tex, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + check_error(); + glTextureParameteri(tex, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + check_error(); - shared_ptr frame(new Frame); - frame->y.reset(new uint8_t[width * height]); - frame->cb.reset(new uint8_t[chroma_width * height]); - frame->cr.reset(new uint8_t[chroma_width * height]); - for (unsigned yy = 0; yy < height; ++yy) { - memcpy(frame->y.get() + width * yy, y + width * yy, width); - memcpy(frame->cb.get() + chroma_width * yy, cb + chroma_width * yy, chroma_width); - memcpy(frame->cr.get() + chroma_width * yy, cr + chroma_width * yy, chroma_width); - } - frame->is_semiplanar = false; - frame->width = width; - frame->height = height; - frame->chroma_subsampling_x = 2; - frame->chroma_subsampling_y = 1; - frame->pitch_y = width; - frame->pitch_chroma = chroma_width; - return frame; + return RefCountedTexture(new GLuint(tex), TextureDeleter()); } } // namespace @@ -685,6 +704,8 @@ void VideoStream::encode_thread_func() abort(); } + init_pbo_pool(); + while (!should_quit) { QueuedFrame qf; { @@ -751,11 +772,8 @@ void VideoStream::encode_thread_func() metric_fade_fence_wait_time_seconds.count_event(duration(stop - start).count()); metric_fade_latency_seconds.count_event(duration(stop - qf.fence_created).count()); - shared_ptr frame = frame_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height); - assert(frame->exif_data.empty()); - // Now JPEG encode it, and send it on to the stream. - string jpeg = encode_jpeg(frame->y.get(), frame->cb.get(), frame->cr.get(), global_flags.width, global_flags.height, /*exif_data=*/""); + string jpeg = encode_jpeg_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height, /*exif_data=*/""); AVPacket pkt; av_init_packet(&pkt); @@ -775,13 +793,25 @@ void VideoStream::encode_thread_func() metric_interpolation_latency_seconds.count_event(duration(stop - qf.fence_created).count()); // Send it on to display. - shared_ptr frame = frame_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height); if (qf.display_decoded_func != nullptr) { - qf.display_decoded_func(frame); + shared_ptr frame(new Frame); + if (qf.type == QueuedFrame::FADED_INTERPOLATED) { + frame->y = clone_r8_texture(qf.resources->fade_y_output_tex, global_flags.width, global_flags.height); + } else { + frame->y = clone_r8_texture(qf.output_tex, global_flags.width, global_flags.height); + } + frame->cb = clone_r8_texture(qf.resources->cb_tex, global_flags.width / 2, global_flags.height); + frame->cr = clone_r8_texture(qf.resources->cr_tex, global_flags.width / 2, global_flags.height); + frame->width = global_flags.width; + frame->height = global_flags.height; + frame->chroma_subsampling_x = 2; + frame->chroma_subsampling_y = 1; + frame->uploaded_ui_thread = RefCountedGLsync(GL_SYNC_GPU_COMMANDS_COMPLETE, /*flags=*/0); + qf.display_decoded_func(move(frame)); } // Now JPEG encode it, and send it on to the stream. - string jpeg = encode_jpeg(frame->y.get(), frame->cb.get(), frame->cr.get(), global_flags.width, global_flags.height, move(qf.exif_data)); + string jpeg = encode_jpeg_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height, move(qf.exif_data)); if (qf.flow_tex != 0) { compute_flow->release_texture(qf.flow_tex); }