package org.jcodec.codecs.h264.io.model;
import static org.jcodec.codecs.h264.decode.CAVLCReader.moreRBSPData;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readBool;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readNBit;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readSE;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readU;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readUEtrace;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeBool;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeNBit;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeSEtrace;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeTrailingBits;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeU;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeUEtrace;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
import org.jcodec.platform.Platform;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Picture Parameter Set entity of H264 bitstream
*
* capable to serialize / deserialize with CAVLC bitstream
*
* @author The JCodec project
*
*/
public class PictureParameterSet {
public static class PPSExt {
public boolean transform_8x8_mode_flag;
public ScalingMatrix scalindMatrix;
public int second_chroma_qp_index_offset;
public boolean[] pic_scaling_list_present_flag;
public boolean isTransform_8x8_mode_flag() {
return transform_8x8_mode_flag;
}
public ScalingMatrix getScalindMatrix() {
return scalindMatrix;
}
public int getSecond_chroma_qp_index_offset() {
return second_chroma_qp_index_offset;
}
public boolean[] getPic_scaling_list_present_flag() {
return pic_scaling_list_present_flag;
}
}
public boolean entropy_coding_mode_flag;
public int[] num_ref_idx_active_minus1;
public int slice_group_change_rate_minus1;
public int pic_parameter_set_id;
public int seq_parameter_set_id;
public boolean pic_order_present_flag;
public int num_slice_groups_minus1;
public int slice_group_map_type;
public boolean weighted_pred_flag;
public int weighted_bipred_idc;
public int pic_init_qp_minus26;
public int pic_init_qs_minus26;
public int chroma_qp_index_offset;
public boolean deblocking_filter_control_present_flag;
public boolean constrained_intra_pred_flag;
public boolean redundant_pic_cnt_present_flag;
public int[] top_left;
public int[] bottom_right;
public int[] run_length_minus1;
public boolean slice_group_change_direction_flag;
public int[] slice_group_id;
public PPSExt extended;
public PictureParameterSet() {
this.num_ref_idx_active_minus1 = new int[2];
}
public static PictureParameterSet read(ByteBuffer is) {
BitReader _in = BitReader.createBitReader(is);
PictureParameterSet pps = new PictureParameterSet();
pps.pic_parameter_set_id = readUEtrace(_in, "PPS: pic_parameter_set_id");
pps.seq_parameter_set_id = readUEtrace(_in, "PPS: seq_parameter_set_id");
pps.entropy_coding_mode_flag = readBool(_in, "PPS: entropy_coding_mode_flag");
pps.pic_order_present_flag = readBool(_in, "PPS: pic_order_present_flag");
pps.num_slice_groups_minus1 = readUEtrace(_in, "PPS: num_slice_groups_minus1");
if (pps.num_slice_groups_minus1 > 0) {
pps.slice_group_map_type = readUEtrace(_in, "PPS: slice_group_map_type");
pps.top_left = new int[pps.num_slice_groups_minus1 + 1];
pps.bottom_right = new int[pps.num_slice_groups_minus1 + 1];
pps.run_length_minus1 = new int[pps.num_slice_groups_minus1 + 1];
if (pps.slice_group_map_type == 0)
for (int iGroup = 0; iGroup <= pps.num_slice_groups_minus1; iGroup++)
pps.run_length_minus1[iGroup] = readUEtrace(_in, "PPS: run_length_minus1");
else if (pps.slice_group_map_type == 2)
for (int iGroup = 0; iGroup < pps.num_slice_groups_minus1; iGroup++) {
pps.top_left[iGroup] = readUEtrace(_in, "PPS: top_left");
pps.bottom_right[iGroup] = readUEtrace(_in, "PPS: bottom_right");
}
else if (pps.slice_group_map_type == 3 || pps.slice_group_map_type == 4 || pps.slice_group_map_type == 5) {
pps.slice_group_change_direction_flag = readBool(_in, "PPS: slice_group_change_direction_flag");
pps.slice_group_change_rate_minus1 = readUEtrace(_in, "PPS: slice_group_change_rate_minus1");
} else if (pps.slice_group_map_type == 6) {
int NumberBitsPerSliceGroupId;
if (pps.num_slice_groups_minus1 + 1 > 4)
NumberBitsPerSliceGroupId = 3;
else if (pps.num_slice_groups_minus1 + 1 > 2)
NumberBitsPerSliceGroupId = 2;
else
NumberBitsPerSliceGroupId = 1;
int pic_size_in_map_units_minus1 = readUEtrace(_in, "PPS: pic_size_in_map_units_minus1");
pps.slice_group_id = new int[pic_size_in_map_units_minus1 + 1];
for (int i = 0; i <= pic_size_in_map_units_minus1; i++) {
pps.slice_group_id[i] = readU(_in, NumberBitsPerSliceGroupId, "PPS: slice_group_id [" + i + "]f");
}
}
}
pps.num_ref_idx_active_minus1 = new int[] {readUEtrace(_in, "PPS: num_ref_idx_l0_active_minus1"), readUEtrace(_in, "PPS: num_ref_idx_l1_active_minus1")};
pps.weighted_pred_flag = readBool(_in, "PPS: weighted_pred_flag");
pps.weighted_bipred_idc = readNBit(_in, 2, "PPS: weighted_bipred_idc");
pps.pic_init_qp_minus26 = readSE(_in, "PPS: pic_init_qp_minus26");
pps.pic_init_qs_minus26 = readSE(_in, "PPS: pic_init_qs_minus26");
pps.chroma_qp_index_offset = readSE(_in, "PPS: chroma_qp_index_offset");
pps.deblocking_filter_control_present_flag = readBool(_in, "PPS: deblocking_filter_control_present_flag");
pps.constrained_intra_pred_flag = readBool(_in, "PPS: constrained_intra_pred_flag");
pps.redundant_pic_cnt_present_flag = readBool(_in, "PPS: redundant_pic_cnt_present_flag");
if (moreRBSPData(_in)) {
pps.extended = new PictureParameterSet.PPSExt();
pps.extended.transform_8x8_mode_flag = readBool(_in, "PPS: transform_8x8_mode_flag");
boolean pic_scaling_matrix_present_flag = readBool(_in, "PPS: pic_scaling_matrix_present_flag");
if (pic_scaling_matrix_present_flag) {
for (int i = 0; i < 6 + 2 * (pps.extended.transform_8x8_mode_flag ? 1 : 0); i++) {
boolean pic_scaling_list_present_flag = readBool(_in, "PPS: pic_scaling_list_present_flag");
if (pic_scaling_list_present_flag) {
pps.extended.scalindMatrix = new ScalingMatrix();
pps.extended.scalindMatrix.ScalingList4x4 = new ScalingList[8];
pps.extended.scalindMatrix.ScalingList8x8 = new ScalingList[8];
if (i < 6) {
pps.extended.scalindMatrix.ScalingList4x4[i] = ScalingList.read(_in, 16);
} else {
pps.extended.scalindMatrix.ScalingList8x8[i - 6] = ScalingList.read(_in, 64);
}
}
}
}
pps.extended.second_chroma_qp_index_offset = readSE(_in, "PPS: second_chroma_qp_index_offset");
}
return pps;
}
public void write(ByteBuffer out) {
BitWriter writer = new BitWriter(out);
writeUEtrace(writer, pic_parameter_set_id, "PPS: pic_parameter_set_id");
writeUEtrace(writer, seq_parameter_set_id, "PPS: seq_parameter_set_id");
writeBool(writer, entropy_coding_mode_flag, "PPS: entropy_coding_mode_flag");
writeBool(writer, pic_order_present_flag, "PPS: pic_order_present_flag");
writeUEtrace(writer, num_slice_groups_minus1, "PPS: num_slice_groups_minus1");
if (num_slice_groups_minus1 > 0) {
writeUEtrace(writer, slice_group_map_type, "PPS: slice_group_map_type");
int[] top_left = new int[1];
int[] bottom_right = new int[1];
int[] run_length_minus1 = new int[1];
if (slice_group_map_type == 0) {
for (int iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++) {
writeUEtrace(writer, run_length_minus1[iGroup], "PPS: ");
}
} else if (slice_group_map_type == 2) {
for (int iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++) {
writeUEtrace(writer, top_left[iGroup], "PPS: ");
writeUEtrace(writer, bottom_right[iGroup], "PPS: ");
}
} else if (slice_group_map_type == 3 || slice_group_map_type == 4 || slice_group_map_type == 5) {
writeBool(writer, slice_group_change_direction_flag, "PPS: slice_group_change_direction_flag");
writeUEtrace(writer, slice_group_change_rate_minus1, "PPS: slice_group_change_rate_minus1");
} else if (slice_group_map_type == 6) {
int NumberBitsPerSliceGroupId;
if (num_slice_groups_minus1 + 1 > 4)
NumberBitsPerSliceGroupId = 3;
else if (num_slice_groups_minus1 + 1 > 2)
NumberBitsPerSliceGroupId = 2;
else
NumberBitsPerSliceGroupId = 1;
writeUEtrace(writer, slice_group_id.length, "PPS: ");
for (int i = 0; i <= slice_group_id.length; i++) {
writeU(writer, slice_group_id[i], NumberBitsPerSliceGroupId);
}
}
}
writeUEtrace(writer, num_ref_idx_active_minus1[0], "PPS: num_ref_idx_l0_active_minus1");
writeUEtrace(writer, num_ref_idx_active_minus1[1], "PPS: num_ref_idx_l1_active_minus1");
writeBool(writer, weighted_pred_flag, "PPS: weighted_pred_flag");
writeNBit(writer, weighted_bipred_idc, 2, "PPS: weighted_bipred_idc");
writeSEtrace(writer, pic_init_qp_minus26, "PPS: pic_init_qp_minus26");
writeSEtrace(writer, pic_init_qs_minus26, "PPS: pic_init_qs_minus26");
writeSEtrace(writer, chroma_qp_index_offset, "PPS: chroma_qp_index_offset");
writeBool(writer, deblocking_filter_control_present_flag, "PPS: deblocking_filter_control_present_flag");
writeBool(writer, constrained_intra_pred_flag, "PPS: constrained_intra_pred_flag");
writeBool(writer, redundant_pic_cnt_present_flag, "PPS: redundant_pic_cnt_present_flag");
if (extended != null) {
writeBool(writer, extended.transform_8x8_mode_flag, "PPS: transform_8x8_mode_flag");
writeBool(writer, extended.scalindMatrix != null, "PPS: scalindMatrix");
if (extended.scalindMatrix != null) {
for (int i = 0; i < 6 + 2 * (extended.transform_8x8_mode_flag ? 1 : 0); i++) {
if (i < 6) {
writeBool(writer, extended.scalindMatrix.ScalingList4x4[i] != null, "PPS: ");
if (extended.scalindMatrix.ScalingList4x4[i] != null) {
extended.scalindMatrix.ScalingList4x4[i].write(writer);
}
} else {
writeBool(writer, extended.scalindMatrix.ScalingList8x8[i - 6] != null, "PPS: ");
if (extended.scalindMatrix.ScalingList8x8[i - 6] != null) {
extended.scalindMatrix.ScalingList8x8[i - 6].write(writer);
}
}
}
}
writeSEtrace(writer, extended.second_chroma_qp_index_offset, "PPS: ");
}
writeTrailingBits(writer);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bottom_right);
result = prime * result + chroma_qp_index_offset;
result = prime * result + (constrained_intra_pred_flag ? 1231 : 1237);
result = prime * result + (deblocking_filter_control_present_flag ? 1231 : 1237);
result = prime * result + (entropy_coding_mode_flag ? 1231 : 1237);
result = prime * result + ((extended == null) ? 0 : extended.hashCode());
result = prime * result + num_ref_idx_active_minus1[0];
result = prime * result + num_ref_idx_active_minus1[1];
result = prime * result + num_slice_groups_minus1;
result = prime * result + pic_init_qp_minus26;
result = prime * result + pic_init_qs_minus26;
result = prime * result + (pic_order_present_flag ? 1231 : 1237);
result = prime * result + pic_parameter_set_id;
result = prime * result + (redundant_pic_cnt_present_flag ? 1231 : 1237);
result = prime * result + Arrays.hashCode(run_length_minus1);
result = prime * result + seq_parameter_set_id;
result = prime * result + (slice_group_change_direction_flag ? 1231 : 1237);
result = prime * result + slice_group_change_rate_minus1;
result = prime * result + Arrays.hashCode(slice_group_id);
result = prime * result + slice_group_map_type;
result = prime * result + Arrays.hashCode(top_left);
result = prime * result + weighted_bipred_idc;
result = prime * result + (weighted_pred_flag ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PictureParameterSet other = (PictureParameterSet) obj;
if (!Platform.arrayEqualsInt(bottom_right, other.bottom_right))
return false;
if (chroma_qp_index_offset != other.chroma_qp_index_offset)
return false;
if (constrained_intra_pred_flag != other.constrained_intra_pred_flag)
return false;
if (deblocking_filter_control_present_flag != other.deblocking_filter_control_present_flag)
return false;
if (entropy_coding_mode_flag != other.entropy_coding_mode_flag)
return false;
if (extended == null) {
if (other.extended != null)
return false;
} else if (!extended.equals(other.extended))
return false;
if (num_ref_idx_active_minus1[0] != other.num_ref_idx_active_minus1[0])
return false;
if (num_ref_idx_active_minus1[1] != other.num_ref_idx_active_minus1[1])
return false;
if (num_slice_groups_minus1 != other.num_slice_groups_minus1)
return false;
if (pic_init_qp_minus26 != other.pic_init_qp_minus26)
return false;
if (pic_init_qs_minus26 != other.pic_init_qs_minus26)
return false;
if (pic_order_present_flag != other.pic_order_present_flag)
return false;
if (pic_parameter_set_id != other.pic_parameter_set_id)
return false;
if (redundant_pic_cnt_present_flag != other.redundant_pic_cnt_present_flag)
return false;
if (!Platform.arrayEqualsInt(run_length_minus1, other.run_length_minus1))
return false;
if (seq_parameter_set_id != other.seq_parameter_set_id)
return false;
if (slice_group_change_direction_flag != other.slice_group_change_direction_flag)
return false;
if (slice_group_change_rate_minus1 != other.slice_group_change_rate_minus1)
return false;
if (!Platform.arrayEqualsInt(slice_group_id, other.slice_group_id))
return false;
if (slice_group_map_type != other.slice_group_map_type)
return false;
if (!Platform.arrayEqualsInt(top_left, other.top_left))
return false;
if (weighted_bipred_idc != other.weighted_bipred_idc)
return false;
if (weighted_pred_flag != other.weighted_pred_flag)
return false;
return true;
}
public PictureParameterSet copy() {
ByteBuffer buf = ByteBuffer.allocate(2048);
write(buf);
buf.flip();
return read(buf);
}
public boolean isEntropy_coding_mode_flag() {
return entropy_coding_mode_flag;
}
public int[] getNum_ref_idx_active_minus1() {
return num_ref_idx_active_minus1;
}
public int getSlice_group_change_rate_minus1() {
return slice_group_change_rate_minus1;
}
public int getPic_parameter_set_id() {
return pic_parameter_set_id;
}
public int getSeq_parameter_set_id() {
return seq_parameter_set_id;
}
public boolean isPic_order_present_flag() {
return pic_order_present_flag;
}
public int getNum_slice_groups_minus1() {
return num_slice_groups_minus1;
}
public int getSlice_group_map_type() {
return slice_group_map_type;
}
public boolean isWeighted_pred_flag() {
return weighted_pred_flag;
}
public int getWeighted_bipred_idc() {
return weighted_bipred_idc;
}
public int getPic_init_qp_minus26() {
return pic_init_qp_minus26;
}
public int getPic_init_qs_minus26() {
return pic_init_qs_minus26;
}
public int getChroma_qp_index_offset() {
return chroma_qp_index_offset;
}
public boolean isDeblocking_filter_control_present_flag() {
return deblocking_filter_control_present_flag;
}
public boolean isConstrained_intra_pred_flag() {
return constrained_intra_pred_flag;
}
public boolean isRedundant_pic_cnt_present_flag() {
return redundant_pic_cnt_present_flag;
}
public int[] getTop_left() {
return top_left;
}
public int[] getBottom_right() {
return bottom_right;
}
public int[] getRun_length_minus1() {
return run_length_minus1;
}
public boolean isSlice_group_change_direction_flag() {
return slice_group_change_direction_flag;
}
public int[] getSlice_group_id() {
return slice_group_id;
}
public PPSExt getExtended() {
return extended;
}
}