]> git.sesse.net Git - nageru/blobdiff - futatabi/video_stream.cpp
Make Futatabi copy Exif data for interpolated frames.
[nageru] / futatabi / video_stream.cpp
index 9647836feaa4294a4653bdb8eb7b84b8af77da27..9a120b51344a70b6b4744519ee07f328c50f7b3b 100644 (file)
@@ -79,7 +79,7 @@ struct VectorDestinationManager {
 };
 static_assert(std::is_standard_layout<VectorDestinationManager>::value, "");
 
-string encode_jpeg(const uint8_t *y_data, const uint8_t *cb_data, const uint8_t *cr_data, unsigned width, unsigned height)
+string encode_jpeg(const uint8_t *y_data, const uint8_t *cb_data, const uint8_t *cr_data, unsigned width, unsigned height, const string exif_data)
 {
        VectorDestinationManager dest;
 
@@ -112,6 +112,10 @@ string encode_jpeg(const uint8_t *y_data, const uint8_t *cb_data, const uint8_t
        // (and nothing else).
        jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *)"CS=ITU601", strlen("CS=ITU601"));
 
+       if (!exif_data.empty()) {
+               jpeg_write_marker(&cinfo, JPEG_APP0 + 1, (const JOCTET *)exif_data.data(), exif_data.size());
+       }
+
        JSAMPROW yptr[8], cbptr[8], crptr[8];
        JSAMPARRAY data[3] = { yptr, cbptr, crptr };
        for (unsigned y = 0; y < height; y += 8) {
@@ -234,7 +238,13 @@ VideoStream::VideoStream(AVFormatContext *file_avctx)
        unique_ptr<uint8_t[]> cb_or_cr(new uint8_t[(global_flags.width / 2) * global_flags.height]);
        memset(y.get(), 16, global_flags.width * global_flags.height);
        memset(cb_or_cr.get(), 128, (global_flags.width / 2) * global_flags.height);
-       last_frame = encode_jpeg(y.get(), cb_or_cr.get(), cb_or_cr.get(), global_flags.width, global_flags.height);
+       last_frame = encode_jpeg(y.get(), cb_or_cr.get(), cb_or_cr.get(), global_flags.width, global_flags.height, /*exif_data=*/"");
+
+       if (file_avctx != nullptr) {
+               with_subtitles = Mux::WITHOUT_SUBTITLES;
+       } else {
+               with_subtitles = Mux::WITH_SUBTITLES;
+       }
 }
 
 VideoStream::~VideoStream()
@@ -297,7 +307,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_codecpar,
-                         AVCOL_SPC_BT709, COARSE_TIMEBASE, /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {}, Mux::WITH_SUBTITLES));
+                         AVCOL_SPC_BT709, COARSE_TIMEBASE, /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {}, with_subtitles));
 
        avcodec_parameters_free(&audio_codecpar);
        encode_thread = thread(&VideoStream::encode_thread_func, this);
@@ -475,6 +485,9 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts
                bool did_decode;
                shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
                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.
+               }
        }
 
        glGenerateTextureMipmap(resources->input_tex);
@@ -668,7 +681,7 @@ void VideoStream::encode_thread_func()
 
                // 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()) {
+               if (!qf.subtitle.empty() && with_subtitles == Mux::WITH_SUBTITLES) {
                        AVPacket pkt;
                        av_init_packet(&pkt);
                        pkt.stream_index = mux->get_subtitle_stream_idx();
@@ -699,7 +712,7 @@ void VideoStream::encode_thread_func()
                        shared_ptr<Frame> frame = frame_from_pbo(qf.resources->pbo_contents, global_flags.width, global_flags.height);
 
                        // 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);
+                       string jpeg = encode_jpeg(frame->y.get(), frame->cb.get(), frame->cr.get(), global_flags.width, global_flags.height, move(frame->exif_data));
 
                        AVPacket pkt;
                        av_init_packet(&pkt);
@@ -721,7 +734,7 @@ void VideoStream::encode_thread_func()
                        }
 
                        // 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);
+                       string jpeg = encode_jpeg(frame->y.get(), frame->cb.get(), frame->cr.get(), global_flags.width, global_flags.height, move(qf.exif_data));
                        if (qf.flow_tex != 0) {
                                compute_flow->release_texture(qf.flow_tex);
                        }