blob: 41357b0e2f4028621ec17556a22a514717e0e72f [file] [log] [blame]
/*
* Copyright (c) 2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file media_libvpx_vp9.cpp
//! \brief Defines the VP9 structure/function that is from libvpx
//!
/*
* This file defines some functions related with vp9 enc, and
* they are ported from libvpx (https://github.com/webmproject/libvpx/).
* The original copyright and licence statement as below.
*/
/*
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the media_libvpx.LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file media_libvpx.PATENTS. All contributing project authors may
* be found in the media_libvpx.AUTHORS file in the root of the source tree.
*/
#include <stdio.h>
#include <stdint.h>
#include "media_libva_encoder.h"
#include "media_libvpx_vp9.h"
struct vp9_write_bit_buffer {
uint8_t *bit_buffer;
int bit_offset;
};
static
void vp9_wb_write_bit(struct vp9_write_bit_buffer *wb, int bit)
{
const int off = wb->bit_offset;
const int p = off / 8;
const int q = 7 - off % 8;
if (q == 7)
{
wb->bit_buffer[p] = bit << q;
}
else
{
wb->bit_buffer[p] &= ~(1 << q);
wb->bit_buffer[p] |= bit << q;
}
wb->bit_offset = off + 1;
}
static
void vp9_wb_write_literal(struct vp9_write_bit_buffer *wb, int data, int bits)
{
int bit;
for (bit = bits - 1; bit >= 0; bit--)
vp9_wb_write_bit(wb, (data >> bit) & 1);
}
static
void write_bitdepth_colorspace_sampling(uint32_t codecProfile,
struct vp9_write_bit_buffer *wb)
{
if (codecProfile >= VP9_PROFILE_2)
{
/* Profile 2 can support 10/12 bits */
/* Currently it is 10 bits */
vp9_wb_write_literal(wb, 0, 1);
}
/* Add the default color-space */
vp9_wb_write_literal(wb, 0, 3);
vp9_wb_write_bit(wb, 0); // 0: [16, 235] (i.e. xvYCC), 1: [0, 255]
if ((codecProfile == VP9_PROFILE_1) ||
(codecProfile == VP9_PROFILE_3))
{
/* sub_sampling_x/y */
/* Currently the sub_sampling_x = 0, sub_sampling_y = 0 */
vp9_wb_write_bit(wb, 0);
vp9_wb_write_bit(wb, 0);
vp9_wb_write_bit(wb, 0); // unused
}
}
#define MAX_TILE_WIDTH_B64 64
#define MIN_TILE_WIDTH_B64 4
static int get_min_log2_tile_cols(const int sb_cols)
{
int min_log2 = 0;
while ((MAX_TILE_WIDTH_B64 << min_log2) < sb_cols)
++min_log2;
return min_log2;
}
static int get_max_log2_tile_cols(const int sb_cols)
{
int max_log2 = 1;
while ((sb_cols >> max_log2) >= MIN_TILE_WIDTH_B64)
++max_log2;
return max_log2 - 1;
}
bool Vp9WriteUncompressHeader(struct _DDI_ENCODE_CONTEXT *ddiEncContext,
uint32_t codecProfile,
uint8_t *headerData,
uint32_t *headerLen,
vp9_header_bitoffset *headerBitoffset)
{
#define VP9_SYNC_CODE_0 0x49
#define VP9_SYNC_CODE_1 0x83
#define VP9_SYNC_CODE_2 0x42
#define VP9_FRAME_MARKER 0x2
#define REFS_PER_FRAME 3
#define REF_FRAMES_LOG2 3
#define REF_FRAMES (1 << REF_FRAMES_LOG2)
#define VP9_KEY_FRAME 0
if ((ddiEncContext == nullptr) ||
(headerData == nullptr) ||
(headerLen == nullptr) ||
(headerBitoffset == nullptr))
return false;
CODEC_VP9_ENCODE_PIC_PARAMS *picParam = (CODEC_VP9_ENCODE_PIC_PARAMS *)ddiEncContext->pPicParams;
CODEC_VP9_ENCODE_SEGMENT_PARAMS *segParams = (CODEC_VP9_ENCODE_SEGMENT_PARAMS *)ddiEncContext->pVpxSegParams;
if (picParam == nullptr)
return false;
struct vp9_write_bit_buffer *wb, vp9_wb;
memset(headerBitoffset, 0, sizeof(vp9_header_bitoffset));
vp9_wb.bit_buffer = (uint8_t *)headerData;
vp9_wb.bit_offset = 0;
wb = &vp9_wb;
vp9_wb_write_literal(wb, VP9_FRAME_MARKER, 2);
/* Only Profile0/1/2/3 is supported */
if (codecProfile > VP9_PROFILE_3)
codecProfile = VP9_PROFILE_0;
switch(codecProfile)
{
case VP9_PROFILE_0:
//Profile 0
vp9_wb_write_literal(wb, 0, 2);
break;
case VP9_PROFILE_1:
//Profile 1
vp9_wb_write_literal(wb, 2, 2);
break;
case VP9_PROFILE_2:
//Profile 2
vp9_wb_write_literal(wb, 1, 2);
break;
case VP9_PROFILE_3:
vp9_wb_write_literal(wb, 6, 3);
break;
default:
break;
}
vp9_wb_write_bit(wb, 0); // show_existing_frame
vp9_wb_write_bit(wb, picParam->PicFlags.fields.frame_type);
vp9_wb_write_bit(wb, picParam->PicFlags.fields.show_frame);
vp9_wb_write_bit(wb, picParam->PicFlags.fields.error_resilient_mode);
if (picParam->PicFlags.fields.frame_type == VP9_KEY_FRAME)
{
vp9_wb_write_literal(wb, VP9_SYNC_CODE_0, 8);
vp9_wb_write_literal(wb, VP9_SYNC_CODE_1, 8);
vp9_wb_write_literal(wb, VP9_SYNC_CODE_2, 8);
write_bitdepth_colorspace_sampling(codecProfile, wb);
/* write the encoded frame size */
vp9_wb_write_literal(wb, picParam->DstFrameWidthMinus1, 16);
vp9_wb_write_literal(wb, picParam->DstFrameHeightMinus1, 16);
/* write display size */
if ((picParam->DstFrameWidthMinus1 != picParam->SrcFrameWidthMinus1) ||
(picParam->DstFrameHeightMinus1 != picParam->SrcFrameHeightMinus1))
{
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, picParam->SrcFrameWidthMinus1, 16);
vp9_wb_write_literal(wb, picParam->SrcFrameHeightMinus1, 16);
}
else
{
vp9_wb_write_bit(wb, 0);
}
}
else
{
/* for the non-Key frame */
if (!picParam->PicFlags.fields.show_frame)
{
vp9_wb_write_bit(wb, picParam->PicFlags.fields.intra_only);
}
if (!picParam->PicFlags.fields.error_resilient_mode)
{
vp9_wb_write_literal(wb, picParam->PicFlags.fields.reset_frame_context, 2);
}
if (picParam->PicFlags.fields.intra_only)
{
vp9_wb_write_literal(wb, VP9_SYNC_CODE_0, 8);
vp9_wb_write_literal(wb, VP9_SYNC_CODE_1, 8);
vp9_wb_write_literal(wb, VP9_SYNC_CODE_2, 8);
/* Add the bit_depth for VP9Profile1/2/3 */
if (codecProfile)
write_bitdepth_colorspace_sampling(codecProfile, wb);
/* write the refreshed_frame_flags */
vp9_wb_write_literal(wb, picParam->RefFlags.fields.refresh_frame_flags, REF_FRAMES);
/* write the encoded frame size */
vp9_wb_write_literal(wb, picParam->DstFrameWidthMinus1, 16);
vp9_wb_write_literal(wb, picParam->DstFrameHeightMinus1, 16);
/* write display size */
if ((picParam->DstFrameWidthMinus1 != picParam->SrcFrameWidthMinus1) ||
(picParam->DstFrameHeightMinus1 != picParam->SrcFrameHeightMinus1))
{
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, picParam->SrcFrameWidthMinus1, 16);
vp9_wb_write_literal(wb, picParam->SrcFrameHeightMinus1, 16);
}
else
{
vp9_wb_write_bit(wb, 0);
}
}
else
{
/* The refresh_frame_map is for the next frame so that it can select Last/Godlen/Alt ref_index */
/*
if ((picParam->RefFlags.fields.ref_frame_ctrl_l0) & (1 << 0))
refresh_flags = 1 << picParam->RefFlags.fields.ref_last_idx;
if ((picParam->RefFlags.fields.ref_frame_ctrl_l0) & (1 << 0))
refresh_flags = 1 << picParam->RefFlags.fields.ref_last_idx;
if ((picParam->RefFlags.fields.ref_frame_ctrl_l0) & (1 << 0))
refresh_flags = 1 << picParam->RefFlags.fields.ref_last_idx;
*/
vp9_wb_write_literal(wb, picParam->RefFlags.fields.refresh_frame_flags, REF_FRAMES);
vp9_wb_write_literal(wb, picParam->RefFlags.fields.LastRefIdx, REF_FRAMES_LOG2);
vp9_wb_write_bit(wb, picParam->RefFlags.fields.LastRefSignBias);
vp9_wb_write_literal(wb, picParam->RefFlags.fields.GoldenRefIdx, REF_FRAMES_LOG2);
vp9_wb_write_bit(wb, picParam->RefFlags.fields.GoldenRefSignBias);
vp9_wb_write_literal(wb, picParam->RefFlags.fields.AltRefIdx, REF_FRAMES_LOG2);
vp9_wb_write_bit(wb, picParam->RefFlags.fields.AltRefSignBias);
/* write three bits with zero so that it can parse width/height directly */
vp9_wb_write_literal(wb, 0, 3);
vp9_wb_write_literal(wb, picParam->DstFrameWidthMinus1, 16);
vp9_wb_write_literal(wb, picParam->DstFrameHeightMinus1, 16);
/* write display size */
if ((picParam->DstFrameWidthMinus1 != picParam->SrcFrameWidthMinus1) ||
(picParam->DstFrameHeightMinus1 != picParam->SrcFrameHeightMinus1))
{
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, picParam->SrcFrameWidthMinus1, 16);
vp9_wb_write_literal(wb, picParam->SrcFrameHeightMinus1, 16);
}
else
{
vp9_wb_write_bit(wb, 0);
}
vp9_wb_write_bit(wb, picParam->PicFlags.fields.allow_high_precision_mv);
#define SWITCHABLE_FILTER 4
#define FILTER_MASK 3
if (picParam->PicFlags.fields.mcomp_filter_type == SWITCHABLE_FILTER)
{
vp9_wb_write_bit(wb, 1);
}
else
{
const int filter_to_literal[4] = { 1, 0, 2, 3 };
uint8_t filter_flag = picParam->PicFlags.fields.mcomp_filter_type;
filter_flag = filter_flag & FILTER_MASK;
vp9_wb_write_bit(wb, 0);
vp9_wb_write_literal(wb, filter_to_literal[filter_flag], 2);
}
}
}
/* write refresh_frame_context/paralle frame_decoding */
if (!picParam->PicFlags.fields.error_resilient_mode)
{
vp9_wb_write_bit(wb, picParam->PicFlags.fields.refresh_frame_context);
vp9_wb_write_bit(wb, picParam->PicFlags.fields.frame_parallel_decoding_mode);
}
vp9_wb_write_literal(wb, picParam->PicFlags.fields.frame_context_idx, 2);
/* write loop filter */
headerBitoffset->bit_offset_lf_level = wb->bit_offset;
vp9_wb_write_literal(wb, picParam->filter_level, 6);
vp9_wb_write_literal(wb, picParam->sharpness_level, 3);
{
int i, mode_flag;
vp9_wb_write_bit(wb, 1);
vp9_wb_write_bit(wb, 1);
headerBitoffset->bit_offset_ref_lf_delta = wb->bit_offset;
for (i = 0; i < 4; i++)
{
/*
* This check is skipped to prepare the bit_offset_lf_ref
if (picParam->LFRefDelta[i] == 0) {
vp9_wb_write_bit(wb, 0);
continue;
}
*/
vp9_wb_write_bit(wb, 1);
mode_flag = picParam->LFRefDelta[i];
if (mode_flag >= 0)
{
vp9_wb_write_literal(wb, mode_flag & (0x3F), 6);
vp9_wb_write_bit(wb, 0);
}
else
{
mode_flag = -mode_flag;
vp9_wb_write_literal(wb, mode_flag & (0x3F), 6);
vp9_wb_write_bit(wb, 1);
}
}
headerBitoffset->bit_offset_mode_lf_delta = wb->bit_offset;
for (i = 0; i < 2; i++)
{
/*
* This check is skipped to prepare the bit_offset_lf_mode
if (picParam->LFModeDelta[i] == 0) {
vp9_wb_write_bit(wb, 0);
continue;
}
*/
vp9_wb_write_bit(wb, 1);
mode_flag = picParam->LFRefDelta[i];
if (mode_flag >= 0)
{
vp9_wb_write_literal(wb, mode_flag & (0x3F), 6);
vp9_wb_write_bit(wb, 0);
}
else
{
mode_flag = -mode_flag;
vp9_wb_write_literal(wb, mode_flag & (0x3F), 6);
vp9_wb_write_bit(wb, 1);
}
}
}
/* write basic quantizer */
headerBitoffset->bit_offset_qindex = wb->bit_offset;
vp9_wb_write_literal(wb, picParam->LumaACQIndex, 8);
if (picParam->LumaDCQIndexDelta)
{
int delta_q = picParam->LumaDCQIndexDelta;
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, abs(delta_q), 4);
vp9_wb_write_bit(wb, delta_q < 0);
}
else
{
vp9_wb_write_bit(wb, 0);
}
if (picParam->ChromaDCQIndexDelta)
{
int delta_q = picParam->ChromaDCQIndexDelta;
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, abs(delta_q), 4);
vp9_wb_write_bit(wb, delta_q < 0);
}
else
{
vp9_wb_write_bit(wb, 0);
}
if (picParam->ChromaACQIndexDelta)
{
int delta_q = picParam->ChromaACQIndexDelta;
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, abs(delta_q), 4);
vp9_wb_write_bit(wb, delta_q < 0);
}
else
{
vp9_wb_write_bit(wb, 0);
}
vp9_wb_write_bit(wb, picParam->PicFlags.fields.segmentation_enabled);
if (picParam->PicFlags.fields.segmentation_enabled)
{
int i;
#define VP9_MAX_PROB 255
vp9_wb_write_bit(wb, picParam->PicFlags.fields.segmentation_update_map);
headerBitoffset->bit_offset_segmentation = wb->bit_offset;
if (picParam->PicFlags.fields.segmentation_update_map)
{
/* write the seg_tree_probs */
/* segment_tree_probs/segment_pred_probs are not passed.
* So the hard-coded prob is writen
*/
for (i = 0; i < 7; i++)
{
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, VP9_MAX_PROB, 8);
}
vp9_wb_write_bit(wb, picParam->PicFlags.fields.segmentation_temporal_update);
if (picParam->PicFlags.fields.segmentation_temporal_update)
{
for (i = 0; i < 3; i++)
{
vp9_wb_write_bit(wb, 1);
vp9_wb_write_literal(wb, VP9_MAX_PROB, 8);
}
}
}
/* write the segment_data info */
vp9_wb_write_bit(wb, picParam->PicFlags.fields.seg_update_data);
if (picParam->PicFlags.fields.seg_update_data)
{
CODEC_VP9_ENCODE_SEG_PARAMS *seg_data;
int seg_delta;
/* abs_delta should be zero */
vp9_wb_write_bit(wb, 0);
for (i = 0; i < 8; i++)
{
seg_data = &segParams->SegData[i];
/* The segment qindex delta */
/* This check is skipped */
/* if (seg_data->SegmentQIndexDelta != 0) */
if (1)
{
vp9_wb_write_bit(wb, 1);
seg_delta = seg_data->SegmentQIndexDelta;
vp9_wb_write_literal(wb, abs(seg_delta), 8);
vp9_wb_write_bit(wb, seg_delta < 0);
}
else
{
vp9_wb_write_bit(wb, 0);
}
/* The segment lf delta */
/* if (seg_data->SegmentLFLevelDelta != 0) */
if (1)
{
vp9_wb_write_bit(wb, 1);
seg_delta = seg_data->SegmentLFLevelDelta;
vp9_wb_write_literal(wb, abs(seg_delta), 6);
vp9_wb_write_bit(wb, seg_delta < 0);
}
else
{
vp9_wb_write_bit(wb, 0);
}
/* segment reference flag */
vp9_wb_write_bit(wb, seg_data->SegmentFlags.fields.SegmentReferenceEnabled);
if (seg_data->SegmentFlags.fields.SegmentReferenceEnabled)
{
vp9_wb_write_literal(wb, seg_data->SegmentFlags.fields.SegmentReference, 2);
}
/* segment skip flag */
vp9_wb_write_bit(wb, seg_data->SegmentFlags.fields.SegmentSkipped);
}
}
}
/* write tile info */
{
int sb_cols = (picParam->DstFrameWidthMinus1 + 64) / 64;
int min_log2_tile_cols, max_log2_tile_cols;
int col_data;
/* write tile column info */
min_log2_tile_cols = get_min_log2_tile_cols(sb_cols);
max_log2_tile_cols = get_max_log2_tile_cols(sb_cols);
col_data = picParam->log2_tile_columns - min_log2_tile_cols;
while (col_data--)
{
vp9_wb_write_bit(wb, 1);
}
if (picParam->log2_tile_columns < max_log2_tile_cols)
{
vp9_wb_write_bit(wb, 0);
}
/* write tile row info */
vp9_wb_write_bit(wb, picParam->log2_tile_rows);
if (picParam->log2_tile_rows)
{
vp9_wb_write_bit(wb, (picParam->log2_tile_rows != 1));
}
}
/* get the bit_offset of the first partition size */
headerBitoffset->bit_offset_first_partition_size = wb->bit_offset;
/* reserve the space for writing the first partitions ize */
vp9_wb_write_literal(wb, 0, 16);
*headerLen = (wb->bit_offset + 7) / 8;
return true;
}