|
|
|
@@ -41,6 +41,16 @@ enum { |
|
|
|
}; |
|
|
|
|
|
|
|
typedef struct VAAPIEncodeH265Context { |
|
|
|
VAAPIEncodeContext common; |
|
|
|
|
|
|
|
// User options. |
|
|
|
int qp; |
|
|
|
int aud; |
|
|
|
int profile; |
|
|
|
int level; |
|
|
|
int sei; |
|
|
|
|
|
|
|
// Derived settings. |
|
|
|
unsigned int ctu_width; |
|
|
|
unsigned int ctu_height; |
|
|
|
|
|
|
|
@@ -48,16 +58,7 @@ typedef struct VAAPIEncodeH265Context { |
|
|
|
int fixed_qp_p; |
|
|
|
int fixed_qp_b; |
|
|
|
|
|
|
|
H265RawAUD aud; |
|
|
|
H265RawVPS vps; |
|
|
|
H265RawSPS sps; |
|
|
|
H265RawPPS pps; |
|
|
|
H265RawSlice slice; |
|
|
|
H265RawSEI sei; |
|
|
|
|
|
|
|
H265RawSEIMasteringDisplayColourVolume mastering_display; |
|
|
|
H265RawSEIContentLightLevelInfo content_light_level; |
|
|
|
|
|
|
|
// Stream state. |
|
|
|
int64_t last_idr_frame; |
|
|
|
int pic_order_cnt; |
|
|
|
|
|
|
|
@@ -65,27 +66,29 @@ typedef struct VAAPIEncodeH265Context { |
|
|
|
int slice_type; |
|
|
|
int pic_type; |
|
|
|
|
|
|
|
// Writer structures. |
|
|
|
H265RawAUD raw_aud; |
|
|
|
H265RawVPS raw_vps; |
|
|
|
H265RawSPS raw_sps; |
|
|
|
H265RawPPS raw_pps; |
|
|
|
H265RawSEI raw_sei; |
|
|
|
H265RawSlice raw_slice; |
|
|
|
|
|
|
|
H265RawSEIMasteringDisplayColourVolume sei_mastering_display; |
|
|
|
H265RawSEIContentLightLevelInfo sei_content_light_level; |
|
|
|
|
|
|
|
CodedBitstreamContext *cbc; |
|
|
|
CodedBitstreamFragment current_access_unit; |
|
|
|
int aud_needed; |
|
|
|
int sei_needed; |
|
|
|
} VAAPIEncodeH265Context; |
|
|
|
|
|
|
|
typedef struct VAAPIEncodeH265Options { |
|
|
|
int qp; |
|
|
|
int aud; |
|
|
|
int profile; |
|
|
|
int level; |
|
|
|
int sei; |
|
|
|
} VAAPIEncodeH265Options; |
|
|
|
|
|
|
|
|
|
|
|
static int vaapi_encode_h265_write_access_unit(AVCodecContext *avctx, |
|
|
|
char *data, size_t *data_len, |
|
|
|
CodedBitstreamFragment *au) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
int err; |
|
|
|
|
|
|
|
err = ff_cbs_write_fragment_data(priv->cbc, au); |
|
|
|
@@ -111,8 +114,7 @@ static int vaapi_encode_h265_add_nal(AVCodecContext *avctx, |
|
|
|
CodedBitstreamFragment *au, |
|
|
|
void *nal_unit) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
H265RawNALUnitHeader *header = nal_unit; |
|
|
|
int err; |
|
|
|
|
|
|
|
@@ -130,27 +132,26 @@ static int vaapi_encode_h265_add_nal(AVCodecContext *avctx, |
|
|
|
static int vaapi_encode_h265_write_sequence_header(AVCodecContext *avctx, |
|
|
|
char *data, size_t *data_len) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
CodedBitstreamFragment *au = &priv->current_access_unit; |
|
|
|
int err; |
|
|
|
|
|
|
|
if (priv->aud_needed) { |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud); |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_aud); |
|
|
|
if (err < 0) |
|
|
|
goto fail; |
|
|
|
priv->aud_needed = 0; |
|
|
|
} |
|
|
|
|
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->vps); |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_vps); |
|
|
|
if (err < 0) |
|
|
|
goto fail; |
|
|
|
|
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->sps); |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_sps); |
|
|
|
if (err < 0) |
|
|
|
goto fail; |
|
|
|
|
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->pps); |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_pps); |
|
|
|
if (err < 0) |
|
|
|
goto fail; |
|
|
|
|
|
|
|
@@ -165,19 +166,18 @@ static int vaapi_encode_h265_write_slice_header(AVCodecContext *avctx, |
|
|
|
VAAPIEncodeSlice *slice, |
|
|
|
char *data, size_t *data_len) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
CodedBitstreamFragment *au = &priv->current_access_unit; |
|
|
|
int err; |
|
|
|
|
|
|
|
if (priv->aud_needed) { |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud); |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_aud); |
|
|
|
if (err < 0) |
|
|
|
goto fail; |
|
|
|
priv->aud_needed = 0; |
|
|
|
} |
|
|
|
|
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->slice); |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_slice); |
|
|
|
if (err < 0) |
|
|
|
goto fail; |
|
|
|
|
|
|
|
@@ -192,12 +192,13 @@ static int vaapi_encode_h265_write_extra_header(AVCodecContext *avctx, |
|
|
|
int index, int *type, |
|
|
|
char *data, size_t *data_len) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
CodedBitstreamFragment *au = &priv->current_access_unit; |
|
|
|
int err, i; |
|
|
|
|
|
|
|
if (priv->sei_needed) { |
|
|
|
H265RawSEI *sei = &priv->raw_sei; |
|
|
|
|
|
|
|
if (priv->aud_needed) { |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud); |
|
|
|
if (err < 0) |
|
|
|
@@ -205,31 +206,32 @@ static int vaapi_encode_h265_write_extra_header(AVCodecContext *avctx, |
|
|
|
priv->aud_needed = 0; |
|
|
|
} |
|
|
|
|
|
|
|
memset(&priv->sei, 0, sizeof(priv->sei)); |
|
|
|
priv->sei.nal_unit_header = (H265RawNALUnitHeader) { |
|
|
|
.nal_unit_type = HEVC_NAL_SEI_PREFIX, |
|
|
|
.nuh_layer_id = 0, |
|
|
|
.nuh_temporal_id_plus1 = 1, |
|
|
|
*sei = (H265RawSEI) { |
|
|
|
.nal_unit_header = { |
|
|
|
.nal_unit_type = HEVC_NAL_SEI_PREFIX, |
|
|
|
.nuh_layer_id = 0, |
|
|
|
.nuh_temporal_id_plus1 = 1, |
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
i = 0; |
|
|
|
|
|
|
|
if (priv->sei_needed & SEI_MASTERING_DISPLAY) { |
|
|
|
priv->sei.payload[i].payload_type = HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO; |
|
|
|
priv->sei.payload[i].payload.mastering_display = priv->mastering_display; |
|
|
|
sei->payload[i].payload_type = HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO; |
|
|
|
sei->payload[i].payload.mastering_display = priv->sei_mastering_display; |
|
|
|
++i; |
|
|
|
} |
|
|
|
|
|
|
|
if (priv->sei_needed & SEI_CONTENT_LIGHT_LEVEL) { |
|
|
|
priv->sei.payload[i].payload_type = HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO; |
|
|
|
priv->sei.payload[i].payload.content_light_level = priv->content_light_level; |
|
|
|
sei->payload[i].payload_type = HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO; |
|
|
|
sei->payload[i].payload.content_light_level = priv->sei_content_light_level; |
|
|
|
++i; |
|
|
|
} |
|
|
|
|
|
|
|
priv->sei.payload_count = i; |
|
|
|
av_assert0(priv->sei.payload_count > 0); |
|
|
|
sei->payload_count = i; |
|
|
|
av_assert0(sei->payload_count > 0); |
|
|
|
|
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, &priv->sei); |
|
|
|
err = vaapi_encode_h265_add_nal(avctx, au, sei); |
|
|
|
if (err < 0) |
|
|
|
goto fail; |
|
|
|
priv->sei_needed = 0; |
|
|
|
@@ -254,10 +256,10 @@ fail: |
|
|
|
static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
H265RawVPS *vps = &priv->vps; |
|
|
|
H265RawSPS *sps = &priv->sps; |
|
|
|
H265RawPPS *pps = &priv->pps; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
H265RawVPS *vps = &priv->raw_vps; |
|
|
|
H265RawSPS *sps = &priv->raw_sps; |
|
|
|
H265RawPPS *pps = &priv->raw_pps; |
|
|
|
H265RawVUI *vui = &sps->vui; |
|
|
|
VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params; |
|
|
|
VAEncPictureParameterBufferHEVC *vpic = ctx->codec_picture_params; |
|
|
|
@@ -613,9 +615,7 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx) |
|
|
|
static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, |
|
|
|
VAAPIEncodePicture *pic) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Options *opt = ctx->codec_options; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; |
|
|
|
int i; |
|
|
|
|
|
|
|
@@ -651,14 +651,16 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, |
|
|
|
} |
|
|
|
priv->pic_order_cnt = pic->display_order - priv->last_idr_frame; |
|
|
|
|
|
|
|
if (opt->aud) { |
|
|
|
if (priv->aud) { |
|
|
|
priv->aud_needed = 1; |
|
|
|
priv->aud.nal_unit_header = (H265RawNALUnitHeader) { |
|
|
|
.nal_unit_type = HEVC_NAL_AUD, |
|
|
|
.nuh_layer_id = 0, |
|
|
|
.nuh_temporal_id_plus1 = 1, |
|
|
|
priv->raw_aud = (H265RawAUD) { |
|
|
|
.nal_unit_header = { |
|
|
|
.nal_unit_type = HEVC_NAL_AUD, |
|
|
|
.nuh_layer_id = 0, |
|
|
|
.nuh_temporal_id_plus1 = 1, |
|
|
|
}, |
|
|
|
.pic_type = priv->pic_type, |
|
|
|
}; |
|
|
|
priv->aud.pic_type = priv->pic_type; |
|
|
|
} else { |
|
|
|
priv->aud_needed = 0; |
|
|
|
} |
|
|
|
@@ -668,7 +670,7 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, |
|
|
|
// Only look for the metadata on I/IDR frame on the output. We |
|
|
|
// may force an IDR frame on the output where the medadata gets |
|
|
|
// changed on the input frame. |
|
|
|
if ((opt->sei & SEI_MASTERING_DISPLAY) && |
|
|
|
if ((priv->sei & SEI_MASTERING_DISPLAY) && |
|
|
|
(pic->type == PICTURE_TYPE_I || pic->type == PICTURE_TYPE_IDR)) { |
|
|
|
AVFrameSideData *sd = |
|
|
|
av_frame_get_side_data(pic->input_image, |
|
|
|
@@ -680,41 +682,43 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, |
|
|
|
|
|
|
|
// SEI is needed when both the primaries and luminance are set |
|
|
|
if (mdm->has_primaries && mdm->has_luminance) { |
|
|
|
H265RawSEIMasteringDisplayColourVolume *mdcv = |
|
|
|
&priv->sei_mastering_display; |
|
|
|
const int mapping[3] = {1, 2, 0}; |
|
|
|
const int chroma_den = 50000; |
|
|
|
const int luma_den = 10000; |
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) { |
|
|
|
const int j = mapping[i]; |
|
|
|
priv->mastering_display.display_primaries_x[i] = |
|
|
|
mdcv->display_primaries_x[i] = |
|
|
|
FFMIN(lrint(chroma_den * |
|
|
|
av_q2d(mdm->display_primaries[j][0])), |
|
|
|
chroma_den); |
|
|
|
priv->mastering_display.display_primaries_y[i] = |
|
|
|
mdcv->display_primaries_y[i] = |
|
|
|
FFMIN(lrint(chroma_den * |
|
|
|
av_q2d(mdm->display_primaries[j][1])), |
|
|
|
chroma_den); |
|
|
|
} |
|
|
|
|
|
|
|
priv->mastering_display.white_point_x = |
|
|
|
mdcv->white_point_x = |
|
|
|
FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[0])), |
|
|
|
chroma_den); |
|
|
|
priv->mastering_display.white_point_y = |
|
|
|
mdcv->white_point_y = |
|
|
|
FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[1])), |
|
|
|
chroma_den); |
|
|
|
|
|
|
|
priv->mastering_display.max_display_mastering_luminance = |
|
|
|
mdcv->max_display_mastering_luminance = |
|
|
|
lrint(luma_den * av_q2d(mdm->max_luminance)); |
|
|
|
priv->mastering_display.min_display_mastering_luminance = |
|
|
|
mdcv->min_display_mastering_luminance = |
|
|
|
FFMIN(lrint(luma_den * av_q2d(mdm->min_luminance)), |
|
|
|
priv->mastering_display.max_display_mastering_luminance); |
|
|
|
mdcv->max_display_mastering_luminance); |
|
|
|
|
|
|
|
priv->sei_needed |= SEI_MASTERING_DISPLAY; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if ((opt->sei & SEI_CONTENT_LIGHT_LEVEL) && |
|
|
|
if ((priv->sei & SEI_CONTENT_LIGHT_LEVEL) && |
|
|
|
(pic->type == PICTURE_TYPE_I || pic->type == PICTURE_TYPE_IDR)) { |
|
|
|
AVFrameSideData *sd = |
|
|
|
av_frame_get_side_data(pic->input_image, |
|
|
|
@@ -723,11 +727,11 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, |
|
|
|
if (sd) { |
|
|
|
AVContentLightMetadata *clm = |
|
|
|
(AVContentLightMetadata *)sd->data; |
|
|
|
H265RawSEIContentLightLevelInfo *clli = |
|
|
|
&priv->sei_content_light_level; |
|
|
|
|
|
|
|
priv->content_light_level.max_content_light_level = |
|
|
|
FFMIN(clm->MaxCLL, 65535); |
|
|
|
priv->content_light_level.max_pic_average_light_level = |
|
|
|
FFMIN(clm->MaxFALL, 65535); |
|
|
|
clli->max_content_light_level = FFMIN(clm->MaxCLL, 65535); |
|
|
|
clli->max_pic_average_light_level = FFMIN(clm->MaxFALL, 65535); |
|
|
|
|
|
|
|
priv->sei_needed |= SEI_CONTENT_LIGHT_LEVEL; |
|
|
|
} |
|
|
|
@@ -798,10 +802,10 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx, |
|
|
|
VAAPIEncodeSlice *slice) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
const H265RawSPS *sps = &priv->sps; |
|
|
|
const H265RawPPS *pps = &priv->pps; |
|
|
|
H265RawSliceHeader *sh = &priv->slice.header; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
const H265RawSPS *sps = &priv->raw_sps; |
|
|
|
const H265RawPPS *pps = &priv->raw_pps; |
|
|
|
H265RawSliceHeader *sh = &priv->raw_slice.header; |
|
|
|
VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; |
|
|
|
VAEncSliceParameterBufferHEVC *vslice = slice->codec_slice_params; |
|
|
|
int i; |
|
|
|
@@ -972,8 +976,7 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx, |
|
|
|
static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Options *opt = ctx->codec_options; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
int err; |
|
|
|
|
|
|
|
err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_HEVC, avctx); |
|
|
|
@@ -988,7 +991,7 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx) |
|
|
|
ctx->surface_height, priv->ctu_width, priv->ctu_height); |
|
|
|
|
|
|
|
if (ctx->va_rc_mode == VA_RC_CQP) { |
|
|
|
priv->fixed_qp_p = opt->qp; |
|
|
|
priv->fixed_qp_p = priv->qp; |
|
|
|
if (avctx->i_quant_factor > 0.0) |
|
|
|
priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor + |
|
|
|
avctx->i_quant_offset) + 0.5); |
|
|
|
@@ -1047,16 +1050,15 @@ static const VAAPIEncodeType vaapi_encode_type_h265 = { |
|
|
|
|
|
|
|
static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Options *opt = |
|
|
|
(VAAPIEncodeH265Options*)ctx->codec_options_data; |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
|
|
|
|
ctx->codec = &vaapi_encode_type_h265; |
|
|
|
|
|
|
|
if (avctx->profile == FF_PROFILE_UNKNOWN) |
|
|
|
avctx->profile = opt->profile; |
|
|
|
avctx->profile = priv->profile; |
|
|
|
if (avctx->level == FF_LEVEL_UNKNOWN) |
|
|
|
avctx->level = opt->level; |
|
|
|
avctx->level = priv->level; |
|
|
|
|
|
|
|
switch (avctx->profile) { |
|
|
|
case FF_PROFILE_HEVC_MAIN: |
|
|
|
@@ -1102,17 +1104,14 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx) |
|
|
|
|
|
|
|
static av_cold int vaapi_encode_h265_close(AVCodecContext *avctx) |
|
|
|
{ |
|
|
|
VAAPIEncodeContext *ctx = avctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = ctx->priv_data; |
|
|
|
VAAPIEncodeH265Context *priv = avctx->priv_data; |
|
|
|
|
|
|
|
if (priv) |
|
|
|
ff_cbs_close(&priv->cbc); |
|
|
|
ff_cbs_close(&priv->cbc); |
|
|
|
|
|
|
|
return ff_vaapi_encode_close(avctx); |
|
|
|
} |
|
|
|
|
|
|
|
#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \ |
|
|
|
offsetof(VAAPIEncodeH265Options, x)) |
|
|
|
#define OFFSET(x) offsetof(VAAPIEncodeH265Context, x) |
|
|
|
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) |
|
|
|
static const AVOption vaapi_encode_h265_options[] = { |
|
|
|
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)", |
|
|
|
@@ -1189,8 +1188,7 @@ AVCodec ff_hevc_vaapi_encoder = { |
|
|
|
.long_name = NULL_IF_CONFIG_SMALL("H.265/HEVC (VAAPI)"), |
|
|
|
.type = AVMEDIA_TYPE_VIDEO, |
|
|
|
.id = AV_CODEC_ID_HEVC, |
|
|
|
.priv_data_size = (sizeof(VAAPIEncodeContext) + |
|
|
|
sizeof(VAAPIEncodeH265Options)), |
|
|
|
.priv_data_size = sizeof(VAAPIEncodeH265Context), |
|
|
|
.init = &vaapi_encode_h265_init, |
|
|
|
.encode2 = &ff_vaapi_encode2, |
|
|
|
.close = &vaapi_encode_h265_close, |
|
|
|
|