X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=nageru%2Fmjpeg_encoder.h;h=aee1b9bf52f0da0891df85313a878635d2bb6bf3;hb=575f6eb1b052bb1291987753b1a8cccc7f1e3ab3;hp=3ce34396efa4253290efe66e1a59a2574ef757b8;hpb=eeda8995329601f9f4e35047358400833eeae68e;p=nageru diff --git a/nageru/mjpeg_encoder.h b/nageru/mjpeg_encoder.h index 3ce3439..aee1b9b 100644 --- a/nageru/mjpeg_encoder.h +++ b/nageru/mjpeg_encoder.h @@ -27,40 +27,113 @@ struct jpeg_compress_struct; struct VADisplayWithCleanup; struct VectorDestinationManager; +#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); \ + } + class MJPEGEncoder { public: MJPEGEncoder(HTTPD *httpd, const std::string &va_display); + ~MJPEGEncoder(); void stop(); void upload_frame(int64_t pts, unsigned card_index, RefCountedFrame frame, const bmusb::VideoFormat &video_format, size_t y_offset, size_t cbcr_offset); + // If the frame was started (data_copy != nullptr) but will not be finished + // (MJPEG decoding was turned off in the meantime), you'll need to call finish_frame() + // to release any VA-API resources. + void finish_frame(RefCountedFrame frame); + + bool using_vaapi() const { return va_dpy != nullptr; } + + // Returns -1 for inactive (ie., don't encode frames for this card right now). + int get_mjpeg_stream_for_card(unsigned card_index); + private: static constexpr int quality = 90; + struct VAResources { + unsigned width, height; + VASurfaceID surface; + VAContextID context; + VABufferID data_buffer; + VAImage image; + }; + + // RAII wrapper to release VAResources on return (even on error). + class ReleaseVAResources { + public: + ReleaseVAResources() : committed(true) {} + + ReleaseVAResources(MJPEGEncoder *mjpeg, const VAResources &resources) + : mjpeg(mjpeg), resources(resources) {} + + ReleaseVAResources(ReleaseVAResources &) = delete; + + ReleaseVAResources(ReleaseVAResources &&other) + : mjpeg(other.mjpeg), resources(other.resources), committed(other.committed) { + other.commit(); + } + + ReleaseVAResources &operator= (ReleaseVAResources &) = delete; + + ReleaseVAResources &operator= (ReleaseVAResources &&other) { + if (!committed) { + mjpeg->release_va_resources(resources); + } + mjpeg = other.mjpeg; + resources = std::move(other.resources); + committed = other.committed; + other.commit(); + return *this; + } + + ~ReleaseVAResources() + { + if (!committed) { + mjpeg->release_va_resources(resources); + } + } + + void commit() { committed = true; } + + private: + MJPEGEncoder *mjpeg = nullptr; + VAResources resources; + bool committed = false; + }; + struct QueuedFrame { int64_t pts; unsigned card_index; RefCountedFrame frame; bmusb::VideoFormat video_format; size_t y_offset, cbcr_offset; + + // Only for frames in the process of being encoded by VA-API. + VAResources resources; + ReleaseVAResources resource_releaser; }; void encoder_thread_func(); - std::vector encode_jpeg(const QueuedFrame &qf); - std::vector encode_jpeg_va(const QueuedFrame &qf); + void va_receiver_thread_func(); + void encode_jpeg_va(QueuedFrame &&qf); std::vector encode_jpeg_libjpeg(const QueuedFrame &qf); + void write_mjpeg_packet(int64_t pts, unsigned card_index, const uint8_t *jpeg, size_t jpeg_size); void init_jpeg_422(unsigned width, unsigned height, VectorDestinationManager *dest, jpeg_compress_struct *cinfo); std::vector get_jpeg_header(unsigned width, unsigned height, jpeg_compress_struct *cinfo); static int write_packet2_thunk(void *opaque, uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time); int write_packet2(uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time); - std::thread encoder_thread; + std::thread encoder_thread, va_receiver_thread; std::mutex mu; std::queue frames_to_be_encoded; // Under mu. - std::condition_variable any_frames_to_be_encoded; + std::condition_variable any_frames_to_be_encoded; // Governs changes in both frames_to_be_encoded and frames_under_encoding - std::queue frames_encoding; // Under mu. + std::queue frames_encoding; // Under mu. Used for VA-API only. std::condition_variable any_frames_encoding; AVFormatContextWithCloser avctx; @@ -82,40 +155,23 @@ private: std::map, VAData> va_data_for_resolution; VAData get_va_data_for_resolution(unsigned width, unsigned height); - struct VAResources { - unsigned width, height; - VASurfaceID surface; - VAContextID context; - VABufferID data_buffer; - }; std::list va_resources_freelist; std::mutex va_resources_mutex; VAResources get_va_resources(unsigned width, unsigned height); void release_va_resources(VAResources resources); - // RAII wrapper to release VAResources on return (even on error). - class ReleaseVAResources { - public: - ReleaseVAResources(MJPEGEncoder *mjpeg, const VAResources &resources) - : mjpeg(mjpeg), resources(resources) {} - ~ReleaseVAResources() - { - if (!committed) { - mjpeg->release_va_resources(resources); - } - } - - void commit() { committed = true; } + static std::unique_ptr try_open_va(const std::string &va_display, std::string *error, VAConfigID *config_id); - private: - MJPEGEncoder * const mjpeg; - const VAResources &resources; - bool committed = false; - }; + uint8_t *tmp_y, *tmp_cbcr, *tmp_cb, *tmp_cr; // Private to the encoder thread. Used by the libjpeg backend only. - static std::unique_ptr try_open_va(const std::string &va_display, std::string *error, VAConfigID *config_id); + std::atomic metric_mjpeg_frames_zero_size_dropped{0}; + std::atomic metric_mjpeg_frames_interlaced_dropped{0}; + std::atomic metric_mjpeg_frames_unsupported_pixel_format_dropped{0}; + std::atomic metric_mjpeg_frames_oversized_dropped{0}; + std::atomic metric_mjpeg_overrun_dropped{0}; + std::atomic metric_mjpeg_overrun_submitted{0}; - uint8_t *tmp_y, *tmp_cbcr, *tmp_cb, *tmp_cr; // Private to the encoder thread. + friend class PBOFrameAllocator; // FIXME }; #endif // !defined(_MJPEG_ENCODER_H)