This source file includes following definitions.
- ctx
- allocate_thread_contexts
- set_image_allocation_functions
- start_thread_pool
- stop_thread_pool
- reset
- set_acceleration_functions
- init_thread_context
- add_task_decode_CTB_row
- add_task_decode_slice_segment
- read_vps_NAL
- read_sps_NAL
- read_pps_NAL
- read_sei_NAL
- read_eos_NAL
- read_slice_NAL
- pop_front
- decode_some
- decode_slice_unit_sequential
- mark_whole_slice_as_processed
- decode_slice_unit_parallel
- decode_slice_unit_WPP
- decode_slice_unit_tiles
- decode_NAL
- process_nal_hdr
- process_picture_order_count
- generate_unavailable_reference_picture
- process_reference_picture_set
- construct_reference_picture_lists
- run_postprocessing_filters_sequential
- run_postprocessing_filters_parallel
- push_picture_to_output_queue
- process_slice_segment_header
- remove_images_from_dpb
- get_highest_TID
- set_limit_TID
- change_framerate
- set_framerate_ratio
- compute_framedrop_table
- calc_tid_and_framerate_ratio
- add_warning
- get_warning
#include "decctx.h"
#include "util.h"
#include "sao.h"
#include "sei.h"
#include "deblock.h"
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "fallback.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SSE4_1
#include "x86/sse.h"
#endif
#ifdef HAVE_ARM
#include "arm/arm.h"
#endif
#define SAVE_INTERMEDIATE_IMAGES 0
#if SAVE_INTERMEDIATE_IMAGES
#include "visualize.h"
#endif
extern void thread_decode_CTB_row(void* d);
extern void thread_decode_slice_segment(void* d);
thread_context::thread_context()
{
IsCuQpDeltaCoded = false;
CuQpDelta = 0;
IsCuChromaQpOffsetCoded = false;
CuQpOffsetCb = 0;
CuQpOffsetCr = 0;
decctx = NULL;
img = NULL;
shdr = NULL;
imgunit = NULL;
sliceunit = NULL;
int offset = (uintptr_t)_coeffBuf & 0x0f;
if (offset == 0) {
coeffBuf = _coeffBuf;
}
else {
coeffBuf = (int16_t *) (((uint8_t *)_coeffBuf) + (16-offset));
}
memset(coeffBuf, 0, 32*32*sizeof(int16_t));
}
slice_unit::slice_unit(decoder_context* decctx)
: nal(NULL),
shdr(NULL),
imgunit(NULL),
flush_reorder_buffer(false),
nThreads(0),
first_decoded_CTB_RS(-1),
last_decoded_CTB_RS(-1),
thread_contexts(NULL),
ctx(decctx)
{
state = Unprocessed;
nThreadContexts = 0;
}
slice_unit::~slice_unit()
{
ctx->nal_parser.free_NAL_unit(nal);
if (thread_contexts) {
delete[] thread_contexts;
}
}
void slice_unit::allocate_thread_contexts(int n)
{
assert(thread_contexts==NULL);
thread_contexts = new thread_context[n];
nThreadContexts = n;
}
image_unit::image_unit()
{
img=NULL;
role=Invalid;
state=Unprocessed;
}
image_unit::~image_unit()
{
for (int i=0;i<slice_units.size();i++) {
delete slice_units[i];
}
for (int i=0;i<tasks.size();i++) {
delete tasks[i];
}
}
base_context::base_context()
{
set_acceleration_functions(de265_acceleration_AUTO);
}
decoder_context::decoder_context()
{
param_sei_check_hash = false;
param_conceal_stream_errors = true;
param_suppress_faulty_pictures = false;
param_disable_deblocking = false;
param_disable_sao = false;
param_sps_headers_fd = -1;
param_vps_headers_fd = -1;
param_pps_headers_fd = -1;
param_slice_headers_fd = -1;
param_image_allocation_functions = de265_image::default_image_allocation;
param_image_allocation_userdata = NULL;
current_vps = NULL;
current_sps = NULL;
current_pps = NULL;
num_worker_threads = 0;
limit_HighestTid = 6;
framerate_ratio = 100;
goal_HighestTid = 6;
current_HighestTid = 6;
layer_framerate_ratio = 100;
compute_framedrop_table();
current_image_poc_lsb = 0;
first_decoded_picture = 0;
NoRaslOutputFlag = 0;
HandleCraAsBlaFlag = 0;
FirstAfterEndOfSequenceNAL = 0;
PicOrderCntMsb = 0;
prevPicOrderCntLsb = 0;
prevPicOrderCntMsb = 0;
img = NULL;
first_decoded_picture = true;
current_image_poc_lsb = -1;
}
decoder_context::~decoder_context()
{
while (!image_units.empty()) {
delete image_units.back();
image_units.pop_back();
}
}
void decoder_context::set_image_allocation_functions(de265_image_allocation* allocfunc,
void* userdata)
{
if (allocfunc) {
param_image_allocation_functions = *allocfunc;
param_image_allocation_userdata = userdata;
}
else {
assert(false);
param_image_allocation_functions = de265_image::default_image_allocation;
param_image_allocation_userdata = NULL;
}
}
de265_error decoder_context::start_thread_pool(int nThreads)
{
::start_thread_pool(&thread_pool_, nThreads);
num_worker_threads = nThreads;
return DE265_OK;
}
void decoder_context::stop_thread_pool()
{
if (get_num_worker_threads()>0) {
::stop_thread_pool(&thread_pool_);
}
}
void decoder_context::reset()
{
if (num_worker_threads>0) {
::stop_thread_pool(&thread_pool_);
}
#if 0
ctx->end_of_stream = false;
ctx->pending_input_NAL = NULL;
ctx->current_vps = NULL;
ctx->current_sps = NULL;
ctx->current_pps = NULL;
ctx->num_worker_threads = 0;
ctx->current_image_poc_lsb = 0;
ctx->first_decoded_picture = 0;
ctx->NoRaslOutputFlag = 0;
ctx->HandleCraAsBlaFlag = 0;
ctx->FirstAfterEndOfSequenceNAL = 0;
ctx->PicOrderCntMsb = 0;
ctx->prevPicOrderCntLsb = 0;
ctx->prevPicOrderCntMsb = 0;
ctx->NumPocStCurrBefore=0;
ctx->NumPocStCurrAfter=0;
ctx->NumPocStFoll=0;
ctx->NumPocLtCurr=0;
ctx->NumPocLtFoll=0;
ctx->nal_unit_type=0;
ctx->IdrPicFlag=0;
ctx->RapPicFlag=0;
#endif
img = NULL;
current_image_poc_lsb = -1;
first_decoded_picture = true;
dpb.clear();
nal_parser.remove_pending_input_data();
while (!image_units.empty()) {
delete image_units.back();
image_units.pop_back();
}
if (num_worker_threads>0) {
start_thread_pool(num_worker_threads);
}
}
void base_context::set_acceleration_functions(enum de265_acceleration l)
{
init_acceleration_functions_fallback(&acceleration);
#ifdef HAVE_SSE4_1
if (l>=de265_acceleration_SSE) {
init_acceleration_functions_sse(&acceleration);
}
#endif
#ifdef HAVE_ARM
if (l>=de265_acceleration_ARM) {
init_acceleration_functions_arm(&acceleration);
}
#endif
}
void decoder_context::init_thread_context(thread_context* tctx)
{
memset(tctx->_coeffBuf, 0, sizeof(tctx->_coeffBuf));
tctx->currentQG_x = -1;
tctx->currentQG_y = -1;
const pic_parameter_set& pps = tctx->img->get_pps();
const seq_parameter_set& sps = tctx->img->get_sps();
if (tctx->shdr->slice_segment_address > 0) {
int prevCtb = pps.CtbAddrTStoRS[ pps.CtbAddrRStoTS[tctx->shdr->slice_segment_address] -1 ];
int ctbX = prevCtb % sps.PicWidthInCtbsY;
int ctbY = prevCtb / sps.PicWidthInCtbsY;
int x = ((ctbX+1) << sps.Log2CtbSizeY)-1;
int y = ((ctbY+1) << sps.Log2CtbSizeY)-1;
x = std::min(x,sps.pic_width_in_luma_samples-1);
y = std::min(y,sps.pic_height_in_luma_samples-1);
tctx->currentQPY = tctx->img->get_QPY(x,y);
}
}
void decoder_context::add_task_decode_CTB_row(thread_context* tctx,
bool firstSliceSubstream,
int ctbRow)
{
thread_task_ctb_row* task = new thread_task_ctb_row;
task->firstSliceSubstream = firstSliceSubstream;
task->tctx = tctx;
task->debug_startCtbRow = ctbRow;
tctx->task = task;
add_task(&thread_pool_, task);
tctx->imgunit->tasks.push_back(task);
}
void decoder_context::add_task_decode_slice_segment(thread_context* tctx, bool firstSliceSubstream,
int ctbx,int ctby)
{
thread_task_slice_segment* task = new thread_task_slice_segment;
task->firstSliceSubstream = firstSliceSubstream;
task->tctx = tctx;
task->debug_startCtbX = ctbx;
task->debug_startCtbY = ctby;
tctx->task = task;
add_task(&thread_pool_, task);
tctx->imgunit->tasks.push_back(task);
}
de265_error decoder_context::read_vps_NAL(bitreader& reader)
{
logdebug(LogHeaders,"---> read VPS\n");
std::shared_ptr<video_parameter_set> new_vps = std::make_shared<video_parameter_set>();
de265_error err = new_vps->read(this,&reader);
if (err != DE265_OK) {
return err;
}
if (param_vps_headers_fd>=0) {
new_vps->dump(param_vps_headers_fd);
}
vps[ new_vps->video_parameter_set_id ] = new_vps;
return DE265_OK;
}
de265_error decoder_context::read_sps_NAL(bitreader& reader)
{
logdebug(LogHeaders,"----> read SPS\n");
std::shared_ptr<seq_parameter_set> new_sps = std::make_shared<seq_parameter_set>();
de265_error err;
if ((err=new_sps->read(this, &reader)) != DE265_OK) {
return err;
}
if (param_sps_headers_fd>=0) {
new_sps->dump(param_sps_headers_fd);
}
sps[ new_sps->seq_parameter_set_id ] = new_sps;
return DE265_OK;
}
de265_error decoder_context::read_pps_NAL(bitreader& reader)
{
logdebug(LogHeaders,"----> read PPS\n");
std::shared_ptr<pic_parameter_set> new_pps = std::make_shared<pic_parameter_set>();
bool success = new_pps->read(&reader,this);
if (param_pps_headers_fd>=0) {
new_pps->dump(param_pps_headers_fd);
}
if (success) {
pps[ (int)new_pps->pic_parameter_set_id ] = new_pps;
}
return success ? DE265_OK : DE265_WARNING_PPS_HEADER_INVALID;
}
de265_error decoder_context::read_sei_NAL(bitreader& reader, bool suffix)
{
logdebug(LogHeaders,"----> read SEI\n");
sei_message sei;
de265_error err = DE265_OK;
if ((err=read_sei(&reader,&sei, suffix, current_sps.get())) == DE265_OK) {
dump_sei(&sei, current_sps.get());
if (image_units.empty()==false && suffix) {
image_units.back()->suffix_SEIs.push_back(sei);
}
}
else {
add_warning(err, false);
}
return err;
}
de265_error decoder_context::read_eos_NAL(bitreader& reader)
{
FirstAfterEndOfSequenceNAL = true;
return DE265_OK;
}
de265_error decoder_context::read_slice_NAL(bitreader& reader, NAL_unit* nal, nal_header& nal_hdr)
{
logdebug(LogHeaders,"---> read slice segment header\n");
slice_segment_header* shdr = new slice_segment_header;
bool continueDecoding;
de265_error err = shdr->read(&reader,this, &continueDecoding);
if (!continueDecoding) {
if (img) { img->integrity = INTEGRITY_NOT_DECODED; }
nal_parser.free_NAL_unit(nal);
delete shdr;
return err;
}
if (param_slice_headers_fd>=0) {
shdr->dump_slice_segment_header(this, param_slice_headers_fd);
}
if (process_slice_segment_header(shdr, &err, nal->pts, &nal_hdr, nal->user_data) == false)
{
if (img!=NULL) img->integrity = INTEGRITY_NOT_DECODED;
nal_parser.free_NAL_unit(nal);
delete shdr;
return err;
}
this->img->add_slice_segment_header(shdr);
skip_bits(&reader,1);
prepare_for_CABAC(&reader);
int headerLength = reader.data - nal->data();
for (int i=0;i<shdr->num_entry_point_offsets;i++) {
shdr->entry_point_offset[i] -= nal->num_skipped_bytes_before(shdr->entry_point_offset[i],
headerLength);
}
if (shdr->first_slice_segment_in_pic_flag) {
image_unit* imgunit = new image_unit;
imgunit->img = this->img;
image_units.push_back(imgunit);
}
if ( ! image_units.empty() ) {
slice_unit* sliceunit = new slice_unit(this);
sliceunit->nal = nal;
sliceunit->shdr = shdr;
sliceunit->reader = reader;
sliceunit->flush_reorder_buffer = flush_reorder_buffer_at_this_frame;
image_units.back()->slice_units.push_back(sliceunit);
}
bool did_work;
err = decode_some(&did_work);
return DE265_OK;
}
template <class T> void pop_front(std::vector<T>& vec)
{
for (int i=1;i<vec.size();i++)
vec[i-1] = vec[i];
vec.pop_back();
}
de265_error decoder_context::decode_some(bool* did_work)
{
de265_error err = DE265_OK;
*did_work = false;
if (image_units.empty()) { return DE265_OK; }
if ( ! image_units.empty() ) {
image_unit* imgunit = image_units[0];
slice_unit* sliceunit = imgunit->get_next_unprocessed_slice_segment();
if (sliceunit != NULL) {
if (sliceunit->flush_reorder_buffer) {
dpb.flush_reorder_buffer();
}
*did_work = true;
err = decode_slice_unit_parallel(imgunit, sliceunit);
if (err) {
return err;
}
}
}
if ( ( image_units.size()>=2 && image_units[0]->all_slice_segments_processed()) ||
( image_units.size()>=1 && image_units[0]->all_slice_segments_processed() &&
nal_parser.number_of_NAL_units_pending()==0 &&
(nal_parser.is_end_of_stream() || nal_parser.is_end_of_frame()) )) {
image_unit* imgunit = image_units[0];
*did_work=true;
imgunit->img->mark_all_CTB_progress(CTB_PROGRESS_PREFILTER);
if (img->decctx->num_worker_threads)
run_postprocessing_filters_parallel(imgunit);
else
run_postprocessing_filters_sequential(imgunit->img);
for (int i=0;i<imgunit->suffix_SEIs.size();i++) {
const sei_message& sei = imgunit->suffix_SEIs[i];
err = process_sei(&sei, imgunit->img);
if (err != DE265_OK)
break;
}
push_picture_to_output_queue(imgunit);
delete imgunit;
pop_front(image_units);
}
return err;
}
de265_error decoder_context::decode_slice_unit_sequential(image_unit* imgunit,
slice_unit* sliceunit)
{
de265_error err = DE265_OK;
remove_images_from_dpb(sliceunit->shdr->RemoveReferencesList);
if (sliceunit->shdr->slice_segment_address >= imgunit->img->get_pps().CtbAddrRStoTS.size()) {
return DE265_ERROR_CTB_OUTSIDE_IMAGE_AREA;
}
struct thread_context tctx;
tctx.shdr = sliceunit->shdr;
tctx.img = imgunit->img;
tctx.decctx = this;
tctx.imgunit = imgunit;
tctx.sliceunit= sliceunit;
tctx.CtbAddrInTS = imgunit->img->get_pps().CtbAddrRStoTS[tctx.shdr->slice_segment_address];
tctx.task = NULL;
init_thread_context(&tctx);
if (sliceunit->reader.bytes_remaining <= 0) {
return DE265_ERROR_PREMATURE_END_OF_SLICE;
}
init_CABAC_decoder(&tctx.cabac_decoder,
sliceunit->reader.data,
sliceunit->reader.bytes_remaining);
if (imgunit->img->get_pps().entropy_coding_sync_enabled_flag &&
sliceunit->shdr->first_slice_segment_in_pic_flag) {
imgunit->ctx_models.resize( (img->get_sps().PicHeightInCtbsY-1) );
}
sliceunit->nThreads=1;
err=read_slice_segment_data(&tctx);
sliceunit->finished_threads.set_progress(1);
return err;
}
void decoder_context::mark_whole_slice_as_processed(image_unit* imgunit,
slice_unit* sliceunit,
int progress)
{
slice_unit* nextSegment = imgunit->get_next_slice_segment(sliceunit);
if (nextSegment) {
for (int ctb=sliceunit->shdr->slice_segment_address;
ctb < nextSegment->shdr->slice_segment_address;
ctb++)
{
if (ctb >= imgunit->img->number_of_ctbs())
break;
imgunit->img->ctb_progress[ctb].set_progress(progress);
}
}
}
de265_error decoder_context::decode_slice_unit_parallel(image_unit* imgunit,
slice_unit* sliceunit)
{
de265_error err = DE265_OK;
remove_images_from_dpb(sliceunit->shdr->RemoveReferencesList);
de265_image* img = imgunit->img;
const pic_parameter_set& pps = img->get_pps();
sliceunit->state = slice_unit::InProgress;
bool use_WPP = (img->decctx->num_worker_threads > 0 &&
pps.entropy_coding_sync_enabled_flag);
bool use_tiles = (img->decctx->num_worker_threads > 0 &&
pps.tiles_enabled_flag);
if (img->decctx->num_worker_threads > 0 &&
pps.entropy_coding_sync_enabled_flag == false &&
pps.tiles_enabled_flag == false) {
img->decctx->add_warning(DE265_WARNING_NO_WPP_CANNOT_USE_MULTITHREADING, true);
}
if (imgunit->is_first_slice_segment(sliceunit)) {
slice_segment_header* shdr = sliceunit->shdr;
int firstCTB = shdr->slice_segment_address;
for (int ctb=0;ctb<firstCTB;ctb++) {
img->ctb_progress[ctb].set_progress(CTB_PROGRESS_PREFILTER);
}
}
slice_unit* prevSlice = imgunit->get_prev_slice_segment(sliceunit);
if (prevSlice && prevSlice->state == slice_unit::Decoded) {
mark_whole_slice_as_processed(imgunit,prevSlice,CTB_PROGRESS_PREFILTER);
}
if (!use_WPP && !use_tiles) {
err = decode_slice_unit_sequential(imgunit, sliceunit);
sliceunit->state = slice_unit::Decoded;
mark_whole_slice_as_processed(imgunit,sliceunit,CTB_PROGRESS_PREFILTER);
return err;
}
if (use_WPP && use_tiles) {
return DE265_WARNING_PPS_HEADER_INVALID;
}
if (use_WPP) {
err = decode_slice_unit_WPP(imgunit, sliceunit);
sliceunit->state = slice_unit::Decoded;
mark_whole_slice_as_processed(imgunit,sliceunit,CTB_PROGRESS_PREFILTER);
return err;
}
else if (use_tiles) {
err = decode_slice_unit_tiles(imgunit, sliceunit);
sliceunit->state = slice_unit::Decoded;
mark_whole_slice_as_processed(imgunit,sliceunit,CTB_PROGRESS_PREFILTER);
return err;
}
assert(false);
return err;
}
de265_error decoder_context::decode_slice_unit_WPP(image_unit* imgunit,
slice_unit* sliceunit)
{
de265_error err = DE265_OK;
de265_image* img = imgunit->img;
slice_segment_header* shdr = sliceunit->shdr;
const pic_parameter_set& pps = img->get_pps();
int nRows = shdr->num_entry_point_offsets +1;
int ctbsWidth = img->get_sps().PicWidthInCtbsY;
assert(img->num_threads_active() == 0);
if (shdr->first_slice_segment_in_pic_flag) {
imgunit->ctx_models.resize( (img->get_sps().PicHeightInCtbsY-1) );
}
sliceunit->allocate_thread_contexts(nRows);
int ctbAddrRS = shdr->slice_segment_address;
int ctbRow = ctbAddrRS / ctbsWidth;
for (int entryPt=0;entryPt<nRows;entryPt++) {
if (entryPt>0) {
ctbRow++;
ctbAddrRS = ctbRow * ctbsWidth;
}
else if (nRows>1 && (ctbAddrRS % ctbsWidth) != 0) {
err = DE265_WARNING_SLICEHEADER_INVALID;
break;
}
thread_context* tctx = sliceunit->get_thread_context(entryPt);
tctx->shdr = shdr;
tctx->decctx = img->decctx;
tctx->img = img;
tctx->imgunit = imgunit;
tctx->sliceunit= sliceunit;
tctx->CtbAddrInTS = pps.CtbAddrRStoTS[ctbAddrRS];
init_thread_context(tctx);
int dataStartIndex;
if (entryPt==0) { dataStartIndex=0; }
else { dataStartIndex=shdr->entry_point_offset[entryPt-1]; }
int dataEnd;
if (entryPt==nRows-1) dataEnd = sliceunit->reader.bytes_remaining;
else dataEnd = shdr->entry_point_offset[entryPt];
if (dataStartIndex<0 || dataEnd>sliceunit->reader.bytes_remaining ||
dataEnd <= dataStartIndex) {
err = DE265_ERROR_PREMATURE_END_OF_SLICE;
break;
}
init_CABAC_decoder(&tctx->cabac_decoder,
&sliceunit->reader.data[dataStartIndex],
dataEnd-dataStartIndex);
img->thread_start(1);
sliceunit->nThreads++;
add_task_decode_CTB_row(tctx, entryPt==0, ctbRow);
}
#if 0
for (;;) {
printf("q:%d r:%d b:%d f:%d\n",
img->nThreadsQueued,
img->nThreadsRunning,
img->nThreadsBlocked,
img->nThreadsFinished);
if (img->debug_is_completed()) break;
usleep(1000);
}
#endif
img->wait_for_completion();
for (int i=0;i<imgunit->tasks.size();i++)
delete imgunit->tasks[i];
imgunit->tasks.clear();
return DE265_OK;
}
de265_error decoder_context::decode_slice_unit_tiles(image_unit* imgunit,
slice_unit* sliceunit)
{
de265_error err = DE265_OK;
de265_image* img = imgunit->img;
slice_segment_header* shdr = sliceunit->shdr;
const pic_parameter_set& pps = img->get_pps();
int nTiles = shdr->num_entry_point_offsets +1;
int ctbsWidth = img->get_sps().PicWidthInCtbsY;
assert(img->num_threads_active() == 0);
sliceunit->allocate_thread_contexts(nTiles);
int ctbAddrRS = shdr->slice_segment_address;
int tileID = pps.TileIdRS[ctbAddrRS];
for (int entryPt=0;entryPt<nTiles;entryPt++) {
if (entryPt>0) {
tileID++;
if (tileID >= pps.num_tile_columns * pps.num_tile_rows) {
err = DE265_WARNING_SLICEHEADER_INVALID;
break;
}
int ctbX = pps.colBd[tileID % pps.num_tile_columns];
int ctbY = pps.rowBd[tileID / pps.num_tile_columns];
ctbAddrRS = ctbY * ctbsWidth + ctbX;
}
thread_context* tctx = sliceunit->get_thread_context(entryPt);
tctx->shdr = shdr;
tctx->decctx = img->decctx;
tctx->img = img;
tctx->imgunit = imgunit;
tctx->sliceunit= sliceunit;
tctx->CtbAddrInTS = pps.CtbAddrRStoTS[ctbAddrRS];
init_thread_context(tctx);
int dataStartIndex;
if (entryPt==0) { dataStartIndex=0; }
else { dataStartIndex=shdr->entry_point_offset[entryPt-1]; }
int dataEnd;
if (entryPt==nTiles-1) dataEnd = sliceunit->reader.bytes_remaining;
else dataEnd = shdr->entry_point_offset[entryPt];
if (dataStartIndex<0 || dataEnd>sliceunit->reader.bytes_remaining ||
dataEnd <= dataStartIndex) {
err = DE265_ERROR_PREMATURE_END_OF_SLICE;
break;
}
init_CABAC_decoder(&tctx->cabac_decoder,
&sliceunit->reader.data[dataStartIndex],
dataEnd-dataStartIndex);
img->thread_start(1);
sliceunit->nThreads++;
add_task_decode_slice_segment(tctx, entryPt==0,
ctbAddrRS % ctbsWidth,
ctbAddrRS / ctbsWidth);
}
img->wait_for_completion();
for (int i=0;i<imgunit->tasks.size();i++)
delete imgunit->tasks[i];
imgunit->tasks.clear();
return err;
}
de265_error decoder_context::decode_NAL(NAL_unit* nal)
{
decoder_context* ctx = this;
de265_error err = DE265_OK;
bitreader reader;
bitreader_init(&reader, nal->data(), nal->size());
nal_header nal_hdr;
nal_hdr.read(&reader);
ctx->process_nal_hdr(&nal_hdr);
if (nal_hdr.nuh_layer_id > 0) {
nal_parser.free_NAL_unit(nal);
return DE265_OK;
}
loginfo(LogHighlevel,"NAL: 0x%x 0x%x - unit type:%s temporal id:%d\n",
nal->data()[0], nal->data()[1],
get_NAL_name(nal_hdr.nal_unit_type),
nal_hdr.nuh_temporal_id);
if (nal_hdr.nuh_temporal_id > current_HighestTid) {
nal_parser.free_NAL_unit(nal);
return DE265_OK;
}
if (nal_hdr.nal_unit_type<32) {
err = read_slice_NAL(reader, nal, nal_hdr);
}
else switch (nal_hdr.nal_unit_type) {
case NAL_UNIT_VPS_NUT:
err = read_vps_NAL(reader);
nal_parser.free_NAL_unit(nal);
break;
case NAL_UNIT_SPS_NUT:
err = read_sps_NAL(reader);
nal_parser.free_NAL_unit(nal);
break;
case NAL_UNIT_PPS_NUT:
err = read_pps_NAL(reader);
nal_parser.free_NAL_unit(nal);
break;
case NAL_UNIT_PREFIX_SEI_NUT:
case NAL_UNIT_SUFFIX_SEI_NUT:
err = read_sei_NAL(reader, nal_hdr.nal_unit_type==NAL_UNIT_SUFFIX_SEI_NUT);
nal_parser.free_NAL_unit(nal);
break;
case NAL_UNIT_EOS_NUT:
ctx->FirstAfterEndOfSequenceNAL = true;
nal_parser.free_NAL_unit(nal);
break;
default:
nal_parser.free_NAL_unit(nal);
break;
}
return err;
}
de265_error decoder_context::decode(int* more)
{
decoder_context* ctx = this;
if (ctx->nal_parser.get_NAL_queue_length() == 0 &&
(ctx->nal_parser.is_end_of_stream() || ctx->nal_parser.is_end_of_frame()) &&
ctx->image_units.empty()) {
ctx->dpb.flush_reorder_buffer();
if (more) { *more = ctx->dpb.num_pictures_in_output_queue(); }
return DE265_OK;
}
if (ctx->nal_parser.is_end_of_stream() == false &&
ctx->nal_parser.is_end_of_frame() == false &&
ctx->nal_parser.get_NAL_queue_length() == 0) {
if (more) { *more=1; }
return DE265_ERROR_WAITING_FOR_INPUT_DATA;
}
if (!ctx->dpb.has_free_dpb_picture(false)) {
if (more) *more = 1;
return DE265_ERROR_IMAGE_BUFFER_FULL;
}
de265_error err = DE265_OK;
bool did_work = false;
if (ctx->nal_parser.get_NAL_queue_length()) {
NAL_unit* nal = ctx->nal_parser.pop_from_NAL_queue();
assert(nal);
err = ctx->decode_NAL(nal);
did_work=true;
}
else if (ctx->nal_parser.is_end_of_frame() == true &&
ctx->image_units.empty()) {
if (more) { *more=1; }
return DE265_ERROR_WAITING_FOR_INPUT_DATA;
}
else {
err = decode_some(&did_work);
}
if (more) {
*more = (err==DE265_OK && did_work);
}
return err;
}
void decoder_context::process_nal_hdr(nal_header* nal)
{
nal_unit_type = nal->nal_unit_type;
IdrPicFlag = isIdrPic(nal->nal_unit_type);
RapPicFlag = isRapPic(nal->nal_unit_type);
}
void decoder_context::process_picture_order_count(slice_segment_header* hdr)
{
loginfo(LogHeaders,"POC computation. lsb:%d prev.pic.lsb:%d msb:%d\n",
hdr->slice_pic_order_cnt_lsb,
prevPicOrderCntLsb,
PicOrderCntMsb);
if (isIRAP(nal_unit_type) &&
NoRaslOutputFlag)
{
PicOrderCntMsb=0;
flush_reorder_buffer_at_this_frame = true;
}
else
{
int MaxPicOrderCntLsb = current_sps->MaxPicOrderCntLsb;
if ((hdr->slice_pic_order_cnt_lsb < prevPicOrderCntLsb) &&
(prevPicOrderCntLsb - hdr->slice_pic_order_cnt_lsb) >= MaxPicOrderCntLsb/2) {
PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb;
}
else if ((hdr->slice_pic_order_cnt_lsb > prevPicOrderCntLsb) &&
(hdr->slice_pic_order_cnt_lsb - prevPicOrderCntLsb) > MaxPicOrderCntLsb/2) {
PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb;
}
else {
PicOrderCntMsb = prevPicOrderCntMsb;
}
}
img->PicOrderCntVal = PicOrderCntMsb + hdr->slice_pic_order_cnt_lsb;
img->picture_order_cnt_lsb = hdr->slice_pic_order_cnt_lsb;
loginfo(LogHeaders,"POC computation. new msb:%d POC=%d\n",
PicOrderCntMsb,
img->PicOrderCntVal);
if (img->nal_hdr.nuh_temporal_id==0 &&
!isSublayerNonReference(nal_unit_type) &&
!isRASL(nal_unit_type) &&
!isRADL(nal_unit_type))
{
loginfo(LogHeaders,"set prevPicOrderCntLsb/Msb\n");
prevPicOrderCntLsb = hdr->slice_pic_order_cnt_lsb;
prevPicOrderCntMsb = PicOrderCntMsb;
}
}
int decoder_context::generate_unavailable_reference_picture(const seq_parameter_set* sps,
int POC, bool longTerm)
{
assert(dpb.has_free_dpb_picture(true));
std::shared_ptr<const seq_parameter_set> current_sps = this->sps[ (int)current_pps->seq_parameter_set_id ];
int idx = dpb.new_image(current_sps, this, 0,0, false);
assert(idx>=0);
de265_image* img = dpb.get_image(idx);
img->fill_image(1<<(sps->BitDepth_Y-1),
1<<(sps->BitDepth_C-1),
1<<(sps->BitDepth_C-1));
img->fill_pred_mode(MODE_INTRA);
img->PicOrderCntVal = POC;
img->picture_order_cnt_lsb = POC & (sps->MaxPicOrderCntLsb-1);
img->PicOutputFlag = false;
img->PicState = (longTerm ? UsedForLongTermReference : UsedForShortTermReference);
img->integrity = INTEGRITY_UNAVAILABLE_REFERENCE;
return idx;
}
void decoder_context::process_reference_picture_set(slice_segment_header* hdr)
{
std::vector<int> removeReferencesList;
const int currentID = img->get_ID();
if (isIRAP(nal_unit_type) && NoRaslOutputFlag) {
int currentPOC = img->PicOrderCntVal;
for (int i=0;i<dpb.size();i++) {
de265_image* img = dpb.get_image(i);
if (img->PicState != UnusedForReference &&
img->PicOrderCntVal < currentPOC &&
img->removed_at_picture_id > img->get_ID()) {
removeReferencesList.push_back(img->get_ID());
img->removed_at_picture_id = img->get_ID();
}
}
}
if (isIDR(nal_unit_type)) {
NumPocStCurrBefore = 0;
NumPocStCurrAfter = 0;
NumPocStFoll = 0;
NumPocLtCurr = 0;
NumPocLtFoll = 0;
}
else {
const ref_pic_set* rps = &hdr->CurrRps;
int i,j,k;
for (i=0, j=0, k=0;
i<rps->NumNegativePics;
i++)
{
if (rps->UsedByCurrPicS0[i]) {
PocStCurrBefore[j++] = img->PicOrderCntVal + rps->DeltaPocS0[i];
}
else {
PocStFoll[k++] = img->PicOrderCntVal + rps->DeltaPocS0[i];
}
}
NumPocStCurrBefore = j;
for (i=0, j=0;
i<rps->NumPositivePics;
i++)
{
if (rps->UsedByCurrPicS1[i]) {
PocStCurrAfter[j++] = img->PicOrderCntVal + rps->DeltaPocS1[i];
}
else {
PocStFoll[k++] = img->PicOrderCntVal + rps->DeltaPocS1[i];
}
}
NumPocStCurrAfter = j;
NumPocStFoll = k;
for (i=0, j=0, k=0;
i<hdr->num_long_term_sps + hdr->num_long_term_pics;
i++)
{
int pocLt = PocLsbLt[i];
if (hdr->delta_poc_msb_present_flag[i]) {
int currentPictureMSB = img->PicOrderCntVal - hdr->slice_pic_order_cnt_lsb;
pocLt += currentPictureMSB
- DeltaPocMsbCycleLt[i] * current_sps->MaxPicOrderCntLsb;
}
if (UsedByCurrPicLt[i]) {
PocLtCurr[j] = pocLt;
CurrDeltaPocMsbPresentFlag[j] = hdr->delta_poc_msb_present_flag[i];
j++;
}
else {
PocLtFoll[k] = pocLt;
FollDeltaPocMsbPresentFlag[k] = hdr->delta_poc_msb_present_flag[i];
k++;
}
}
NumPocLtCurr = j;
NumPocLtFoll = k;
}
std::vector<char> picInAnyList(dpb.size(), false);
dpb.log_dpb_content();
for (int i=0;i<NumPocLtCurr;i++) {
int k;
if (!CurrDeltaPocMsbPresentFlag[i]) {
k = dpb.DPB_index_of_picture_with_LSB(PocLtCurr[i], currentID, true);
}
else {
k = dpb.DPB_index_of_picture_with_POC(PocLtCurr[i], currentID, true);
}
RefPicSetLtCurr[i] = k;
if (k>=0) picInAnyList[k]=true;
else {
int concealedPicture = generate_unavailable_reference_picture(current_sps.get(),
PocLtCurr[i], true);
RefPicSetLtCurr[i] = k = concealedPicture;
picInAnyList[concealedPicture]=true;
}
if (dpb.get_image(k)->integrity != INTEGRITY_CORRECT) {
img->integrity = INTEGRITY_DERIVED_FROM_FAULTY_REFERENCE;
}
}
for (int i=0;i<NumPocLtFoll;i++) {
int k;
if (!FollDeltaPocMsbPresentFlag[i]) {
k = dpb.DPB_index_of_picture_with_LSB(PocLtFoll[i], currentID, true);
}
else {
k = dpb.DPB_index_of_picture_with_POC(PocLtFoll[i], currentID, true);
}
RefPicSetLtFoll[i] = k;
if (k>=0) picInAnyList[k]=true;
else {
int concealedPicture = k = generate_unavailable_reference_picture(current_sps.get(),
PocLtFoll[i], true);
RefPicSetLtFoll[i] = concealedPicture;
picInAnyList[concealedPicture]=true;
}
}
for (int i=0;i<NumPocLtCurr;i++) {
dpb.get_image(RefPicSetLtCurr[i])->PicState = UsedForLongTermReference;
}
for (int i=0;i<NumPocLtFoll;i++) {
dpb.get_image(RefPicSetLtFoll[i])->PicState = UsedForLongTermReference;
}
for (int i=0;i<NumPocStCurrBefore;i++) {
int k = dpb.DPB_index_of_picture_with_POC(PocStCurrBefore[i], currentID);
RefPicSetStCurrBefore[i] = k;
if (k>=0) picInAnyList[k]=true;
else {
int concealedPicture = generate_unavailable_reference_picture(current_sps.get(),
PocStCurrBefore[i], false);
RefPicSetStCurrBefore[i] = k = concealedPicture;
if (concealedPicture < picInAnyList.size()) {
picInAnyList[concealedPicture] = true;
}
}
if (dpb.get_image(k)->integrity != INTEGRITY_CORRECT) {
img->integrity = INTEGRITY_DERIVED_FROM_FAULTY_REFERENCE;
}
}
for (int i=0;i<NumPocStCurrAfter;i++) {
int k = dpb.DPB_index_of_picture_with_POC(PocStCurrAfter[i], currentID);
RefPicSetStCurrAfter[i] = k;
if (k>=0) picInAnyList[k]=true;
else {
int concealedPicture = generate_unavailable_reference_picture(current_sps.get(),
PocStCurrAfter[i], false);
RefPicSetStCurrAfter[i] = k = concealedPicture;
picInAnyList[concealedPicture]=true;
}
if (dpb.get_image(k)->integrity != INTEGRITY_CORRECT) {
img->integrity = INTEGRITY_DERIVED_FROM_FAULTY_REFERENCE;
}
}
for (int i=0;i<NumPocStFoll;i++) {
int k = dpb.DPB_index_of_picture_with_POC(PocStFoll[i], currentID);
RefPicSetStFoll[i] = k;
if (k>=0) picInAnyList[k]=true;
}
for (int i=0;i<dpb.size();i++)
if (i>=picInAnyList.size() || !picInAnyList[i])
{
de265_image* dpbimg = dpb.get_image(i);
if (dpbimg != img &&
dpbimg->removed_at_picture_id > img->get_ID())
{
if (dpbimg->PicState != UnusedForReference) {
removeReferencesList.push_back(dpbimg->get_ID());
dpbimg->removed_at_picture_id = img->get_ID();
}
}
}
hdr->RemoveReferencesList = removeReferencesList;
}
bool decoder_context::construct_reference_picture_lists(slice_segment_header* hdr)
{
int NumPocTotalCurr = hdr->NumPocTotalCurr;
int NumRpsCurrTempList0 = libde265_max(hdr->num_ref_idx_l0_active, NumPocTotalCurr);
int RefPicListTemp0[3*MAX_NUM_REF_PICS];
int RefPicListTemp1[3*MAX_NUM_REF_PICS];
char isLongTerm[2][3*MAX_NUM_REF_PICS];
memset(isLongTerm,0,2*3*MAX_NUM_REF_PICS);
int rIdx=0;
while (rIdx < NumRpsCurrTempList0) {
for (int i=0;i<NumPocStCurrBefore && rIdx<NumRpsCurrTempList0; rIdx++,i++)
RefPicListTemp0[rIdx] = RefPicSetStCurrBefore[i];
for (int i=0;i<NumPocStCurrAfter && rIdx<NumRpsCurrTempList0; rIdx++,i++)
RefPicListTemp0[rIdx] = RefPicSetStCurrAfter[i];
for (int i=0;i<NumPocLtCurr && rIdx<NumRpsCurrTempList0; rIdx++,i++) {
RefPicListTemp0[rIdx] = RefPicSetLtCurr[i];
isLongTerm[0][rIdx] = true;
}
if (rIdx==0) {
add_warning(DE265_WARNING_FAULTY_REFERENCE_PICTURE_LIST, false);
return false;
}
}
assert(hdr->num_ref_idx_l0_active <= 16);
for (rIdx=0; rIdx<hdr->num_ref_idx_l0_active; rIdx++) {
int idx = hdr->ref_pic_list_modification_flag_l0 ? hdr->list_entry_l0[rIdx] : rIdx;
hdr->RefPicList[0][rIdx] = RefPicListTemp0[idx];
hdr->LongTermRefPic[0][rIdx] = isLongTerm[0][idx];
de265_image* img_0_rIdx = dpb.get_image(hdr->RefPicList[0][rIdx]);
if (img_0_rIdx==NULL) {
return false;
}
hdr->RefPicList_POC[0][rIdx] = img_0_rIdx->PicOrderCntVal;
hdr->RefPicList_PicState[0][rIdx] = img_0_rIdx->PicState;
}
if (hdr->slice_type == SLICE_TYPE_B) {
int NumRpsCurrTempList1 = libde265_max(hdr->num_ref_idx_l1_active, NumPocTotalCurr);
int rIdx=0;
while (rIdx < NumRpsCurrTempList1) {
for (int i=0;i<NumPocStCurrAfter && rIdx<NumRpsCurrTempList1; rIdx++,i++) {
RefPicListTemp1[rIdx] = RefPicSetStCurrAfter[i];
}
for (int i=0;i<NumPocStCurrBefore && rIdx<NumRpsCurrTempList1; rIdx++,i++) {
RefPicListTemp1[rIdx] = RefPicSetStCurrBefore[i];
}
for (int i=0;i<NumPocLtCurr && rIdx<NumRpsCurrTempList1; rIdx++,i++) {
RefPicListTemp1[rIdx] = RefPicSetLtCurr[i];
isLongTerm[1][rIdx] = true;
}
if (rIdx==0) {
add_warning(DE265_WARNING_FAULTY_REFERENCE_PICTURE_LIST, false);
return false;
}
}
if (hdr->num_ref_idx_l0_active > 16) {
add_warning(DE265_WARNING_NONEXISTING_REFERENCE_PICTURE_ACCESSED, false);
return false;
}
assert(hdr->num_ref_idx_l1_active <= 16);
for (rIdx=0; rIdx<hdr->num_ref_idx_l1_active; rIdx++) {
int idx = hdr->ref_pic_list_modification_flag_l1 ? hdr->list_entry_l1[rIdx] : rIdx;
hdr->RefPicList[1][rIdx] = RefPicListTemp1[idx];
hdr->LongTermRefPic[1][rIdx] = isLongTerm[1][idx];
de265_image* img_1_rIdx = dpb.get_image(hdr->RefPicList[1][rIdx]);
if (img_1_rIdx == NULL) { return false; }
hdr->RefPicList_POC[1][rIdx] = img_1_rIdx->PicOrderCntVal;
hdr->RefPicList_PicState[1][rIdx] = img_1_rIdx->PicState;
}
}
loginfo(LogHeaders,"RefPicList[0] =");
for (rIdx=0; rIdx<hdr->num_ref_idx_l0_active; rIdx++) {
loginfo(LogHeaders,"* [%d]=%d (LT=%d)",
hdr->RefPicList[0][rIdx],
hdr->RefPicList_POC[0][rIdx],
hdr->LongTermRefPic[0][rIdx]
);
}
loginfo(LogHeaders,"*\n");
if (hdr->slice_type == SLICE_TYPE_B) {
loginfo(LogHeaders,"RefPicList[1] =");
for (rIdx=0; rIdx<hdr->num_ref_idx_l1_active; rIdx++) {
loginfo(LogHeaders,"* [%d]=%d (LT=%d)",
hdr->RefPicList[1][rIdx],
hdr->RefPicList_POC[1][rIdx],
hdr->LongTermRefPic[1][rIdx]
);
}
loginfo(LogHeaders,"*\n");
}
return true;
}
void decoder_context::run_postprocessing_filters_sequential(de265_image* img)
{
#if SAVE_INTERMEDIATE_IMAGES
char buf[1000];
sprintf(buf,"pre-lf-%05d.yuv", img->PicOrderCntVal);
write_picture_to_file(img, buf);
#endif
if (!img->decctx->param_disable_deblocking) {
apply_deblocking_filter(img);
}
#if SAVE_INTERMEDIATE_IMAGES
sprintf(buf,"pre-sao-%05d.yuv", img->PicOrderCntVal);
write_picture_to_file(img, buf);
#endif
if (!img->decctx->param_disable_sao) {
apply_sample_adaptive_offset_sequential(img);
}
#if SAVE_INTERMEDIATE_IMAGES
sprintf(buf,"sao-%05d.yuv", img->PicOrderCntVal);
write_picture_to_file(img, buf);
#endif
}
void decoder_context::run_postprocessing_filters_parallel(image_unit* imgunit)
{
de265_image* img = imgunit->img;
int saoWaitsForProgress = CTB_PROGRESS_PREFILTER;
bool waitForCompletion = false;
if (!img->decctx->param_disable_deblocking) {
add_deblocking_tasks(imgunit);
saoWaitsForProgress = CTB_PROGRESS_DEBLK_H;
}
if (!img->decctx->param_disable_sao) {
waitForCompletion |= add_sao_tasks(imgunit, saoWaitsForProgress);
}
img->wait_for_completion();
}
de265_error decoder_context::push_picture_to_output_queue(image_unit* imgunit)
{
de265_image* outimg = imgunit->img;
if (outimg==NULL) { return DE265_OK; }
if (outimg->PicOutputFlag) {
loginfo(LogDPB,"new picture has output-flag=true\n");
if (outimg->integrity != INTEGRITY_CORRECT &&
param_suppress_faulty_pictures) {
}
else {
dpb.insert_image_into_reorder_buffer(outimg);
}
loginfo(LogDPB,"push image %d into reordering queue\n", outimg->PicOrderCntVal);
}
int maxNumPicsInReorderBuffer = 0;
if (outimg->has_vps()) {
int sublayer = outimg->get_vps().vps_max_sub_layers -1;
maxNumPicsInReorderBuffer = outimg->get_vps().layer[sublayer].vps_max_num_reorder_pics;
}
if (dpb.num_pictures_in_reorder_buffer() > maxNumPicsInReorderBuffer) {
dpb.output_next_picture_in_reorder_buffer();
}
dpb.log_dpb_queues();
return DE265_OK;
}
bool decoder_context::process_slice_segment_header(slice_segment_header* hdr,
de265_error* err, de265_PTS pts,
nal_header* nal_hdr,
void* user_data)
{
*err = DE265_OK;
flush_reorder_buffer_at_this_frame = false;
int pps_id = hdr->slice_pic_parameter_set_id;
if (pps[pps_id]->pps_read==false) {
logerror(LogHeaders, "PPS %d has not been read\n", pps_id);
assert(false);
}
current_pps = pps[pps_id];
current_sps = sps[ (int)current_pps->seq_parameter_set_id ];
current_vps = vps[ (int)current_sps->video_parameter_set_id ];
calc_tid_and_framerate_ratio();
if (hdr->first_slice_segment_in_pic_flag) {
current_image_poc_lsb = hdr->slice_pic_order_cnt_lsb;
seq_parameter_set* sps = current_sps.get();
int image_buffer_idx;
bool isOutputImage = (!sps->sample_adaptive_offset_enabled_flag || param_disable_sao);
image_buffer_idx = dpb.new_image(current_sps, this, pts, user_data, isOutputImage);
if (image_buffer_idx == -1) {
*err = DE265_ERROR_IMAGE_BUFFER_FULL;
return false;
}
img = dpb.get_image(image_buffer_idx);
img->nal_hdr = *nal_hdr;
img->set_headers(current_vps, current_sps, current_pps);
img->decctx = this;
img->clear_metadata();
if (isIRAP(nal_unit_type)) {
if (isIDR(nal_unit_type) ||
isBLA(nal_unit_type) ||
first_decoded_picture ||
FirstAfterEndOfSequenceNAL)
{
NoRaslOutputFlag = true;
FirstAfterEndOfSequenceNAL = false;
}
else if (0)
{
}
else
{
NoRaslOutputFlag = false;
HandleCraAsBlaFlag = false;
}
}
if (isRASL(nal_unit_type) &&
NoRaslOutputFlag)
{
img->PicOutputFlag = false;
}
else
{
img->PicOutputFlag = !!hdr->pic_output_flag;
}
process_picture_order_count(hdr);
if (hdr->first_slice_segment_in_pic_flag) {
img->PicState = UsedForShortTermReference;
process_reference_picture_set(hdr);
}
img->PicState = UsedForShortTermReference;
log_set_current_POC(img->PicOrderCntVal);
first_decoded_picture = false;
}
else {
if (img == NULL) {
return false;
}
}
if (hdr->slice_type == SLICE_TYPE_B ||
hdr->slice_type == SLICE_TYPE_P)
{
bool success = construct_reference_picture_lists(hdr);
if (!success) {
return false;
}
}
loginfo(LogHeaders,"end of process-slice-header\n");
dpb.log_dpb_content();
if (hdr->dependent_slice_segment_flag==0) {
hdr->SliceAddrRS = hdr->slice_segment_address;
} else {
hdr->SliceAddrRS = previous_slice_header->SliceAddrRS;
}
previous_slice_header = hdr;
loginfo(LogHeaders,"SliceAddrRS = %d\n",hdr->SliceAddrRS);
return true;
}
void decoder_context::remove_images_from_dpb(const std::vector<int>& removeImageList)
{
for (int i=0;i<removeImageList.size();i++) {
int idx = dpb.DPB_index_of_picture_with_ID( removeImageList[i] );
if (idx>=0) {
de265_image* dpbimg = dpb.get_image( idx );
dpbimg->PicState = UnusedForReference;
}
}
}
int decoder_context::get_highest_TID() const
{
if (current_sps) { return current_sps->sps_max_sub_layers-1; }
if (current_vps) { return current_vps->vps_max_sub_layers-1; }
return 6;
}
void decoder_context::set_limit_TID(int max_tid)
{
limit_HighestTid = max_tid;
calc_tid_and_framerate_ratio();
}
int decoder_context::change_framerate(int more)
{
if (current_sps == NULL) { return framerate_ratio; }
int highestTid = get_highest_TID();
assert(more>=-1 && more<=1);
goal_HighestTid += more;
goal_HighestTid = std::max(goal_HighestTid, 0);
goal_HighestTid = std::min(goal_HighestTid, highestTid);
framerate_ratio = framedrop_tid_index[goal_HighestTid];
calc_tid_and_framerate_ratio();
return framerate_ratio;
}
void decoder_context::set_framerate_ratio(int percent)
{
framerate_ratio = percent;
calc_tid_and_framerate_ratio();
}
void decoder_context::compute_framedrop_table()
{
int highestTID = get_highest_TID();
for (int tid=highestTID ; tid>=0 ; tid--) {
int lower = 100 * tid /(highestTID+1);
int higher = 100 * (tid+1)/(highestTID+1);
for (int l=lower; l<=higher; l++) {
int ratio = 100 * (l-lower) / (higher-lower);
if (tid > limit_HighestTid) {
tid = limit_HighestTid;
ratio = 100;
}
framedrop_tab[l].tid = tid;
framedrop_tab[l].ratio = ratio;
}
framedrop_tid_index[tid] = higher;
}
#if 0
for (int i=0;i<=100;i++) {
printf("%d%%: %d/%d",i, framedrop_tab[i].tid, framedrop_tab[i].ratio);
for (int k=0;k<=highestTID;k++) {
if (framedrop_tid_index[k] == i) printf(" ** TID=%d **",k);
}
printf("\n");
}
#endif
}
void decoder_context::calc_tid_and_framerate_ratio()
{
int highestTID = get_highest_TID();
if (framedrop_tab[100].tid != highestTID) {
compute_framedrop_table();
}
goal_HighestTid = framedrop_tab[framerate_ratio].tid;
layer_framerate_ratio = framedrop_tab[framerate_ratio].ratio;
current_HighestTid = goal_HighestTid;
}
void error_queue::add_warning(de265_error warning, bool once)
{
bool add=true;
if (once) {
for (int i=0;i<nWarningsShown;i++) {
if (warnings_shown[i] == warning) {
add=false;
break;
}
}
}
if (!add) {
return;
}
if (once) {
if (nWarningsShown < MAX_WARNINGS) {
warnings_shown[nWarningsShown++] = warning;
}
}
if (nWarnings == MAX_WARNINGS) {
warnings[MAX_WARNINGS-1] = DE265_WARNING_WARNING_BUFFER_FULL;
return;
}
warnings[nWarnings++] = warning;
}
error_queue::error_queue()
{
nWarnings = 0;
nWarningsShown = 0;
}
de265_error error_queue::get_warning()
{
if (nWarnings==0) {
return DE265_OK;
}
de265_error warn = warnings[0];
nWarnings--;
memmove(warnings, &warnings[1], nWarnings*sizeof(de265_error));
return warn;
}