X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=quicksync_encoder.cpp;h=4e974223d15def72a9d348a2a4eb94039077d0e2;hb=bea1be71fd06888ac03fcddb47f6d497f8e07aab;hp=7d211798a504d07e91137e77a77815732ebad3a1;hpb=1462715cd71d8f61b9e53b31c34d591d150f2df3;p=nageru diff --git a/quicksync_encoder.cpp b/quicksync_encoder.cpp index 7d21179..4e97422 100644 --- a/quicksync_encoder.cpp +++ b/quicksync_encoder.cpp @@ -1,17 +1,17 @@ -//#include "sysdeps.h" #include "quicksync_encoder.h" +#include // Must be above the Xlib includes. #include + #include -#include #include #include #include -#include +#include #include #include #include -#include +#include #include #include #include @@ -19,24 +19,40 @@ #include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include +extern "C" { + +#include +#include +#include +#include + +} // namespace + #include "audio_encoder.h" #include "context.h" #include "defs.h" +#include "disk_space_estimator.h" +#include "ffmpeg_raii.h" #include "flags.h" #include "mux.h" +#include "ref_counted_frame.h" #include "timebase.h" #include "x264_encoder.h" using namespace std; +using namespace std::placeholders; class QOpenGLContext; class QSurface; @@ -193,12 +209,22 @@ FrameReorderer::Frame FrameReorderer::get_first_frame() class QuickSyncEncoderImpl { public: - QuickSyncEncoderImpl(const std::string &filename, QSurface *surface, const string &va_display, int width, int height, Mux *stream_mux, AudioEncoder *stream_audio_encoder); + QuickSyncEncoderImpl(const std::string &filename, movit::ResourcePool *resource_pool, QSurface *surface, const string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *x264_encoder, DiskSpaceEstimator *disk_space_estimator); ~QuickSyncEncoderImpl(); void add_audio(int64_t pts, vector audio); bool begin_frame(GLuint *y_tex, GLuint *cbcr_tex); RefCountedGLsync end_frame(int64_t pts, int64_t duration, const vector &input_frames); void shutdown(); + void release_gl_resources(); + void set_stream_mux(Mux *mux) + { + stream_mux = mux; + } + + // So we never get negative dts. + int64_t global_delay() const { + return int64_t(ip_period - 1) * (TIMEBASE / MAX_FPS); + } private: struct storage_task { @@ -213,11 +239,6 @@ private: int64_t pts, duration; }; - // So we never get negative dts. - int64_t global_delay() const { - return int64_t(ip_period - 1) * (TIMEBASE / MAX_FPS); - } - void open_output_file(const std::string &filename); void encode_thread_func(); void encode_remaining_frames_as_p(int encoding_frame_num, int gop_start_display_frame_num, int64_t last_dts); @@ -225,7 +246,6 @@ private: void encode_frame(PendingFrame frame, int encoding_frame_num, int display_frame_num, int gop_start_display_frame_num, int frame_type, int64_t pts, int64_t dts, int64_t duration); void storage_task_thread(); - void encode_remaining_audio(); void storage_task_enqueue(storage_task task); void save_codeddata(storage_task task); int render_packedsequence(); @@ -246,11 +266,12 @@ private: VADisplay va_open_display(const string &va_display); void va_close_display(VADisplay va_dpy); int setup_encode(); - int release_encode(); + void release_encode(); void update_ReferenceFrames(int frame_type); int update_RefPicList(int frame_type); bool is_shutdown = false; + bool has_released_gl_resources = false; bool use_zerocopy; int drm_fd = -1; @@ -269,17 +290,16 @@ private: int current_storage_frame; map pending_video_frames; // under frame_queue_mutex - map> pending_audio_frames; // under frame_queue_mutex + movit::ResourcePool *resource_pool; QSurface *surface; unique_ptr file_audio_encoder; - AudioEncoder *stream_audio_encoder; - - Mux* stream_mux; // To HTTP. - unique_ptr file_mux; // To local disk. unique_ptr reorderer; - unique_ptr x264_encoder; // nullptr if not using x264. + X264Encoder *x264_encoder; // nullptr if not using x264. + + Mux* stream_mux = nullptr; // To HTTP. + unique_ptr file_mux; // To local disk. Display *x11_display = nullptr; @@ -338,6 +358,8 @@ private: int frame_height; int frame_width_mbaligned; int frame_height_mbaligned; + + DiskSpaceEstimator *disk_space_estimator; }; // Supposedly vaRenderPicture() is supposed to destroy the buffer implicitly, @@ -1149,8 +1171,8 @@ int QuickSyncEncoderImpl::setup_encode() VAStatus va_status; VASurfaceID *tmp_surfaceid; int codedbuf_size, i; - static VASurfaceID src_surface[SURFACE_NUM]; - static VASurfaceID ref_surface[SURFACE_NUM]; + VASurfaceID src_surface[SURFACE_NUM]; + VASurfaceID ref_surface[SURFACE_NUM]; va_status = vaCreateConfig(va_dpy, h264_profile, VAEntrypointEncSlice, &config_attrib[0], config_attrib_num, &config_id); @@ -1201,17 +1223,12 @@ int QuickSyncEncoderImpl::setup_encode() //glGenFramebuffers(SURFACE_NUM, fbos); for (i = 0; i < SURFACE_NUM; i++) { - glGenTextures(1, &gl_surfaces[i].y_tex); - glGenTextures(1, &gl_surfaces[i].cbcr_tex); - - if (!use_zerocopy) { - // Create Y image. - glBindTexture(GL_TEXTURE_2D, gl_surfaces[i].y_tex); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8, frame_width, frame_height); - - // Create CbCr image. - glBindTexture(GL_TEXTURE_2D, gl_surfaces[i].cbcr_tex); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_RG8, frame_width / 2, frame_height / 2); + if (use_zerocopy) { + gl_surfaces[i].y_tex = resource_pool->create_2d_texture(GL_R8, 1, 1); + gl_surfaces[i].cbcr_tex = resource_pool->create_2d_texture(GL_RG8, 1, 1); + } else { + gl_surfaces[i].y_tex = resource_pool->create_2d_texture(GL_R8, frame_width, frame_height); + gl_surfaces[i].cbcr_tex = resource_pool->create_2d_texture(GL_RG8, frame_width / 2, frame_height / 2); // Generate a PBO to read into. It doesn't necessarily fit 1:1 with the VA-API // buffers, due to potentially differing pitch. @@ -1635,26 +1652,6 @@ void QuickSyncEncoderImpl::save_codeddata(storage_task task) stream_mux->add_packet(pkt, task.pts + global_delay(), task.dts + global_delay()); } } - // Encode and add all audio frames up to and including the pts of this video frame. - for ( ;; ) { - int64_t audio_pts; - vector audio; - { - unique_lock lock(frame_queue_mutex); - frame_queue_nonempty.wait(lock, [this]{ return storage_thread_should_quit || !pending_audio_frames.empty(); }); - if (storage_thread_should_quit && pending_audio_frames.empty()) return; - auto it = pending_audio_frames.begin(); - if (it->first > task.pts) break; - audio_pts = it->first; - audio = move(it->second); - pending_audio_frames.erase(it); - } - - file_audio_encoder->encode_audio(audio, audio_pts + global_delay()); - stream_audio_encoder->encode_audio(audio, audio_pts + global_delay()); - - if (audio_pts == task.pts) break; - } } @@ -1694,27 +1691,37 @@ void QuickSyncEncoderImpl::storage_task_thread() } } -int QuickSyncEncoderImpl::release_encode() +void QuickSyncEncoderImpl::release_encode() { for (unsigned i = 0; i < SURFACE_NUM; i++) { vaDestroyBuffer(va_dpy, gl_surfaces[i].coded_buf); vaDestroySurfaces(va_dpy, &gl_surfaces[i].src_surface, 1); vaDestroySurfaces(va_dpy, &gl_surfaces[i].ref_surface, 1); + } + vaDestroyContext(va_dpy, context_id); + vaDestroyConfig(va_dpy, config_id); +} + +void QuickSyncEncoderImpl::release_gl_resources() +{ + assert(is_shutdown); + if (has_released_gl_resources) { + return; + } + + for (unsigned i = 0; i < SURFACE_NUM; i++) { if (!use_zerocopy) { glBindBuffer(GL_PIXEL_PACK_BUFFER, gl_surfaces[i].pbo); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glDeleteBuffers(1, &gl_surfaces[i].pbo); } - glDeleteTextures(1, &gl_surfaces[i].y_tex); - glDeleteTextures(1, &gl_surfaces[i].cbcr_tex); + resource_pool->release_2d_texture(gl_surfaces[i].y_tex); + resource_pool->release_2d_texture(gl_surfaces[i].cbcr_tex); } - vaDestroyContext(va_dpy, context_id); - vaDestroyConfig(va_dpy, config_id); - - return 0; + has_released_gl_resources = true; } int QuickSyncEncoderImpl::deinit_va() @@ -1726,14 +1733,10 @@ int QuickSyncEncoderImpl::deinit_va() return 0; } -namespace { - -} // namespace - -QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, QSurface *surface, const string &va_display, int width, int height, Mux *stream_mux, AudioEncoder *stream_audio_encoder) - : current_storage_frame(0), surface(surface), stream_audio_encoder(stream_audio_encoder), stream_mux(stream_mux), frame_width(width), frame_height(height) +QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, movit::ResourcePool *resource_pool, QSurface *surface, const string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *x264_encoder, DiskSpaceEstimator *disk_space_estimator) + : current_storage_frame(0), resource_pool(resource_pool), surface(surface), x264_encoder(x264_encoder), frame_width(width), frame_height(height), disk_space_estimator(disk_space_estimator) { - file_audio_encoder.reset(new AudioEncoder(AUDIO_OUTPUT_CODEC_NAME, DEFAULT_AUDIO_OUTPUT_BIT_RATE)); + file_audio_encoder.reset(new AudioEncoder(AUDIO_OUTPUT_CODEC_NAME, DEFAULT_AUDIO_OUTPUT_BIT_RATE, oformat)); open_output_file(filename); file_audio_encoder->add_mux(file_mux.get()); @@ -1747,7 +1750,9 @@ QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, QSurface reorderer.reset(new FrameReorderer(ip_period - 1, frame_width, frame_height)); } if (global_flags.x264_video_to_http) { - x264_encoder.reset(new X264Encoder(stream_mux)); + assert(x264_encoder != nullptr); + } else { + assert(x264_encoder == nullptr); } init_va(va_display); @@ -1772,12 +1777,14 @@ QuickSyncEncoderImpl::QuickSyncEncoderImpl(const std::string &filename, QSurface exit(1); } encode_thread_func(); + delete_context(context); }); } QuickSyncEncoderImpl::~QuickSyncEncoderImpl() { shutdown(); + release_gl_resources(); } bool QuickSyncEncoderImpl::begin_frame(GLuint *y_tex, GLuint *cbcr_tex) @@ -1854,11 +1861,7 @@ bool QuickSyncEncoderImpl::begin_frame(GLuint *y_tex, GLuint *cbcr_tex) void QuickSyncEncoderImpl::add_audio(int64_t pts, vector audio) { assert(!is_shutdown); - { - unique_lock lock(frame_queue_mutex); - pending_audio_frames[pts] = move(audio); - } - frame_queue_nonempty.notify_all(); + file_audio_encoder->encode_audio(audio, pts + global_delay()); } RefCountedGLsync QuickSyncEncoderImpl::end_frame(int64_t pts, int64_t duration, const vector &input_frames) @@ -1919,7 +1922,6 @@ void QuickSyncEncoderImpl::shutdown() frame_queue_nonempty.notify_all(); } encode_thread.join(); - x264_encoder.reset(); { unique_lock lock(storage_task_queue_mutex); storage_thread_should_quit = true; @@ -1927,7 +1929,9 @@ void QuickSyncEncoderImpl::shutdown() storage_task_queue_changed.notify_all(); } storage_thread.join(); - encode_remaining_audio(); + + // Encode any leftover audio in the queues, and also any delayed frames. + file_audio_encoder->encode_last_audio(); release_encode(); deinit_va(); @@ -1950,7 +1954,10 @@ void QuickSyncEncoderImpl::open_output_file(const std::string &filename) exit(1); } - file_mux.reset(new Mux(avctx, frame_width, frame_height, Mux::CODEC_H264, file_audio_encoder->get_codec(), TIMEBASE, DEFAULT_AUDIO_OUTPUT_BIT_RATE, nullptr)); + string video_extradata = ""; // FIXME: See other comment about global headers. + 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(), TIMEBASE, + std::bind(&DiskSpaceEstimator::report_write, disk_space_estimator, filename, _1))); } void QuickSyncEncoderImpl::encode_thread_func() @@ -2033,25 +2040,6 @@ void QuickSyncEncoderImpl::encode_remaining_frames_as_p(int encoding_frame_num, } } -void QuickSyncEncoderImpl::encode_remaining_audio() -{ - // This really ought to be empty by now, but just to be sure... - for (auto &pending_frame : pending_audio_frames) { - int64_t audio_pts = pending_frame.first; - vector audio = move(pending_frame.second); - - file_audio_encoder->encode_audio(audio, audio_pts + global_delay()); - if (stream_audio_encoder) { - stream_audio_encoder->encode_audio(audio, audio_pts + global_delay()); - } - } - pending_audio_frames.clear(); - - // Encode any leftover audio in the queues, and also any delayed frames. - // Note: stream_audio_encoder is not owned by us, so don't call encode_last_audio(). - file_audio_encoder->encode_last_audio(); -} - void QuickSyncEncoderImpl::add_packet_for_uncompressed_frame(int64_t pts, int64_t duration, const uint8_t *data) { AVPacket pkt; @@ -2142,6 +2130,9 @@ void QuickSyncEncoderImpl::encode_frame(QuickSyncEncoderImpl::PendingFrame frame CHECK_VASTATUS(va_status, "vaBeginPicture"); if (frame_type == FRAME_IDR) { + // FIXME: If the mux wants global headers, we should not put the + // SPS/PPS before each IDR frame, but rather put it into the + // codec extradata (formatted differently?). render_sequence(); render_picture(frame_type, display_frame_num, gop_start_display_frame_num); if (h264_packedheader) { @@ -2171,8 +2162,8 @@ void QuickSyncEncoderImpl::encode_frame(QuickSyncEncoderImpl::PendingFrame frame } // Proxy object. -QuickSyncEncoder::QuickSyncEncoder(const std::string &filename, QSurface *surface, const string &va_display, int width, int height, Mux *stream_mux, AudioEncoder *stream_audio_encoder) - : impl(new QuickSyncEncoderImpl(filename, surface, va_display, width, height, stream_mux, stream_audio_encoder)) {} +QuickSyncEncoder::QuickSyncEncoder(const std::string &filename, movit::ResourcePool *resource_pool, QSurface *surface, const string &va_display, int width, int height, AVOutputFormat *oformat, X264Encoder *x264_encoder, DiskSpaceEstimator *disk_space_estimator) + : impl(new QuickSyncEncoderImpl(filename, resource_pool, surface, va_display, width, height, oformat, x264_encoder, disk_space_estimator)) {} // Must be defined here because unique_ptr<> destructor needs to know the impl. QuickSyncEncoder::~QuickSyncEncoder() {} @@ -2196,3 +2187,12 @@ void QuickSyncEncoder::shutdown() { impl->shutdown(); } + +void QuickSyncEncoder::set_stream_mux(Mux *mux) +{ + impl->set_stream_mux(mux); +} + +int64_t QuickSyncEncoder::global_delay() const { + return impl->global_delay(); +}