blob: 0095a42d2a3170c8f86fbf12d14ef4c36896a7c0 [file] [log] [blame]
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +03001/*
2 * Copyright © 2013 Intel Corporation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23/* Copyright (c) 2012 Intel Corporation. All Rights Reserved.
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a
26 * copy of this software and associated documentation files (the
27 * "Software"), to deal in the Software without restriction, including
28 * without limitation the rights to use, copy, modify, merge, publish,
29 * distribute, sub license, and/or sell copies of the Software, and to
30 * permit persons to whom the Software is furnished to do so, subject to
31 * the following conditions:
32 *
33 * The above copyright notice and this permission notice (including the
34 * next paragraph) shall be included in all copies or substantial portions
35 * of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
38 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
40 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
41 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
42 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
43 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44 */
45
Andrew Wedgbury9cd661e2014-04-07 12:40:35 +010046#include "config.h"
47
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +030048#include <stdlib.h>
49#include <stdint.h>
50#include <string.h>
51#include <unistd.h>
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +030052#include <assert.h>
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +030053
54#include <sys/types.h>
55#include <sys/stat.h>
56#include <fcntl.h>
57
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +030058#include <pthread.h>
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +030059
60#include <va/va.h>
61#include <va/va_drm.h>
62#include <va/va_drmcommon.h>
63#include <va/va_enc_h264.h>
64#include <va/va_vpp.h>
65
66#include "compositor.h"
67#include "vaapi-recorder.h"
68
69#define NAL_REF_IDC_NONE 0
70#define NAL_REF_IDC_LOW 1
71#define NAL_REF_IDC_MEDIUM 2
72#define NAL_REF_IDC_HIGH 3
73
74#define NAL_NON_IDR 1
75#define NAL_IDR 5
76#define NAL_SPS 7
77#define NAL_PPS 8
78#define NAL_SEI 6
79
80#define SLICE_TYPE_P 0
81#define SLICE_TYPE_B 1
82#define SLICE_TYPE_I 2
83
84#define ENTROPY_MODE_CAVLC 0
85#define ENTROPY_MODE_CABAC 1
86
87#define PROFILE_IDC_BASELINE 66
88#define PROFILE_IDC_MAIN 77
89#define PROFILE_IDC_HIGH 100
90
91struct vaapi_recorder {
Ander Conselvan de Oliveiraa62ef3e2013-09-06 17:49:38 +030092 int drm_fd, output_fd;
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +030093 int width, height;
94 int frame_count;
95
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +030096 int destroying;
97 pthread_t worker_thread;
98 pthread_mutex_t mutex;
99 pthread_cond_t input_cond;
100
101 struct {
102 int valid;
103 int prime_fd, stride;
104 } input;
105
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300106 VADisplay va_dpy;
107
108 /* video post processing is used for colorspace conversion */
109 struct {
110 VAConfigID cfg;
111 VAContextID ctx;
112 VABufferID pipeline_buf;
113 VASurfaceID output;
114 } vpp;
115
116 struct {
117 VAConfigID cfg;
118 VAContextID ctx;
119 VASurfaceID reference_picture[3];
120
121 int intra_period;
122 int output_size;
123 int constraint_set_flag;
124
125 struct {
126 VAEncSequenceParameterBufferH264 seq;
127 VAEncPictureParameterBufferH264 pic;
128 VAEncSliceParameterBufferH264 slice;
129 } param;
130 } encoder;
131};
132
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +0300133static void *
134worker_thread_function(void *);
135
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300136/* bistream code used for writing the packed headers */
137
138#define BITSTREAM_ALLOCATE_STEPPING 4096
139
140struct bitstream {
141 unsigned int *buffer;
142 int bit_offset;
143 int max_size_in_dword;
144};
145
146static unsigned int
147va_swap32(unsigned int val)
148{
149 unsigned char *pval = (unsigned char *)&val;
150
151 return ((pval[0] << 24) |
152 (pval[1] << 16) |
153 (pval[2] << 8) |
154 (pval[3] << 0));
155}
156
157static void
158bitstream_start(struct bitstream *bs)
159{
160 bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING;
161 bs->buffer = calloc(bs->max_size_in_dword * sizeof(int), 1);
162 bs->bit_offset = 0;
163}
164
165static void
166bitstream_end(struct bitstream *bs)
167{
168 int pos = (bs->bit_offset >> 5);
169 int bit_offset = (bs->bit_offset & 0x1f);
170 int bit_left = 32 - bit_offset;
171
172 if (bit_offset) {
173 bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left));
174 }
175}
176
177static void
178bitstream_put_ui(struct bitstream *bs, unsigned int val, int size_in_bits)
179{
180 int pos = (bs->bit_offset >> 5);
181 int bit_offset = (bs->bit_offset & 0x1f);
182 int bit_left = 32 - bit_offset;
183
184 if (!size_in_bits)
185 return;
186
187 bs->bit_offset += size_in_bits;
188
189 if (bit_left > size_in_bits) {
190 bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val);
191 return;
192 }
193
194 size_in_bits -= bit_left;
195 bs->buffer[pos] =
196 (bs->buffer[pos] << bit_left) | (val >> size_in_bits);
197 bs->buffer[pos] = va_swap32(bs->buffer[pos]);
198
199 if (pos + 1 == bs->max_size_in_dword) {
200 bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING;
201 bs->buffer =
202 realloc(bs->buffer,
203 bs->max_size_in_dword * sizeof(unsigned int));
204 }
205
206 bs->buffer[pos + 1] = val;
207}
208
209static void
210bitstream_put_ue(struct bitstream *bs, unsigned int val)
211{
212 int size_in_bits = 0;
213 int tmp_val = ++val;
214
215 while (tmp_val) {
216 tmp_val >>= 1;
217 size_in_bits++;
218 }
219
220 bitstream_put_ui(bs, 0, size_in_bits - 1); // leading zero
221 bitstream_put_ui(bs, val, size_in_bits);
222}
223
224static void
225bitstream_put_se(struct bitstream *bs, int val)
226{
227 unsigned int new_val;
228
229 if (val <= 0)
230 new_val = -2 * val;
231 else
232 new_val = 2 * val - 1;
233
234 bitstream_put_ue(bs, new_val);
235}
236
237static void
238bitstream_byte_aligning(struct bitstream *bs, int bit)
239{
240 int bit_offset = (bs->bit_offset & 0x7);
241 int bit_left = 8 - bit_offset;
242 int new_val;
243
244 if (!bit_offset)
245 return;
246
247 if (bit)
248 new_val = (1 << bit_left) - 1;
249 else
250 new_val = 0;
251
252 bitstream_put_ui(bs, new_val, bit_left);
253}
254
255static VAStatus
256encoder_create_config(struct vaapi_recorder *r)
257{
258 VAConfigAttrib attrib[2];
259 VAStatus status;
260
261 /* FIXME: should check if VAEntrypointEncSlice is supported */
262
263 /* FIXME: should check if specified attributes are supported */
264
265 attrib[0].type = VAConfigAttribRTFormat;
266 attrib[0].value = VA_RT_FORMAT_YUV420;
267
268 attrib[1].type = VAConfigAttribRateControl;
269 attrib[1].value = VA_RC_CQP;
270
271 status = vaCreateConfig(r->va_dpy, VAProfileH264Main,
272 VAEntrypointEncSlice, attrib, 2,
273 &r->encoder.cfg);
274 if (status != VA_STATUS_SUCCESS)
275 return status;
276
277 status = vaCreateContext(r->va_dpy, r->encoder.cfg,
278 r->width, r->height, VA_PROGRESSIVE, 0, 0,
279 &r->encoder.ctx);
280 if (status != VA_STATUS_SUCCESS) {
281 vaDestroyConfig(r->va_dpy, r->encoder.cfg);
282 return status;
283 }
284
285 return VA_STATUS_SUCCESS;
286}
287
288static void
289encoder_destroy_config(struct vaapi_recorder *r)
290{
291 vaDestroyContext(r->va_dpy, r->encoder.ctx);
292 vaDestroyConfig(r->va_dpy, r->encoder.cfg);
293}
294
295static void
296encoder_init_seq_parameters(struct vaapi_recorder *r)
297{
298 int width_in_mbs, height_in_mbs;
299 int frame_cropping_flag = 0;
300 int frame_crop_bottom_offset = 0;
301
302 width_in_mbs = (r->width + 15) / 16;
303 height_in_mbs = (r->height + 15) / 16;
304
305 r->encoder.param.seq.level_idc = 41;
306 r->encoder.param.seq.intra_period = r->encoder.intra_period;
307 r->encoder.param.seq.max_num_ref_frames = 4;
308 r->encoder.param.seq.picture_width_in_mbs = width_in_mbs;
309 r->encoder.param.seq.picture_height_in_mbs = height_in_mbs;
310 r->encoder.param.seq.seq_fields.bits.frame_mbs_only_flag = 1;
311
312 /* Tc = num_units_in_tick / time_scale */
313 r->encoder.param.seq.time_scale = 1800;
314 r->encoder.param.seq.num_units_in_tick = 15;
315
316 if (height_in_mbs * 16 - r->height > 0) {
317 frame_cropping_flag = 1;
318 frame_crop_bottom_offset = (height_in_mbs * 16 - r->height) / 2;
319 }
320
321 r->encoder.param.seq.frame_cropping_flag = frame_cropping_flag;
322 r->encoder.param.seq.frame_crop_bottom_offset = frame_crop_bottom_offset;
323
324 r->encoder.param.seq.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2;
325}
326
327static VABufferID
328encoder_update_seq_parameters(struct vaapi_recorder *r)
329{
330 VABufferID seq_buf;
331 VAStatus status;
332
333 status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
334 VAEncSequenceParameterBufferType,
335 sizeof(r->encoder.param.seq),
336 1, &r->encoder.param.seq,
337 &seq_buf);
338
339 if (status == VA_STATUS_SUCCESS)
340 return seq_buf;
341 else
342 return VA_INVALID_ID;
343}
344
345static void
346encoder_init_pic_parameters(struct vaapi_recorder *r)
347{
348 VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
349
350 pic->pic_init_qp = 0;
351
352 /* ENTROPY_MODE_CABAC */
353 pic->pic_fields.bits.entropy_coding_mode_flag = 1;
354
355 pic->pic_fields.bits.deblocking_filter_control_present_flag = 1;
356}
357
358static VABufferID
359encoder_update_pic_parameters(struct vaapi_recorder *r,
360 VABufferID output_buf)
361{
362 VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
363 VAStatus status;
364 VABufferID pic_param_buf;
365 VASurfaceID curr_pic, pic0;
366
367 curr_pic = r->encoder.reference_picture[r->frame_count % 2];
368 pic0 = r->encoder.reference_picture[(r->frame_count + 1) % 2];
369
370 pic->CurrPic.picture_id = curr_pic;
371 pic->CurrPic.TopFieldOrderCnt = r->frame_count * 2;
372 pic->ReferenceFrames[0].picture_id = pic0;
373 pic->ReferenceFrames[1].picture_id = r->encoder.reference_picture[2];
374 pic->ReferenceFrames[2].picture_id = VA_INVALID_ID;
375
376 pic->coded_buf = output_buf;
377 pic->frame_num = r->frame_count;
378
379 pic->pic_fields.bits.idr_pic_flag = (r->frame_count == 0);
380 pic->pic_fields.bits.reference_pic_flag = 1;
381
382 status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
383 VAEncPictureParameterBufferType,
384 sizeof(VAEncPictureParameterBufferH264), 1,
385 pic, &pic_param_buf);
386
387 if (status == VA_STATUS_SUCCESS)
388 return pic_param_buf;
389 else
390 return VA_INVALID_ID;
391}
392
393static VABufferID
394encoder_update_slice_parameter(struct vaapi_recorder *r, int slice_type)
395{
396 VABufferID slice_param_buf;
397 VAStatus status;
398
399 int width_in_mbs = (r->width + 15) / 16;
400 int height_in_mbs = (r->height + 15) / 16;
401
402 memset(&r->encoder.param.slice, 0, sizeof r->encoder.param.slice);
403
404 r->encoder.param.slice.num_macroblocks = width_in_mbs * height_in_mbs;
405 r->encoder.param.slice.slice_type = slice_type;
406
407 r->encoder.param.slice.slice_alpha_c0_offset_div2 = 2;
408 r->encoder.param.slice.slice_beta_offset_div2 = 2;
409
410 status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
411 VAEncSliceParameterBufferType,
412 sizeof(r->encoder.param.slice), 1,
413 &r->encoder.param.slice,
414 &slice_param_buf);
415
416 if (status == VA_STATUS_SUCCESS)
417 return slice_param_buf;
418 else
419 return VA_INVALID_ID;
420}
421
422static VABufferID
423encoder_update_misc_hdr_parameter(struct vaapi_recorder *r)
424{
425 VAEncMiscParameterBuffer *misc_param;
426 VAEncMiscParameterHRD *hrd;
427 VABufferID buffer;
428 VAStatus status;
429
430 int total_size =
431 sizeof(VAEncMiscParameterBuffer) +
432 sizeof(VAEncMiscParameterRateControl);
433
434 status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
435 VAEncMiscParameterBufferType, total_size,
436 1, NULL, &buffer);
437 if (status != VA_STATUS_SUCCESS)
438 return VA_INVALID_ID;
439
440 status = vaMapBuffer(r->va_dpy, buffer, (void **) &misc_param);
441 if (status != VA_STATUS_SUCCESS) {
442 vaDestroyBuffer(r->va_dpy, buffer);
443 return VA_INVALID_ID;
444 }
445
446 misc_param->type = VAEncMiscParameterTypeHRD;
447 hrd = (VAEncMiscParameterHRD *) misc_param->data;
448
449 hrd->initial_buffer_fullness = 0;
450 hrd->buffer_size = 0;
451
452 vaUnmapBuffer(r->va_dpy, buffer);
453
454 return buffer;
455}
456
457static int
458setup_encoder(struct vaapi_recorder *r)
459{
460 VAStatus status;
461
462 status = encoder_create_config(r);
463 if (status != VA_STATUS_SUCCESS) {
464 return -1;
465 }
466
467 status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
468 r->width, r->height,
469 r->encoder.reference_picture, 3,
470 NULL, 0);
471 if (status != VA_STATUS_SUCCESS) {
472 encoder_destroy_config(r);
473 return -1;
474 }
475
476 /* VAProfileH264Main */
477 r->encoder.constraint_set_flag |= (1 << 1); /* Annex A.2.2 */
478
479 r->encoder.output_size = r->width * r->height;
480
481 r->encoder.intra_period = 30;
482
483 encoder_init_seq_parameters(r);
484 encoder_init_pic_parameters(r);
485
486 return 0;
487}
488
489static void
490encoder_destroy(struct vaapi_recorder *r)
491{
492 vaDestroySurfaces(r->va_dpy, r->encoder.reference_picture, 3);
493
494 encoder_destroy_config(r);
495}
496
497static void
498nal_start_code_prefix(struct bitstream *bs)
499{
500 bitstream_put_ui(bs, 0x00000001, 32);
501}
502
503static void
504nal_header(struct bitstream *bs, int nal_ref_idc, int nal_unit_type)
505{
506 /* forbidden_zero_bit: 0 */
507 bitstream_put_ui(bs, 0, 1);
508
509 bitstream_put_ui(bs, nal_ref_idc, 2);
510 bitstream_put_ui(bs, nal_unit_type, 5);
511}
512
513static void
514rbsp_trailing_bits(struct bitstream *bs)
515{
516 bitstream_put_ui(bs, 1, 1);
517 bitstream_byte_aligning(bs, 0);
518}
519
520static void sps_rbsp(struct bitstream *bs,
521 VAEncSequenceParameterBufferH264 *seq,
522 int constraint_set_flag)
523{
524 int i;
525
526 bitstream_put_ui(bs, PROFILE_IDC_MAIN, 8);
527
528 /* constraint_set[0-3] flag */
529 for (i = 0; i < 4; i++) {
530 int set = (constraint_set_flag & (1 << i)) ? 1 : 0;
531 bitstream_put_ui(bs, set, 1);
532 }
533
534 /* reserved_zero_4bits */
535 bitstream_put_ui(bs, 0, 4);
536 bitstream_put_ui(bs, seq->level_idc, 8);
537 bitstream_put_ue(bs, seq->seq_parameter_set_id);
538
539 bitstream_put_ue(bs, seq->seq_fields.bits.log2_max_frame_num_minus4);
540 bitstream_put_ue(bs, seq->seq_fields.bits.pic_order_cnt_type);
541 bitstream_put_ue(bs,
542 seq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
543
544 bitstream_put_ue(bs, seq->max_num_ref_frames);
545
546 /* gaps_in_frame_num_value_allowed_flag */
547 bitstream_put_ui(bs, 0, 1);
548
549 /* pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 */
550 bitstream_put_ue(bs, seq->picture_width_in_mbs - 1);
551 bitstream_put_ue(bs, seq->picture_height_in_mbs - 1);
552
553 bitstream_put_ui(bs, seq->seq_fields.bits.frame_mbs_only_flag, 1);
554 bitstream_put_ui(bs, seq->seq_fields.bits.direct_8x8_inference_flag, 1);
555
556 bitstream_put_ui(bs, seq->frame_cropping_flag, 1);
557
558 if (seq->frame_cropping_flag) {
559 bitstream_put_ue(bs, seq->frame_crop_left_offset);
560 bitstream_put_ue(bs, seq->frame_crop_right_offset);
561 bitstream_put_ue(bs, seq->frame_crop_top_offset);
562 bitstream_put_ue(bs, seq->frame_crop_bottom_offset);
563 }
564
565 /* vui_parameters_present_flag */
566 bitstream_put_ui(bs, 1, 1);
567
568 /* aspect_ratio_info_present_flag */
569 bitstream_put_ui(bs, 0, 1);
570 /* overscan_info_present_flag */
571 bitstream_put_ui(bs, 0, 1);
572
573 /* video_signal_type_present_flag */
574 bitstream_put_ui(bs, 0, 1);
575 /* chroma_loc_info_present_flag */
576 bitstream_put_ui(bs, 0, 1);
577
578 /* timing_info_present_flag */
579 bitstream_put_ui(bs, 1, 1);
580 bitstream_put_ui(bs, seq->num_units_in_tick, 32);
581 bitstream_put_ui(bs, seq->time_scale, 32);
582 /* fixed_frame_rate_flag */
583 bitstream_put_ui(bs, 1, 1);
584
585 /* nal_hrd_parameters_present_flag */
586 bitstream_put_ui(bs, 0, 1);
587
588 /* vcl_hrd_parameters_present_flag */
589 bitstream_put_ui(bs, 0, 1);
590
591 /* low_delay_hrd_flag */
592 bitstream_put_ui(bs, 0, 1);
593
594 /* pic_struct_present_flag */
595 bitstream_put_ui(bs, 0, 1);
596 /* bitstream_restriction_flag */
597 bitstream_put_ui(bs, 0, 1);
598
599 rbsp_trailing_bits(bs);
600}
601
602static void pps_rbsp(struct bitstream *bs,
603 VAEncPictureParameterBufferH264 *pic)
604{
605 /* pic_parameter_set_id, seq_parameter_set_id */
606 bitstream_put_ue(bs, pic->pic_parameter_set_id);
607 bitstream_put_ue(bs, pic->seq_parameter_set_id);
608
609 bitstream_put_ui(bs, pic->pic_fields.bits.entropy_coding_mode_flag, 1);
610
611 /* pic_order_present_flag: 0 */
612 bitstream_put_ui(bs, 0, 1);
613
614 /* num_slice_groups_minus1 */
615 bitstream_put_ue(bs, 0);
616
617 bitstream_put_ue(bs, pic->num_ref_idx_l0_active_minus1);
618 bitstream_put_ue(bs, pic->num_ref_idx_l1_active_minus1);
619
620 bitstream_put_ui(bs, pic->pic_fields.bits.weighted_pred_flag, 1);
621 bitstream_put_ui(bs, pic->pic_fields.bits.weighted_bipred_idc, 2);
622
623 /* pic_init_qp_minus26, pic_init_qs_minus26, chroma_qp_index_offset */
624 bitstream_put_se(bs, pic->pic_init_qp - 26);
625 bitstream_put_se(bs, 0);
626 bitstream_put_se(bs, 0);
627
628 bitstream_put_ui(bs, pic->pic_fields.bits.deblocking_filter_control_present_flag, 1);
629
630 /* constrained_intra_pred_flag, redundant_pic_cnt_present_flag */
631 bitstream_put_ui(bs, 0, 1);
632 bitstream_put_ui(bs, 0, 1);
633
634 bitstream_put_ui(bs, pic->pic_fields.bits.transform_8x8_mode_flag, 1);
635
636 /* pic_scaling_matrix_present_flag */
637 bitstream_put_ui(bs, 0, 1);
638 bitstream_put_se(bs, pic->second_chroma_qp_index_offset );
639
640 rbsp_trailing_bits(bs);
641}
642
643static int
644build_packed_pic_buffer(struct vaapi_recorder *r,
645 void **header_buffer)
646{
647 struct bitstream bs;
648
649 bitstream_start(&bs);
650 nal_start_code_prefix(&bs);
651 nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS);
652 pps_rbsp(&bs, &r->encoder.param.pic);
653 bitstream_end(&bs);
654
655 *header_buffer = bs.buffer;
656 return bs.bit_offset;
657}
658
659static int
660build_packed_seq_buffer(struct vaapi_recorder *r,
661 void **header_buffer)
662{
663 struct bitstream bs;
664
665 bitstream_start(&bs);
666 nal_start_code_prefix(&bs);
667 nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS);
668 sps_rbsp(&bs, &r->encoder.param.seq, r->encoder.constraint_set_flag);
669 bitstream_end(&bs);
670
671 *header_buffer = bs.buffer;
672 return bs.bit_offset;
673}
674
675static int
676create_packed_header_buffers(struct vaapi_recorder *r, VABufferID *buffers,
677 VAEncPackedHeaderType type,
678 void *data, int bit_length)
679{
680 VAEncPackedHeaderParameterBuffer packed_header;
681 VAStatus status;
682
683 packed_header.type = type;
684 packed_header.bit_length = bit_length;
685 packed_header.has_emulation_bytes = 0;
686
687 status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
688 VAEncPackedHeaderParameterBufferType,
689 sizeof packed_header, 1, &packed_header,
690 &buffers[0]);
691 if (status != VA_STATUS_SUCCESS)
692 return 0;
693
694 status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
695 VAEncPackedHeaderDataBufferType,
696 (bit_length + 7) / 8, 1, data, &buffers[1]);
697 if (status != VA_STATUS_SUCCESS) {
698 vaDestroyBuffer(r->va_dpy, buffers[0]);
699 return 0;
700 }
701
702 return 2;
703}
704
705static int
706encoder_prepare_headers(struct vaapi_recorder *r, VABufferID *buffers)
707{
708 VABufferID *p;
709
710 int bit_length;
711 void *data;
712
713 p = buffers;
714
715 bit_length = build_packed_seq_buffer(r, &data);
716 p += create_packed_header_buffers(r, p, VAEncPackedHeaderSequence,
717 data, bit_length);
718 free(data);
719
720 bit_length = build_packed_pic_buffer(r, &data);
721 p += create_packed_header_buffers(r, p, VAEncPackedHeaderPicture,
722 data, bit_length);
723 free(data);
724
725 return p - buffers;
726}
727
728static VAStatus
729encoder_render_picture(struct vaapi_recorder *r, VASurfaceID input,
730 VABufferID *buffers, int count)
731{
732 VAStatus status;
733
734 status = vaBeginPicture(r->va_dpy, r->encoder.ctx, input);
735 if (status != VA_STATUS_SUCCESS)
736 return status;
737
738 status = vaRenderPicture(r->va_dpy, r->encoder.ctx, buffers, count);
739 if (status != VA_STATUS_SUCCESS)
740 return status;
741
742 status = vaEndPicture(r->va_dpy, r->encoder.ctx);
743 if (status != VA_STATUS_SUCCESS)
744 return status;
745
746 return vaSyncSurface(r->va_dpy, input);
747}
748
749static VABufferID
750encoder_create_output_buffer(struct vaapi_recorder *r)
751{
752 VABufferID output_buf;
753 VAStatus status;
754
755 status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
756 VAEncCodedBufferType, r->encoder.output_size,
757 1, NULL, &output_buf);
758 if (status == VA_STATUS_SUCCESS)
759 return output_buf;
760 else
761 return VA_INVALID_ID;
762}
763
764static int
765encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf)
766{
767 VACodedBufferSegment *segment;
768 VAStatus status;
769 int count;
770
771 status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment);
772 if (status != VA_STATUS_SUCCESS)
773 return -1;
774
775 if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) {
776 r->encoder.output_size *= 2;
777 vaUnmapBuffer(r->va_dpy, output_buf);
778 return -1;
779 }
780
781 count = write(r->output_fd, segment->buf, segment->size);
782
783 vaUnmapBuffer(r->va_dpy, output_buf);
784
785 return count;
786}
787
788static void
789encoder_encode(struct vaapi_recorder *r, VASurfaceID input)
790{
791 VABufferID output_buf = VA_INVALID_ID;
792
793 VABufferID buffers[8];
794 int count = 0;
795
796 int slice_type;
797 int ret, i;
798
799 if ((r->frame_count % r->encoder.intra_period) == 0)
800 slice_type = SLICE_TYPE_I;
801 else
802 slice_type = SLICE_TYPE_P;
803
804 buffers[count++] = encoder_update_seq_parameters(r);
805 buffers[count++] = encoder_update_misc_hdr_parameter(r);
806 buffers[count++] = encoder_update_slice_parameter(r, slice_type);
807
808 for (i = 0; i < count; i++)
809 if (buffers[i] == VA_INVALID_ID)
810 goto bail;
811
812 if (r->frame_count == 0)
813 count += encoder_prepare_headers(r, buffers + count);
814
815 do {
816 output_buf = encoder_create_output_buffer(r);
817 if (output_buf == VA_INVALID_ID)
818 goto bail;
819
820 buffers[count++] =
821 encoder_update_pic_parameters(r, output_buf);
822 if (buffers[count - 1] == VA_INVALID_ID)
823 goto bail;
824
825 encoder_render_picture(r, input, buffers, count);
826 ret = encoder_write_output(r, output_buf);
827
828 vaDestroyBuffer(r->va_dpy, output_buf);
829 output_buf = VA_INVALID_ID;
830
831 vaDestroyBuffer(r->va_dpy, buffers[--count]);
832 } while (ret < 0);
833
834 for (i = 0; i < count; i++)
835 vaDestroyBuffer(r->va_dpy, buffers[i]);
836
837 r->frame_count++;
838 return;
839
840bail:
841 for (i = 0; i < count; i++)
842 vaDestroyBuffer(r->va_dpy, buffers[i]);
843 if (output_buf != VA_INVALID_ID)
844 vaDestroyBuffer(r->va_dpy, output_buf);
845}
846
847
848static int
849setup_vpp(struct vaapi_recorder *r)
850{
851 VAStatus status;
852
853 status = vaCreateConfig(r->va_dpy, VAProfileNone,
854 VAEntrypointVideoProc, NULL, 0,
855 &r->vpp.cfg);
856 if (status != VA_STATUS_SUCCESS) {
857 weston_log("vaapi: failed to create VPP config\n");
858 return -1;
859 }
860
861 status = vaCreateContext(r->va_dpy, r->vpp.cfg, r->width, r->height,
862 0, NULL, 0, &r->vpp.ctx);
863 if (status != VA_STATUS_SUCCESS) {
864 weston_log("vaapi: failed to create VPP context\n");
865 goto err_cfg;
866 }
867
868 status = vaCreateBuffer(r->va_dpy, r->vpp.ctx,
869 VAProcPipelineParameterBufferType,
870 sizeof(VAProcPipelineParameterBuffer),
871 1, NULL, &r->vpp.pipeline_buf);
872 if (status != VA_STATUS_SUCCESS) {
873 weston_log("vaapi: failed to create VPP pipeline buffer\n");
874 goto err_ctx;
875 }
876
877 status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
878 r->width, r->height, &r->vpp.output, 1,
879 NULL, 0);
880 if (status != VA_STATUS_SUCCESS) {
881 weston_log("vaapi: failed to create YUV surface\n");
882 goto err_buf;
883 }
884
885 return 0;
886
887err_buf:
888 vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
889err_ctx:
890 vaDestroyConfig(r->va_dpy, r->vpp.ctx);
891err_cfg:
892 vaDestroyConfig(r->va_dpy, r->vpp.cfg);
893
894 return -1;
895}
896
897static void
898vpp_destroy(struct vaapi_recorder *r)
899{
900 vaDestroySurfaces(r->va_dpy, &r->vpp.output, 1);
901 vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
902 vaDestroyConfig(r->va_dpy, r->vpp.ctx);
903 vaDestroyConfig(r->va_dpy, r->vpp.cfg);
904}
905
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +0300906static int
907setup_worker_thread(struct vaapi_recorder *r)
908{
909 pthread_mutex_init(&r->mutex, NULL);
910 pthread_cond_init(&r->input_cond, NULL);
911 pthread_create(&r->worker_thread, NULL, worker_thread_function, r);
912
913 return 1;
914}
915
916static void
917destroy_worker_thread(struct vaapi_recorder *r)
918{
919 pthread_mutex_lock(&r->mutex);
920
921 /* Make sure the worker thread finishes */
922 r->destroying = 1;
923 pthread_cond_signal(&r->input_cond);
924
925 pthread_mutex_unlock(&r->mutex);
926
927 pthread_join(r->worker_thread, NULL);
928
929 pthread_mutex_destroy(&r->mutex);
930 pthread_cond_destroy(&r->input_cond);
931}
932
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300933struct vaapi_recorder *
934vaapi_recorder_create(int drm_fd, int width, int height, const char *filename)
935{
936 struct vaapi_recorder *r;
937 VAStatus status;
938 int major, minor;
939 int flags;
940
941 r = calloc(1, sizeof *r);
942 if (!r)
943 return NULL;
944
945 r->width = width;
946 r->height = height;
Ander Conselvan de Oliveiraa62ef3e2013-09-06 17:49:38 +0300947 r->drm_fd = drm_fd;
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300948
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +0300949 if (setup_worker_thread(r) < 0)
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300950 goto err_free;
951
Kristian Høgsberga2e20c32013-10-09 13:48:09 -0700952 flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
953 r->output_fd = open(filename, flags, 0644);
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +0300954 if (r->output_fd < 0)
955 goto err_thread;
956
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300957 r->va_dpy = vaGetDisplayDRM(drm_fd);
958 if (!r->va_dpy) {
959 weston_log("failed to create VA display\n");
960 goto err_fd;
961 }
962
963 status = vaInitialize(r->va_dpy, &major, &minor);
964 if (status != VA_STATUS_SUCCESS) {
965 weston_log("vaapi: failed to initialize display\n");
966 goto err_fd;
967 }
968
969 if (setup_vpp(r) < 0) {
970 weston_log("vaapi: failed to initialize VPP pipeline\n");
971 goto err_va_dpy;
972 }
973
974 if (setup_encoder(r) < 0) {
975 goto err_vpp;
976 }
977
978 return r;
979
980err_vpp:
981 vpp_destroy(r);
982err_va_dpy:
983 vaTerminate(r->va_dpy);
984err_fd:
985 close(r->output_fd);
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +0300986err_thread:
987 destroy_worker_thread(r);
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300988err_free:
989 free(r);
990
991 return NULL;
992}
993
994void
995vaapi_recorder_destroy(struct vaapi_recorder *r)
996{
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +0300997 destroy_worker_thread(r);
998
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +0300999 encoder_destroy(r);
1000 vpp_destroy(r);
1001
1002 vaTerminate(r->va_dpy);
1003
1004 close(r->output_fd);
Ander Conselvan de Oliveiraa62ef3e2013-09-06 17:49:38 +03001005 close(r->drm_fd);
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +03001006
1007 free(r);
1008}
1009
1010static VAStatus
1011create_surface_from_fd(struct vaapi_recorder *r, int prime_fd,
1012 int stride, VASurfaceID *surface)
1013{
1014 VASurfaceAttrib va_attribs[2];
1015 VASurfaceAttribExternalBuffers va_attrib_extbuf;
1016 VAStatus status;
1017
1018 unsigned long buffer_fd = prime_fd;
1019
1020 va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX;
1021 va_attrib_extbuf.width = r->width;
1022 va_attrib_extbuf.height = r->height;
1023 va_attrib_extbuf.data_size = r->height * stride;
1024 va_attrib_extbuf.num_planes = 1;
1025 va_attrib_extbuf.pitches[0] = stride;
1026 va_attrib_extbuf.offsets[0] = 0;
1027 va_attrib_extbuf.buffers = &buffer_fd;
1028 va_attrib_extbuf.num_buffers = 1;
1029 va_attrib_extbuf.flags = 0;
1030 va_attrib_extbuf.private_data = NULL;
1031
1032 va_attribs[0].type = VASurfaceAttribMemoryType;
1033 va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
1034 va_attribs[0].value.type = VAGenericValueTypeInteger;
1035 va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
1036
1037 va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor;
1038 va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
1039 va_attribs[1].value.type = VAGenericValueTypePointer;
1040 va_attribs[1].value.value.p = &va_attrib_extbuf;
1041
1042 status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_RGB32,
1043 r->width, r->height, surface, 1,
1044 va_attribs, 2);
1045
1046 return status;
1047}
1048
1049static VAStatus
1050convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface)
1051{
1052 VAProcPipelineParameterBuffer *pipeline_param;
1053 VAStatus status;
1054
1055 status = vaMapBuffer(r->va_dpy, r->vpp.pipeline_buf,
1056 (void **) &pipeline_param);
1057 if (status != VA_STATUS_SUCCESS)
1058 return status;
1059
1060 memset(pipeline_param, 0, sizeof *pipeline_param);
1061
1062 pipeline_param->surface = rgb_surface;
1063 pipeline_param->surface_color_standard = VAProcColorStandardNone;
1064
1065 pipeline_param->output_background_color = 0xff000000;
1066 pipeline_param->output_color_standard = VAProcColorStandardNone;
1067
1068 status = vaUnmapBuffer(r->va_dpy, r->vpp.pipeline_buf);
1069 if (status != VA_STATUS_SUCCESS)
1070 return status;
1071
1072 status = vaBeginPicture(r->va_dpy, r->vpp.ctx, r->vpp.output);
1073 if (status != VA_STATUS_SUCCESS)
1074 return status;
1075
1076 status = vaRenderPicture(r->va_dpy, r->vpp.ctx,
1077 &r->vpp.pipeline_buf, 1);
1078 if (status != VA_STATUS_SUCCESS)
1079 return status;
1080
1081 status = vaEndPicture(r->va_dpy, r->vpp.ctx);
1082 if (status != VA_STATUS_SUCCESS)
1083 return status;
1084
1085 return status;
1086}
1087
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +03001088static void
1089recorder_frame(struct vaapi_recorder *r)
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +03001090{
1091 VASurfaceID rgb_surface;
1092 VAStatus status;
1093
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +03001094 status = create_surface_from_fd(r, r->input.prime_fd,
1095 r->input.stride, &rgb_surface);
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +03001096 if (status != VA_STATUS_SUCCESS) {
1097 weston_log("[libva recorder] "
1098 "failed to create surface from bo\n");
1099 return;
1100 }
1101
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +03001102 close(r->input.prime_fd);
1103
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +03001104 status = convert_rgb_to_yuv(r, rgb_surface);
1105 if (status != VA_STATUS_SUCCESS) {
1106 weston_log("[libva recorder] "
1107 "color space conversion failed\n");
1108 return;
1109 }
1110
1111 encoder_encode(r, r->vpp.output);
1112
1113 vaDestroySurfaces(r->va_dpy, &rgb_surface, 1);
1114}
1115
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +03001116static void *
1117worker_thread_function(void *data)
1118{
1119 struct vaapi_recorder *r = data;
Ander Conselvan de Oliveira6aae4d32013-08-23 17:15:48 +03001120
Ander Conselvan de Oliveirab85ded02013-09-06 17:49:37 +03001121 pthread_mutex_lock(&r->mutex);
1122
1123 while (!r->destroying) {
1124 if (!r->input.valid)
1125 pthread_cond_wait(&r->input_cond, &r->mutex);
1126
1127 /* If the thread is awaken by destroy_worker_thread(),
1128 * there might not be valid input */
1129 if (!r->input.valid)
1130 continue;
1131
1132 recorder_frame(r);
1133 r->input.valid = 0;
1134 }
1135
1136 pthread_mutex_unlock(&r->mutex);
1137
1138 return NULL;
1139}
1140
1141void
1142vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride)
1143{
1144 pthread_mutex_lock(&r->mutex);
1145
1146 /* The mutex is never released while encoding, so this point should
1147 * never be reached if input.valid is true. */
1148 assert(!r->input.valid);
1149
1150 r->input.prime_fd = prime_fd;
1151 r->input.stride = stride;
1152 r->input.valid = 1;
1153 pthread_cond_signal(&r->input_cond);
1154
1155 pthread_mutex_unlock(&r->mutex);
1156}