]> git.sesse.net Git - nageru/blobdiff - nageru/quicksync_encoder.cpp
Add support for V4L2 output.
[nageru] / nageru / quicksync_encoder.cpp
index b5d7c2d9dccf428665c954b161a57e6f76c3570c..70bf8002073d64aba40f2aa6021c3e19ddf9a352 100644 (file)
@@ -80,7 +80,7 @@ std::atomic<int64_t> metric_quick_sync_stalled_frames{0};
 #define CHECK_VASTATUS(va_status, func)                                 \
     if (va_status != VA_STATUS_SUCCESS) {                               \
         fprintf(stderr, "%s:%d (%s) failed with %d\n", __func__, __LINE__, func, va_status); \
-        exit(1);                                                        \
+        abort();                                                        \
     }
 
 #undef BUFFER_OFFSET
@@ -719,6 +719,9 @@ void QuickSyncEncoderImpl::enable_zerocopy_if_possible()
        } else if (global_flags.x264_video_to_http) {
                fprintf(stderr, "Disabling zerocopy H.264 encoding due to --http-x264-video.\n");
                use_zerocopy = false;
+       } else if (!global_flags.v4l_output_device.empty()) {
+               fprintf(stderr, "Disabling zerocopy H.264 encoding due to --v4l-output.\n");
+               use_zerocopy = false;
        } else {
                use_zerocopy = true;
        }
