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