1*09537850SAkhilesh Sanikop // Copyright 2019 The libgav1 Authors
2*09537850SAkhilesh Sanikop //
3*09537850SAkhilesh Sanikop // Licensed under the Apache License, Version 2.0 (the "License");
4*09537850SAkhilesh Sanikop // you may not use this file except in compliance with the License.
5*09537850SAkhilesh Sanikop // You may obtain a copy of the License at
6*09537850SAkhilesh Sanikop //
7*09537850SAkhilesh Sanikop // http://www.apache.org/licenses/LICENSE-2.0
8*09537850SAkhilesh Sanikop //
9*09537850SAkhilesh Sanikop // Unless required by applicable law or agreed to in writing, software
10*09537850SAkhilesh Sanikop // distributed under the License is distributed on an "AS IS" BASIS,
11*09537850SAkhilesh Sanikop // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*09537850SAkhilesh Sanikop // See the License for the specific language governing permissions and
13*09537850SAkhilesh Sanikop // limitations under the License.
14*09537850SAkhilesh Sanikop
15*09537850SAkhilesh Sanikop #include "src/decoder_impl.h"
16*09537850SAkhilesh Sanikop
17*09537850SAkhilesh Sanikop #include <algorithm>
18*09537850SAkhilesh Sanikop #include <atomic>
19*09537850SAkhilesh Sanikop #include <cassert>
20*09537850SAkhilesh Sanikop #include <iterator>
21*09537850SAkhilesh Sanikop #include <new>
22*09537850SAkhilesh Sanikop #include <utility>
23*09537850SAkhilesh Sanikop
24*09537850SAkhilesh Sanikop #include "src/dsp/common.h"
25*09537850SAkhilesh Sanikop #include "src/dsp/constants.h"
26*09537850SAkhilesh Sanikop #include "src/dsp/dsp.h"
27*09537850SAkhilesh Sanikop #include "src/film_grain.h"
28*09537850SAkhilesh Sanikop #include "src/frame_buffer_utils.h"
29*09537850SAkhilesh Sanikop #include "src/frame_scratch_buffer.h"
30*09537850SAkhilesh Sanikop #include "src/loop_restoration_info.h"
31*09537850SAkhilesh Sanikop #include "src/obu_parser.h"
32*09537850SAkhilesh Sanikop #include "src/post_filter.h"
33*09537850SAkhilesh Sanikop #include "src/prediction_mask.h"
34*09537850SAkhilesh Sanikop #include "src/threading_strategy.h"
35*09537850SAkhilesh Sanikop #include "src/utils/blocking_counter.h"
36*09537850SAkhilesh Sanikop #include "src/utils/common.h"
37*09537850SAkhilesh Sanikop #include "src/utils/constants.h"
38*09537850SAkhilesh Sanikop #include "src/utils/logging.h"
39*09537850SAkhilesh Sanikop #include "src/utils/raw_bit_reader.h"
40*09537850SAkhilesh Sanikop #include "src/utils/segmentation.h"
41*09537850SAkhilesh Sanikop #include "src/utils/threadpool.h"
42*09537850SAkhilesh Sanikop #include "src/yuv_buffer.h"
43*09537850SAkhilesh Sanikop
44*09537850SAkhilesh Sanikop namespace libgav1 {
45*09537850SAkhilesh Sanikop namespace {
46*09537850SAkhilesh Sanikop
47*09537850SAkhilesh Sanikop constexpr int kMaxBlockWidth4x4 = 32;
48*09537850SAkhilesh Sanikop constexpr int kMaxBlockHeight4x4 = 32;
49*09537850SAkhilesh Sanikop
50*09537850SAkhilesh Sanikop // Computes the bottom border size in pixels. If CDEF, loop restoration or
51*09537850SAkhilesh Sanikop // SuperRes is enabled, adds extra border pixels to facilitate those steps to
52*09537850SAkhilesh Sanikop // happen nearly in-place (a few extra rows instead of an entire frame buffer).
53*09537850SAkhilesh Sanikop // The logic in this function should match the corresponding logic for
54*09537850SAkhilesh Sanikop // |vertical_shift| in the PostFilter constructor.
GetBottomBorderPixels(const bool do_cdef,const bool do_restoration,const bool do_superres,const int subsampling_y)55*09537850SAkhilesh Sanikop int GetBottomBorderPixels(const bool do_cdef, const bool do_restoration,
56*09537850SAkhilesh Sanikop const bool do_superres, const int subsampling_y) {
57*09537850SAkhilesh Sanikop int extra_border = 0;
58*09537850SAkhilesh Sanikop if (do_cdef) {
59*09537850SAkhilesh Sanikop extra_border += kCdefBorder;
60*09537850SAkhilesh Sanikop } else if (do_restoration) {
61*09537850SAkhilesh Sanikop // If CDEF is enabled, loop restoration is safe without extra border.
62*09537850SAkhilesh Sanikop extra_border += kRestorationVerticalBorder;
63*09537850SAkhilesh Sanikop }
64*09537850SAkhilesh Sanikop if (do_superres) extra_border += kSuperResVerticalBorder;
65*09537850SAkhilesh Sanikop // Double the number of extra bottom border pixels if the bottom border will
66*09537850SAkhilesh Sanikop // be subsampled.
67*09537850SAkhilesh Sanikop extra_border <<= subsampling_y;
68*09537850SAkhilesh Sanikop return Align(kBorderPixels + extra_border, 2); // Must be a multiple of 2.
69*09537850SAkhilesh Sanikop }
70*09537850SAkhilesh Sanikop
71*09537850SAkhilesh Sanikop // Sets |frame_scratch_buffer->tile_decoding_failed| to true (while holding on
72*09537850SAkhilesh Sanikop // to |frame_scratch_buffer->superblock_row_mutex|) and notifies the first
73*09537850SAkhilesh Sanikop // |count| condition variables in
74*09537850SAkhilesh Sanikop // |frame_scratch_buffer->superblock_row_progress_condvar|.
SetFailureAndNotifyAll(FrameScratchBuffer * const frame_scratch_buffer,int count)75*09537850SAkhilesh Sanikop void SetFailureAndNotifyAll(FrameScratchBuffer* const frame_scratch_buffer,
76*09537850SAkhilesh Sanikop int count) {
77*09537850SAkhilesh Sanikop {
78*09537850SAkhilesh Sanikop std::lock_guard<std::mutex> lock(
79*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_mutex);
80*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_decoding_failed = true;
81*09537850SAkhilesh Sanikop }
82*09537850SAkhilesh Sanikop std::condition_variable* const condvars =
83*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_progress_condvar.get();
84*09537850SAkhilesh Sanikop for (int i = 0; i < count; ++i) {
85*09537850SAkhilesh Sanikop condvars[i].notify_one();
86*09537850SAkhilesh Sanikop }
87*09537850SAkhilesh Sanikop }
88*09537850SAkhilesh Sanikop
89*09537850SAkhilesh Sanikop // Helper class that releases the frame scratch buffer in the destructor.
90*09537850SAkhilesh Sanikop class FrameScratchBufferReleaser {
91*09537850SAkhilesh Sanikop public:
FrameScratchBufferReleaser(FrameScratchBufferPool * frame_scratch_buffer_pool,std::unique_ptr<FrameScratchBuffer> * frame_scratch_buffer)92*09537850SAkhilesh Sanikop FrameScratchBufferReleaser(
93*09537850SAkhilesh Sanikop FrameScratchBufferPool* frame_scratch_buffer_pool,
94*09537850SAkhilesh Sanikop std::unique_ptr<FrameScratchBuffer>* frame_scratch_buffer)
95*09537850SAkhilesh Sanikop : frame_scratch_buffer_pool_(frame_scratch_buffer_pool),
96*09537850SAkhilesh Sanikop frame_scratch_buffer_(frame_scratch_buffer) {}
~FrameScratchBufferReleaser()97*09537850SAkhilesh Sanikop ~FrameScratchBufferReleaser() {
98*09537850SAkhilesh Sanikop frame_scratch_buffer_pool_->Release(std::move(*frame_scratch_buffer_));
99*09537850SAkhilesh Sanikop }
100*09537850SAkhilesh Sanikop
101*09537850SAkhilesh Sanikop private:
102*09537850SAkhilesh Sanikop FrameScratchBufferPool* const frame_scratch_buffer_pool_;
103*09537850SAkhilesh Sanikop std::unique_ptr<FrameScratchBuffer>* const frame_scratch_buffer_;
104*09537850SAkhilesh Sanikop };
105*09537850SAkhilesh Sanikop
106*09537850SAkhilesh Sanikop // Sets the |frame|'s segmentation map for two cases. The third case is handled
107*09537850SAkhilesh Sanikop // in Tile::DecodeBlock().
SetSegmentationMap(const ObuFrameHeader & frame_header,const SegmentationMap * prev_segment_ids,RefCountedBuffer * const frame)108*09537850SAkhilesh Sanikop void SetSegmentationMap(const ObuFrameHeader& frame_header,
109*09537850SAkhilesh Sanikop const SegmentationMap* prev_segment_ids,
110*09537850SAkhilesh Sanikop RefCountedBuffer* const frame) {
111*09537850SAkhilesh Sanikop if (!frame_header.segmentation.enabled) {
112*09537850SAkhilesh Sanikop // All segment_id's are 0.
113*09537850SAkhilesh Sanikop frame->segmentation_map()->Clear();
114*09537850SAkhilesh Sanikop } else if (!frame_header.segmentation.update_map) {
115*09537850SAkhilesh Sanikop // Copy from prev_segment_ids.
116*09537850SAkhilesh Sanikop if (prev_segment_ids == nullptr) {
117*09537850SAkhilesh Sanikop // Treat a null prev_segment_ids pointer as if it pointed to a
118*09537850SAkhilesh Sanikop // segmentation map containing all 0s.
119*09537850SAkhilesh Sanikop frame->segmentation_map()->Clear();
120*09537850SAkhilesh Sanikop } else {
121*09537850SAkhilesh Sanikop frame->segmentation_map()->CopyFrom(*prev_segment_ids);
122*09537850SAkhilesh Sanikop }
123*09537850SAkhilesh Sanikop }
124*09537850SAkhilesh Sanikop }
125*09537850SAkhilesh Sanikop
DecodeTilesNonFrameParallel(const ObuSequenceHeader & sequence_header,const ObuFrameHeader & frame_header,const Vector<std::unique_ptr<Tile>> & tiles,FrameScratchBuffer * const frame_scratch_buffer,PostFilter * const post_filter)126*09537850SAkhilesh Sanikop StatusCode DecodeTilesNonFrameParallel(
127*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header,
128*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header,
129*09537850SAkhilesh Sanikop const Vector<std::unique_ptr<Tile>>& tiles,
130*09537850SAkhilesh Sanikop FrameScratchBuffer* const frame_scratch_buffer,
131*09537850SAkhilesh Sanikop PostFilter* const post_filter) {
132*09537850SAkhilesh Sanikop // Decode in superblock row order.
133*09537850SAkhilesh Sanikop const int block_width4x4 = sequence_header.use_128x128_superblock ? 32 : 16;
134*09537850SAkhilesh Sanikop std::unique_ptr<TileScratchBuffer> tile_scratch_buffer =
135*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_scratch_buffer_pool.Get();
136*09537850SAkhilesh Sanikop if (tile_scratch_buffer == nullptr) return kLibgav1StatusOutOfMemory;
137*09537850SAkhilesh Sanikop for (int row4x4 = 0; row4x4 < frame_header.rows4x4;
138*09537850SAkhilesh Sanikop row4x4 += block_width4x4) {
139*09537850SAkhilesh Sanikop for (const auto& tile_ptr : tiles) {
140*09537850SAkhilesh Sanikop if (!tile_ptr->ProcessSuperBlockRow<kProcessingModeParseAndDecode, true>(
141*09537850SAkhilesh Sanikop row4x4, tile_scratch_buffer.get())) {
142*09537850SAkhilesh Sanikop return kLibgav1StatusUnknownError;
143*09537850SAkhilesh Sanikop }
144*09537850SAkhilesh Sanikop }
145*09537850SAkhilesh Sanikop post_filter->ApplyFilteringForOneSuperBlockRow(
146*09537850SAkhilesh Sanikop row4x4, block_width4x4, row4x4 + block_width4x4 >= frame_header.rows4x4,
147*09537850SAkhilesh Sanikop /*do_deblock=*/true);
148*09537850SAkhilesh Sanikop }
149*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_scratch_buffer_pool.Release(
150*09537850SAkhilesh Sanikop std::move(tile_scratch_buffer));
151*09537850SAkhilesh Sanikop return kStatusOk;
152*09537850SAkhilesh Sanikop }
153*09537850SAkhilesh Sanikop
DecodeTilesThreadedNonFrameParallel(const Vector<std::unique_ptr<Tile>> & tiles,FrameScratchBuffer * const frame_scratch_buffer,PostFilter * const post_filter,BlockingCounterWithStatus * const pending_tiles)154*09537850SAkhilesh Sanikop StatusCode DecodeTilesThreadedNonFrameParallel(
155*09537850SAkhilesh Sanikop const Vector<std::unique_ptr<Tile>>& tiles,
156*09537850SAkhilesh Sanikop FrameScratchBuffer* const frame_scratch_buffer,
157*09537850SAkhilesh Sanikop PostFilter* const post_filter,
158*09537850SAkhilesh Sanikop BlockingCounterWithStatus* const pending_tiles) {
159*09537850SAkhilesh Sanikop ThreadingStrategy& threading_strategy =
160*09537850SAkhilesh Sanikop frame_scratch_buffer->threading_strategy;
161*09537850SAkhilesh Sanikop const int num_workers = threading_strategy.tile_thread_count();
162*09537850SAkhilesh Sanikop BlockingCounterWithStatus pending_workers(num_workers);
163*09537850SAkhilesh Sanikop std::atomic<int> tile_counter(0);
164*09537850SAkhilesh Sanikop const int tile_count = static_cast<int>(tiles.size());
165*09537850SAkhilesh Sanikop bool tile_decoding_failed = false;
166*09537850SAkhilesh Sanikop // Submit tile decoding jobs to the thread pool.
167*09537850SAkhilesh Sanikop for (int i = 0; i < num_workers; ++i) {
168*09537850SAkhilesh Sanikop threading_strategy.tile_thread_pool()->Schedule([&tiles, tile_count,
169*09537850SAkhilesh Sanikop &tile_counter,
170*09537850SAkhilesh Sanikop &pending_workers,
171*09537850SAkhilesh Sanikop &pending_tiles]() {
172*09537850SAkhilesh Sanikop bool failed = false;
173*09537850SAkhilesh Sanikop int index;
174*09537850SAkhilesh Sanikop while ((index = tile_counter.fetch_add(1, std::memory_order_relaxed)) <
175*09537850SAkhilesh Sanikop tile_count) {
176*09537850SAkhilesh Sanikop if (!failed) {
177*09537850SAkhilesh Sanikop const auto& tile_ptr = tiles[index];
178*09537850SAkhilesh Sanikop if (!tile_ptr->ParseAndDecode()) {
179*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Error decoding tile #%d", tile_ptr->number());
180*09537850SAkhilesh Sanikop failed = true;
181*09537850SAkhilesh Sanikop }
182*09537850SAkhilesh Sanikop } else {
183*09537850SAkhilesh Sanikop pending_tiles->Decrement(false);
184*09537850SAkhilesh Sanikop }
185*09537850SAkhilesh Sanikop }
186*09537850SAkhilesh Sanikop pending_workers.Decrement(!failed);
187*09537850SAkhilesh Sanikop });
188*09537850SAkhilesh Sanikop }
189*09537850SAkhilesh Sanikop // Have the current thread partake in tile decoding.
190*09537850SAkhilesh Sanikop int index;
191*09537850SAkhilesh Sanikop while ((index = tile_counter.fetch_add(1, std::memory_order_relaxed)) <
192*09537850SAkhilesh Sanikop tile_count) {
193*09537850SAkhilesh Sanikop if (!tile_decoding_failed) {
194*09537850SAkhilesh Sanikop const auto& tile_ptr = tiles[index];
195*09537850SAkhilesh Sanikop if (!tile_ptr->ParseAndDecode()) {
196*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Error decoding tile #%d", tile_ptr->number());
197*09537850SAkhilesh Sanikop tile_decoding_failed = true;
198*09537850SAkhilesh Sanikop }
199*09537850SAkhilesh Sanikop } else {
200*09537850SAkhilesh Sanikop pending_tiles->Decrement(false);
201*09537850SAkhilesh Sanikop }
202*09537850SAkhilesh Sanikop }
203*09537850SAkhilesh Sanikop // Wait until all the workers are done. This ensures that all the tiles have
204*09537850SAkhilesh Sanikop // been parsed.
205*09537850SAkhilesh Sanikop tile_decoding_failed |= !pending_workers.Wait();
206*09537850SAkhilesh Sanikop // Wait until all the tiles have been decoded.
207*09537850SAkhilesh Sanikop tile_decoding_failed |= !pending_tiles->Wait();
208*09537850SAkhilesh Sanikop if (tile_decoding_failed) return kStatusUnknownError;
209*09537850SAkhilesh Sanikop assert(threading_strategy.post_filter_thread_pool() != nullptr);
210*09537850SAkhilesh Sanikop post_filter->ApplyFilteringThreaded();
211*09537850SAkhilesh Sanikop return kStatusOk;
212*09537850SAkhilesh Sanikop }
213*09537850SAkhilesh Sanikop
DecodeTilesFrameParallel(const ObuSequenceHeader & sequence_header,const ObuFrameHeader & frame_header,const Vector<std::unique_ptr<Tile>> & tiles,const SymbolDecoderContext & saved_symbol_decoder_context,const SegmentationMap * const prev_segment_ids,FrameScratchBuffer * const frame_scratch_buffer,PostFilter * const post_filter,RefCountedBuffer * const current_frame)214*09537850SAkhilesh Sanikop StatusCode DecodeTilesFrameParallel(
215*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header,
216*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header,
217*09537850SAkhilesh Sanikop const Vector<std::unique_ptr<Tile>>& tiles,
218*09537850SAkhilesh Sanikop const SymbolDecoderContext& saved_symbol_decoder_context,
219*09537850SAkhilesh Sanikop const SegmentationMap* const prev_segment_ids,
220*09537850SAkhilesh Sanikop FrameScratchBuffer* const frame_scratch_buffer,
221*09537850SAkhilesh Sanikop PostFilter* const post_filter, RefCountedBuffer* const current_frame) {
222*09537850SAkhilesh Sanikop // Parse the frame.
223*09537850SAkhilesh Sanikop for (const auto& tile : tiles) {
224*09537850SAkhilesh Sanikop if (!tile->Parse()) {
225*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to parse tile number: %d\n", tile->number());
226*09537850SAkhilesh Sanikop return kStatusUnknownError;
227*09537850SAkhilesh Sanikop }
228*09537850SAkhilesh Sanikop }
229*09537850SAkhilesh Sanikop if (frame_header.enable_frame_end_update_cdf) {
230*09537850SAkhilesh Sanikop frame_scratch_buffer->symbol_decoder_context = saved_symbol_decoder_context;
231*09537850SAkhilesh Sanikop }
232*09537850SAkhilesh Sanikop current_frame->SetFrameContext(frame_scratch_buffer->symbol_decoder_context);
233*09537850SAkhilesh Sanikop SetSegmentationMap(frame_header, prev_segment_ids, current_frame);
234*09537850SAkhilesh Sanikop // Mark frame as parsed.
235*09537850SAkhilesh Sanikop current_frame->SetFrameState(kFrameStateParsed);
236*09537850SAkhilesh Sanikop std::unique_ptr<TileScratchBuffer> tile_scratch_buffer =
237*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_scratch_buffer_pool.Get();
238*09537850SAkhilesh Sanikop if (tile_scratch_buffer == nullptr) {
239*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
240*09537850SAkhilesh Sanikop }
241*09537850SAkhilesh Sanikop const int block_width4x4 = sequence_header.use_128x128_superblock ? 32 : 16;
242*09537850SAkhilesh Sanikop // Decode in superblock row order (inter prediction in the Tile class will
243*09537850SAkhilesh Sanikop // block until the required superblocks in the reference frame are decoded).
244*09537850SAkhilesh Sanikop for (int row4x4 = 0; row4x4 < frame_header.rows4x4;
245*09537850SAkhilesh Sanikop row4x4 += block_width4x4) {
246*09537850SAkhilesh Sanikop for (const auto& tile_ptr : tiles) {
247*09537850SAkhilesh Sanikop if (!tile_ptr->ProcessSuperBlockRow<kProcessingModeDecodeOnly, false>(
248*09537850SAkhilesh Sanikop row4x4, tile_scratch_buffer.get())) {
249*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to decode tile number: %d\n",
250*09537850SAkhilesh Sanikop tile_ptr->number());
251*09537850SAkhilesh Sanikop return kStatusUnknownError;
252*09537850SAkhilesh Sanikop }
253*09537850SAkhilesh Sanikop }
254*09537850SAkhilesh Sanikop const int progress_row = post_filter->ApplyFilteringForOneSuperBlockRow(
255*09537850SAkhilesh Sanikop row4x4, block_width4x4, row4x4 + block_width4x4 >= frame_header.rows4x4,
256*09537850SAkhilesh Sanikop /*do_deblock=*/true);
257*09537850SAkhilesh Sanikop if (progress_row >= 0) {
258*09537850SAkhilesh Sanikop current_frame->SetProgress(progress_row);
259*09537850SAkhilesh Sanikop }
260*09537850SAkhilesh Sanikop }
261*09537850SAkhilesh Sanikop // Mark frame as decoded (we no longer care about row-level progress since the
262*09537850SAkhilesh Sanikop // entire frame has been decoded).
263*09537850SAkhilesh Sanikop current_frame->SetFrameState(kFrameStateDecoded);
264*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_scratch_buffer_pool.Release(
265*09537850SAkhilesh Sanikop std::move(tile_scratch_buffer));
266*09537850SAkhilesh Sanikop return kStatusOk;
267*09537850SAkhilesh Sanikop }
268*09537850SAkhilesh Sanikop
269*09537850SAkhilesh Sanikop // Helper function used by DecodeTilesThreadedFrameParallel. Applies the
270*09537850SAkhilesh Sanikop // deblocking filter for tile boundaries for the superblock row at |row4x4|.
ApplyDeblockingFilterForTileBoundaries(PostFilter * const post_filter,const std::unique_ptr<Tile> * tile_row_base,const ObuFrameHeader & frame_header,int row4x4,int block_width4x4,int tile_columns,bool decode_entire_tiles_in_worker_threads)271*09537850SAkhilesh Sanikop void ApplyDeblockingFilterForTileBoundaries(
272*09537850SAkhilesh Sanikop PostFilter* const post_filter, const std::unique_ptr<Tile>* tile_row_base,
273*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header, int row4x4, int block_width4x4,
274*09537850SAkhilesh Sanikop int tile_columns, bool decode_entire_tiles_in_worker_threads) {
275*09537850SAkhilesh Sanikop // Apply vertical deblock filtering for the first 64 columns of each tile.
276*09537850SAkhilesh Sanikop for (int tile_column = 0; tile_column < tile_columns; ++tile_column) {
277*09537850SAkhilesh Sanikop const Tile& tile = *tile_row_base[tile_column];
278*09537850SAkhilesh Sanikop post_filter->ApplyDeblockFilter(
279*09537850SAkhilesh Sanikop kLoopFilterTypeVertical, row4x4, tile.column4x4_start(),
280*09537850SAkhilesh Sanikop tile.column4x4_start() + kNum4x4InLoopFilterUnit, block_width4x4);
281*09537850SAkhilesh Sanikop }
282*09537850SAkhilesh Sanikop if (decode_entire_tiles_in_worker_threads &&
283*09537850SAkhilesh Sanikop row4x4 == tile_row_base[0]->row4x4_start()) {
284*09537850SAkhilesh Sanikop // This is the first superblock row of a tile row. In this case, apply
285*09537850SAkhilesh Sanikop // horizontal deblock filtering for the entire superblock row.
286*09537850SAkhilesh Sanikop post_filter->ApplyDeblockFilter(kLoopFilterTypeHorizontal, row4x4, 0,
287*09537850SAkhilesh Sanikop frame_header.columns4x4, block_width4x4);
288*09537850SAkhilesh Sanikop } else {
289*09537850SAkhilesh Sanikop // Apply horizontal deblock filtering for the first 64 columns of the
290*09537850SAkhilesh Sanikop // first tile.
291*09537850SAkhilesh Sanikop const Tile& first_tile = *tile_row_base[0];
292*09537850SAkhilesh Sanikop post_filter->ApplyDeblockFilter(
293*09537850SAkhilesh Sanikop kLoopFilterTypeHorizontal, row4x4, first_tile.column4x4_start(),
294*09537850SAkhilesh Sanikop first_tile.column4x4_start() + kNum4x4InLoopFilterUnit, block_width4x4);
295*09537850SAkhilesh Sanikop // Apply horizontal deblock filtering for the last 64 columns of the
296*09537850SAkhilesh Sanikop // previous tile and the first 64 columns of the current tile.
297*09537850SAkhilesh Sanikop for (int tile_column = 1; tile_column < tile_columns; ++tile_column) {
298*09537850SAkhilesh Sanikop const Tile& tile = *tile_row_base[tile_column];
299*09537850SAkhilesh Sanikop // If the previous tile has more than 64 columns, then include those
300*09537850SAkhilesh Sanikop // for the horizontal deblock.
301*09537850SAkhilesh Sanikop const Tile& previous_tile = *tile_row_base[tile_column - 1];
302*09537850SAkhilesh Sanikop const int column4x4_start =
303*09537850SAkhilesh Sanikop tile.column4x4_start() -
304*09537850SAkhilesh Sanikop ((tile.column4x4_start() - kNum4x4InLoopFilterUnit !=
305*09537850SAkhilesh Sanikop previous_tile.column4x4_start())
306*09537850SAkhilesh Sanikop ? kNum4x4InLoopFilterUnit
307*09537850SAkhilesh Sanikop : 0);
308*09537850SAkhilesh Sanikop post_filter->ApplyDeblockFilter(
309*09537850SAkhilesh Sanikop kLoopFilterTypeHorizontal, row4x4, column4x4_start,
310*09537850SAkhilesh Sanikop tile.column4x4_start() + kNum4x4InLoopFilterUnit, block_width4x4);
311*09537850SAkhilesh Sanikop }
312*09537850SAkhilesh Sanikop // Apply horizontal deblock filtering for the last 64 columns of the
313*09537850SAkhilesh Sanikop // last tile.
314*09537850SAkhilesh Sanikop const Tile& last_tile = *tile_row_base[tile_columns - 1];
315*09537850SAkhilesh Sanikop // Identify the last column4x4 value and do horizontal filtering for
316*09537850SAkhilesh Sanikop // that column4x4. The value of last column4x4 is the nearest multiple
317*09537850SAkhilesh Sanikop // of 16 that is before tile.column4x4_end().
318*09537850SAkhilesh Sanikop const int column4x4_start = (last_tile.column4x4_end() - 1) & ~15;
319*09537850SAkhilesh Sanikop // If column4x4_start is the same as tile.column4x4_start() then it
320*09537850SAkhilesh Sanikop // means that the last tile has <= 64 columns. So there is nothing left
321*09537850SAkhilesh Sanikop // to deblock (since it was already deblocked in the loop above).
322*09537850SAkhilesh Sanikop if (column4x4_start != last_tile.column4x4_start()) {
323*09537850SAkhilesh Sanikop post_filter->ApplyDeblockFilter(
324*09537850SAkhilesh Sanikop kLoopFilterTypeHorizontal, row4x4, column4x4_start,
325*09537850SAkhilesh Sanikop last_tile.column4x4_end(), block_width4x4);
326*09537850SAkhilesh Sanikop }
327*09537850SAkhilesh Sanikop }
328*09537850SAkhilesh Sanikop }
329*09537850SAkhilesh Sanikop
330*09537850SAkhilesh Sanikop // Helper function used by DecodeTilesThreadedFrameParallel. Decodes the
331*09537850SAkhilesh Sanikop // superblock row starting at |row4x4| for tile at index |tile_index| in the
332*09537850SAkhilesh Sanikop // list of tiles |tiles|. If the decoding is successful, then it does the
333*09537850SAkhilesh Sanikop // following:
334*09537850SAkhilesh Sanikop // * Schedule the next superblock row in the current tile column for decoding
335*09537850SAkhilesh Sanikop // (the next superblock row may be in a different tile than the current
336*09537850SAkhilesh Sanikop // one).
337*09537850SAkhilesh Sanikop // * If an entire superblock row of the frame has been decoded, it notifies
338*09537850SAkhilesh Sanikop // the waiters (if there are any).
DecodeSuperBlockRowInTile(const Vector<std::unique_ptr<Tile>> & tiles,size_t tile_index,int row4x4,const int superblock_size4x4,const int tile_columns,const int superblock_rows,FrameScratchBuffer * const frame_scratch_buffer,PostFilter * const post_filter,BlockingCounter * const pending_jobs)339*09537850SAkhilesh Sanikop void DecodeSuperBlockRowInTile(
340*09537850SAkhilesh Sanikop const Vector<std::unique_ptr<Tile>>& tiles, size_t tile_index, int row4x4,
341*09537850SAkhilesh Sanikop const int superblock_size4x4, const int tile_columns,
342*09537850SAkhilesh Sanikop const int superblock_rows, FrameScratchBuffer* const frame_scratch_buffer,
343*09537850SAkhilesh Sanikop PostFilter* const post_filter, BlockingCounter* const pending_jobs) {
344*09537850SAkhilesh Sanikop std::unique_ptr<TileScratchBuffer> scratch_buffer =
345*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_scratch_buffer_pool.Get();
346*09537850SAkhilesh Sanikop if (scratch_buffer == nullptr) {
347*09537850SAkhilesh Sanikop SetFailureAndNotifyAll(frame_scratch_buffer, superblock_rows);
348*09537850SAkhilesh Sanikop return;
349*09537850SAkhilesh Sanikop }
350*09537850SAkhilesh Sanikop Tile& tile = *tiles[tile_index];
351*09537850SAkhilesh Sanikop const bool ok = tile.ProcessSuperBlockRow<kProcessingModeDecodeOnly, false>(
352*09537850SAkhilesh Sanikop row4x4, scratch_buffer.get());
353*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_scratch_buffer_pool.Release(
354*09537850SAkhilesh Sanikop std::move(scratch_buffer));
355*09537850SAkhilesh Sanikop if (!ok) {
356*09537850SAkhilesh Sanikop SetFailureAndNotifyAll(frame_scratch_buffer, superblock_rows);
357*09537850SAkhilesh Sanikop return;
358*09537850SAkhilesh Sanikop }
359*09537850SAkhilesh Sanikop if (post_filter->DoDeblock()) {
360*09537850SAkhilesh Sanikop // Apply vertical deblock filtering for all the columns in this tile except
361*09537850SAkhilesh Sanikop // for the first 64 columns.
362*09537850SAkhilesh Sanikop post_filter->ApplyDeblockFilter(
363*09537850SAkhilesh Sanikop kLoopFilterTypeVertical, row4x4,
364*09537850SAkhilesh Sanikop tile.column4x4_start() + kNum4x4InLoopFilterUnit, tile.column4x4_end(),
365*09537850SAkhilesh Sanikop superblock_size4x4);
366*09537850SAkhilesh Sanikop // Apply horizontal deblock filtering for all the columns in this tile
367*09537850SAkhilesh Sanikop // except for the first and the last 64 columns.
368*09537850SAkhilesh Sanikop // Note about the last tile of each row: For the last tile, column4x4_end
369*09537850SAkhilesh Sanikop // may not be a multiple of 16. In that case it is still okay to simply
370*09537850SAkhilesh Sanikop // subtract 16 since ApplyDeblockFilter() will only do the filters in
371*09537850SAkhilesh Sanikop // increments of 64 columns (or 32 columns for chroma with subsampling).
372*09537850SAkhilesh Sanikop post_filter->ApplyDeblockFilter(
373*09537850SAkhilesh Sanikop kLoopFilterTypeHorizontal, row4x4,
374*09537850SAkhilesh Sanikop tile.column4x4_start() + kNum4x4InLoopFilterUnit,
375*09537850SAkhilesh Sanikop tile.column4x4_end() - kNum4x4InLoopFilterUnit, superblock_size4x4);
376*09537850SAkhilesh Sanikop }
377*09537850SAkhilesh Sanikop const int superblock_size4x4_log2 = FloorLog2(superblock_size4x4);
378*09537850SAkhilesh Sanikop const int index = row4x4 >> superblock_size4x4_log2;
379*09537850SAkhilesh Sanikop int* const superblock_row_progress =
380*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_progress.get();
381*09537850SAkhilesh Sanikop std::condition_variable* const superblock_row_progress_condvar =
382*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_progress_condvar.get();
383*09537850SAkhilesh Sanikop bool notify;
384*09537850SAkhilesh Sanikop {
385*09537850SAkhilesh Sanikop std::lock_guard<std::mutex> lock(
386*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_mutex);
387*09537850SAkhilesh Sanikop notify = ++superblock_row_progress[index] == tile_columns;
388*09537850SAkhilesh Sanikop }
389*09537850SAkhilesh Sanikop if (notify) {
390*09537850SAkhilesh Sanikop // We are done decoding this superblock row. Notify the post filtering
391*09537850SAkhilesh Sanikop // thread.
392*09537850SAkhilesh Sanikop superblock_row_progress_condvar[index].notify_one();
393*09537850SAkhilesh Sanikop }
394*09537850SAkhilesh Sanikop // Schedule the next superblock row (if one exists).
395*09537850SAkhilesh Sanikop ThreadPool& thread_pool =
396*09537850SAkhilesh Sanikop *frame_scratch_buffer->threading_strategy.thread_pool();
397*09537850SAkhilesh Sanikop const int next_row4x4 = row4x4 + superblock_size4x4;
398*09537850SAkhilesh Sanikop if (!tile.IsRow4x4Inside(next_row4x4)) {
399*09537850SAkhilesh Sanikop tile_index += tile_columns;
400*09537850SAkhilesh Sanikop }
401*09537850SAkhilesh Sanikop if (tile_index >= tiles.size()) return;
402*09537850SAkhilesh Sanikop pending_jobs->IncrementBy(1);
403*09537850SAkhilesh Sanikop thread_pool.Schedule([&tiles, tile_index, next_row4x4, superblock_size4x4,
404*09537850SAkhilesh Sanikop tile_columns, superblock_rows, frame_scratch_buffer,
405*09537850SAkhilesh Sanikop post_filter, pending_jobs]() {
406*09537850SAkhilesh Sanikop DecodeSuperBlockRowInTile(tiles, tile_index, next_row4x4,
407*09537850SAkhilesh Sanikop superblock_size4x4, tile_columns, superblock_rows,
408*09537850SAkhilesh Sanikop frame_scratch_buffer, post_filter, pending_jobs);
409*09537850SAkhilesh Sanikop pending_jobs->Decrement();
410*09537850SAkhilesh Sanikop });
411*09537850SAkhilesh Sanikop }
412*09537850SAkhilesh Sanikop
DecodeTilesThreadedFrameParallel(const ObuSequenceHeader & sequence_header,const ObuFrameHeader & frame_header,const Vector<std::unique_ptr<Tile>> & tiles,const SymbolDecoderContext & saved_symbol_decoder_context,const SegmentationMap * const prev_segment_ids,FrameScratchBuffer * const frame_scratch_buffer,PostFilter * const post_filter,RefCountedBuffer * const current_frame)413*09537850SAkhilesh Sanikop StatusCode DecodeTilesThreadedFrameParallel(
414*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header,
415*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header,
416*09537850SAkhilesh Sanikop const Vector<std::unique_ptr<Tile>>& tiles,
417*09537850SAkhilesh Sanikop const SymbolDecoderContext& saved_symbol_decoder_context,
418*09537850SAkhilesh Sanikop const SegmentationMap* const prev_segment_ids,
419*09537850SAkhilesh Sanikop FrameScratchBuffer* const frame_scratch_buffer,
420*09537850SAkhilesh Sanikop PostFilter* const post_filter, RefCountedBuffer* const current_frame) {
421*09537850SAkhilesh Sanikop // Parse the frame.
422*09537850SAkhilesh Sanikop ThreadPool& thread_pool =
423*09537850SAkhilesh Sanikop *frame_scratch_buffer->threading_strategy.thread_pool();
424*09537850SAkhilesh Sanikop std::atomic<int> tile_counter(0);
425*09537850SAkhilesh Sanikop const int tile_count = static_cast<int>(tiles.size());
426*09537850SAkhilesh Sanikop const int num_workers = thread_pool.num_threads();
427*09537850SAkhilesh Sanikop BlockingCounterWithStatus parse_workers(num_workers);
428*09537850SAkhilesh Sanikop // Submit tile parsing jobs to the thread pool.
429*09537850SAkhilesh Sanikop for (int i = 0; i < num_workers; ++i) {
430*09537850SAkhilesh Sanikop thread_pool.Schedule([&tiles, tile_count, &tile_counter, &parse_workers]() {
431*09537850SAkhilesh Sanikop bool failed = false;
432*09537850SAkhilesh Sanikop int index;
433*09537850SAkhilesh Sanikop while ((index = tile_counter.fetch_add(1, std::memory_order_relaxed)) <
434*09537850SAkhilesh Sanikop tile_count) {
435*09537850SAkhilesh Sanikop if (!failed) {
436*09537850SAkhilesh Sanikop const auto& tile_ptr = tiles[index];
437*09537850SAkhilesh Sanikop if (!tile_ptr->Parse()) {
438*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Error parsing tile #%d", tile_ptr->number());
439*09537850SAkhilesh Sanikop failed = true;
440*09537850SAkhilesh Sanikop }
441*09537850SAkhilesh Sanikop }
442*09537850SAkhilesh Sanikop }
443*09537850SAkhilesh Sanikop parse_workers.Decrement(!failed);
444*09537850SAkhilesh Sanikop });
445*09537850SAkhilesh Sanikop }
446*09537850SAkhilesh Sanikop
447*09537850SAkhilesh Sanikop // Have the current thread participate in parsing.
448*09537850SAkhilesh Sanikop bool failed = false;
449*09537850SAkhilesh Sanikop int index;
450*09537850SAkhilesh Sanikop while ((index = tile_counter.fetch_add(1, std::memory_order_relaxed)) <
451*09537850SAkhilesh Sanikop tile_count) {
452*09537850SAkhilesh Sanikop if (!failed) {
453*09537850SAkhilesh Sanikop const auto& tile_ptr = tiles[index];
454*09537850SAkhilesh Sanikop if (!tile_ptr->Parse()) {
455*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Error parsing tile #%d", tile_ptr->number());
456*09537850SAkhilesh Sanikop failed = true;
457*09537850SAkhilesh Sanikop }
458*09537850SAkhilesh Sanikop }
459*09537850SAkhilesh Sanikop }
460*09537850SAkhilesh Sanikop
461*09537850SAkhilesh Sanikop // Wait until all the parse workers are done. This ensures that all the tiles
462*09537850SAkhilesh Sanikop // have been parsed.
463*09537850SAkhilesh Sanikop if (!parse_workers.Wait() || failed) {
464*09537850SAkhilesh Sanikop return kLibgav1StatusUnknownError;
465*09537850SAkhilesh Sanikop }
466*09537850SAkhilesh Sanikop if (frame_header.enable_frame_end_update_cdf) {
467*09537850SAkhilesh Sanikop frame_scratch_buffer->symbol_decoder_context = saved_symbol_decoder_context;
468*09537850SAkhilesh Sanikop }
469*09537850SAkhilesh Sanikop current_frame->SetFrameContext(frame_scratch_buffer->symbol_decoder_context);
470*09537850SAkhilesh Sanikop SetSegmentationMap(frame_header, prev_segment_ids, current_frame);
471*09537850SAkhilesh Sanikop current_frame->SetFrameState(kFrameStateParsed);
472*09537850SAkhilesh Sanikop
473*09537850SAkhilesh Sanikop // Decode the frame.
474*09537850SAkhilesh Sanikop const int block_width4x4 = sequence_header.use_128x128_superblock ? 32 : 16;
475*09537850SAkhilesh Sanikop const int block_width4x4_log2 =
476*09537850SAkhilesh Sanikop sequence_header.use_128x128_superblock ? 5 : 4;
477*09537850SAkhilesh Sanikop const int superblock_rows =
478*09537850SAkhilesh Sanikop (frame_header.rows4x4 + block_width4x4 - 1) >> block_width4x4_log2;
479*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->superblock_row_progress.Resize(superblock_rows) ||
480*09537850SAkhilesh Sanikop !frame_scratch_buffer->superblock_row_progress_condvar.Resize(
481*09537850SAkhilesh Sanikop superblock_rows)) {
482*09537850SAkhilesh Sanikop return kLibgav1StatusOutOfMemory;
483*09537850SAkhilesh Sanikop }
484*09537850SAkhilesh Sanikop int* const superblock_row_progress =
485*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_progress.get();
486*09537850SAkhilesh Sanikop memset(superblock_row_progress, 0,
487*09537850SAkhilesh Sanikop superblock_rows * sizeof(superblock_row_progress[0]));
488*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_decoding_failed = false;
489*09537850SAkhilesh Sanikop const int tile_columns = frame_header.tile_info.tile_columns;
490*09537850SAkhilesh Sanikop const bool decode_entire_tiles_in_worker_threads =
491*09537850SAkhilesh Sanikop num_workers >= tile_columns;
492*09537850SAkhilesh Sanikop BlockingCounter pending_jobs(
493*09537850SAkhilesh Sanikop decode_entire_tiles_in_worker_threads ? num_workers : tile_columns);
494*09537850SAkhilesh Sanikop if (decode_entire_tiles_in_worker_threads) {
495*09537850SAkhilesh Sanikop // Submit tile decoding jobs to the thread pool.
496*09537850SAkhilesh Sanikop tile_counter = 0;
497*09537850SAkhilesh Sanikop for (int i = 0; i < num_workers; ++i) {
498*09537850SAkhilesh Sanikop thread_pool.Schedule([&tiles, tile_count, &tile_counter, &pending_jobs,
499*09537850SAkhilesh Sanikop frame_scratch_buffer, superblock_rows]() {
500*09537850SAkhilesh Sanikop bool failed = false;
501*09537850SAkhilesh Sanikop int index;
502*09537850SAkhilesh Sanikop while ((index = tile_counter.fetch_add(1, std::memory_order_relaxed)) <
503*09537850SAkhilesh Sanikop tile_count) {
504*09537850SAkhilesh Sanikop if (failed) continue;
505*09537850SAkhilesh Sanikop const auto& tile_ptr = tiles[index];
506*09537850SAkhilesh Sanikop if (!tile_ptr->Decode(
507*09537850SAkhilesh Sanikop &frame_scratch_buffer->superblock_row_mutex,
508*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_progress.get(),
509*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_progress_condvar
510*09537850SAkhilesh Sanikop .get())) {
511*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Error decoding tile #%d", tile_ptr->number());
512*09537850SAkhilesh Sanikop failed = true;
513*09537850SAkhilesh Sanikop SetFailureAndNotifyAll(frame_scratch_buffer, superblock_rows);
514*09537850SAkhilesh Sanikop }
515*09537850SAkhilesh Sanikop }
516*09537850SAkhilesh Sanikop pending_jobs.Decrement();
517*09537850SAkhilesh Sanikop });
518*09537850SAkhilesh Sanikop }
519*09537850SAkhilesh Sanikop } else {
520*09537850SAkhilesh Sanikop // Schedule the jobs for first tile row.
521*09537850SAkhilesh Sanikop for (int tile_index = 0; tile_index < tile_columns; ++tile_index) {
522*09537850SAkhilesh Sanikop thread_pool.Schedule([&tiles, tile_index, block_width4x4, tile_columns,
523*09537850SAkhilesh Sanikop superblock_rows, frame_scratch_buffer, post_filter,
524*09537850SAkhilesh Sanikop &pending_jobs]() {
525*09537850SAkhilesh Sanikop DecodeSuperBlockRowInTile(
526*09537850SAkhilesh Sanikop tiles, tile_index, 0, block_width4x4, tile_columns, superblock_rows,
527*09537850SAkhilesh Sanikop frame_scratch_buffer, post_filter, &pending_jobs);
528*09537850SAkhilesh Sanikop pending_jobs.Decrement();
529*09537850SAkhilesh Sanikop });
530*09537850SAkhilesh Sanikop }
531*09537850SAkhilesh Sanikop }
532*09537850SAkhilesh Sanikop
533*09537850SAkhilesh Sanikop // Current thread will do the post filters.
534*09537850SAkhilesh Sanikop std::condition_variable* const superblock_row_progress_condvar =
535*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_progress_condvar.get();
536*09537850SAkhilesh Sanikop const std::unique_ptr<Tile>* tile_row_base = &tiles[0];
537*09537850SAkhilesh Sanikop for (int row4x4 = 0, index = 0; row4x4 < frame_header.rows4x4;
538*09537850SAkhilesh Sanikop row4x4 += block_width4x4, ++index) {
539*09537850SAkhilesh Sanikop if (!tile_row_base[0]->IsRow4x4Inside(row4x4)) {
540*09537850SAkhilesh Sanikop tile_row_base += tile_columns;
541*09537850SAkhilesh Sanikop }
542*09537850SAkhilesh Sanikop {
543*09537850SAkhilesh Sanikop std::unique_lock<std::mutex> lock(
544*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_mutex);
545*09537850SAkhilesh Sanikop while (superblock_row_progress[index] != tile_columns &&
546*09537850SAkhilesh Sanikop !frame_scratch_buffer->tile_decoding_failed) {
547*09537850SAkhilesh Sanikop superblock_row_progress_condvar[index].wait(lock);
548*09537850SAkhilesh Sanikop }
549*09537850SAkhilesh Sanikop if (frame_scratch_buffer->tile_decoding_failed) break;
550*09537850SAkhilesh Sanikop }
551*09537850SAkhilesh Sanikop if (post_filter->DoDeblock()) {
552*09537850SAkhilesh Sanikop // Apply deblocking filter for the tile boundaries of this superblock row.
553*09537850SAkhilesh Sanikop // The deblocking filter for the internal blocks will be applied in the
554*09537850SAkhilesh Sanikop // tile worker threads. In this thread, we will only have to apply
555*09537850SAkhilesh Sanikop // deblocking filter for the tile boundaries.
556*09537850SAkhilesh Sanikop ApplyDeblockingFilterForTileBoundaries(
557*09537850SAkhilesh Sanikop post_filter, tile_row_base, frame_header, row4x4, block_width4x4,
558*09537850SAkhilesh Sanikop tile_columns, decode_entire_tiles_in_worker_threads);
559*09537850SAkhilesh Sanikop }
560*09537850SAkhilesh Sanikop // Apply all the post filters other than deblocking.
561*09537850SAkhilesh Sanikop const int progress_row = post_filter->ApplyFilteringForOneSuperBlockRow(
562*09537850SAkhilesh Sanikop row4x4, block_width4x4, row4x4 + block_width4x4 >= frame_header.rows4x4,
563*09537850SAkhilesh Sanikop /*do_deblock=*/false);
564*09537850SAkhilesh Sanikop if (progress_row >= 0) {
565*09537850SAkhilesh Sanikop current_frame->SetProgress(progress_row);
566*09537850SAkhilesh Sanikop }
567*09537850SAkhilesh Sanikop }
568*09537850SAkhilesh Sanikop // Wait until all the pending jobs are done. This ensures that all the tiles
569*09537850SAkhilesh Sanikop // have been decoded and wrapped up.
570*09537850SAkhilesh Sanikop pending_jobs.Wait();
571*09537850SAkhilesh Sanikop {
572*09537850SAkhilesh Sanikop std::lock_guard<std::mutex> lock(
573*09537850SAkhilesh Sanikop frame_scratch_buffer->superblock_row_mutex);
574*09537850SAkhilesh Sanikop if (frame_scratch_buffer->tile_decoding_failed) {
575*09537850SAkhilesh Sanikop return kLibgav1StatusUnknownError;
576*09537850SAkhilesh Sanikop }
577*09537850SAkhilesh Sanikop }
578*09537850SAkhilesh Sanikop
579*09537850SAkhilesh Sanikop current_frame->SetFrameState(kFrameStateDecoded);
580*09537850SAkhilesh Sanikop return kStatusOk;
581*09537850SAkhilesh Sanikop }
582*09537850SAkhilesh Sanikop
583*09537850SAkhilesh Sanikop } // namespace
584*09537850SAkhilesh Sanikop
585*09537850SAkhilesh Sanikop // static
Create(const DecoderSettings * settings,std::unique_ptr<DecoderImpl> * output)586*09537850SAkhilesh Sanikop StatusCode DecoderImpl::Create(const DecoderSettings* settings,
587*09537850SAkhilesh Sanikop std::unique_ptr<DecoderImpl>* output) {
588*09537850SAkhilesh Sanikop if (settings->threads <= 0) {
589*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Invalid settings->threads: %d.", settings->threads);
590*09537850SAkhilesh Sanikop return kStatusInvalidArgument;
591*09537850SAkhilesh Sanikop }
592*09537850SAkhilesh Sanikop if (settings->frame_parallel) {
593*09537850SAkhilesh Sanikop if (settings->release_input_buffer == nullptr) {
594*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
595*09537850SAkhilesh Sanikop "release_input_buffer callback must not be null when "
596*09537850SAkhilesh Sanikop "frame_parallel is true.");
597*09537850SAkhilesh Sanikop return kStatusInvalidArgument;
598*09537850SAkhilesh Sanikop }
599*09537850SAkhilesh Sanikop }
600*09537850SAkhilesh Sanikop std::unique_ptr<DecoderImpl> impl(new (std::nothrow) DecoderImpl(settings));
601*09537850SAkhilesh Sanikop if (impl == nullptr) {
602*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate DecoderImpl.");
603*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
604*09537850SAkhilesh Sanikop }
605*09537850SAkhilesh Sanikop const StatusCode status = impl->Init();
606*09537850SAkhilesh Sanikop if (status != kStatusOk) return status;
607*09537850SAkhilesh Sanikop *output = std::move(impl);
608*09537850SAkhilesh Sanikop return kStatusOk;
609*09537850SAkhilesh Sanikop }
610*09537850SAkhilesh Sanikop
DecoderImpl(const DecoderSettings * settings)611*09537850SAkhilesh Sanikop DecoderImpl::DecoderImpl(const DecoderSettings* settings)
612*09537850SAkhilesh Sanikop : buffer_pool_(settings->on_frame_buffer_size_changed,
613*09537850SAkhilesh Sanikop settings->get_frame_buffer, settings->release_frame_buffer,
614*09537850SAkhilesh Sanikop settings->callback_private_data),
615*09537850SAkhilesh Sanikop settings_(*settings) {
616*09537850SAkhilesh Sanikop dsp::DspInit();
617*09537850SAkhilesh Sanikop }
618*09537850SAkhilesh Sanikop
~DecoderImpl()619*09537850SAkhilesh Sanikop DecoderImpl::~DecoderImpl() {
620*09537850SAkhilesh Sanikop // Clean up and wait until all the threads have stopped. We just have to pass
621*09537850SAkhilesh Sanikop // in a dummy status that is not kStatusOk or kStatusTryAgain to trigger the
622*09537850SAkhilesh Sanikop // path that clears all the threads and structs.
623*09537850SAkhilesh Sanikop SignalFailure(kStatusUnknownError);
624*09537850SAkhilesh Sanikop // Release any other frame buffer references that we may be holding on to.
625*09537850SAkhilesh Sanikop ReleaseOutputFrame();
626*09537850SAkhilesh Sanikop output_frame_queue_.Clear();
627*09537850SAkhilesh Sanikop for (auto& reference_frame : state_.reference_frame) {
628*09537850SAkhilesh Sanikop reference_frame = nullptr;
629*09537850SAkhilesh Sanikop }
630*09537850SAkhilesh Sanikop }
631*09537850SAkhilesh Sanikop
Init()632*09537850SAkhilesh Sanikop StatusCode DecoderImpl::Init() {
633*09537850SAkhilesh Sanikop if (!output_frame_queue_.Init(kMaxLayers)) {
634*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "output_frame_queue_.Init() failed.");
635*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
636*09537850SAkhilesh Sanikop }
637*09537850SAkhilesh Sanikop return kStatusOk;
638*09537850SAkhilesh Sanikop }
639*09537850SAkhilesh Sanikop
InitializeFrameThreadPoolAndTemporalUnitQueue(const uint8_t * data,size_t size)640*09537850SAkhilesh Sanikop StatusCode DecoderImpl::InitializeFrameThreadPoolAndTemporalUnitQueue(
641*09537850SAkhilesh Sanikop const uint8_t* data, size_t size) {
642*09537850SAkhilesh Sanikop is_frame_parallel_ = false;
643*09537850SAkhilesh Sanikop if (settings_.frame_parallel) {
644*09537850SAkhilesh Sanikop DecoderState state;
645*09537850SAkhilesh Sanikop std::unique_ptr<ObuParser> obu(new (std::nothrow) ObuParser(
646*09537850SAkhilesh Sanikop data, size, settings_.operating_point, &buffer_pool_, &state));
647*09537850SAkhilesh Sanikop if (obu == nullptr) {
648*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate OBU parser.");
649*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
650*09537850SAkhilesh Sanikop }
651*09537850SAkhilesh Sanikop RefCountedBufferPtr current_frame;
652*09537850SAkhilesh Sanikop const StatusCode status = obu->ParseOneFrame(¤t_frame);
653*09537850SAkhilesh Sanikop if (status != kStatusOk) {
654*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to parse OBU.");
655*09537850SAkhilesh Sanikop return status;
656*09537850SAkhilesh Sanikop }
657*09537850SAkhilesh Sanikop current_frame = nullptr;
658*09537850SAkhilesh Sanikop // We assume that the first frame that was parsed will contain the frame
659*09537850SAkhilesh Sanikop // header. This assumption is usually true in practice. So we will simply
660*09537850SAkhilesh Sanikop // not use frame parallel mode if this is not the case.
661*09537850SAkhilesh Sanikop if (settings_.threads > 1 &&
662*09537850SAkhilesh Sanikop !InitializeThreadPoolsForFrameParallel(
663*09537850SAkhilesh Sanikop settings_.threads, obu->frame_header().tile_info.tile_count,
664*09537850SAkhilesh Sanikop obu->frame_header().tile_info.tile_columns, &frame_thread_pool_,
665*09537850SAkhilesh Sanikop &frame_scratch_buffer_pool_)) {
666*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
667*09537850SAkhilesh Sanikop }
668*09537850SAkhilesh Sanikop }
669*09537850SAkhilesh Sanikop const int max_allowed_frames =
670*09537850SAkhilesh Sanikop (frame_thread_pool_ != nullptr) ? frame_thread_pool_->num_threads() : 1;
671*09537850SAkhilesh Sanikop assert(max_allowed_frames > 0);
672*09537850SAkhilesh Sanikop if (!temporal_units_.Init(max_allowed_frames)) {
673*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "temporal_units_.Init() failed.");
674*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
675*09537850SAkhilesh Sanikop }
676*09537850SAkhilesh Sanikop is_frame_parallel_ = frame_thread_pool_ != nullptr;
677*09537850SAkhilesh Sanikop return kStatusOk;
678*09537850SAkhilesh Sanikop }
679*09537850SAkhilesh Sanikop
EnqueueFrame(const uint8_t * data,size_t size,int64_t user_private_data,void * buffer_private_data)680*09537850SAkhilesh Sanikop StatusCode DecoderImpl::EnqueueFrame(const uint8_t* data, size_t size,
681*09537850SAkhilesh Sanikop int64_t user_private_data,
682*09537850SAkhilesh Sanikop void* buffer_private_data) {
683*09537850SAkhilesh Sanikop if (data == nullptr || size == 0) return kStatusInvalidArgument;
684*09537850SAkhilesh Sanikop if (HasFailure()) return kStatusUnknownError;
685*09537850SAkhilesh Sanikop if (!seen_first_frame_) {
686*09537850SAkhilesh Sanikop seen_first_frame_ = true;
687*09537850SAkhilesh Sanikop const StatusCode status =
688*09537850SAkhilesh Sanikop InitializeFrameThreadPoolAndTemporalUnitQueue(data, size);
689*09537850SAkhilesh Sanikop if (status != kStatusOk) {
690*09537850SAkhilesh Sanikop return SignalFailure(status);
691*09537850SAkhilesh Sanikop }
692*09537850SAkhilesh Sanikop }
693*09537850SAkhilesh Sanikop if (temporal_units_.Full()) {
694*09537850SAkhilesh Sanikop return kStatusTryAgain;
695*09537850SAkhilesh Sanikop }
696*09537850SAkhilesh Sanikop if (is_frame_parallel_) {
697*09537850SAkhilesh Sanikop return ParseAndSchedule(data, size, user_private_data, buffer_private_data);
698*09537850SAkhilesh Sanikop }
699*09537850SAkhilesh Sanikop TemporalUnit temporal_unit(data, size, user_private_data,
700*09537850SAkhilesh Sanikop buffer_private_data);
701*09537850SAkhilesh Sanikop temporal_units_.Push(std::move(temporal_unit));
702*09537850SAkhilesh Sanikop return kStatusOk;
703*09537850SAkhilesh Sanikop }
704*09537850SAkhilesh Sanikop
SignalFailure(StatusCode status)705*09537850SAkhilesh Sanikop StatusCode DecoderImpl::SignalFailure(StatusCode status) {
706*09537850SAkhilesh Sanikop if (status == kStatusOk || status == kStatusTryAgain) return status;
707*09537850SAkhilesh Sanikop // Set the |failure_status_| first so that any pending jobs in
708*09537850SAkhilesh Sanikop // |frame_thread_pool_| will exit right away when the thread pool is being
709*09537850SAkhilesh Sanikop // released below.
710*09537850SAkhilesh Sanikop {
711*09537850SAkhilesh Sanikop std::lock_guard<std::mutex> lock(mutex_);
712*09537850SAkhilesh Sanikop failure_status_ = status;
713*09537850SAkhilesh Sanikop }
714*09537850SAkhilesh Sanikop // Make sure all waiting threads exit.
715*09537850SAkhilesh Sanikop buffer_pool_.Abort();
716*09537850SAkhilesh Sanikop frame_thread_pool_ = nullptr;
717*09537850SAkhilesh Sanikop while (!temporal_units_.Empty()) {
718*09537850SAkhilesh Sanikop if (settings_.release_input_buffer != nullptr) {
719*09537850SAkhilesh Sanikop settings_.release_input_buffer(
720*09537850SAkhilesh Sanikop settings_.callback_private_data,
721*09537850SAkhilesh Sanikop temporal_units_.Front().buffer_private_data);
722*09537850SAkhilesh Sanikop }
723*09537850SAkhilesh Sanikop temporal_units_.Pop();
724*09537850SAkhilesh Sanikop }
725*09537850SAkhilesh Sanikop return status;
726*09537850SAkhilesh Sanikop }
727*09537850SAkhilesh Sanikop
728*09537850SAkhilesh Sanikop // DequeueFrame() follows the following policy to avoid holding unnecessary
729*09537850SAkhilesh Sanikop // frame buffer references in output_frame_: output_frame_ must be null when
730*09537850SAkhilesh Sanikop // DequeueFrame() returns false.
DequeueFrame(const DecoderBuffer ** out_ptr)731*09537850SAkhilesh Sanikop StatusCode DecoderImpl::DequeueFrame(const DecoderBuffer** out_ptr) {
732*09537850SAkhilesh Sanikop if (out_ptr == nullptr) {
733*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Invalid argument: out_ptr == nullptr.");
734*09537850SAkhilesh Sanikop return kStatusInvalidArgument;
735*09537850SAkhilesh Sanikop }
736*09537850SAkhilesh Sanikop // We assume a call to DequeueFrame() indicates that the caller is no longer
737*09537850SAkhilesh Sanikop // using the previous output frame, so we can release it.
738*09537850SAkhilesh Sanikop ReleaseOutputFrame();
739*09537850SAkhilesh Sanikop if (temporal_units_.Empty()) {
740*09537850SAkhilesh Sanikop // No input frames to decode.
741*09537850SAkhilesh Sanikop *out_ptr = nullptr;
742*09537850SAkhilesh Sanikop return kStatusNothingToDequeue;
743*09537850SAkhilesh Sanikop }
744*09537850SAkhilesh Sanikop TemporalUnit& temporal_unit = temporal_units_.Front();
745*09537850SAkhilesh Sanikop if (!is_frame_parallel_) {
746*09537850SAkhilesh Sanikop // If |output_frame_queue_| is not empty, then return the first frame from
747*09537850SAkhilesh Sanikop // that queue.
748*09537850SAkhilesh Sanikop if (!output_frame_queue_.Empty()) {
749*09537850SAkhilesh Sanikop RefCountedBufferPtr frame = std::move(output_frame_queue_.Front());
750*09537850SAkhilesh Sanikop output_frame_queue_.Pop();
751*09537850SAkhilesh Sanikop buffer_.user_private_data = temporal_unit.user_private_data;
752*09537850SAkhilesh Sanikop if (output_frame_queue_.Empty()) {
753*09537850SAkhilesh Sanikop temporal_units_.Pop();
754*09537850SAkhilesh Sanikop }
755*09537850SAkhilesh Sanikop const StatusCode status = CopyFrameToOutputBuffer(frame);
756*09537850SAkhilesh Sanikop if (status != kStatusOk) {
757*09537850SAkhilesh Sanikop return status;
758*09537850SAkhilesh Sanikop }
759*09537850SAkhilesh Sanikop *out_ptr = &buffer_;
760*09537850SAkhilesh Sanikop return kStatusOk;
761*09537850SAkhilesh Sanikop }
762*09537850SAkhilesh Sanikop // Decode the next available temporal unit and return.
763*09537850SAkhilesh Sanikop const StatusCode status = DecodeTemporalUnit(temporal_unit, out_ptr);
764*09537850SAkhilesh Sanikop if (status != kStatusOk) {
765*09537850SAkhilesh Sanikop // In case of failure, discard all the output frames that we may be
766*09537850SAkhilesh Sanikop // holding on references to.
767*09537850SAkhilesh Sanikop output_frame_queue_.Clear();
768*09537850SAkhilesh Sanikop }
769*09537850SAkhilesh Sanikop if (settings_.release_input_buffer != nullptr) {
770*09537850SAkhilesh Sanikop settings_.release_input_buffer(settings_.callback_private_data,
771*09537850SAkhilesh Sanikop temporal_unit.buffer_private_data);
772*09537850SAkhilesh Sanikop }
773*09537850SAkhilesh Sanikop if (output_frame_queue_.Empty()) {
774*09537850SAkhilesh Sanikop temporal_units_.Pop();
775*09537850SAkhilesh Sanikop }
776*09537850SAkhilesh Sanikop return status;
777*09537850SAkhilesh Sanikop }
778*09537850SAkhilesh Sanikop {
779*09537850SAkhilesh Sanikop std::unique_lock<std::mutex> lock(mutex_);
780*09537850SAkhilesh Sanikop if (settings_.blocking_dequeue) {
781*09537850SAkhilesh Sanikop while (!temporal_unit.decoded && failure_status_ == kStatusOk) {
782*09537850SAkhilesh Sanikop decoded_condvar_.wait(lock);
783*09537850SAkhilesh Sanikop }
784*09537850SAkhilesh Sanikop } else {
785*09537850SAkhilesh Sanikop if (!temporal_unit.decoded && failure_status_ == kStatusOk) {
786*09537850SAkhilesh Sanikop return kStatusTryAgain;
787*09537850SAkhilesh Sanikop }
788*09537850SAkhilesh Sanikop }
789*09537850SAkhilesh Sanikop if (failure_status_ != kStatusOk) {
790*09537850SAkhilesh Sanikop const StatusCode failure_status = failure_status_;
791*09537850SAkhilesh Sanikop lock.unlock();
792*09537850SAkhilesh Sanikop return SignalFailure(failure_status);
793*09537850SAkhilesh Sanikop }
794*09537850SAkhilesh Sanikop }
795*09537850SAkhilesh Sanikop if (settings_.release_input_buffer != nullptr &&
796*09537850SAkhilesh Sanikop !temporal_unit.released_input_buffer) {
797*09537850SAkhilesh Sanikop temporal_unit.released_input_buffer = true;
798*09537850SAkhilesh Sanikop settings_.release_input_buffer(settings_.callback_private_data,
799*09537850SAkhilesh Sanikop temporal_unit.buffer_private_data);
800*09537850SAkhilesh Sanikop }
801*09537850SAkhilesh Sanikop if (temporal_unit.status != kStatusOk) {
802*09537850SAkhilesh Sanikop temporal_units_.Pop();
803*09537850SAkhilesh Sanikop return SignalFailure(temporal_unit.status);
804*09537850SAkhilesh Sanikop }
805*09537850SAkhilesh Sanikop if (!temporal_unit.has_displayable_frame) {
806*09537850SAkhilesh Sanikop *out_ptr = nullptr;
807*09537850SAkhilesh Sanikop temporal_units_.Pop();
808*09537850SAkhilesh Sanikop return kStatusOk;
809*09537850SAkhilesh Sanikop }
810*09537850SAkhilesh Sanikop assert(temporal_unit.output_layer_count > 0);
811*09537850SAkhilesh Sanikop StatusCode status = CopyFrameToOutputBuffer(
812*09537850SAkhilesh Sanikop temporal_unit.output_layers[temporal_unit.output_layer_count - 1].frame);
813*09537850SAkhilesh Sanikop temporal_unit.output_layers[temporal_unit.output_layer_count - 1].frame =
814*09537850SAkhilesh Sanikop nullptr;
815*09537850SAkhilesh Sanikop if (status != kStatusOk) {
816*09537850SAkhilesh Sanikop temporal_units_.Pop();
817*09537850SAkhilesh Sanikop return SignalFailure(status);
818*09537850SAkhilesh Sanikop }
819*09537850SAkhilesh Sanikop buffer_.user_private_data = temporal_unit.user_private_data;
820*09537850SAkhilesh Sanikop *out_ptr = &buffer_;
821*09537850SAkhilesh Sanikop if (--temporal_unit.output_layer_count == 0) {
822*09537850SAkhilesh Sanikop temporal_units_.Pop();
823*09537850SAkhilesh Sanikop }
824*09537850SAkhilesh Sanikop return kStatusOk;
825*09537850SAkhilesh Sanikop }
826*09537850SAkhilesh Sanikop
ParseAndSchedule(const uint8_t * data,size_t size,int64_t user_private_data,void * buffer_private_data)827*09537850SAkhilesh Sanikop StatusCode DecoderImpl::ParseAndSchedule(const uint8_t* data, size_t size,
828*09537850SAkhilesh Sanikop int64_t user_private_data,
829*09537850SAkhilesh Sanikop void* buffer_private_data) {
830*09537850SAkhilesh Sanikop TemporalUnit temporal_unit(data, size, user_private_data,
831*09537850SAkhilesh Sanikop buffer_private_data);
832*09537850SAkhilesh Sanikop std::unique_ptr<ObuParser> obu(new (std::nothrow) ObuParser(
833*09537850SAkhilesh Sanikop temporal_unit.data, temporal_unit.size, settings_.operating_point,
834*09537850SAkhilesh Sanikop &buffer_pool_, &state_));
835*09537850SAkhilesh Sanikop if (obu == nullptr) {
836*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate OBU parser.");
837*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
838*09537850SAkhilesh Sanikop }
839*09537850SAkhilesh Sanikop if (has_sequence_header_) {
840*09537850SAkhilesh Sanikop obu->set_sequence_header(sequence_header_);
841*09537850SAkhilesh Sanikop }
842*09537850SAkhilesh Sanikop StatusCode status;
843*09537850SAkhilesh Sanikop int position_in_temporal_unit = 0;
844*09537850SAkhilesh Sanikop while (obu->HasData()) {
845*09537850SAkhilesh Sanikop RefCountedBufferPtr current_frame;
846*09537850SAkhilesh Sanikop status = obu->ParseOneFrame(¤t_frame);
847*09537850SAkhilesh Sanikop if (status != kStatusOk) {
848*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to parse OBU.");
849*09537850SAkhilesh Sanikop return status;
850*09537850SAkhilesh Sanikop }
851*09537850SAkhilesh Sanikop if (!MaybeInitializeQuantizerMatrix(obu->frame_header())) {
852*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "InitializeQuantizerMatrix() failed.");
853*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
854*09537850SAkhilesh Sanikop }
855*09537850SAkhilesh Sanikop if (!MaybeInitializeWedgeMasks(obu->frame_header().frame_type)) {
856*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "InitializeWedgeMasks() failed.");
857*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
858*09537850SAkhilesh Sanikop }
859*09537850SAkhilesh Sanikop if (IsNewSequenceHeader(*obu)) {
860*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header = obu->sequence_header();
861*09537850SAkhilesh Sanikop const Libgav1ImageFormat image_format =
862*09537850SAkhilesh Sanikop ComposeImageFormat(sequence_header.color_config.is_monochrome,
863*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
864*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_y);
865*09537850SAkhilesh Sanikop const int max_bottom_border = GetBottomBorderPixels(
866*09537850SAkhilesh Sanikop /*do_cdef=*/true, /*do_restoration=*/true,
867*09537850SAkhilesh Sanikop /*do_superres=*/true, sequence_header.color_config.subsampling_y);
868*09537850SAkhilesh Sanikop // TODO(vigneshv): This may not be the right place to call this callback
869*09537850SAkhilesh Sanikop // for the frame parallel case. Investigate and fix it.
870*09537850SAkhilesh Sanikop if (!buffer_pool_.OnFrameBufferSizeChanged(
871*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth, image_format,
872*09537850SAkhilesh Sanikop sequence_header.max_frame_width, sequence_header.max_frame_height,
873*09537850SAkhilesh Sanikop kBorderPixels, kBorderPixels, kBorderPixels, max_bottom_border)) {
874*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "buffer_pool_.OnFrameBufferSizeChanged failed.");
875*09537850SAkhilesh Sanikop return kStatusUnknownError;
876*09537850SAkhilesh Sanikop }
877*09537850SAkhilesh Sanikop }
878*09537850SAkhilesh Sanikop // This can happen when there are multiple spatial/temporal layers and if
879*09537850SAkhilesh Sanikop // all the layers are outside the current operating point.
880*09537850SAkhilesh Sanikop if (current_frame == nullptr) {
881*09537850SAkhilesh Sanikop continue;
882*09537850SAkhilesh Sanikop }
883*09537850SAkhilesh Sanikop // Note that we cannot set EncodedFrame.temporal_unit here. It will be set
884*09537850SAkhilesh Sanikop // in the code below after |temporal_unit| is std::move'd into the
885*09537850SAkhilesh Sanikop // |temporal_units_| queue.
886*09537850SAkhilesh Sanikop if (!temporal_unit.frames.emplace_back(obu.get(), state_, current_frame,
887*09537850SAkhilesh Sanikop position_in_temporal_unit++)) {
888*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "temporal_unit.frames.emplace_back failed.");
889*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
890*09537850SAkhilesh Sanikop }
891*09537850SAkhilesh Sanikop state_.UpdateReferenceFrames(current_frame,
892*09537850SAkhilesh Sanikop obu->frame_header().refresh_frame_flags);
893*09537850SAkhilesh Sanikop }
894*09537850SAkhilesh Sanikop // This function cannot fail after this point. So it is okay to move the
895*09537850SAkhilesh Sanikop // |temporal_unit| into |temporal_units_| queue.
896*09537850SAkhilesh Sanikop temporal_units_.Push(std::move(temporal_unit));
897*09537850SAkhilesh Sanikop if (temporal_units_.Back().frames.empty()) {
898*09537850SAkhilesh Sanikop std::lock_guard<std::mutex> lock(mutex_);
899*09537850SAkhilesh Sanikop temporal_units_.Back().has_displayable_frame = false;
900*09537850SAkhilesh Sanikop temporal_units_.Back().decoded = true;
901*09537850SAkhilesh Sanikop return kStatusOk;
902*09537850SAkhilesh Sanikop }
903*09537850SAkhilesh Sanikop for (auto& frame : temporal_units_.Back().frames) {
904*09537850SAkhilesh Sanikop EncodedFrame* const encoded_frame = &frame;
905*09537850SAkhilesh Sanikop encoded_frame->temporal_unit = &temporal_units_.Back();
906*09537850SAkhilesh Sanikop frame_thread_pool_->Schedule([this, encoded_frame]() {
907*09537850SAkhilesh Sanikop if (HasFailure()) return;
908*09537850SAkhilesh Sanikop const StatusCode status = DecodeFrame(encoded_frame);
909*09537850SAkhilesh Sanikop encoded_frame->state = {};
910*09537850SAkhilesh Sanikop encoded_frame->frame = nullptr;
911*09537850SAkhilesh Sanikop TemporalUnit& temporal_unit = *encoded_frame->temporal_unit;
912*09537850SAkhilesh Sanikop std::lock_guard<std::mutex> lock(mutex_);
913*09537850SAkhilesh Sanikop if (failure_status_ != kStatusOk) return;
914*09537850SAkhilesh Sanikop // temporal_unit's status defaults to kStatusOk. So we need to set it only
915*09537850SAkhilesh Sanikop // on error. If |failure_status_| is not kStatusOk at this point, it means
916*09537850SAkhilesh Sanikop // that there has already been a failure. So we don't care about this
917*09537850SAkhilesh Sanikop // subsequent failure. We will simply return the error code of the first
918*09537850SAkhilesh Sanikop // failure.
919*09537850SAkhilesh Sanikop if (status != kStatusOk) {
920*09537850SAkhilesh Sanikop temporal_unit.status = status;
921*09537850SAkhilesh Sanikop if (failure_status_ == kStatusOk) {
922*09537850SAkhilesh Sanikop failure_status_ = status;
923*09537850SAkhilesh Sanikop }
924*09537850SAkhilesh Sanikop }
925*09537850SAkhilesh Sanikop temporal_unit.decoded =
926*09537850SAkhilesh Sanikop ++temporal_unit.decoded_count == temporal_unit.frames.size();
927*09537850SAkhilesh Sanikop if (temporal_unit.decoded && settings_.output_all_layers &&
928*09537850SAkhilesh Sanikop temporal_unit.output_layer_count > 1) {
929*09537850SAkhilesh Sanikop std::sort(
930*09537850SAkhilesh Sanikop temporal_unit.output_layers,
931*09537850SAkhilesh Sanikop temporal_unit.output_layers + temporal_unit.output_layer_count);
932*09537850SAkhilesh Sanikop }
933*09537850SAkhilesh Sanikop if (temporal_unit.decoded || failure_status_ != kStatusOk) {
934*09537850SAkhilesh Sanikop decoded_condvar_.notify_one();
935*09537850SAkhilesh Sanikop }
936*09537850SAkhilesh Sanikop });
937*09537850SAkhilesh Sanikop }
938*09537850SAkhilesh Sanikop return kStatusOk;
939*09537850SAkhilesh Sanikop }
940*09537850SAkhilesh Sanikop
DecodeFrame(EncodedFrame * const encoded_frame)941*09537850SAkhilesh Sanikop StatusCode DecoderImpl::DecodeFrame(EncodedFrame* const encoded_frame) {
942*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header = encoded_frame->sequence_header;
943*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header = encoded_frame->frame_header;
944*09537850SAkhilesh Sanikop RefCountedBufferPtr current_frame = std::move(encoded_frame->frame);
945*09537850SAkhilesh Sanikop
946*09537850SAkhilesh Sanikop std::unique_ptr<FrameScratchBuffer> frame_scratch_buffer =
947*09537850SAkhilesh Sanikop frame_scratch_buffer_pool_.Get();
948*09537850SAkhilesh Sanikop if (frame_scratch_buffer == nullptr) {
949*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Error when getting FrameScratchBuffer.");
950*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
951*09537850SAkhilesh Sanikop }
952*09537850SAkhilesh Sanikop // |frame_scratch_buffer| will be released when this local variable goes out
953*09537850SAkhilesh Sanikop // of scope (i.e.) on any return path in this function.
954*09537850SAkhilesh Sanikop FrameScratchBufferReleaser frame_scratch_buffer_releaser(
955*09537850SAkhilesh Sanikop &frame_scratch_buffer_pool_, &frame_scratch_buffer);
956*09537850SAkhilesh Sanikop
957*09537850SAkhilesh Sanikop StatusCode status;
958*09537850SAkhilesh Sanikop if (!frame_header.show_existing_frame) {
959*09537850SAkhilesh Sanikop if (encoded_frame->tile_buffers.empty()) {
960*09537850SAkhilesh Sanikop // This means that the last call to ParseOneFrame() did not actually
961*09537850SAkhilesh Sanikop // have any tile groups. This could happen in rare cases (for example,
962*09537850SAkhilesh Sanikop // if there is a Metadata OBU after the TileGroup OBU). We currently do
963*09537850SAkhilesh Sanikop // not have a reason to handle those cases, so we simply continue.
964*09537850SAkhilesh Sanikop return kStatusOk;
965*09537850SAkhilesh Sanikop }
966*09537850SAkhilesh Sanikop status = DecodeTiles(sequence_header, frame_header,
967*09537850SAkhilesh Sanikop encoded_frame->tile_buffers, encoded_frame->state,
968*09537850SAkhilesh Sanikop frame_scratch_buffer.get(), current_frame.get());
969*09537850SAkhilesh Sanikop if (status != kStatusOk) {
970*09537850SAkhilesh Sanikop return status;
971*09537850SAkhilesh Sanikop }
972*09537850SAkhilesh Sanikop } else {
973*09537850SAkhilesh Sanikop if (!current_frame->WaitUntilDecoded()) {
974*09537850SAkhilesh Sanikop return kStatusUnknownError;
975*09537850SAkhilesh Sanikop }
976*09537850SAkhilesh Sanikop }
977*09537850SAkhilesh Sanikop if (!frame_header.show_frame && !frame_header.show_existing_frame) {
978*09537850SAkhilesh Sanikop // This frame is not displayable. Not an error.
979*09537850SAkhilesh Sanikop return kStatusOk;
980*09537850SAkhilesh Sanikop }
981*09537850SAkhilesh Sanikop RefCountedBufferPtr film_grain_frame;
982*09537850SAkhilesh Sanikop status = ApplyFilmGrain(
983*09537850SAkhilesh Sanikop sequence_header, frame_header, current_frame, &film_grain_frame,
984*09537850SAkhilesh Sanikop frame_scratch_buffer->threading_strategy.thread_pool());
985*09537850SAkhilesh Sanikop if (status != kStatusOk) {
986*09537850SAkhilesh Sanikop return status;
987*09537850SAkhilesh Sanikop }
988*09537850SAkhilesh Sanikop
989*09537850SAkhilesh Sanikop TemporalUnit& temporal_unit = *encoded_frame->temporal_unit;
990*09537850SAkhilesh Sanikop std::lock_guard<std::mutex> lock(mutex_);
991*09537850SAkhilesh Sanikop if (temporal_unit.has_displayable_frame && !settings_.output_all_layers) {
992*09537850SAkhilesh Sanikop assert(temporal_unit.output_frame_position >= 0);
993*09537850SAkhilesh Sanikop // A displayable frame was already found in this temporal unit. This can
994*09537850SAkhilesh Sanikop // happen if there are multiple spatial/temporal layers. Since
995*09537850SAkhilesh Sanikop // |settings_.output_all_layers| is false, we will output only the last
996*09537850SAkhilesh Sanikop // displayable frame.
997*09537850SAkhilesh Sanikop if (temporal_unit.output_frame_position >
998*09537850SAkhilesh Sanikop encoded_frame->position_in_temporal_unit) {
999*09537850SAkhilesh Sanikop return kStatusOk;
1000*09537850SAkhilesh Sanikop }
1001*09537850SAkhilesh Sanikop // Replace any output frame that we may have seen before with the current
1002*09537850SAkhilesh Sanikop // frame.
1003*09537850SAkhilesh Sanikop assert(temporal_unit.output_layer_count == 1);
1004*09537850SAkhilesh Sanikop --temporal_unit.output_layer_count;
1005*09537850SAkhilesh Sanikop }
1006*09537850SAkhilesh Sanikop temporal_unit.has_displayable_frame = true;
1007*09537850SAkhilesh Sanikop temporal_unit.output_layers[temporal_unit.output_layer_count].frame =
1008*09537850SAkhilesh Sanikop std::move(film_grain_frame);
1009*09537850SAkhilesh Sanikop temporal_unit.output_layers[temporal_unit.output_layer_count]
1010*09537850SAkhilesh Sanikop .position_in_temporal_unit = encoded_frame->position_in_temporal_unit;
1011*09537850SAkhilesh Sanikop ++temporal_unit.output_layer_count;
1012*09537850SAkhilesh Sanikop temporal_unit.output_frame_position =
1013*09537850SAkhilesh Sanikop encoded_frame->position_in_temporal_unit;
1014*09537850SAkhilesh Sanikop return kStatusOk;
1015*09537850SAkhilesh Sanikop }
1016*09537850SAkhilesh Sanikop
DecodeTemporalUnit(const TemporalUnit & temporal_unit,const DecoderBuffer ** out_ptr)1017*09537850SAkhilesh Sanikop StatusCode DecoderImpl::DecodeTemporalUnit(const TemporalUnit& temporal_unit,
1018*09537850SAkhilesh Sanikop const DecoderBuffer** out_ptr) {
1019*09537850SAkhilesh Sanikop std::unique_ptr<ObuParser> obu(new (std::nothrow) ObuParser(
1020*09537850SAkhilesh Sanikop temporal_unit.data, temporal_unit.size, settings_.operating_point,
1021*09537850SAkhilesh Sanikop &buffer_pool_, &state_));
1022*09537850SAkhilesh Sanikop if (obu == nullptr) {
1023*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate OBU parser.");
1024*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1025*09537850SAkhilesh Sanikop }
1026*09537850SAkhilesh Sanikop if (has_sequence_header_) {
1027*09537850SAkhilesh Sanikop obu->set_sequence_header(sequence_header_);
1028*09537850SAkhilesh Sanikop }
1029*09537850SAkhilesh Sanikop StatusCode status;
1030*09537850SAkhilesh Sanikop std::unique_ptr<FrameScratchBuffer> frame_scratch_buffer =
1031*09537850SAkhilesh Sanikop frame_scratch_buffer_pool_.Get();
1032*09537850SAkhilesh Sanikop if (frame_scratch_buffer == nullptr) {
1033*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Error when getting FrameScratchBuffer.");
1034*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1035*09537850SAkhilesh Sanikop }
1036*09537850SAkhilesh Sanikop // |frame_scratch_buffer| will be released when this local variable goes out
1037*09537850SAkhilesh Sanikop // of scope (i.e.) on any return path in this function.
1038*09537850SAkhilesh Sanikop FrameScratchBufferReleaser frame_scratch_buffer_releaser(
1039*09537850SAkhilesh Sanikop &frame_scratch_buffer_pool_, &frame_scratch_buffer);
1040*09537850SAkhilesh Sanikop
1041*09537850SAkhilesh Sanikop while (obu->HasData()) {
1042*09537850SAkhilesh Sanikop RefCountedBufferPtr current_frame;
1043*09537850SAkhilesh Sanikop status = obu->ParseOneFrame(¤t_frame);
1044*09537850SAkhilesh Sanikop if (status != kStatusOk) {
1045*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to parse OBU.");
1046*09537850SAkhilesh Sanikop return status;
1047*09537850SAkhilesh Sanikop }
1048*09537850SAkhilesh Sanikop if (!MaybeInitializeQuantizerMatrix(obu->frame_header())) {
1049*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "InitializeQuantizerMatrix() failed.");
1050*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1051*09537850SAkhilesh Sanikop }
1052*09537850SAkhilesh Sanikop if (!MaybeInitializeWedgeMasks(obu->frame_header().frame_type)) {
1053*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "InitializeWedgeMasks() failed.");
1054*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1055*09537850SAkhilesh Sanikop }
1056*09537850SAkhilesh Sanikop if (IsNewSequenceHeader(*obu)) {
1057*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header = obu->sequence_header();
1058*09537850SAkhilesh Sanikop const Libgav1ImageFormat image_format =
1059*09537850SAkhilesh Sanikop ComposeImageFormat(sequence_header.color_config.is_monochrome,
1060*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
1061*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_y);
1062*09537850SAkhilesh Sanikop const int max_bottom_border = GetBottomBorderPixels(
1063*09537850SAkhilesh Sanikop /*do_cdef=*/true, /*do_restoration=*/true,
1064*09537850SAkhilesh Sanikop /*do_superres=*/true, sequence_header.color_config.subsampling_y);
1065*09537850SAkhilesh Sanikop if (!buffer_pool_.OnFrameBufferSizeChanged(
1066*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth, image_format,
1067*09537850SAkhilesh Sanikop sequence_header.max_frame_width, sequence_header.max_frame_height,
1068*09537850SAkhilesh Sanikop kBorderPixels, kBorderPixels, kBorderPixels, max_bottom_border)) {
1069*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "buffer_pool_.OnFrameBufferSizeChanged failed.");
1070*09537850SAkhilesh Sanikop return kStatusUnknownError;
1071*09537850SAkhilesh Sanikop }
1072*09537850SAkhilesh Sanikop }
1073*09537850SAkhilesh Sanikop if (!obu->frame_header().show_existing_frame) {
1074*09537850SAkhilesh Sanikop if (obu->tile_buffers().empty()) {
1075*09537850SAkhilesh Sanikop // This means that the last call to ParseOneFrame() did not actually
1076*09537850SAkhilesh Sanikop // have any tile groups. This could happen in rare cases (for example,
1077*09537850SAkhilesh Sanikop // if there is a Metadata OBU after the TileGroup OBU). We currently do
1078*09537850SAkhilesh Sanikop // not have a reason to handle those cases, so we simply continue.
1079*09537850SAkhilesh Sanikop continue;
1080*09537850SAkhilesh Sanikop }
1081*09537850SAkhilesh Sanikop status = DecodeTiles(obu->sequence_header(), obu->frame_header(),
1082*09537850SAkhilesh Sanikop obu->tile_buffers(), state_,
1083*09537850SAkhilesh Sanikop frame_scratch_buffer.get(), current_frame.get());
1084*09537850SAkhilesh Sanikop if (status != kStatusOk) {
1085*09537850SAkhilesh Sanikop return status;
1086*09537850SAkhilesh Sanikop }
1087*09537850SAkhilesh Sanikop }
1088*09537850SAkhilesh Sanikop state_.UpdateReferenceFrames(current_frame,
1089*09537850SAkhilesh Sanikop obu->frame_header().refresh_frame_flags);
1090*09537850SAkhilesh Sanikop if (obu->frame_header().show_frame ||
1091*09537850SAkhilesh Sanikop obu->frame_header().show_existing_frame) {
1092*09537850SAkhilesh Sanikop if (!output_frame_queue_.Empty() && !settings_.output_all_layers) {
1093*09537850SAkhilesh Sanikop // There is more than one displayable frame in the current operating
1094*09537850SAkhilesh Sanikop // point and |settings_.output_all_layers| is false. In this case, we
1095*09537850SAkhilesh Sanikop // simply return the last displayable frame as the output frame and
1096*09537850SAkhilesh Sanikop // ignore the rest.
1097*09537850SAkhilesh Sanikop assert(output_frame_queue_.Size() == 1);
1098*09537850SAkhilesh Sanikop output_frame_queue_.Pop();
1099*09537850SAkhilesh Sanikop }
1100*09537850SAkhilesh Sanikop RefCountedBufferPtr film_grain_frame;
1101*09537850SAkhilesh Sanikop status = ApplyFilmGrain(
1102*09537850SAkhilesh Sanikop obu->sequence_header(), obu->frame_header(), current_frame,
1103*09537850SAkhilesh Sanikop &film_grain_frame,
1104*09537850SAkhilesh Sanikop frame_scratch_buffer->threading_strategy.film_grain_thread_pool());
1105*09537850SAkhilesh Sanikop if (status != kStatusOk) return status;
1106*09537850SAkhilesh Sanikop output_frame_queue_.Push(std::move(film_grain_frame));
1107*09537850SAkhilesh Sanikop }
1108*09537850SAkhilesh Sanikop }
1109*09537850SAkhilesh Sanikop if (output_frame_queue_.Empty()) {
1110*09537850SAkhilesh Sanikop // No displayable frame in the temporal unit. Not an error.
1111*09537850SAkhilesh Sanikop *out_ptr = nullptr;
1112*09537850SAkhilesh Sanikop return kStatusOk;
1113*09537850SAkhilesh Sanikop }
1114*09537850SAkhilesh Sanikop status = CopyFrameToOutputBuffer(output_frame_queue_.Front());
1115*09537850SAkhilesh Sanikop output_frame_queue_.Pop();
1116*09537850SAkhilesh Sanikop if (status != kStatusOk) {
1117*09537850SAkhilesh Sanikop return status;
1118*09537850SAkhilesh Sanikop }
1119*09537850SAkhilesh Sanikop buffer_.user_private_data = temporal_unit.user_private_data;
1120*09537850SAkhilesh Sanikop *out_ptr = &buffer_;
1121*09537850SAkhilesh Sanikop return kStatusOk;
1122*09537850SAkhilesh Sanikop }
1123*09537850SAkhilesh Sanikop
CopyFrameToOutputBuffer(const RefCountedBufferPtr & frame)1124*09537850SAkhilesh Sanikop StatusCode DecoderImpl::CopyFrameToOutputBuffer(
1125*09537850SAkhilesh Sanikop const RefCountedBufferPtr& frame) {
1126*09537850SAkhilesh Sanikop YuvBuffer* yuv_buffer = frame->buffer();
1127*09537850SAkhilesh Sanikop
1128*09537850SAkhilesh Sanikop buffer_.chroma_sample_position = frame->chroma_sample_position();
1129*09537850SAkhilesh Sanikop
1130*09537850SAkhilesh Sanikop if (yuv_buffer->is_monochrome()) {
1131*09537850SAkhilesh Sanikop buffer_.image_format = kImageFormatMonochrome400;
1132*09537850SAkhilesh Sanikop } else {
1133*09537850SAkhilesh Sanikop if (yuv_buffer->subsampling_x() == 0 && yuv_buffer->subsampling_y() == 0) {
1134*09537850SAkhilesh Sanikop buffer_.image_format = kImageFormatYuv444;
1135*09537850SAkhilesh Sanikop } else if (yuv_buffer->subsampling_x() == 1 &&
1136*09537850SAkhilesh Sanikop yuv_buffer->subsampling_y() == 0) {
1137*09537850SAkhilesh Sanikop buffer_.image_format = kImageFormatYuv422;
1138*09537850SAkhilesh Sanikop } else if (yuv_buffer->subsampling_x() == 1 &&
1139*09537850SAkhilesh Sanikop yuv_buffer->subsampling_y() == 1) {
1140*09537850SAkhilesh Sanikop buffer_.image_format = kImageFormatYuv420;
1141*09537850SAkhilesh Sanikop } else {
1142*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
1143*09537850SAkhilesh Sanikop "Invalid chroma subsampling values: cannot determine buffer "
1144*09537850SAkhilesh Sanikop "image format.");
1145*09537850SAkhilesh Sanikop return kStatusInvalidArgument;
1146*09537850SAkhilesh Sanikop }
1147*09537850SAkhilesh Sanikop }
1148*09537850SAkhilesh Sanikop buffer_.color_range = sequence_header_.color_config.color_range;
1149*09537850SAkhilesh Sanikop buffer_.color_primary = sequence_header_.color_config.color_primary;
1150*09537850SAkhilesh Sanikop buffer_.transfer_characteristics =
1151*09537850SAkhilesh Sanikop sequence_header_.color_config.transfer_characteristics;
1152*09537850SAkhilesh Sanikop buffer_.matrix_coefficients =
1153*09537850SAkhilesh Sanikop sequence_header_.color_config.matrix_coefficients;
1154*09537850SAkhilesh Sanikop
1155*09537850SAkhilesh Sanikop buffer_.bitdepth = yuv_buffer->bitdepth();
1156*09537850SAkhilesh Sanikop const int num_planes =
1157*09537850SAkhilesh Sanikop yuv_buffer->is_monochrome() ? kMaxPlanesMonochrome : kMaxPlanes;
1158*09537850SAkhilesh Sanikop int plane = kPlaneY;
1159*09537850SAkhilesh Sanikop for (; plane < num_planes; ++plane) {
1160*09537850SAkhilesh Sanikop buffer_.stride[plane] = yuv_buffer->stride(plane);
1161*09537850SAkhilesh Sanikop buffer_.plane[plane] = yuv_buffer->data(plane);
1162*09537850SAkhilesh Sanikop buffer_.displayed_width[plane] = yuv_buffer->width(plane);
1163*09537850SAkhilesh Sanikop buffer_.displayed_height[plane] = yuv_buffer->height(plane);
1164*09537850SAkhilesh Sanikop }
1165*09537850SAkhilesh Sanikop for (; plane < kMaxPlanes; ++plane) {
1166*09537850SAkhilesh Sanikop buffer_.stride[plane] = 0;
1167*09537850SAkhilesh Sanikop buffer_.plane[plane] = nullptr;
1168*09537850SAkhilesh Sanikop buffer_.displayed_width[plane] = 0;
1169*09537850SAkhilesh Sanikop buffer_.displayed_height[plane] = 0;
1170*09537850SAkhilesh Sanikop }
1171*09537850SAkhilesh Sanikop buffer_.spatial_id = frame->spatial_id();
1172*09537850SAkhilesh Sanikop buffer_.temporal_id = frame->temporal_id();
1173*09537850SAkhilesh Sanikop buffer_.buffer_private_data = frame->buffer_private_data();
1174*09537850SAkhilesh Sanikop if (frame->hdr_cll_set()) {
1175*09537850SAkhilesh Sanikop buffer_.has_hdr_cll = 1;
1176*09537850SAkhilesh Sanikop buffer_.hdr_cll = frame->hdr_cll();
1177*09537850SAkhilesh Sanikop } else {
1178*09537850SAkhilesh Sanikop buffer_.has_hdr_cll = 0;
1179*09537850SAkhilesh Sanikop }
1180*09537850SAkhilesh Sanikop if (frame->hdr_mdcv_set()) {
1181*09537850SAkhilesh Sanikop buffer_.has_hdr_mdcv = 1;
1182*09537850SAkhilesh Sanikop buffer_.hdr_mdcv = frame->hdr_mdcv();
1183*09537850SAkhilesh Sanikop } else {
1184*09537850SAkhilesh Sanikop buffer_.has_hdr_mdcv = 0;
1185*09537850SAkhilesh Sanikop }
1186*09537850SAkhilesh Sanikop if (frame->itut_t35_set()) {
1187*09537850SAkhilesh Sanikop buffer_.has_itut_t35 = 1;
1188*09537850SAkhilesh Sanikop buffer_.itut_t35 = frame->itut_t35();
1189*09537850SAkhilesh Sanikop } else {
1190*09537850SAkhilesh Sanikop buffer_.has_itut_t35 = 0;
1191*09537850SAkhilesh Sanikop }
1192*09537850SAkhilesh Sanikop output_frame_ = frame;
1193*09537850SAkhilesh Sanikop return kStatusOk;
1194*09537850SAkhilesh Sanikop }
1195*09537850SAkhilesh Sanikop
ReleaseOutputFrame()1196*09537850SAkhilesh Sanikop void DecoderImpl::ReleaseOutputFrame() {
1197*09537850SAkhilesh Sanikop for (auto& plane : buffer_.plane) {
1198*09537850SAkhilesh Sanikop plane = nullptr;
1199*09537850SAkhilesh Sanikop }
1200*09537850SAkhilesh Sanikop output_frame_ = nullptr;
1201*09537850SAkhilesh Sanikop }
1202*09537850SAkhilesh Sanikop
DecodeTiles(const ObuSequenceHeader & sequence_header,const ObuFrameHeader & frame_header,const Vector<TileBuffer> & tile_buffers,const DecoderState & state,FrameScratchBuffer * const frame_scratch_buffer,RefCountedBuffer * const current_frame)1203*09537850SAkhilesh Sanikop StatusCode DecoderImpl::DecodeTiles(
1204*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header,
1205*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header, const Vector<TileBuffer>& tile_buffers,
1206*09537850SAkhilesh Sanikop const DecoderState& state, FrameScratchBuffer* const frame_scratch_buffer,
1207*09537850SAkhilesh Sanikop RefCountedBuffer* const current_frame) {
1208*09537850SAkhilesh Sanikop frame_scratch_buffer->tile_scratch_buffer_pool.Reset(
1209*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth);
1210*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->loop_restoration_info.Reset(
1211*09537850SAkhilesh Sanikop &frame_header.loop_restoration, frame_header.upscaled_width,
1212*09537850SAkhilesh Sanikop frame_header.height, sequence_header.color_config.subsampling_x,
1213*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_y,
1214*09537850SAkhilesh Sanikop sequence_header.color_config.is_monochrome)) {
1215*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
1216*09537850SAkhilesh Sanikop "Failed to allocate memory for loop restoration info units.");
1217*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1218*09537850SAkhilesh Sanikop }
1219*09537850SAkhilesh Sanikop ThreadingStrategy& threading_strategy =
1220*09537850SAkhilesh Sanikop frame_scratch_buffer->threading_strategy;
1221*09537850SAkhilesh Sanikop if (!is_frame_parallel_ &&
1222*09537850SAkhilesh Sanikop !threading_strategy.Reset(frame_header, settings_.threads)) {
1223*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1224*09537850SAkhilesh Sanikop }
1225*09537850SAkhilesh Sanikop const bool do_cdef =
1226*09537850SAkhilesh Sanikop PostFilter::DoCdef(frame_header, settings_.post_filter_mask);
1227*09537850SAkhilesh Sanikop const int num_planes = sequence_header.color_config.is_monochrome
1228*09537850SAkhilesh Sanikop ? kMaxPlanesMonochrome
1229*09537850SAkhilesh Sanikop : kMaxPlanes;
1230*09537850SAkhilesh Sanikop const bool do_restoration = PostFilter::DoRestoration(
1231*09537850SAkhilesh Sanikop frame_header.loop_restoration, settings_.post_filter_mask, num_planes);
1232*09537850SAkhilesh Sanikop const bool do_superres =
1233*09537850SAkhilesh Sanikop PostFilter::DoSuperRes(frame_header, settings_.post_filter_mask);
1234*09537850SAkhilesh Sanikop // Use kBorderPixels for the left, right, and top borders. Only the bottom
1235*09537850SAkhilesh Sanikop // border may need to be bigger. Cdef border is needed only if we apply Cdef
1236*09537850SAkhilesh Sanikop // without multithreading.
1237*09537850SAkhilesh Sanikop const int bottom_border = GetBottomBorderPixels(
1238*09537850SAkhilesh Sanikop do_cdef && threading_strategy.post_filter_thread_pool() == nullptr,
1239*09537850SAkhilesh Sanikop do_restoration, do_superres, sequence_header.color_config.subsampling_y);
1240*09537850SAkhilesh Sanikop current_frame->set_chroma_sample_position(
1241*09537850SAkhilesh Sanikop sequence_header.color_config.chroma_sample_position);
1242*09537850SAkhilesh Sanikop if (!current_frame->Realloc(sequence_header.color_config.bitdepth,
1243*09537850SAkhilesh Sanikop sequence_header.color_config.is_monochrome,
1244*09537850SAkhilesh Sanikop frame_header.upscaled_width, frame_header.height,
1245*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
1246*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_y,
1247*09537850SAkhilesh Sanikop /*left_border=*/kBorderPixels,
1248*09537850SAkhilesh Sanikop /*right_border=*/kBorderPixels,
1249*09537850SAkhilesh Sanikop /*top_border=*/kBorderPixels, bottom_border)) {
1250*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate memory for the decoder buffer.");
1251*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1252*09537850SAkhilesh Sanikop }
1253*09537850SAkhilesh Sanikop if (frame_header.cdef.bits > 0) {
1254*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->cdef_index.Reset(
1255*09537850SAkhilesh Sanikop DivideBy16(frame_header.rows4x4 + kMaxBlockHeight4x4),
1256*09537850SAkhilesh Sanikop DivideBy16(frame_header.columns4x4 + kMaxBlockWidth4x4),
1257*09537850SAkhilesh Sanikop /*zero_initialize=*/false)) {
1258*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate memory for cdef index.");
1259*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1260*09537850SAkhilesh Sanikop }
1261*09537850SAkhilesh Sanikop }
1262*09537850SAkhilesh Sanikop if (do_cdef) {
1263*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->cdef_skip.Reset(
1264*09537850SAkhilesh Sanikop DivideBy2(frame_header.rows4x4 + kMaxBlockHeight4x4),
1265*09537850SAkhilesh Sanikop DivideBy16(frame_header.columns4x4 + kMaxBlockWidth4x4),
1266*09537850SAkhilesh Sanikop /*zero_initialize=*/true)) {
1267*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate memory for cdef skip.");
1268*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1269*09537850SAkhilesh Sanikop }
1270*09537850SAkhilesh Sanikop }
1271*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->inter_transform_sizes.Reset(
1272*09537850SAkhilesh Sanikop frame_header.rows4x4 + kMaxBlockHeight4x4,
1273*09537850SAkhilesh Sanikop frame_header.columns4x4 + kMaxBlockWidth4x4,
1274*09537850SAkhilesh Sanikop /*zero_initialize=*/false)) {
1275*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate memory for inter_transform_sizes.");
1276*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1277*09537850SAkhilesh Sanikop }
1278*09537850SAkhilesh Sanikop if (frame_header.use_ref_frame_mvs) {
1279*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->motion_field.mv.Reset(
1280*09537850SAkhilesh Sanikop DivideBy2(frame_header.rows4x4), DivideBy2(frame_header.columns4x4),
1281*09537850SAkhilesh Sanikop /*zero_initialize=*/false) ||
1282*09537850SAkhilesh Sanikop !frame_scratch_buffer->motion_field.reference_offset.Reset(
1283*09537850SAkhilesh Sanikop DivideBy2(frame_header.rows4x4), DivideBy2(frame_header.columns4x4),
1284*09537850SAkhilesh Sanikop /*zero_initialize=*/false)) {
1285*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
1286*09537850SAkhilesh Sanikop "Failed to allocate memory for temporal motion vectors.");
1287*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1288*09537850SAkhilesh Sanikop }
1289*09537850SAkhilesh Sanikop
1290*09537850SAkhilesh Sanikop // For each motion vector, only mv[0] needs to be initialized to
1291*09537850SAkhilesh Sanikop // kInvalidMvValue, mv[1] is not necessary to be initialized and can be
1292*09537850SAkhilesh Sanikop // set to an arbitrary value. For simplicity, mv[1] is set to 0.
1293*09537850SAkhilesh Sanikop // The following memory initialization of contiguous memory is very fast. It
1294*09537850SAkhilesh Sanikop // is not recommended to make the initialization multi-threaded, unless the
1295*09537850SAkhilesh Sanikop // memory which needs to be initialized in each thread is still contiguous.
1296*09537850SAkhilesh Sanikop MotionVector invalid_mv;
1297*09537850SAkhilesh Sanikop invalid_mv.mv[0] = kInvalidMvValue;
1298*09537850SAkhilesh Sanikop invalid_mv.mv[1] = 0;
1299*09537850SAkhilesh Sanikop MotionVector* const motion_field_mv =
1300*09537850SAkhilesh Sanikop &frame_scratch_buffer->motion_field.mv[0][0];
1301*09537850SAkhilesh Sanikop std::fill(motion_field_mv,
1302*09537850SAkhilesh Sanikop motion_field_mv + frame_scratch_buffer->motion_field.mv.size(),
1303*09537850SAkhilesh Sanikop invalid_mv);
1304*09537850SAkhilesh Sanikop }
1305*09537850SAkhilesh Sanikop
1306*09537850SAkhilesh Sanikop // The addition of kMaxBlockHeight4x4 and kMaxBlockWidth4x4 is necessary so
1307*09537850SAkhilesh Sanikop // that the block parameters cache can be filled in for the last row/column
1308*09537850SAkhilesh Sanikop // without having to check for boundary conditions.
1309*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->block_parameters_holder.Reset(
1310*09537850SAkhilesh Sanikop frame_header.rows4x4 + kMaxBlockHeight4x4,
1311*09537850SAkhilesh Sanikop frame_header.columns4x4 + kMaxBlockWidth4x4)) {
1312*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1313*09537850SAkhilesh Sanikop }
1314*09537850SAkhilesh Sanikop const dsp::Dsp* const dsp =
1315*09537850SAkhilesh Sanikop dsp::GetDspTable(sequence_header.color_config.bitdepth);
1316*09537850SAkhilesh Sanikop if (dsp == nullptr) {
1317*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to get the dsp table for bitdepth %d.",
1318*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth);
1319*09537850SAkhilesh Sanikop return kStatusInternalError;
1320*09537850SAkhilesh Sanikop }
1321*09537850SAkhilesh Sanikop
1322*09537850SAkhilesh Sanikop const int tile_count = frame_header.tile_info.tile_count;
1323*09537850SAkhilesh Sanikop assert(tile_count >= 1);
1324*09537850SAkhilesh Sanikop Vector<std::unique_ptr<Tile>> tiles;
1325*09537850SAkhilesh Sanikop if (!tiles.reserve(tile_count)) {
1326*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "tiles.reserve(%d) failed.\n", tile_count);
1327*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1328*09537850SAkhilesh Sanikop }
1329*09537850SAkhilesh Sanikop
1330*09537850SAkhilesh Sanikop if (threading_strategy.row_thread_pool(0) != nullptr || is_frame_parallel_) {
1331*09537850SAkhilesh Sanikop if (frame_scratch_buffer->residual_buffer_pool == nullptr) {
1332*09537850SAkhilesh Sanikop frame_scratch_buffer->residual_buffer_pool.reset(
1333*09537850SAkhilesh Sanikop new (std::nothrow) ResidualBufferPool(
1334*09537850SAkhilesh Sanikop sequence_header.use_128x128_superblock,
1335*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
1336*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_y,
1337*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth == 8 ? sizeof(int16_t)
1338*09537850SAkhilesh Sanikop : sizeof(int32_t)));
1339*09537850SAkhilesh Sanikop if (frame_scratch_buffer->residual_buffer_pool == nullptr) {
1340*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to allocate residual buffer.\n");
1341*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1342*09537850SAkhilesh Sanikop }
1343*09537850SAkhilesh Sanikop } else {
1344*09537850SAkhilesh Sanikop frame_scratch_buffer->residual_buffer_pool->Reset(
1345*09537850SAkhilesh Sanikop sequence_header.use_128x128_superblock,
1346*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
1347*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_y,
1348*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth == 8 ? sizeof(int16_t)
1349*09537850SAkhilesh Sanikop : sizeof(int32_t));
1350*09537850SAkhilesh Sanikop }
1351*09537850SAkhilesh Sanikop }
1352*09537850SAkhilesh Sanikop
1353*09537850SAkhilesh Sanikop if (threading_strategy.post_filter_thread_pool() != nullptr && do_cdef) {
1354*09537850SAkhilesh Sanikop // We need to store 4 rows per 64x64 unit.
1355*09537850SAkhilesh Sanikop const int num_units =
1356*09537850SAkhilesh Sanikop MultiplyBy4(RightShiftWithCeiling(frame_header.rows4x4, 4));
1357*09537850SAkhilesh Sanikop // subsampling_y is set to zero irrespective of the actual frame's
1358*09537850SAkhilesh Sanikop // subsampling since we need to store exactly |num_units| rows of the loop
1359*09537850SAkhilesh Sanikop // restoration border pixels.
1360*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->cdef_border.Realloc(
1361*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth,
1362*09537850SAkhilesh Sanikop sequence_header.color_config.is_monochrome,
1363*09537850SAkhilesh Sanikop MultiplyBy4(frame_header.columns4x4), num_units,
1364*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
1365*09537850SAkhilesh Sanikop /*subsampling_y=*/0, kBorderPixels, kBorderPixels, kBorderPixels,
1366*09537850SAkhilesh Sanikop kBorderPixels, nullptr, nullptr, nullptr)) {
1367*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1368*09537850SAkhilesh Sanikop }
1369*09537850SAkhilesh Sanikop }
1370*09537850SAkhilesh Sanikop
1371*09537850SAkhilesh Sanikop if (do_restoration &&
1372*09537850SAkhilesh Sanikop (do_cdef || threading_strategy.post_filter_thread_pool() != nullptr)) {
1373*09537850SAkhilesh Sanikop // We need to store 4 rows per 64x64 unit.
1374*09537850SAkhilesh Sanikop const int num_units =
1375*09537850SAkhilesh Sanikop MultiplyBy4(RightShiftWithCeiling(frame_header.rows4x4, 4));
1376*09537850SAkhilesh Sanikop // subsampling_y is set to zero irrespective of the actual frame's
1377*09537850SAkhilesh Sanikop // subsampling since we need to store exactly |num_units| rows of the loop
1378*09537850SAkhilesh Sanikop // restoration border pixels.
1379*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->loop_restoration_border.Realloc(
1380*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth,
1381*09537850SAkhilesh Sanikop sequence_header.color_config.is_monochrome,
1382*09537850SAkhilesh Sanikop frame_header.upscaled_width, num_units,
1383*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
1384*09537850SAkhilesh Sanikop /*subsampling_y=*/0, kBorderPixels, kBorderPixels, kBorderPixels,
1385*09537850SAkhilesh Sanikop kBorderPixels, nullptr, nullptr, nullptr)) {
1386*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1387*09537850SAkhilesh Sanikop }
1388*09537850SAkhilesh Sanikop }
1389*09537850SAkhilesh Sanikop
1390*09537850SAkhilesh Sanikop if (do_superres) {
1391*09537850SAkhilesh Sanikop const int pixel_size = sequence_header.color_config.bitdepth == 8
1392*09537850SAkhilesh Sanikop ? sizeof(uint8_t)
1393*09537850SAkhilesh Sanikop : sizeof(uint16_t);
1394*09537850SAkhilesh Sanikop const int coefficients_size = kSuperResFilterTaps *
1395*09537850SAkhilesh Sanikop Align(frame_header.upscaled_width, 16) *
1396*09537850SAkhilesh Sanikop pixel_size;
1397*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->superres_coefficients[kPlaneTypeY].Resize(
1398*09537850SAkhilesh Sanikop coefficients_size)) {
1399*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
1400*09537850SAkhilesh Sanikop "Failed to Resize superres_coefficients[kPlaneTypeY].");
1401*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1402*09537850SAkhilesh Sanikop }
1403*09537850SAkhilesh Sanikop #if LIBGAV1_MSAN
1404*09537850SAkhilesh Sanikop // Quiet SuperRes_NEON() msan warnings.
1405*09537850SAkhilesh Sanikop memset(frame_scratch_buffer->superres_coefficients[kPlaneTypeY].get(), 0,
1406*09537850SAkhilesh Sanikop coefficients_size);
1407*09537850SAkhilesh Sanikop #endif
1408*09537850SAkhilesh Sanikop const int uv_coefficients_size =
1409*09537850SAkhilesh Sanikop kSuperResFilterTaps *
1410*09537850SAkhilesh Sanikop Align(SubsampledValue(frame_header.upscaled_width, 1), 16) * pixel_size;
1411*09537850SAkhilesh Sanikop if (!sequence_header.color_config.is_monochrome &&
1412*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x != 0 &&
1413*09537850SAkhilesh Sanikop !frame_scratch_buffer->superres_coefficients[kPlaneTypeUV].Resize(
1414*09537850SAkhilesh Sanikop uv_coefficients_size)) {
1415*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
1416*09537850SAkhilesh Sanikop "Failed to Resize superres_coefficients[kPlaneTypeUV].");
1417*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1418*09537850SAkhilesh Sanikop }
1419*09537850SAkhilesh Sanikop #if LIBGAV1_MSAN
1420*09537850SAkhilesh Sanikop if (!sequence_header.color_config.is_monochrome &&
1421*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x != 0) {
1422*09537850SAkhilesh Sanikop // Quiet SuperRes_NEON() msan warnings.
1423*09537850SAkhilesh Sanikop memset(frame_scratch_buffer->superres_coefficients[kPlaneTypeUV].get(), 0,
1424*09537850SAkhilesh Sanikop uv_coefficients_size);
1425*09537850SAkhilesh Sanikop }
1426*09537850SAkhilesh Sanikop #endif
1427*09537850SAkhilesh Sanikop }
1428*09537850SAkhilesh Sanikop
1429*09537850SAkhilesh Sanikop if (do_superres && threading_strategy.post_filter_thread_pool() != nullptr) {
1430*09537850SAkhilesh Sanikop const int num_threads =
1431*09537850SAkhilesh Sanikop threading_strategy.post_filter_thread_pool()->num_threads() + 1;
1432*09537850SAkhilesh Sanikop // subsampling_y is set to zero irrespective of the actual frame's
1433*09537850SAkhilesh Sanikop // subsampling since we need to store exactly |num_threads| rows of the
1434*09537850SAkhilesh Sanikop // down-scaled pixels.
1435*09537850SAkhilesh Sanikop // Left and right borders are for line extension. They are doubled for the Y
1436*09537850SAkhilesh Sanikop // plane to make sure the U and V planes have enough space after possible
1437*09537850SAkhilesh Sanikop // subsampling.
1438*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->superres_line_buffer.Realloc(
1439*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth,
1440*09537850SAkhilesh Sanikop sequence_header.color_config.is_monochrome,
1441*09537850SAkhilesh Sanikop MultiplyBy4(frame_header.columns4x4), num_threads,
1442*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x,
1443*09537850SAkhilesh Sanikop /*subsampling_y=*/0, 2 * kSuperResHorizontalBorder,
1444*09537850SAkhilesh Sanikop 2 * (kSuperResHorizontalBorder + kSuperResHorizontalPadding), 0, 0,
1445*09537850SAkhilesh Sanikop nullptr, nullptr, nullptr)) {
1446*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to resize superres line buffer.\n");
1447*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1448*09537850SAkhilesh Sanikop }
1449*09537850SAkhilesh Sanikop }
1450*09537850SAkhilesh Sanikop
1451*09537850SAkhilesh Sanikop if (is_frame_parallel_ && !IsIntraFrame(frame_header.frame_type)) {
1452*09537850SAkhilesh Sanikop // We can parse the current frame if all the reference frames have been
1453*09537850SAkhilesh Sanikop // parsed.
1454*09537850SAkhilesh Sanikop for (const int index : frame_header.reference_frame_index) {
1455*09537850SAkhilesh Sanikop if (!state.reference_frame[index]->WaitUntilParsed()) {
1456*09537850SAkhilesh Sanikop return kStatusUnknownError;
1457*09537850SAkhilesh Sanikop }
1458*09537850SAkhilesh Sanikop }
1459*09537850SAkhilesh Sanikop }
1460*09537850SAkhilesh Sanikop
1461*09537850SAkhilesh Sanikop // If prev_segment_ids is a null pointer, it is treated as if it pointed to
1462*09537850SAkhilesh Sanikop // a segmentation map containing all 0s.
1463*09537850SAkhilesh Sanikop const SegmentationMap* prev_segment_ids = nullptr;
1464*09537850SAkhilesh Sanikop if (frame_header.primary_reference_frame == kPrimaryReferenceNone) {
1465*09537850SAkhilesh Sanikop frame_scratch_buffer->symbol_decoder_context.Initialize(
1466*09537850SAkhilesh Sanikop frame_header.quantizer.base_index);
1467*09537850SAkhilesh Sanikop } else {
1468*09537850SAkhilesh Sanikop const int index =
1469*09537850SAkhilesh Sanikop frame_header
1470*09537850SAkhilesh Sanikop .reference_frame_index[frame_header.primary_reference_frame];
1471*09537850SAkhilesh Sanikop assert(index != -1);
1472*09537850SAkhilesh Sanikop const RefCountedBuffer* prev_frame = state.reference_frame[index].get();
1473*09537850SAkhilesh Sanikop frame_scratch_buffer->symbol_decoder_context = prev_frame->FrameContext();
1474*09537850SAkhilesh Sanikop if (frame_header.segmentation.enabled &&
1475*09537850SAkhilesh Sanikop prev_frame->columns4x4() == frame_header.columns4x4 &&
1476*09537850SAkhilesh Sanikop prev_frame->rows4x4() == frame_header.rows4x4) {
1477*09537850SAkhilesh Sanikop prev_segment_ids = prev_frame->segmentation_map();
1478*09537850SAkhilesh Sanikop }
1479*09537850SAkhilesh Sanikop }
1480*09537850SAkhilesh Sanikop
1481*09537850SAkhilesh Sanikop // The Tile class must make use of a separate buffer to store the unfiltered
1482*09537850SAkhilesh Sanikop // pixels for the intra prediction of the next superblock row. This is done
1483*09537850SAkhilesh Sanikop // only when one of the following conditions are true:
1484*09537850SAkhilesh Sanikop // * is_frame_parallel_ is true.
1485*09537850SAkhilesh Sanikop // * settings_.threads == 1.
1486*09537850SAkhilesh Sanikop // In the non-frame-parallel multi-threaded case, we do not run the post
1487*09537850SAkhilesh Sanikop // filters in the decode loop. So this buffer need not be used.
1488*09537850SAkhilesh Sanikop const bool use_intra_prediction_buffer =
1489*09537850SAkhilesh Sanikop is_frame_parallel_ || settings_.threads == 1;
1490*09537850SAkhilesh Sanikop if (use_intra_prediction_buffer) {
1491*09537850SAkhilesh Sanikop if (!frame_scratch_buffer->intra_prediction_buffers.Resize(
1492*09537850SAkhilesh Sanikop frame_header.tile_info.tile_rows)) {
1493*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to Resize intra_prediction_buffers.");
1494*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1495*09537850SAkhilesh Sanikop }
1496*09537850SAkhilesh Sanikop IntraPredictionBuffer* const intra_prediction_buffers =
1497*09537850SAkhilesh Sanikop frame_scratch_buffer->intra_prediction_buffers.get();
1498*09537850SAkhilesh Sanikop for (int plane = kPlaneY; plane < num_planes; ++plane) {
1499*09537850SAkhilesh Sanikop const int subsampling =
1500*09537850SAkhilesh Sanikop (plane == kPlaneY) ? 0 : sequence_header.color_config.subsampling_x;
1501*09537850SAkhilesh Sanikop const size_t intra_prediction_buffer_size =
1502*09537850SAkhilesh Sanikop ((MultiplyBy4(frame_header.columns4x4) >> subsampling) *
1503*09537850SAkhilesh Sanikop (sequence_header.color_config.bitdepth == 8 ? sizeof(uint8_t)
1504*09537850SAkhilesh Sanikop : sizeof(uint16_t)));
1505*09537850SAkhilesh Sanikop for (int tile_row = 0; tile_row < frame_header.tile_info.tile_rows;
1506*09537850SAkhilesh Sanikop ++tile_row) {
1507*09537850SAkhilesh Sanikop if (!intra_prediction_buffers[tile_row][plane].Resize(
1508*09537850SAkhilesh Sanikop intra_prediction_buffer_size)) {
1509*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
1510*09537850SAkhilesh Sanikop "Failed to allocate intra prediction buffer for tile "
1511*09537850SAkhilesh Sanikop "row %d plane %d.\n",
1512*09537850SAkhilesh Sanikop tile_row, plane);
1513*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1514*09537850SAkhilesh Sanikop }
1515*09537850SAkhilesh Sanikop }
1516*09537850SAkhilesh Sanikop }
1517*09537850SAkhilesh Sanikop }
1518*09537850SAkhilesh Sanikop
1519*09537850SAkhilesh Sanikop PostFilter post_filter(frame_header, sequence_header, frame_scratch_buffer,
1520*09537850SAkhilesh Sanikop current_frame->buffer(), dsp,
1521*09537850SAkhilesh Sanikop settings_.post_filter_mask);
1522*09537850SAkhilesh Sanikop SymbolDecoderContext saved_symbol_decoder_context;
1523*09537850SAkhilesh Sanikop BlockingCounterWithStatus pending_tiles(tile_count);
1524*09537850SAkhilesh Sanikop for (int tile_number = 0; tile_number < tile_count; ++tile_number) {
1525*09537850SAkhilesh Sanikop std::unique_ptr<Tile> tile = Tile::Create(
1526*09537850SAkhilesh Sanikop tile_number, tile_buffers[tile_number].data,
1527*09537850SAkhilesh Sanikop tile_buffers[tile_number].size, sequence_header, frame_header,
1528*09537850SAkhilesh Sanikop current_frame, state, frame_scratch_buffer, wedge_masks_,
1529*09537850SAkhilesh Sanikop quantizer_matrix_, &saved_symbol_decoder_context, prev_segment_ids,
1530*09537850SAkhilesh Sanikop &post_filter, dsp, threading_strategy.row_thread_pool(tile_number),
1531*09537850SAkhilesh Sanikop &pending_tiles, is_frame_parallel_, use_intra_prediction_buffer);
1532*09537850SAkhilesh Sanikop if (tile == nullptr) {
1533*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "Failed to create tile.");
1534*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1535*09537850SAkhilesh Sanikop }
1536*09537850SAkhilesh Sanikop tiles.push_back_unchecked(std::move(tile));
1537*09537850SAkhilesh Sanikop }
1538*09537850SAkhilesh Sanikop assert(tiles.size() == static_cast<size_t>(tile_count));
1539*09537850SAkhilesh Sanikop if (is_frame_parallel_) {
1540*09537850SAkhilesh Sanikop if (frame_scratch_buffer->threading_strategy.thread_pool() == nullptr) {
1541*09537850SAkhilesh Sanikop return DecodeTilesFrameParallel(
1542*09537850SAkhilesh Sanikop sequence_header, frame_header, tiles, saved_symbol_decoder_context,
1543*09537850SAkhilesh Sanikop prev_segment_ids, frame_scratch_buffer, &post_filter, current_frame);
1544*09537850SAkhilesh Sanikop }
1545*09537850SAkhilesh Sanikop return DecodeTilesThreadedFrameParallel(
1546*09537850SAkhilesh Sanikop sequence_header, frame_header, tiles, saved_symbol_decoder_context,
1547*09537850SAkhilesh Sanikop prev_segment_ids, frame_scratch_buffer, &post_filter, current_frame);
1548*09537850SAkhilesh Sanikop }
1549*09537850SAkhilesh Sanikop StatusCode status;
1550*09537850SAkhilesh Sanikop if (settings_.threads == 1) {
1551*09537850SAkhilesh Sanikop status = DecodeTilesNonFrameParallel(sequence_header, frame_header, tiles,
1552*09537850SAkhilesh Sanikop frame_scratch_buffer, &post_filter);
1553*09537850SAkhilesh Sanikop } else {
1554*09537850SAkhilesh Sanikop status = DecodeTilesThreadedNonFrameParallel(tiles, frame_scratch_buffer,
1555*09537850SAkhilesh Sanikop &post_filter, &pending_tiles);
1556*09537850SAkhilesh Sanikop }
1557*09537850SAkhilesh Sanikop if (status != kStatusOk) return status;
1558*09537850SAkhilesh Sanikop if (frame_header.enable_frame_end_update_cdf) {
1559*09537850SAkhilesh Sanikop frame_scratch_buffer->symbol_decoder_context = saved_symbol_decoder_context;
1560*09537850SAkhilesh Sanikop }
1561*09537850SAkhilesh Sanikop current_frame->SetFrameContext(frame_scratch_buffer->symbol_decoder_context);
1562*09537850SAkhilesh Sanikop SetSegmentationMap(frame_header, prev_segment_ids, current_frame);
1563*09537850SAkhilesh Sanikop return kStatusOk;
1564*09537850SAkhilesh Sanikop }
1565*09537850SAkhilesh Sanikop
ApplyFilmGrain(const ObuSequenceHeader & sequence_header,const ObuFrameHeader & frame_header,const RefCountedBufferPtr & displayable_frame,RefCountedBufferPtr * film_grain_frame,ThreadPool * thread_pool)1566*09537850SAkhilesh Sanikop StatusCode DecoderImpl::ApplyFilmGrain(
1567*09537850SAkhilesh Sanikop const ObuSequenceHeader& sequence_header,
1568*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header,
1569*09537850SAkhilesh Sanikop const RefCountedBufferPtr& displayable_frame,
1570*09537850SAkhilesh Sanikop RefCountedBufferPtr* film_grain_frame, ThreadPool* thread_pool) {
1571*09537850SAkhilesh Sanikop if (!sequence_header.film_grain_params_present ||
1572*09537850SAkhilesh Sanikop !displayable_frame->film_grain_params().apply_grain ||
1573*09537850SAkhilesh Sanikop (settings_.post_filter_mask & 0x10) == 0) {
1574*09537850SAkhilesh Sanikop *film_grain_frame = displayable_frame;
1575*09537850SAkhilesh Sanikop return kStatusOk;
1576*09537850SAkhilesh Sanikop }
1577*09537850SAkhilesh Sanikop if (!frame_header.show_existing_frame &&
1578*09537850SAkhilesh Sanikop frame_header.refresh_frame_flags == 0) {
1579*09537850SAkhilesh Sanikop // If show_existing_frame is true, then the current frame is a previously
1580*09537850SAkhilesh Sanikop // saved reference frame. If refresh_frame_flags is nonzero, then the
1581*09537850SAkhilesh Sanikop // state_.UpdateReferenceFrames() call above has saved the current frame as
1582*09537850SAkhilesh Sanikop // a reference frame. Therefore, if both of these conditions are false, then
1583*09537850SAkhilesh Sanikop // the current frame is not saved as a reference frame. displayable_frame
1584*09537850SAkhilesh Sanikop // should hold the only reference to the current frame.
1585*09537850SAkhilesh Sanikop assert(displayable_frame.use_count() == 1);
1586*09537850SAkhilesh Sanikop // Add film grain noise in place.
1587*09537850SAkhilesh Sanikop *film_grain_frame = displayable_frame;
1588*09537850SAkhilesh Sanikop } else {
1589*09537850SAkhilesh Sanikop *film_grain_frame = buffer_pool_.GetFreeBuffer();
1590*09537850SAkhilesh Sanikop if (*film_grain_frame == nullptr) {
1591*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR,
1592*09537850SAkhilesh Sanikop "Could not get film_grain_frame from the buffer pool.");
1593*09537850SAkhilesh Sanikop return kStatusResourceExhausted;
1594*09537850SAkhilesh Sanikop }
1595*09537850SAkhilesh Sanikop if (!(*film_grain_frame)
1596*09537850SAkhilesh Sanikop ->Realloc(displayable_frame->buffer()->bitdepth(),
1597*09537850SAkhilesh Sanikop displayable_frame->buffer()->is_monochrome(),
1598*09537850SAkhilesh Sanikop displayable_frame->upscaled_width(),
1599*09537850SAkhilesh Sanikop displayable_frame->frame_height(),
1600*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_x(),
1601*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_y(),
1602*09537850SAkhilesh Sanikop kBorderPixelsFilmGrain, kBorderPixelsFilmGrain,
1603*09537850SAkhilesh Sanikop kBorderPixelsFilmGrain, kBorderPixelsFilmGrain)) {
1604*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "film_grain_frame->Realloc() failed.");
1605*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1606*09537850SAkhilesh Sanikop }
1607*09537850SAkhilesh Sanikop (*film_grain_frame)
1608*09537850SAkhilesh Sanikop ->set_chroma_sample_position(
1609*09537850SAkhilesh Sanikop displayable_frame->chroma_sample_position());
1610*09537850SAkhilesh Sanikop (*film_grain_frame)->set_spatial_id(displayable_frame->spatial_id());
1611*09537850SAkhilesh Sanikop (*film_grain_frame)->set_temporal_id(displayable_frame->temporal_id());
1612*09537850SAkhilesh Sanikop }
1613*09537850SAkhilesh Sanikop const bool color_matrix_is_identity =
1614*09537850SAkhilesh Sanikop sequence_header.color_config.matrix_coefficients ==
1615*09537850SAkhilesh Sanikop kMatrixCoefficientsIdentity;
1616*09537850SAkhilesh Sanikop assert(displayable_frame->buffer()->stride(kPlaneU) ==
1617*09537850SAkhilesh Sanikop displayable_frame->buffer()->stride(kPlaneV));
1618*09537850SAkhilesh Sanikop const int input_stride_uv = displayable_frame->buffer()->stride(kPlaneU);
1619*09537850SAkhilesh Sanikop assert((*film_grain_frame)->buffer()->stride(kPlaneU) ==
1620*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->stride(kPlaneV));
1621*09537850SAkhilesh Sanikop const int output_stride_uv = (*film_grain_frame)->buffer()->stride(kPlaneU);
1622*09537850SAkhilesh Sanikop #if LIBGAV1_MAX_BITDEPTH >= 10
1623*09537850SAkhilesh Sanikop if (displayable_frame->buffer()->bitdepth() == 10) {
1624*09537850SAkhilesh Sanikop FilmGrain<10> film_grain(displayable_frame->film_grain_params(),
1625*09537850SAkhilesh Sanikop displayable_frame->buffer()->is_monochrome(),
1626*09537850SAkhilesh Sanikop color_matrix_is_identity,
1627*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_x(),
1628*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_y(),
1629*09537850SAkhilesh Sanikop displayable_frame->upscaled_width(),
1630*09537850SAkhilesh Sanikop displayable_frame->frame_height(), thread_pool);
1631*09537850SAkhilesh Sanikop if (!film_grain.AddNoise(
1632*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneY),
1633*09537850SAkhilesh Sanikop displayable_frame->buffer()->stride(kPlaneY),
1634*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneU),
1635*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneV), input_stride_uv,
1636*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneY),
1637*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->stride(kPlaneY),
1638*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneU),
1639*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneV), output_stride_uv)) {
1640*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "film_grain.AddNoise() failed.");
1641*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1642*09537850SAkhilesh Sanikop }
1643*09537850SAkhilesh Sanikop return kStatusOk;
1644*09537850SAkhilesh Sanikop }
1645*09537850SAkhilesh Sanikop #endif // LIBGAV1_MAX_BITDEPTH >= 10
1646*09537850SAkhilesh Sanikop #if LIBGAV1_MAX_BITDEPTH == 12
1647*09537850SAkhilesh Sanikop if (displayable_frame->buffer()->bitdepth() == 12) {
1648*09537850SAkhilesh Sanikop FilmGrain<12> film_grain(displayable_frame->film_grain_params(),
1649*09537850SAkhilesh Sanikop displayable_frame->buffer()->is_monochrome(),
1650*09537850SAkhilesh Sanikop color_matrix_is_identity,
1651*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_x(),
1652*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_y(),
1653*09537850SAkhilesh Sanikop displayable_frame->upscaled_width(),
1654*09537850SAkhilesh Sanikop displayable_frame->frame_height(), thread_pool);
1655*09537850SAkhilesh Sanikop if (!film_grain.AddNoise(
1656*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneY),
1657*09537850SAkhilesh Sanikop displayable_frame->buffer()->stride(kPlaneY),
1658*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneU),
1659*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneV), input_stride_uv,
1660*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneY),
1661*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->stride(kPlaneY),
1662*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneU),
1663*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneV), output_stride_uv)) {
1664*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "film_grain.AddNoise() failed.");
1665*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1666*09537850SAkhilesh Sanikop }
1667*09537850SAkhilesh Sanikop return kStatusOk;
1668*09537850SAkhilesh Sanikop }
1669*09537850SAkhilesh Sanikop #endif // LIBGAV1_MAX_BITDEPTH == 12
1670*09537850SAkhilesh Sanikop FilmGrain<8> film_grain(displayable_frame->film_grain_params(),
1671*09537850SAkhilesh Sanikop displayable_frame->buffer()->is_monochrome(),
1672*09537850SAkhilesh Sanikop color_matrix_is_identity,
1673*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_x(),
1674*09537850SAkhilesh Sanikop displayable_frame->buffer()->subsampling_y(),
1675*09537850SAkhilesh Sanikop displayable_frame->upscaled_width(),
1676*09537850SAkhilesh Sanikop displayable_frame->frame_height(), thread_pool);
1677*09537850SAkhilesh Sanikop if (!film_grain.AddNoise(
1678*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneY),
1679*09537850SAkhilesh Sanikop displayable_frame->buffer()->stride(kPlaneY),
1680*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneU),
1681*09537850SAkhilesh Sanikop displayable_frame->buffer()->data(kPlaneV), input_stride_uv,
1682*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneY),
1683*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->stride(kPlaneY),
1684*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneU),
1685*09537850SAkhilesh Sanikop (*film_grain_frame)->buffer()->data(kPlaneV), output_stride_uv)) {
1686*09537850SAkhilesh Sanikop LIBGAV1_DLOG(ERROR, "film_grain.AddNoise() failed.");
1687*09537850SAkhilesh Sanikop return kStatusOutOfMemory;
1688*09537850SAkhilesh Sanikop }
1689*09537850SAkhilesh Sanikop return kStatusOk;
1690*09537850SAkhilesh Sanikop }
1691*09537850SAkhilesh Sanikop
IsNewSequenceHeader(const ObuParser & obu)1692*09537850SAkhilesh Sanikop bool DecoderImpl::IsNewSequenceHeader(const ObuParser& obu) {
1693*09537850SAkhilesh Sanikop if (std::find_if(obu.obu_headers().begin(), obu.obu_headers().end(),
1694*09537850SAkhilesh Sanikop [](const ObuHeader& obu_header) {
1695*09537850SAkhilesh Sanikop return obu_header.type == kObuSequenceHeader;
1696*09537850SAkhilesh Sanikop }) == obu.obu_headers().end()) {
1697*09537850SAkhilesh Sanikop return false;
1698*09537850SAkhilesh Sanikop }
1699*09537850SAkhilesh Sanikop const ObuSequenceHeader sequence_header = obu.sequence_header();
1700*09537850SAkhilesh Sanikop const bool sequence_header_changed =
1701*09537850SAkhilesh Sanikop !has_sequence_header_ ||
1702*09537850SAkhilesh Sanikop sequence_header_.color_config.bitdepth !=
1703*09537850SAkhilesh Sanikop sequence_header.color_config.bitdepth ||
1704*09537850SAkhilesh Sanikop sequence_header_.color_config.is_monochrome !=
1705*09537850SAkhilesh Sanikop sequence_header.color_config.is_monochrome ||
1706*09537850SAkhilesh Sanikop sequence_header_.color_config.subsampling_x !=
1707*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_x ||
1708*09537850SAkhilesh Sanikop sequence_header_.color_config.subsampling_y !=
1709*09537850SAkhilesh Sanikop sequence_header.color_config.subsampling_y ||
1710*09537850SAkhilesh Sanikop sequence_header_.max_frame_width != sequence_header.max_frame_width ||
1711*09537850SAkhilesh Sanikop sequence_header_.max_frame_height != sequence_header.max_frame_height;
1712*09537850SAkhilesh Sanikop sequence_header_ = sequence_header;
1713*09537850SAkhilesh Sanikop has_sequence_header_ = true;
1714*09537850SAkhilesh Sanikop return sequence_header_changed;
1715*09537850SAkhilesh Sanikop }
1716*09537850SAkhilesh Sanikop
MaybeInitializeWedgeMasks(FrameType frame_type)1717*09537850SAkhilesh Sanikop bool DecoderImpl::MaybeInitializeWedgeMasks(FrameType frame_type) {
1718*09537850SAkhilesh Sanikop if (IsIntraFrame(frame_type) || wedge_masks_initialized_) {
1719*09537850SAkhilesh Sanikop return true;
1720*09537850SAkhilesh Sanikop }
1721*09537850SAkhilesh Sanikop if (!GenerateWedgeMask(&wedge_masks_)) {
1722*09537850SAkhilesh Sanikop return false;
1723*09537850SAkhilesh Sanikop }
1724*09537850SAkhilesh Sanikop wedge_masks_initialized_ = true;
1725*09537850SAkhilesh Sanikop return true;
1726*09537850SAkhilesh Sanikop }
1727*09537850SAkhilesh Sanikop
MaybeInitializeQuantizerMatrix(const ObuFrameHeader & frame_header)1728*09537850SAkhilesh Sanikop bool DecoderImpl::MaybeInitializeQuantizerMatrix(
1729*09537850SAkhilesh Sanikop const ObuFrameHeader& frame_header) {
1730*09537850SAkhilesh Sanikop if (quantizer_matrix_initialized_ || !frame_header.quantizer.use_matrix) {
1731*09537850SAkhilesh Sanikop return true;
1732*09537850SAkhilesh Sanikop }
1733*09537850SAkhilesh Sanikop if (!InitializeQuantizerMatrix(&quantizer_matrix_)) {
1734*09537850SAkhilesh Sanikop return false;
1735*09537850SAkhilesh Sanikop }
1736*09537850SAkhilesh Sanikop quantizer_matrix_initialized_ = true;
1737*09537850SAkhilesh Sanikop return true;
1738*09537850SAkhilesh Sanikop }
1739*09537850SAkhilesh Sanikop
1740*09537850SAkhilesh Sanikop } // namespace libgav1
1741