@@ -822,7 +825,7 @@ int QuickSyncEncoderImpl::init_va(const string &va_display)
     va_dpy = try_open_va(va_display, &h264_profile, &error);
     if (va_dpy == nullptr) {
        fprintf(stderr, "error: %s\n", error.c_str());
-        exit(1);
+        abort();
     }
     if (!va_dpy->can_use_zerocopy) {
         use_zerocopy = false;
@@ -860,7 +863,7 @@ int QuickSyncEncoderImpl::init_va(const string &va_display)
     /* check the interested configattrib */
     if ((attrib[VAConfigAttribRTFormat].value & VA_RT_FORMAT_YUV420) == 0) {
         printf("Not find desired YUV420 RT format\n");
-        exit(1);
+        abort();
     } else {
         config_attrib[config_attrib_num].type = VAConfigAttribRTFormat;
         config_attrib[config_attrib_num].value = VA_RT_FORMAT_YUV420;
@@ -870,7 +873,7 @@ int QuickSyncEncoderImpl::init_va(const string &va_display)
     if (attrib[VAConfigAttribRateControl].value != VA_ATTRIB_NOT_SUPPORTED) {
         if (!(attrib[VAConfigAttribRateControl].value & VA_RC_CQP)) {
             fprintf(stderr, "ERROR: VA-API encoder does not support CQP mode.\n");
-            exit(1);
+            abort();
         }
 
         config_attrib[config_attrib_num].type = VAConfigAttribRateControl;
@@ -1023,7 +1026,7 @@ void QuickSyncEncoderImpl::update_ReferenceFrames(int current_display_frame, int
     pic_param.CurrPic.frame_idx = current_ref_frame_num;
 
     CurrentCurrPic.flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE;
-    unique_lock<mutex> lock(storage_task_queue_mutex);
+    lock_guard<mutex> lock(storage_task_queue_mutex);
 
     // Insert the new frame at the start of the reference queue.
     reference_frames.push_front(ReferenceFrame{ CurrentCurrPic, current_display_frame });
@@ -1434,7 +1437,7 @@ void QuickSyncEncoderImpl::save_codeddata(GLSurface *surf, storage_task task)
 // this is weird. but it seems to put a new frame onto the queue
 void QuickSyncEncoderImpl::storage_task_enqueue(storage_task task)
 {
-       unique_lock<mutex> lock(storage_task_queue_mutex);
+       lock_guard<mutex> lock(storage_task_queue_mutex);
        storage_task_queue.push(move(task));
        storage_task_queue_changed.notify_all();
 }
@@ -1468,7 +1471,7 @@ void QuickSyncEncoderImpl::storage_task_thread()
 
                // Unlock the frame, and all its references.
                {
-                       unique_lock<mutex> lock(storage_task_queue_mutex);
+                       lock_guard<mutex> lock(storage_task_queue_mutex);
                        release_gl_surface(display_order);
 
                        for (size_t frame_num : ref_display_frame_numbers) {
@@ -1481,13 +1484,18 @@ void QuickSyncEncoderImpl::storage_task_thread()
 void QuickSyncEncoderImpl::release_encode()
 {
        for (unsigned i = 0; i < SURFACE_NUM; i++) {
-               vaDestroyBuffer(va_dpy->va_dpy, gl_surfaces[i].coded_buf);
-               vaDestroySurfaces(va_dpy->va_dpy, &gl_surfaces[i].src_surface, 1);
-               vaDestroySurfaces(va_dpy->va_dpy, &gl_surfaces[i].ref_surface, 1);
+               VAStatus va_status = vaDestroyBuffer(va_dpy->va_dpy, gl_surfaces[i].coded_buf);
+               CHECK_VASTATUS(va_status, "vaDestroyBuffer");
+               va_status = vaDestroySurfaces(va_dpy->va_dpy, &gl_surfaces[i].src_surface, 1);
+               CHECK_VASTATUS(va_status, "vaDestroySurfaces");
+               va_status = vaDestroySurfaces(va_dpy->va_dpy, &gl_surfaces[i].ref_surface, 1);
+               CHECK_VASTATUS(va_status, "vaDestroySurfaces");
        }
 
-       vaDestroyContext(va_dpy->va_dpy, context_id);
-       vaDestroyConfig(va_dpy->va_dpy, config_id);
+       VAStatus va_status = vaDestroyContext(va_dpy->va_dpy, context_id);
+       CHECK_VASTATUS(va_status, "vaDestroyContext");
+       va_status = vaDestroyConfig(va_dpy->va_dpy, config_id);
+       CHECK_VASTATUS(va_status, "vaDestroyConfig");
 }
 
 void QuickSyncEncoderImpl::release_gl_resources()
@@ -1542,6 +1550,10 @@ QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, Resource
                memset(&slice_param, 0, sizeof(slice_param));
        }
 
+       if (!global_flags.v4l_output_device.empty()) {
+               v4l_output.reset(new V4LOutput(global_flags.v4l_output_device.c_str(), width, height));
+       }
+
        call_once(quick_sync_metrics_inited, [](){
                mixer_latency_histogram.init("mixer");
                qs_latency_histogram.init("quick_sync");
@@ -1559,7 +1571,7 @@ QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, Resource
                if (!make_current(context, this->surface)) {
                        printf("display=%p surface=%p context=%p curr=%p err=%d\n", eglGetCurrentDisplay(), this->surface, context, eglGetCurrentContext(),
                                eglGetError());
-                       exit(1);
+                       abort();
                }
                encode_thread_func();
                delete_context(context);
@@ -1701,7 +1713,7 @@ RefCountedGLsync QuickSyncEncoderImpl::end_frame()
                GLenum type = global_flags.x264_bit_depth > 8 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
                GLSurface *surf;
                {
-                       unique_lock<mutex> lock(storage_task_queue_mutex);
+                       lock_guard<mutex> lock(storage_task_queue_mutex);
                        surf = surface_for_frame[current_storage_frame];
                        assert(surf != nullptr);
                }
@@ -1740,7 +1752,7 @@ RefCountedGLsync QuickSyncEncoderImpl::end_frame()
        check_error();
 
        {
-               unique_lock<mutex> lock(frame_queue_mutex);
+               lock_guard<mutex> lock(frame_queue_mutex);
                current_video_frame.fence = fence;
                pending_video_frames.push(move(current_video_frame));
                ++current_storage_frame;
@@ -1756,13 +1768,13 @@ void QuickSyncEncoderImpl::shutdown()
        }
 
        {
-               unique_lock<mutex> lock(frame_queue_mutex);
+               lock_guard<mutex> lock(frame_queue_mutex);
                encode_thread_should_quit = true;
                frame_queue_nonempty.notify_all();
        }
        encode_thread.join();
        {
-               unique_lock<mutex> lock(storage_task_queue_mutex);
+               lock_guard<mutex> lock(storage_task_queue_mutex);
                storage_thread_should_quit = true;
                frame_queue_nonempty.notify_all();
                storage_task_queue_changed.notify_all();
@@ -1792,15 +1804,14 @@ void QuickSyncEncoderImpl::open_output_file(const std::string &filename)
 {
        AVFormatContext *avctx = avformat_alloc_context();
        avctx->oformat = av_guess_format(NULL, filename.c_str(), NULL);
-       assert(filename.size() < sizeof(avctx->filename) - 1);
-       strcpy(avctx->filename, filename.c_str());
+       avctx->url = strdup(filename.c_str());
 
        string url = "file:" + filename;
        int ret = avio_open2(&avctx->pb, url.c_str(), AVIO_FLAG_WRITE, &avctx->interrupt_callback, NULL);
        if (ret < 0) {
                char tmp[AV_ERROR_MAX_STRING_SIZE];
                fprintf(stderr, "%s: avio_open2() failed: %s\n", filename.c_str(), av_make_error_string(tmp, sizeof(tmp), ret));
-               exit(1);
+               abort();
        }
 
        string video_extradata;  // FIXME: See other comment about global headers.
@@ -1813,7 +1824,7 @@ void QuickSyncEncoderImpl::open_output_file(const std::string &filename)
        {
                lock_guard<mutex> lock(file_audio_encoder_mutex);
                AVCodecParametersWithDeleter audio_codecpar = file_audio_encoder->get_codec_parameters();
-               file_mux.reset(new Mux(avctx, frame_width, frame_height, Mux::CODEC_H264, video_extradata, audio_codecpar.get(), get_color_space(global_flags.ycbcr_rec709_coefficients), Mux::WITH_AUDIO, TIMEBASE,
+               file_mux.reset(new Mux(avctx, frame_width, frame_height, Mux::CODEC_H264, video_extradata, audio_codecpar.get(), get_color_space(global_flags.ycbcr_rec709_coefficients), TIMEBASE,
                        std::bind(&DiskSpaceEstimator::report_append, disk_space_estimator, filename, _1),
                        Mux::WRITE_BACKGROUND,
                        { &current_file_mux_metrics, &total_mux_metrics }));
@@ -1860,7 +1871,7 @@ void QuickSyncEncoderImpl::encode_thread_func()
                pass_frame(frame, display_frame_num, frame.pts, frame.duration);
 
                if (global_flags.x264_video_to_disk) {
-                       unique_lock<mutex> lock(storage_task_queue_mutex);
+                       lock_guard<mutex> lock(storage_task_queue_mutex);
                        release_gl_surface(display_frame_num);
                        continue;
                }
@@ -1883,7 +1894,7 @@ void QuickSyncEncoderImpl::encode_thread_func()
                        if (frame_type == FRAME_IDR) {
                                // Release any reference frames from the previous GOP.
                                {
-                                       unique_lock<mutex> lock(storage_task_queue_mutex);
+                                       lock_guard<mutex> lock(storage_task_queue_mutex);
                                        for (const ReferenceFrame &frame : reference_frames) {
                                                release_gl_surface(frame.display_number);
                                        }
@@ -1976,7 +1987,7 @@ void QuickSyncEncoderImpl::pass_frame(QuickSyncEncoderImpl::PendingFrame frame,
 
        GLSurface *surf;
        {
-               unique_lock<mutex> lock(storage_task_queue_mutex);
+               lock_guard<mutex> lock(storage_task_queue_mutex);
                surf = surface_for_frame[display_frame_num];
                assert(surf != nullptr);
        }
@@ -1986,6 +1997,10 @@ void QuickSyncEncoderImpl::pass_frame(QuickSyncEncoderImpl::PendingFrame frame,
        } else if (global_flags.x264_video_to_http || global_flags.x264_video_to_disk) {
                x264_encoder->add_frame(pts, duration, frame.ycbcr_coefficients, data, received_ts);
        }
+
+       if (v4l_output != nullptr) {
+               v4l_output->send_frame(data);
+       }
 }
 
 void QuickSyncEncoderImpl::encode_frame(QuickSyncEncoderImpl::PendingFrame frame, int encoding_frame_num, int display_frame_num, int gop_start_display_frame_num,
@@ -1995,7 +2010,7 @@ void QuickSyncEncoderImpl::encode_frame(QuickSyncEncoderImpl::PendingFrame frame
 
        GLSurface *surf;
        {
-               unique_lock<mutex> lock(storage_task_queue_mutex);
+               lock_guard<mutex> lock(storage_task_queue_mutex);
                surf = surface_for_frame[display_frame_num];
                assert(surf != nullptr);
        }
@@ -2059,7 +2074,7 @@ void QuickSyncEncoderImpl::encode_frame(QuickSyncEncoderImpl::PendingFrame frame
        // Lock the references for this frame; otherwise, they could be
        // rendered to before this frame is done encoding.
        {
-               unique_lock<mutex> lock(storage_task_queue_mutex);
+               lock_guard<mutex> lock(storage_task_queue_mutex);
                for (const ReferenceFrame &frame : reference_frames) {
                        assert(surface_for_frame.count(frame.display_number));
                        ++surface_for_frame[frame.display_number]->refcount;
@@ -2175,5 +2190,5 @@ string QuickSyncEncoder::get_usable_va_display()
        fprintf(stderr, "to expose Quick Sync. Alternatively, you can use --record-x264-video\n");
        fprintf(stderr, "to use software instead of hardware H.264 encoding, at the expense\n");
        fprintf(stderr, "of increased CPU usage and possibly bit rate.\n");
-       exit(1);
+       abort();
 }