#include <va/va_drmcommon.h>
#include <va/va_enc_h264.h>
#include <va/va_x11.h>
+#include <algorithm>
#include <condition_variable>
#include <cstdint>
#include <map>
public:
H264EncoderImpl(QSurface *surface, int width, int height, HTTPD *httpd);
~H264EncoderImpl();
- void add_audio(int64_t pts, std::vector<float> audio); // Needs to come before end_frame() of same pts.
+ void add_audio(int64_t pts, vector<float> audio);
bool begin_frame(GLuint *y_tex, GLuint *cbcr_tex);
- void end_frame(RefCountedGLsync fence, int64_t pts, const std::vector<RefCountedFrame> &input_frames);
+ void end_frame(RefCountedGLsync fence, int64_t pts, const vector<RefCountedFrame> &input_frames);
+ void shutdown();
private:
struct storage_task {
unsigned long long display_order;
int frame_type;
- std::vector<float> audio;
+ vector<float> audio;
int64_t pts, dts;
};
struct PendingFrame {
RefCountedGLsync fence;
- std::vector<RefCountedFrame> input_frames;
+ vector<RefCountedFrame> input_frames;
int64_t pts;
};
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> storage_task_queue; // protected by storage_task_queue_mutex
+ queue<storage_task> 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<int, PendingFrame> pending_video_frames; // under frame_queue_mutex
- std::map<int64_t, std::vector<float>> pending_audio_frames; // under frame_queue_mutex
+ map<int, PendingFrame> pending_video_frames; // under frame_queue_mutex
+ map<int64_t, vector<float>> pending_audio_frames; // under frame_queue_mutex
QSurface *surface;
AVCodecContext *context_audio;
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.
bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val);
} else {
size_in_bits -= bit_left;
- bs->buffer[pos] = (bs->buffer[pos] << bit_left) | (val >> size_in_bits);
+ if (bit_left >= 32) {
+ bs->buffer[pos] = (val >> size_in_bits);
+ } else {
+ bs->buffer[pos] = (bs->buffer[pos] << bit_left) | (val >> size_in_bits);
+ }
bs->buffer[pos] = va_swap32(bs->buffer[pos]);
if (pos + 1 == bs->max_size_in_dword) {
}
}
-#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);
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<class T, class C>
+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)
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;
string data;
- const int64_t global_delay = (ip_period - 1) * (TIMEBASE / MAX_FPS); // So we never get negative dts.
+ const int64_t global_delay = int64_t(ip_period - 1) * (TIMEBASE / MAX_FPS); // So we never get negative dts.
va_status = vaMapBuffer(va_dpy, gl_surfaces[task.display_order % SURFACE_NUM].coded_buf, (void **)(&buf_list));
CHECK_VASTATUS(va_status, "vaMapBuffer");
H264EncoderImpl::~H264EncoderImpl()
{
- {
- unique_lock<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);
- 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<mutex> lock(storage_task_queue_mutex);
void H264EncoderImpl::add_audio(int64_t pts, vector<float> audio)
{
+ assert(!is_shutdown);
{
unique_lock<mutex> lock(frame_queue_mutex);
pending_audio_frames[pts] = move(audio);
void H264EncoderImpl::end_frame(RefCountedGLsync fence, int64_t pts, const vector<RefCountedFrame> &input_frames)
{
+ assert(!is_shutdown);
{
unique_lock<mutex> lock(frame_queue_mutex);
pending_video_frames[current_storage_frame] = PendingFrame{ fence, input_frames, pts };
frame_queue_nonempty.notify_all();
}
+void H264EncoderImpl::shutdown()
+{
+ if (is_shutdown) {
+ return;
+ }
+
+ {
+ unique_lock<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);
+ 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;
// Must be defined here because unique_ptr<> destructor needs to know the impl.
H264Encoder::~H264Encoder() {}
-void H264Encoder::add_audio(int64_t pts, std::vector<float> audio)
+void H264Encoder::add_audio(int64_t pts, vector<float> audio)
{
impl->add_audio(pts, audio);
}
return impl->begin_frame(y_tex, cbcr_tex);
}
-void H264Encoder::end_frame(RefCountedGLsync fence, int64_t pts, const std::vector<RefCountedFrame> &input_frames)
+void H264Encoder::end_frame(RefCountedGLsync fence, int64_t pts, const vector<RefCountedFrame> &input_frames)
{
impl->end_frame(fence, pts, input_frames);
}
+void H264Encoder::shutdown()
+{
+ impl->shutdown();
+}
+
// Real class.