#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.
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)
+// 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;
- 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)
-{
- 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;
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.