blob: 4fe47dac5b51a983892f10a3ee3c33a62f6a6656 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -----------------------------------------------------------------------------
//
// Functions used to skip frames and glimpse at future frame features.
//
// Author: Yannis Guyon (yguyon@google.com)
#include "src/dec/incr/decoder_skip.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include "src/common/constants.h"
#include "src/dec/incr/decoder_info.h"
#include "src/dec/incr/decoder_state.h"
#include "src/dec/tile_dec.h"
#include "src/dec/wp2_dec_i.h"
#include "src/utils/utils.h"
#include "src/wp2/base.h"
#include "src/wp2/decode.h"
namespace WP2 {
//------------------------------------------------------------------------------
void CopyGlimpseFromCurrent(Decoder::State* const state) {
state->glimpsed_bitstream_position =
state->data_source->GetNumDiscardedBytes();
state->glimpsed_frame_index = state->current_frame_index;
if (state->stage < Decoder::State::Stage::WAITING_FOR_TILE_DATA) {
state->glimpsed_frame_anmf =
(state->stage > Decoder::State::Stage::WAITING_FOR_ANMF_CHUNK);
state->glimpsed_frame_glbl = false;
state->glimpsed_frame_gparams.Reset();
state->num_glimpsed_tiles = 0;
} else {
state->glimpsed_frame_anmf = true;
state->glimpsed_frame_glbl = true;
// DecodeTileChunkSize() needs only GlobalParamas::type_,transf_,has_alpha_.
state->glimpsed_frame_gparams.type_ = state->gparams.type_;
state->glimpsed_frame_gparams.transf_ = state->gparams.transf_;
state->glimpsed_frame_gparams.has_alpha_ = state->gparams.has_alpha_;
state->num_glimpsed_tiles = state->tiles_layout.num_decoded_tiles;
if (state->tiles_layout.num_decoded_tiles <
state->tiles_layout.tiles.size()) {
const Tile& partial_tile =
state->tiles_layout.tiles[state->tiles_layout.num_decoded_tiles];
if (partial_tile.chunk_size_is_known) {
++state->num_glimpsed_tiles;
state->glimpsed_bitstream_position += partial_tile.chunk_size;
}
}
}
}
WP2Status GlimpseTillNextANMFChunk(const DecoderConfig& config,
const BitstreamFeatures& features,
Decoder::State* const state) {
// Only the next ANMF chunk is wanted; skip any tile before it.
if (state->glimpsed_frame_anmf && state->glimpsed_frame_glbl) {
const Decoder::State::InternalFrameFeatures& glimpsed_frame_features =
state->frames[state->glimpsed_frame_index];
const Rectangle& window = glimpsed_frame_features.info.window;
const uint32_t tile_width =
TileWidth(features.tile_shape, window.width, window.height);
const uint32_t tile_height =
TileHeight(features.tile_shape, window.width, window.height);
const uint32_t num_tiles =
GetNumTiles(window.width, window.height, tile_width, tile_height);
while (state->num_glimpsed_tiles < num_tiles) {
const Rectangle& tile_rect =
GetTileRect(window.width, window.height, tile_width, tile_height,
state->num_glimpsed_tiles);
uint32_t tile_chunk_size = 0;
WP2_CHECK_STATUS(DecodeTileChunkSize(
features.rgb_bit_depth, state->glimpsed_frame_gparams, tile_rect,
state->data_source, &tile_chunk_size));
state->data_source->MarkNumBytesAsRead(tile_chunk_size);
++state->num_glimpsed_tiles;
}
WP2_CHECK_STATUS(
SaveFrameNumBytes(state->data_source->GetNumDiscardedBytes() +
state->data_source->GetNumReadBytes(),
&state->frames[state->glimpsed_frame_index]));
if (!glimpsed_frame_features.info.is_last) {
++state->glimpsed_frame_index;
state->glimpsed_frame_anmf = false;
state->glimpsed_frame_glbl = false;
state->glimpsed_frame_gparams.Reset();
state->num_glimpsed_tiles = 0;
}
}
if (!state->glimpsed_frame_anmf) {
WP2_CHECK_STATUS(DecodeFrameInfo(config, state->data_source, features,
/*glimpse=*/true, state));
state->glimpsed_frame_anmf = true;
assert(!state->glimpsed_frame_glbl);
}
if (!state->glimpsed_frame_glbl) {
WP2_CHECK_STATUS(DecodeGLBL(state->data_source, config, features,
&state->glimpsed_frame_gparams));
state->glimpsed_frame_glbl = true;
assert(state->glimpsed_frame_anmf);
}
return WP2_STATUS_OK;
}
WP2Status GlimpseAtNextFrame(const DecoderConfig& config,
const BitstreamFeatures& features,
Decoder::State* const state) {
assert(state->data_source->GetNumReadBytes() == 0);
const size_t current_bitstream_position =
state->data_source->GetNumDiscardedBytes();
if (state->glimpsed_bitstream_position < current_bitstream_position) {
CopyGlimpseFromCurrent(state);
}
state->data_source->MarkNumBytesAsRead(state->glimpsed_bitstream_position -
current_bitstream_position);
const WP2Status status = GlimpseTillNextANMFChunk(config, features, state);
state->glimpsed_bitstream_position =
current_bitstream_position + state->data_source->GetNumReadBytes();
state->data_source->UnmarkAllReadBytes();
return status;
}
//------------------------------------------------------------------------------
bool DiscardWholeFrames(Decoder::State* const state) {
if (state->frames.empty()) return false;
uint32_t target_frame_index =
std::min(state->discard_frames_before_index,
static_cast<uint32_t>(state->frames.size()));
if (target_frame_index <= state->current_frame_index) return false;
size_t target_bitstream_position =
state->frames[target_frame_index - 1].bitstream_position;
if (state->frames[target_frame_index - 1].num_bytes !=
Decoder::State::kUnknown) {
target_bitstream_position +=
state->frames[target_frame_index - 1].num_bytes;
} else {
// Previous 'size' is not yet available so one less discarded frame.
target_frame_index -= 1;
if (target_frame_index <= state->current_frame_index) return false;
}
const size_t bitstream_position = state->data_source->GetNumDiscardedBytes();
if (bitstream_position > target_bitstream_position) {
state->status = WP2_STATUS_BAD_READ; // Data source changed.
} else {
state->data_source->Discard(target_bitstream_position - bitstream_position);
state->current_frame_index = target_frame_index;
state->frame_num_converted_rows = 0;
state->frame_num_final_rows = 0;
state->intertile_filter.Clear();
}
return true; // Going directly to WAITING_FOR_ANMF_CHUNK is expected.
}
bool DiscardTillMetadata(Decoder::State* const state) {
if (state->ShouldDiscardAllFrames() && !state->frames.empty() &&
state->frames.back().is_last &&
state->frames.back().num_bytes != Decoder::State::kUnknown) {
const size_t metadata_bitstream_position =
state->frames.back().bitstream_position +
state->frames.back().num_bytes;
const size_t bitstream_position =
state->data_source->GetNumDiscardedBytes();
if (bitstream_position > metadata_bitstream_position) {
// This function is called only during frame decoding, so being past the
// first byte of metadata can only mean that the data source changed.
state->status = WP2_STATUS_BAD_READ;
return false;
}
state->data_source->Discard(metadata_bitstream_position -
bitstream_position);
// If 'metadata_bitstream_position' is known, so are all frames.
assert(!state->frames.empty() && state->frames.back().info.is_last);
state->current_frame_index = (uint32_t)state->frames.size() - 1;
state->frame_num_converted_rows = 0;
state->frame_num_final_rows = 0;
state->intertile_filter.Clear();
return true; // Going directly to WAITING_FOR_METADATA_CHUNKS is expected.
}
return false;
}
//------------------------------------------------------------------------------
} // namespace WP2