summaryrefslogtreecommitdiff
path: root/encode2.c
diff options
context:
space:
mode:
authorMikhail Burakov <mburakov@mailbox.org>2023-05-29 12:14:39 +0200
committerMikhail Burakov <mburakov@mailbox.org>2023-05-29 12:16:01 +0200
commitd869207ed9fc54268d84b0c07c22ec14910c9333 (patch)
tree87dabc4b439e93cced4f113e7e986103a30ab12a /encode2.c
parent6ac7270839edcb8d839f2e40debbaeadf69063c5 (diff)
Entirely replace older encode implementation with a new one
Diffstat (limited to 'encode2.c')
-rw-r--r--encode2.c833
1 files changed, 0 insertions, 833 deletions
diff --git a/encode2.c b/encode2.c
deleted file mode 100644
index 309e7c4..0000000
--- a/encode2.c
+++ /dev/null
@@ -1,833 +0,0 @@
-/*
- * Copyright (C) 2023 Mikhail Burakov. This file is part of streamer.
- *
- * streamer is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * streamer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with streamer. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <va/va.h>
-#include <va/va_drm.h>
-#include <va/va_drmcommon.h>
-
-#include "bitstream.h"
-#include "encode.h"
-#include "gpu.h"
-#include "hevc.h"
-#include "toolbox/utils.h"
-
-struct EncodeContext {
- struct GpuContext* gpu_context;
- uint32_t width;
- uint32_t height;
- enum YuvColorspace colorspace;
- enum YuvRange range;
-
- int render_node;
- VADisplay va_display;
- VAConfigID va_config_id;
-
- struct {
- bool packed_header_sequence;
- bool packed_header_slice;
- } codec_quirks;
-
- VAContextID va_context_id;
- VASurfaceID input_surface_id;
- struct GpuFrame* gpu_frame;
-
- VASurfaceID recon_surface_ids[2];
- VABufferID output_buffer_id;
-
- VAEncSequenceParameterBufferHEVC seq;
- VAEncPictureParameterBufferHEVC pic;
- VAEncSliceParameterBufferHEVC slice;
- size_t frame_counter;
-};
-
-static const char* VaErrorString(VAStatus error) {
- static const char* va_error_strings[] = {
- "VA_STATUS_SUCCESS",
- "VA_STATUS_ERROR_OPERATION_FAILED",
- "VA_STATUS_ERROR_ALLOCATION_FAILED",
- "VA_STATUS_ERROR_INVALID_DISPLAY",
- "VA_STATUS_ERROR_INVALID_CONFIG",
- "VA_STATUS_ERROR_INVALID_CONTEXT",
- "VA_STATUS_ERROR_INVALID_SURFACE",
- "VA_STATUS_ERROR_INVALID_BUFFER",
- "VA_STATUS_ERROR_INVALID_IMAGE",
- "VA_STATUS_ERROR_INVALID_SUBPICTURE",
- "VA_STATUS_ERROR_ATTR_NOT_SUPPORTED",
- "VA_STATUS_ERROR_MAX_NUM_EXCEEDED",
- "VA_STATUS_ERROR_UNSUPPORTED_PROFILE",
- "VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT",
- "VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT",
- "VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE",
- "VA_STATUS_ERROR_SURFACE_BUSY",
- "VA_STATUS_ERROR_FLAG_NOT_SUPPORTED",
- "VA_STATUS_ERROR_INVALID_PARAMETER",
- "VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED",
- "VA_STATUS_ERROR_UNIMPLEMENTED",
- "VA_STATUS_ERROR_SURFACE_IN_DISPLAYING",
- "VA_STATUS_ERROR_INVALID_IMAGE_FORMAT",
- "VA_STATUS_ERROR_DECODING_ERROR",
- "VA_STATUS_ERROR_ENCODING_ERROR",
- "VA_STATUS_ERROR_INVALID_VALUE",
- "???",
- "???",
- "???",
- "???",
- "???",
- "???",
- "VA_STATUS_ERROR_UNSUPPORTED_FILTER",
- "VA_STATUS_ERROR_INVALID_FILTER_CHAIN",
- "VA_STATUS_ERROR_HW_BUSY",
- "???",
- "VA_STATUS_ERROR_UNSUPPORTED_MEMORY_TYPE",
- "VA_STATUS_ERROR_NOT_ENOUGH_BUFFER",
- "VA_STATUS_ERROR_TIMEDOUT",
- };
- return VA_STATUS_SUCCESS <= error && error <= VA_STATUS_ERROR_TIMEDOUT
- ? va_error_strings[error - VA_STATUS_SUCCESS]
- : "???";
-}
-
-static void OnVaLogMessage(void* context, const char* message) {
- (void)context;
- size_t len = strlen(message);
- while (message[len - 1] == '\n') len--;
- LOG("%.*s", (int)len, message);
-}
-
-static bool InitializeCodecQuirks(struct EncodeContext* encode_context) {
- bool result = false;
- VAProfile dummy_profile;
- VAEntrypoint dummy_entrypoint;
- int num_attribs = vaMaxNumConfigAttributes(encode_context->va_display);
- VAConfigAttrib* attrib_list =
- malloc((size_t)num_attribs * sizeof(VAConfigAttrib));
- VAStatus status = vaQueryConfigAttributes(
- encode_context->va_display, encode_context->va_config_id, &dummy_profile,
- &dummy_entrypoint, attrib_list, &num_attribs);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to query va config attributes (%s)", VaErrorString(status));
- goto rollback_attrib_list;
- }
-
- for (int i = 0; i < num_attribs; i++) {
- if (attrib_list[i].type == VAConfigAttribEncPackedHeaders) {
- encode_context->codec_quirks.packed_header_sequence =
- !!(attrib_list[i].value & VA_ENC_PACKED_HEADER_SEQUENCE);
- encode_context->codec_quirks.packed_header_slice =
- !!(attrib_list[i].value & VA_ENC_PACKED_HEADER_SLICE);
- }
- }
- result = true;
-
-rollback_attrib_list:
- free(attrib_list);
- return result;
-}
-
-static struct GpuFrame* VaSurfaceToGpuFrame(VADisplay va_display,
- VASurfaceID va_surface_id,
- struct GpuContext* gpu_context) {
- VADRMPRIMESurfaceDescriptor prime;
- VAStatus status = vaExportSurfaceHandle(
- va_display, va_surface_id, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
- VA_EXPORT_SURFACE_WRITE_ONLY | VA_EXPORT_SURFACE_COMPOSED_LAYERS, &prime);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to export va surface (%s)", VaErrorString(status));
- return NULL;
- }
-
- struct GpuFramePlane planes[] = {{.dmabuf_fd = -1},
- {.dmabuf_fd = -1},
- {.dmabuf_fd = -1},
- {.dmabuf_fd = -1}};
- static_assert(LENGTH(planes) == LENGTH(prime.layers[0].object_index),
- "Suspicious VADRMPRIMESurfaceDescriptor structure");
-
- for (size_t i = 0; i < prime.layers[0].num_planes; i++) {
- uint32_t object_index = prime.layers[0].object_index[i];
- planes[i] = (struct GpuFramePlane){
- .dmabuf_fd = prime.objects[object_index].fd,
- .pitch = prime.layers[0].pitch[i],
- .offset = prime.layers[0].offset[i],
- .modifier = prime.objects[object_index].drm_format_modifier,
- };
- }
-
- struct GpuFrame* gpu_frame =
- GpuContextCreateFrame(gpu_context, prime.width, prime.height,
- prime.fourcc, prime.layers[0].num_planes, planes);
- if (!gpu_frame) {
- LOG("Failed to create gpu frame");
- goto release_planes;
- }
- return gpu_frame;
-
-release_planes:
- CloseUniqueFds((int[]){planes[0].dmabuf_fd, planes[1].dmabuf_fd,
- planes[2].dmabuf_fd, planes[3].dmabuf_fd});
- return NULL;
-}
-
-static void InitializeSeqHeader(struct EncodeContext* encode_context,
- uint16_t pic_width_in_luma_samples,
- uint16_t pic_height_in_luma_samples) {
- encode_context->seq = (VAEncSequenceParameterBufferHEVC){
- .general_profile_idc = 1, // Main profile
- .general_level_idc = 120, // Level 4
- .general_tier_flag = 0, // Main tier
-
- .intra_period = 120, // Where this one comes from?
- .intra_idr_period = 120, // Each I frame is an IDR frame
- .ip_period = 1, // No B-frames
- .bits_per_second = 0, // TODO (investigate)
-
- .pic_width_in_luma_samples = pic_width_in_luma_samples,
- .pic_height_in_luma_samples = pic_height_in_luma_samples,
-
- .seq_fields.bits =
- {
- .chroma_format_idc = 1, // 4:2:0
- .separate_colour_plane_flag = 0, // Table 6-1
- .bit_depth_luma_minus8 = 0, // 8 bpp luma
- .bit_depth_chroma_minus8 = 0, // 8 bpp chroma
- .scaling_list_enabled_flag = 0, // defaulted
- .strong_intra_smoothing_enabled_flag = 0, // defaulted
-
- // mburakov: ffmpeg hardcodes these for i965 Skylake driver.
- .amp_enabled_flag = 1, // TODO (quirks)
- .sample_adaptive_offset_enabled_flag = 0, // TODO (quirks)
- .pcm_enabled_flag = 0, // TODO (quirks)
- .pcm_loop_filter_disabled_flag = 0, // defaulted
- .sps_temporal_mvp_enabled_flag = 0, // TODO (quirks)
-
- .low_delay_seq = 1, // No B-frames
- .hierachical_flag = 0, // defaulted
- },
-
- // mburakov: ffmpeg hardcodes these for i965 Skylake driver.
- .log2_min_luma_coding_block_size_minus3 = 0, // TODO (quirks)
- .log2_diff_max_min_luma_coding_block_size = 2, // TODO (quirks)
- .log2_min_transform_block_size_minus2 = 0, // hardcoded
- .log2_diff_max_min_transform_block_size = 3, // hardcoded
- .max_transform_hierarchy_depth_inter = 3, // hardcoded
- .max_transform_hierarchy_depth_intra = 3, // hardcoded
-
- .pcm_sample_bit_depth_luma_minus1 = 0, // defaulted
- .pcm_sample_bit_depth_chroma_minus1 = 0, // defaulted
- .log2_min_pcm_luma_coding_block_size_minus3 = 0, // defaulted
- .log2_max_pcm_luma_coding_block_size_minus3 = 0, // defaulted
-
- .vui_parameters_present_flag = 1,
- .vui_fields.bits =
- {
- .aspect_ratio_info_present_flag = 0, // defaulted
- .neutral_chroma_indication_flag = 0, // defaulted
- .field_seq_flag = 0, // defaulted
- .vui_timing_info_present_flag = 1, // hardcoded
- .bitstream_restriction_flag = 1, // hardcoded
- .tiles_fixed_structure_flag = 0, // defaulted
- .motion_vectors_over_pic_boundaries_flag = 1, // hardcoded
- .restricted_ref_pic_lists_flag = 1, // hardcoded
- .log2_max_mv_length_horizontal = 15, // hardcoded
- .log2_max_mv_length_vertical = 15, // hardcoded
- },
-
- .vui_num_units_in_tick = 1, // TODO (investigate)
- .vui_time_scale = 60, // TODO (investigate)
- .min_spatial_segmentation_idc = 0, // defaulted
- .max_bytes_per_pic_denom = 0, // hardcoded
- .max_bits_per_min_cu_denom = 0, // hardcoded
-
- .scc_fields.bits =
- {
- .palette_mode_enabled_flag = 0, // defaulted
- },
- };
-}
-
-static void InitializePicHeader(struct EncodeContext* encode_context) {
- const typeof(encode_context->seq.seq_fields.bits)* seq_bits =
- &encode_context->seq.seq_fields.bits;
-
- uint8_t collocated_ref_pic_index =
- seq_bits->sps_temporal_mvp_enabled_flag ? 0 : 0xff;
-
- encode_context->pic = (VAEncPictureParameterBufferHEVC){
- .decoded_curr_pic =
- {
- .picture_id = VA_INVALID_ID, // dynamic
- .flags = VA_PICTURE_HEVC_INVALID, // dynamic
- },
-
- // .reference_frames[15],
-
- .coded_buf = encode_context->output_buffer_id,
- .collocated_ref_pic_index = collocated_ref_pic_index,
- .last_picture = 0, // hardcoded
-
- .pic_init_qp = 30, // Fixed quality
- .diff_cu_qp_delta_depth = 0, // Fixed quality
- .pps_cb_qp_offset = 0, // hardcoded
- .pps_cr_qp_offset = 0, // hardcoded
-
- .num_tile_columns_minus1 = 0, // No tiles
- .num_tile_rows_minus1 = 0, // No tiles
- .column_width_minus1 = {0}, // No tiles
- .row_height_minus1 = {0}, // No tiles
-
- .log2_parallel_merge_level_minus2 = 0, // defaulted
- .ctu_max_bitsize_allowed = 0, // hardcoded
- .num_ref_idx_l0_default_active_minus1 = 0, // hardcoded
- .num_ref_idx_l1_default_active_minus1 = 0, // hardcoded
- .slice_pic_parameter_set_id = 0, // hardcoded
- .nal_unit_type = 0, // dynamic
-
- .pic_fields.bits =
- {
- .idr_pic_flag = 0, // dynamic
- .coding_type = 0, // dynamic
- .reference_pic_flag = 1, // No B-frames
-
- .dependent_slice_segments_enabled_flag = 0, // defaulted
- .sign_data_hiding_enabled_flag = 0, // defaulted
- .constrained_intra_pred_flag = 0, // defaulted
- .transform_skip_enabled_flag = 0, // TODO (quirks)
- .cu_qp_delta_enabled_flag = 0, // Fixed quality
- .weighted_pred_flag = 0, // defaulted
- .weighted_bipred_flag = 0, // defaulted
- .transquant_bypass_enabled_flag = 0, // defaulted
- .tiles_enabled_flag = 0, // No tiles
- .entropy_coding_sync_enabled_flag = 0, // defaulted
- .loop_filter_across_tiles_enabled_flag = 0, // No tiles
-
- .pps_loop_filter_across_slices_enabled_flag = 1, // hardcoded
- .scaling_list_data_present_flag = 0, // defaulted
-
- .screen_content_flag = 0, // TODO (investigate)
- .enable_gpu_weighted_prediction = 0, // hardcoded
- .no_output_of_prior_pics_flag = 0, // hardcoded
- },
-
- .hierarchical_level_plus1 = 0, // defaulted
- .scc_fields.bits =
- {
- .pps_curr_pic_ref_enabled_flag = 0, // defaulted
- },
- };
-
- for (size_t i = 0; i < LENGTH(encode_context->pic.reference_frames); i++) {
- encode_context->pic.reference_frames[i] = (VAPictureHEVC){
- .picture_id = VA_INVALID_ID,
- .flags = VA_PICTURE_HEVC_INVALID,
- };
- }
-}
-
-static void InitializeSliceHeader(struct EncodeContext* encode_context,
- uint32_t num_ctu_in_slice) {
- const typeof(encode_context->seq.seq_fields.bits)* seq_bits =
- &encode_context->seq.seq_fields.bits;
-
- encode_context->slice = (VAEncSliceParameterBufferHEVC){
- .slice_segment_address = 0, // No slice segments
- .num_ctu_in_slice = num_ctu_in_slice,
-
- .slice_type = 0, // dynamic
- .slice_pic_parameter_set_id =
- encode_context->pic.slice_pic_parameter_set_id,
-
- .num_ref_idx_l0_active_minus1 =
- encode_context->pic.num_ref_idx_l0_default_active_minus1,
- .num_ref_idx_l1_active_minus1 =
- encode_context->pic.num_ref_idx_l1_default_active_minus1,
-
- .luma_log2_weight_denom = 0, // defaulted
- .delta_chroma_log2_weight_denom = 0, // defaulted
-
- // .delta_luma_weight_l0[15],
- // .luma_offset_l0[15],
- // .delta_chroma_weight_l0[15][2],
- // .chroma_offset_l0[15][2],
- // .delta_luma_weight_l1[15],
- // .luma_offset_l1[15],
- // .delta_chroma_weight_l1[15][2],
- // .chroma_offset_l1[15][2],
-
- .max_num_merge_cand = 5, // defaulted
- .slice_qp_delta = 0, // Fixed quality
- .slice_cb_qp_offset = 0, // defaulted
- .slice_cr_qp_offset = 0, // defaulted
-
- .slice_beta_offset_div2 = 0, // defaulted
- .slice_tc_offset_div2 = 0, // defaulted
-
- .slice_fields.bits =
- {
- .last_slice_of_pic_flag = 1, // No slice segments
- .dependent_slice_segment_flag = 0, // No slice segments
- .colour_plane_id = 0, // defaulted
- .slice_temporal_mvp_enabled_flag =
- seq_bits->sps_temporal_mvp_enabled_flag,
- .slice_sao_luma_flag =
- seq_bits->sample_adaptive_offset_enabled_flag,
- .slice_sao_chroma_flag =
- seq_bits->sample_adaptive_offset_enabled_flag,
- .num_ref_idx_active_override_flag = 0, // hardcoded
- .mvd_l1_zero_flag = 0, // defaulted
- .cabac_init_flag = 0, // defaulted
- .slice_deblocking_filter_disabled_flag = 0, // defaulted
- .slice_loop_filter_across_slices_enabled_flag = 0, // defaulted
- .collocated_from_l0_flag = 0, // No B-frames
- },
-
- .pred_weight_table_bit_offset = 0, // defaulted
- .pred_weight_table_bit_length = 0, // defaulted
- };
-
- for (size_t i = 0; i < LENGTH(encode_context->slice.ref_pic_list0); i++) {
- encode_context->slice.ref_pic_list0[i] = (VAPictureHEVC){
- .picture_id = VA_INVALID_ID,
- .flags = VA_PICTURE_HEVC_INVALID,
- };
- }
-
- for (size_t i = 0; i < LENGTH(encode_context->slice.ref_pic_list1); i++) {
- encode_context->slice.ref_pic_list1[i] = (VAPictureHEVC){
- .picture_id = VA_INVALID_ID,
- .flags = VA_PICTURE_HEVC_INVALID,
- };
- }
-}
-
-struct EncodeContext* EncodeContextCreate(struct GpuContext* gpu_context,
- uint32_t width, uint32_t height,
- enum YuvColorspace colorspace,
- enum YuvRange range) {
- struct EncodeContext* encode_context = malloc(sizeof(struct EncodeContext));
- if (!encode_context) {
- LOG("Faield to allocate encode context (%s)", strerror(errno));
- return NULL;
- }
-
- *encode_context = (struct EncodeContext){
- .gpu_context = gpu_context,
- .width = width,
- .height = height,
- .colorspace = colorspace,
- .range = range,
- };
-
- encode_context->render_node = open("/dev/dri/renderD128", O_RDWR);
- if (encode_context->render_node == -1) {
- LOG("Failed to open render node (%s)", strerror(errno));
- goto rollback_encode_context;
- }
-
- encode_context->va_display = vaGetDisplayDRM(encode_context->render_node);
- if (!encode_context->va_display) {
- LOG("Failed to get va display (%s)", strerror(errno));
- goto rollback_render_node;
- }
-
- vaSetErrorCallback(encode_context->va_display, OnVaLogMessage, NULL);
-#ifndef NDEBUG
- vaSetInfoCallback(encode_context->va_display, OnVaLogMessage, NULL);
-#endif // NDEBUG
-
- int major, minor;
- VAStatus status = vaInitialize(encode_context->va_display, &major, &minor);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to initialize va (%s)", VaErrorString(status));
- goto rollback_va_display;
- }
-
- LOG("Initialized VA %d.%d", major, minor);
- // TODO(mburakov): Check entry points?
-
- VAConfigAttrib attrib_list[] = {
- {.type = VAConfigAttribRTFormat, .value = VA_RT_FORMAT_YUV420},
- };
- status = vaCreateConfig(encode_context->va_display, VAProfileHEVCMain,
- VAEntrypointEncSlice, attrib_list,
- LENGTH(attrib_list), &encode_context->va_config_id);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to create va config (%s)", VaErrorString(status));
- goto rollback_va_display;
- }
-
- if (!InitializeCodecQuirks(encode_context)) {
- LOG("Failed to initialize codec quirks");
- goto rollback_va_config_id;
- }
-
- // TODO(mburakov): ffmpeg attempts to deduce this.
- static const uint32_t min_cb_size = 16;
- uint32_t width_in_cb = (width + min_cb_size - 1) / min_cb_size;
- uint32_t height_in_cb = (height + min_cb_size - 1) / min_cb_size;
-
- // TODO(mburakov): ffmpeg attempts to deduce this.
- static const uint32_t slice_block_size = 32;
- uint32_t slice_block_rows =
- (encode_context->width + slice_block_size - 1) / slice_block_size;
- uint32_t slice_block_cols =
- (encode_context->height + slice_block_size - 1) / slice_block_size;
- uint32_t num_ctu_in_slice = slice_block_rows * slice_block_cols;
-
- status = vaCreateContext(
- encode_context->va_display, encode_context->va_config_id,
- (int)(width_in_cb * min_cb_size), (int)(height_in_cb * min_cb_size),
- VA_PROGRESSIVE, NULL, 0, &encode_context->va_context_id);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to create va context (%s)", VaErrorString(status));
- goto rollback_va_config_id;
- }
-
- status =
- vaCreateSurfaces(encode_context->va_display, VA_RT_FORMAT_YUV420, width,
- height, &encode_context->input_surface_id, 1, NULL, 0);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to create va input surface (%s)", VaErrorString(status));
- goto rollback_va_context_id;
- }
-
- encode_context->gpu_frame = VaSurfaceToGpuFrame(
- encode_context->va_display, encode_context->input_surface_id,
- encode_context->gpu_context);
- if (!encode_context->gpu_frame) {
- LOG("Failed to convert va surface to gpu frame");
- goto rollback_input_surface_id;
- }
-
- status =
- vaCreateSurfaces(encode_context->va_display, VA_RT_FORMAT_YUV420,
- width_in_cb * min_cb_size, height_in_cb * min_cb_size,
- encode_context->recon_surface_ids,
- LENGTH(encode_context->recon_surface_ids), NULL, 0);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to create va recon surfaces (%s)", VaErrorString(status));
- goto rollback_gpu_frame;
- }
-
- unsigned int max_encoded_size =
- encode_context->width * encode_context->height * 3 / 2;
- status =
- vaCreateBuffer(encode_context->va_display, encode_context->va_context_id,
- VAEncCodedBufferType, max_encoded_size, 1, NULL,
- &encode_context->output_buffer_id);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to create va output buffer (%s)", VaErrorString(status));
- goto rollback_recon_surface_ids;
- }
-
- InitializeSeqHeader(encode_context, (uint16_t)(width_in_cb * min_cb_size),
- (uint16_t)(height_in_cb * min_cb_size));
- InitializePicHeader(encode_context);
- InitializeSliceHeader(encode_context, num_ctu_in_slice);
- return encode_context;
-
-rollback_recon_surface_ids:
- vaDestroySurfaces(encode_context->va_display,
- encode_context->recon_surface_ids,
- LENGTH(encode_context->recon_surface_ids));
-rollback_gpu_frame:
- GpuContextDestroyFrame(encode_context->gpu_context,
- encode_context->gpu_frame);
-rollback_input_surface_id:
- vaDestroySurfaces(encode_context->va_display,
- &encode_context->input_surface_id, 1);
-rollback_va_context_id:
- vaDestroyContext(encode_context->va_display, encode_context->va_config_id);
-rollback_va_config_id:
- vaDestroyConfig(encode_context->va_display, encode_context->va_config_id);
-rollback_va_display:
- vaTerminate(encode_context->va_display);
-rollback_render_node:
- close(encode_context->render_node);
-rollback_encode_context:
- free(encode_context);
- return NULL;
-}
-
-const struct GpuFrame* EncodeContextGetFrame(
- struct EncodeContext* encode_context) {
- return encode_context->gpu_frame;
-}
-
-static bool UploadBuffer(const struct EncodeContext* encode_context,
- VABufferType va_buffer_type, unsigned int size,
- void* data, VABufferID** presult) {
- VAStatus status =
- vaCreateBuffer(encode_context->va_display, encode_context->va_context_id,
- va_buffer_type, size, 1, data, *presult);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to create buffer (%s)", VaErrorString(status));
- return false;
- }
- (*presult)++;
- return true;
-}
-
-static bool UploadPackedBuffer(const struct EncodeContext* encode_context,
- VAEncPackedHeaderType packed_header_type,
- unsigned int bit_length, void* data,
- VABufferID** presult) {
- VAEncPackedHeaderParameterBuffer packed_header = {
- .type = packed_header_type,
- .bit_length = bit_length,
- .has_emulation_bytes = 1,
- };
- return UploadBuffer(encode_context, VAEncPackedHeaderParameterBufferType,
- sizeof(packed_header), &packed_header, presult) &&
- UploadBuffer(encode_context, VAEncPackedHeaderDataBufferType,
- (bit_length + 7) / 8, data, presult);
-}
-
-static void UpdatePicHeader(struct EncodeContext* encode_context, bool idr) {
- encode_context->pic.decoded_curr_pic = (VAPictureHEVC){
- .picture_id =
- encode_context
- ->recon_surface_ids[encode_context->frame_counter %
- LENGTH(encode_context->recon_surface_ids)],
- .pic_order_cnt = (int32_t)(encode_context->frame_counter %
- encode_context->seq.intra_idr_period),
- };
-
- if (idr) {
- encode_context->pic.reference_frames[0] = (VAPictureHEVC){
- .picture_id = VA_INVALID_ID,
- .flags = VA_PICTURE_HEVC_INVALID,
- };
- encode_context->pic.nal_unit_type = IDR_W_RADL;
- encode_context->pic.pic_fields.bits.idr_pic_flag = 1;
- encode_context->pic.pic_fields.bits.coding_type = 1;
- } else {
- encode_context->pic.reference_frames[0] = (VAPictureHEVC){
- .picture_id =
- encode_context
- ->recon_surface_ids[(encode_context->frame_counter - 1) %
- LENGTH(encode_context->recon_surface_ids)],
- .pic_order_cnt = (int32_t)((encode_context->frame_counter - 1) %
- encode_context->seq.intra_idr_period),
- };
- encode_context->pic.nal_unit_type = TRAIL_R;
- encode_context->pic.pic_fields.bits.idr_pic_flag = 0;
- encode_context->pic.pic_fields.bits.coding_type = 2;
- }
-}
-
-static bool DrainBuffers(int fd, struct iovec* iovec, int count) {
- for (;;) {
- ssize_t result = writev(fd, iovec, count);
- if (result < 0) {
- if (errno == EINTR) continue;
- LOG("Failed to write (%s)", strerror(errno));
- return false;
- }
- for (int i = 0; i < count; i++) {
- size_t delta = MIN((size_t)result, iovec[i].iov_len);
- iovec[i].iov_base = (uint8_t*)iovec[i].iov_base + delta;
- iovec[i].iov_len -= delta;
- result -= delta;
- }
- if (!result) return true;
- }
-}
-
-bool EncodeContextEncodeFrame(struct EncodeContext* encode_context, int fd) {
- bool result = false;
- VABufferID buffers[8];
- VABufferID* buffer_ptr = buffers;
-
- bool idr =
- !(encode_context->frame_counter % encode_context->seq.intra_idr_period);
- if (idr && !UploadBuffer(encode_context, VAEncSequenceParameterBufferType,
- sizeof(encode_context->seq), &encode_context->seq,
- &buffer_ptr)) {
- LOG("Failed to upload sequence parameter buffer");
- goto rollback_buffers;
- }
-
- if (encode_context->codec_quirks.packed_header_sequence && idr) {
- char buffer[256];
- struct Bitstream bitstream = {
- .data = buffer,
- .size = 0,
- };
-
- static const struct MoreVideoParameters mvp = {
- .vps_max_dec_pic_buffering_minus1 = 1, // No B-frames
- .vps_max_num_reorder_pics = 0, // No B-frames
- };
- uint32_t conf_win_right_offset_luma =
- encode_context->seq.pic_width_in_luma_samples - encode_context->width;
- uint32_t conf_win_bottom_offset_luma =
- encode_context->seq.pic_height_in_luma_samples - encode_context->height;
- const struct MoreSeqParameters msp = {
- .conf_win_left_offset = 0,
- .conf_win_right_offset = conf_win_right_offset_luma / 2,
- .conf_win_top_offset = 0,
- .conf_win_bottom_offset = conf_win_bottom_offset_luma / 2,
- .sps_max_dec_pic_buffering_minus1 = 1, // No B-frames
- .sps_max_num_reorder_pics = 0, // No B-frames
- .video_signal_type_present_flag = 1,
- .video_full_range_flag = encode_context->range == kFullRange,
- .colour_description_present_flag = 1,
- .colour_primaries = 2, // Unsepcified
- .transfer_characteristics = 2, // Unspecified
- .matrix_coeffs =
- encode_context->colorspace == kItuRec601 ? 6 : 1, // Table E.5
- };
-
- PackVideoParameterSetNalUnit(&bitstream, &encode_context->seq, &mvp);
- PackSeqParameterSetNalUnit(&bitstream, &encode_context->seq, &msp);
- PackPicParameterSetNalUnit(&bitstream, &encode_context->pic);
- if (!UploadPackedBuffer(encode_context, VAEncPackedHeaderSequence,
- (unsigned int)bitstream.size, bitstream.data,
- &buffer_ptr)) {
- LOG("Failed to upload packed sequence header");
- goto rollback_buffers;
- }
- }
-
- UpdatePicHeader(encode_context, idr);
- if (!UploadBuffer(encode_context, VAEncPictureParameterBufferType,
- sizeof(encode_context->pic), &encode_context->pic,
- &buffer_ptr)) {
- LOG("Failed to upload picture parameter buffer");
- goto rollback_buffers;
- }
-
- encode_context->slice.slice_type = idr ? I : P;
- encode_context->slice.ref_pic_list0[0] =
- encode_context->pic.reference_frames[0];
- if (encode_context->codec_quirks.packed_header_slice) {
- char buffer[256];
- struct Bitstream bitstream = {
- .data = buffer,
- .size = 0,
- };
- static const struct NegativePics negative_pics[] = {
- {
- .delta_poc_s0_minus1 = 0,
- .used_by_curr_pic_s0_flag = true,
- },
- };
- const struct MoreSliceParamerters msp = {
- .first_slice_segment_in_pic_flag = 1,
- .num_negative_pics = idr ? 0 : LENGTH(negative_pics),
- .negative_pics = idr ? NULL : negative_pics,
- };
- PackSliceSegmentHeaderNalUnit(&bitstream, &encode_context->seq,
- &encode_context->pic, &encode_context->slice,
- &msp);
- if (!UploadPackedBuffer(encode_context, VAEncPackedHeaderSlice,
- (unsigned int)bitstream.size, bitstream.data,
- &buffer_ptr)) {
- LOG("Failed to upload packed sequence header");
- goto rollback_buffers;
- }
- }
-
- if (!UploadBuffer(encode_context, VAEncSliceParameterBufferType,
- sizeof(encode_context->slice), &encode_context->slice,
- &buffer_ptr)) {
- LOG("Failed to upload slice parameter buffer");
- goto rollback_buffers;
- }
-
- VAStatus status =
- vaBeginPicture(encode_context->va_display, encode_context->va_context_id,
- encode_context->input_surface_id);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to begin va picture (%s)", VaErrorString(status));
- goto rollback_buffers;
- }
-
- int num_buffers = (int)(buffer_ptr - buffers);
- status = vaRenderPicture(encode_context->va_display,
- encode_context->va_context_id, buffers, num_buffers);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to render va picture (%s)", VaErrorString(status));
- goto rollback_buffers;
- }
-
- status =
- vaEndPicture(encode_context->va_display, encode_context->va_context_id);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to end va picture (%s)", VaErrorString(status));
- goto rollback_buffers;
- }
-
- status = vaSyncBuffer(encode_context->va_display,
- encode_context->output_buffer_id, VA_TIMEOUT_INFINITE);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to sync va buffer (%s)", VaErrorString(status));
- goto rollback_buffers;
- }
-
- VACodedBufferSegment* segment;
- status = vaMapBuffer(encode_context->va_display,
- encode_context->output_buffer_id, (void**)&segment);
- if (status != VA_STATUS_SUCCESS) {
- LOG("Failed to map va buffer (%s)", VaErrorString(status));
- goto rollback_buffers;
- }
- if (segment->next != NULL) {
- LOG("Next segment non-null!");
- abort();
- }
-
- struct iovec iovec[] = {
- {.iov_base = &segment->size, .iov_len = sizeof(segment->size)},
- {.iov_base = segment->buf, .iov_len = segment->size},
- };
- if (!DrainBuffers(fd, iovec, LENGTH(iovec))) {
- LOG("Failed to drain encoded frame");
- goto rollback_segment;
- }
-
- encode_context->frame_counter++;
- result = true;
-
-rollback_segment:
- vaUnmapBuffer(encode_context->va_display, encode_context->output_buffer_id);
-rollback_buffers:
- while (buffer_ptr-- > buffers)
- vaDestroyBuffer(encode_context->va_display, *buffer_ptr);
- return result;
-}
-
-void EncodeContextDestroy(struct EncodeContext* encode_context) {
- vaDestroyBuffer(encode_context->va_display, encode_context->output_buffer_id);
- GpuContextDestroyFrame(encode_context->gpu_context,
- encode_context->gpu_frame);
- vaDestroySurfaces(encode_context->va_display,
- &encode_context->input_surface_id, 1);
- vaDestroyContext(encode_context->va_display, encode_context->va_context_id);
- vaDestroyConfig(encode_context->va_display, encode_context->va_config_id);
- vaTerminate(encode_context->va_display);
- close(encode_context->render_node);
- free(encode_context);
-}