X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=h264encode.cpp;h=02ada023b182879a0f0bf3fea15d3302dcad541b;hb=9903ba2fb66d20f39e4b73fd6f948e5dbc528fcb;hp=a417c30fb8fbb5dbda794816d44d9d383790bc9a;hpb=8ed6a8ebad548bc2c162100c70fa3d952fb0ce17;p=nageru diff --git a/h264encode.cpp b/h264encode.cpp index a417c30..02ada02 100644 --- a/h264encode.cpp +++ b/h264encode.cpp @@ -22,6 +22,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -109,20 +110,21 @@ class H264EncoderImpl { public: H264EncoderImpl(QSurface *surface, int width, int height, HTTPD *httpd); ~H264EncoderImpl(); - void add_audio(int64_t pts, std::vector audio); // Needs to come before end_frame() of same pts. + void add_audio(int64_t pts, vector audio); bool begin_frame(GLuint *y_tex, GLuint *cbcr_tex); - void end_frame(RefCountedGLsync fence, int64_t pts, const std::vector &input_frames); + void end_frame(RefCountedGLsync fence, int64_t pts, const vector &input_frames); + void shutdown(); private: struct storage_task { unsigned long long display_order; int frame_type; - std::vector audio; + vector audio; int64_t pts, dts; }; struct PendingFrame { RefCountedGLsync fence; - std::vector input_frames; + vector input_frames; int64_t pts; }; @@ -154,22 +156,24 @@ private: void update_ReferenceFrames(int frame_type); int update_RefPicList(int frame_type); - std::thread encode_thread, storage_thread; + bool is_shutdown = false; - std::mutex storage_task_queue_mutex; - std::condition_variable storage_task_queue_changed; + thread encode_thread, storage_thread; + + mutex storage_task_queue_mutex; + condition_variable storage_task_queue_changed; int srcsurface_status[SURFACE_NUM]; // protected by storage_task_queue_mutex - std::queue storage_task_queue; // protected by storage_task_queue_mutex + queue storage_task_queue; // protected by storage_task_queue_mutex bool storage_thread_should_quit = false; // protected by storage_task_queue_mutex - std::mutex frame_queue_mutex; - std::condition_variable frame_queue_nonempty; + mutex frame_queue_mutex; + condition_variable frame_queue_nonempty; bool encode_thread_should_quit = false; // under frame_queue_mutex int current_storage_frame; - std::map pending_video_frames; // under frame_queue_mutex - std::map> pending_audio_frames; // under frame_queue_mutex + map pending_video_frames; // under frame_queue_mutex + map> pending_audio_frames; // under frame_queue_mutex QSurface *surface; AVCodecContext *context_audio; @@ -228,7 +232,6 @@ private: int frame_height_mbaligned; }; - // Supposedly vaRenderPicture() is supposed to destroy the buffer implicitly, // but if we don't delete it here, we get leaks. The GStreamer implementation // does the same. @@ -824,149 +827,6 @@ static const char *rc_to_string(int rc_mode) } } -#if 0 -static int process_cmdline(int argc, char *argv[]) -{ - char c; - const struct option long_opts[] = { - {"help", no_argument, NULL, 0 }, - {"bitrate", required_argument, NULL, 1 }, - {"minqp", required_argument, NULL, 2 }, - {"initialqp", required_argument, NULL, 3 }, - {"intra_period", required_argument, NULL, 4 }, - {"idr_period", required_argument, NULL, 5 }, - {"ip_period", required_argument, NULL, 6 }, - {"rcmode", required_argument, NULL, 7 }, - {"srcyuv", required_argument, NULL, 9 }, - {"recyuv", required_argument, NULL, 10 }, - {"fourcc", required_argument, NULL, 11 }, - {"syncmode", no_argument, NULL, 12 }, - {"enablePSNR", no_argument, NULL, 13 }, - {"prit", required_argument, NULL, 14 }, - {"priv", required_argument, NULL, 15 }, - {"framecount", required_argument, NULL, 16 }, - {"entropy", required_argument, NULL, 17 }, - {"profile", required_argument, NULL, 18 }, - {NULL, no_argument, NULL, 0 }}; - int long_index; - - while ((c =getopt_long_only(argc, argv, "w:h:n:f:o:?", long_opts, &long_index)) != EOF) { - switch (c) { - case 'w': - frame_width = atoi(optarg); - break; - case 'h': - frame_height = atoi(optarg); - break; - case 'n': - case 'f': - frame_rate = atoi(optarg); - break; - case 'o': - coded_fn = strdup(optarg); - break; - case 0: - print_help(); - exit(0); - case 1: - frame_bitrate = atoi(optarg); - break; - case 2: - minimal_qp = atoi(optarg); - break; - case 3: - initial_qp = atoi(optarg); - break; - case 4: - intra_period = atoi(optarg); - break; - case 5: - intra_idr_period = atoi(optarg); - break; - case 6: - ip_period = atoi(optarg); - break; - case 7: - rc_mode = string_to_rc(optarg); - if (rc_mode < 0) { - print_help(); - exit(1); - } - break; - case 9: - srcyuv_fn = strdup(optarg); - break; - case 11: - srcyuv_fourcc = string_to_fourcc(optarg); - if (srcyuv_fourcc <= 0) { - print_help(); - exit(1); - } - break; - case 13: - calc_psnr = 1; - break; - case 17: - h264_entropy_mode = atoi(optarg) ? 1: 0; - break; - case 18: - if (strncmp(optarg, "BP", 2) == 0) - h264_profile = VAProfileH264Baseline; - else if (strncmp(optarg, "MP", 2) == 0) - h264_profile = VAProfileH264Main; - else if (strncmp(optarg, "HP", 2) == 0) - h264_profile = VAProfileH264High; - else - h264_profile = (VAProfile)0; - break; - case ':': - case '?': - print_help(); - exit(0); - } - } - - if (ip_period < 1) { - printf(" ip_period must be greater than 0\n"); - exit(0); - } - if (intra_period != 1 && intra_period % ip_period != 0) { - printf(" intra_period must be a multiplier of ip_period\n"); - exit(0); - } - if (intra_period != 0 && intra_idr_period % intra_period != 0) { - printf(" intra_idr_period must be a multiplier of intra_period\n"); - exit(0); - } - - if (frame_bitrate == 0) - frame_bitrate = frame_width * frame_height * 12 * MAX_FPS / 50; - - if (coded_fn == NULL) { - struct stat buf; - if (stat("/tmp", &buf) == 0) - coded_fn = strdup("/tmp/test.264"); - else if (stat("/sdcard", &buf) == 0) - coded_fn = strdup("/sdcard/test.264"); - else - coded_fn = strdup("./test.264"); - } - - - frame_width_mbaligned = (frame_width + 15) & (~15); - frame_height_mbaligned = (frame_height + 15) & (~15); - if (frame_width != frame_width_mbaligned || - frame_height != frame_height_mbaligned) { - printf("Source frame is %dx%d and will code clip to %dx%d with crop\n", - frame_width, frame_height, - frame_width_mbaligned, frame_height_mbaligned - ); - } - - return 0; -} -#endif - VADisplay H264EncoderImpl::va_open_display(void) { x11_display = XOpenDisplay(NULL); @@ -1210,68 +1070,15 @@ int H264EncoderImpl::setup_encode() return 0; } - - -#define partition(ref, field, key, ascending) \ - while (i <= j) { \ - if (ascending) { \ - while (ref[i].field < key) \ - i++; \ - while (ref[j].field > key) \ - j--; \ - } else { \ - while (ref[i].field > key) \ - i++; \ - while (ref[j].field < key) \ - j--; \ - } \ - if (i <= j) { \ - tmp = ref[i]; \ - ref[i] = ref[j]; \ - ref[j] = tmp; \ - i++; \ - j--; \ - } \ - } \ - -static void sort_one(VAPictureH264 ref[], int left, int right, - int ascending, int frame_idx) -{ - int i = left, j = right; - unsigned int key; - VAPictureH264 tmp; - - if (frame_idx) { - key = ref[(left + right) / 2].frame_idx; - partition(ref, frame_idx, key, ascending); - } else { - key = ref[(left + right) / 2].TopFieldOrderCnt; - partition(ref, TopFieldOrderCnt, (signed int)key, ascending); - } - - /* recursion */ - if (left < j) - sort_one(ref, left, j, ascending, frame_idx); - - if (i < right) - sort_one(ref, i, right, ascending, frame_idx); -} - -static void sort_two(VAPictureH264 ref[], int left, int right, unsigned int key, unsigned int frame_idx, - int partition_ascending, int list0_ascending, int list1_ascending) +// Given a list like 1 9 3 0 2 8 4 and a pivot element 3, will produce +// +// 2 1 0 [3] 4 8 9 +template +static void sort_two(T *begin, T *end, const T &pivot, const C &less_than) { - int i = left, j = right; - VAPictureH264 tmp; - - if (frame_idx) { - partition(ref, frame_idx, key, partition_ascending); - } else { - partition(ref, TopFieldOrderCnt, (signed int)key, partition_ascending); - } - - - sort_one(ref, left, i-1, list0_ascending, frame_idx); - sort_one(ref, j+1, right, list1_ascending, frame_idx); + T *middle = partition(begin, end, [&](const T &elem) { return less_than(elem, pivot); }); + sort(begin, middle, [&](const T &a, const T &b) { return less_than(b, a); }); + sort(middle, end, less_than); } void H264EncoderImpl::update_ReferenceFrames(int frame_type) @@ -1297,21 +1104,25 @@ void H264EncoderImpl::update_ReferenceFrames(int frame_type) int H264EncoderImpl::update_RefPicList(int frame_type) { - unsigned int current_poc = CurrentCurrPic.TopFieldOrderCnt; + const auto descending_by_frame_idx = [](const VAPictureH264 &a, const VAPictureH264 &b) { + return a.frame_idx > b.frame_idx; + }; + const auto ascending_by_top_field_order_cnt = [](const VAPictureH264 &a, const VAPictureH264 &b) { + return a.TopFieldOrderCnt < b.TopFieldOrderCnt; + }; + const auto descending_by_top_field_order_cnt = [](const VAPictureH264 &a, const VAPictureH264 &b) { + return a.TopFieldOrderCnt > b.TopFieldOrderCnt; + }; if (frame_type == FRAME_P) { memcpy(RefPicList0_P, ReferenceFrames, numShortTerm * sizeof(VAPictureH264)); - sort_one(RefPicList0_P, 0, numShortTerm-1, 0, 1); - } - - if (frame_type == FRAME_B) { + sort(&RefPicList0_P[0], &RefPicList0_P[numShortTerm], descending_by_frame_idx); + } else if (frame_type == FRAME_B) { memcpy(RefPicList0_B, ReferenceFrames, numShortTerm * sizeof(VAPictureH264)); - sort_two(RefPicList0_B, 0, numShortTerm-1, current_poc, 0, - 1, 0, 1); + sort_two(&RefPicList0_B[0], &RefPicList0_B[numShortTerm], CurrentCurrPic, ascending_by_top_field_order_cnt); memcpy(RefPicList1_B, ReferenceFrames, numShortTerm * sizeof(VAPictureH264)); - sort_two(RefPicList1_B, 0, numShortTerm-1, current_poc, 0, - 0, 1, 0); + sort_two(&RefPicList1_B[0], &RefPicList1_B[numShortTerm], CurrentCurrPic, descending_by_top_field_order_cnt); } return 0; @@ -1841,26 +1652,12 @@ H264EncoderImpl::H264EncoderImpl(QSurface *surface, int width, int height, HTTPD H264EncoderImpl::~H264EncoderImpl() { - { - unique_lock lock(frame_queue_mutex); - encode_thread_should_quit = true; - frame_queue_nonempty.notify_all(); - } - encode_thread.join(); - { - unique_lock lock(storage_task_queue_mutex); - storage_thread_should_quit = true; - frame_queue_nonempty.notify_all(); - storage_task_queue_changed.notify_all(); - } - storage_thread.join(); - - release_encode(); - deinit_va(); + shutdown(); } bool H264EncoderImpl::begin_frame(GLuint *y_tex, GLuint *cbcr_tex) { + assert(!is_shutdown); { // Wait until this frame slot is done encoding. unique_lock lock(storage_task_queue_mutex); @@ -1925,6 +1722,7 @@ bool H264EncoderImpl::begin_frame(GLuint *y_tex, GLuint *cbcr_tex) void H264EncoderImpl::add_audio(int64_t pts, vector audio) { + assert(!is_shutdown); { unique_lock lock(frame_queue_mutex); pending_audio_frames[pts] = move(audio); @@ -1934,6 +1732,7 @@ void H264EncoderImpl::add_audio(int64_t pts, vector audio) void H264EncoderImpl::end_frame(RefCountedGLsync fence, int64_t pts, const vector &input_frames) { + assert(!is_shutdown); { unique_lock lock(frame_queue_mutex); pending_video_frames[current_storage_frame] = PendingFrame{ fence, input_frames, pts }; @@ -1942,6 +1741,31 @@ void H264EncoderImpl::end_frame(RefCountedGLsync fence, int64_t pts, const vecto frame_queue_nonempty.notify_all(); } +void H264EncoderImpl::shutdown() +{ + if (is_shutdown) { + return; + } + + { + unique_lock lock(frame_queue_mutex); + encode_thread_should_quit = true; + frame_queue_nonempty.notify_all(); + } + encode_thread.join(); + { + unique_lock lock(storage_task_queue_mutex); + storage_thread_should_quit = true; + frame_queue_nonempty.notify_all(); + storage_task_queue_changed.notify_all(); + } + storage_thread.join(); + + release_encode(); + deinit_va(); + is_shutdown = true; +} + void H264EncoderImpl::encode_thread_func() { int64_t last_dts = -1; @@ -2067,7 +1891,7 @@ H264Encoder::H264Encoder(QSurface *surface, int width, int height, HTTPD *httpd) // Must be defined here because unique_ptr<> destructor needs to know the impl. H264Encoder::~H264Encoder() {} -void H264Encoder::add_audio(int64_t pts, std::vector audio) +void H264Encoder::add_audio(int64_t pts, vector audio) { impl->add_audio(pts, audio); } @@ -2077,9 +1901,14 @@ bool H264Encoder::begin_frame(GLuint *y_tex, GLuint *cbcr_tex) return impl->begin_frame(y_tex, cbcr_tex); } -void H264Encoder::end_frame(RefCountedGLsync fence, int64_t pts, const std::vector &input_frames) +void H264Encoder::end_frame(RefCountedGLsync fence, int64_t pts, const vector &input_frames) { impl->end_frame(fence, pts, input_frames); } +void H264Encoder::shutdown() +{ + impl->shutdown(); +} + // Real class.