FFmpeg
thread.c
Go to the documentation of this file.
1 /*
2  * VVC thread logic
3  *
4  * Copyright (C) 2023 Nuo Mi
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 #include <stdatomic.h>
24 
25 #include "libavcodec/executor.h"
26 #include "libavutil/mem.h"
27 #include "libavutil/thread.h"
28 
29 #include "thread.h"
30 #include "ctu.h"
31 #include "filter.h"
32 #include "inter.h"
33 #include "intra.h"
34 #include "refs.h"
35 
36 typedef struct ProgressListener {
38  struct VVCTask *task;
41 
42 typedef enum VVCTaskStage {
43  VVC_TASK_STAGE_INIT, // for CTU(0, 0) only
54 } VVCTaskStage;
55 
56 typedef struct VVCTask {
57  union {
58  struct VVCTask *next; //for executor debug only
60  } u;
61 
63 
64  // ctu x, y, and raster scan order
65  int rx, ry, rs;
67 
70 
71  // for parse task only
74  int ctu_idx; //ctu idx in the current slice
75 
76  // tasks with target scores met are ready for scheduling
79 } VVCTask;
80 
81 typedef struct VVCRowThread {
83 } VVCRowThread;
84 
85 typedef struct VVCFrameThread {
86  // error return for tasks
88 
91 
92  int ctu_size;
93  int ctu_width;
95  int ctu_count;
96 
97  //protected by lock
100 
102 
106 
107 #define PRIORITY_LOWEST 2
108 static void add_task(VVCContext *s, VVCTask *t)
109 {
110  VVCFrameThread *ft = t->fc->ft;
111  FFTask *task = &t->u.task;
112  const int priorities[] = {
113  0, // VVC_TASK_STAGE_INIT,
114  0, // VVC_TASK_STAGE_PARSE,
115  1, // VVC_TASK_STAGE_DEBLOCK_BS
116  // For an 8K clip, a CTU line completed in the reference frame may trigger 64 and more inter tasks.
117  // We assign these tasks the lowest priority to avoid being overwhelmed with inter tasks.
118  PRIORITY_LOWEST, // VVC_TASK_STAGE_INTER
119  1, // VVC_TASK_STAGE_RECON,
120  1, // VVC_TASK_STAGE_LMCS,
121  1, // VVC_TASK_STAGE_DEBLOCK_V,
122  1, // VVC_TASK_STAGE_DEBLOCK_H,
123  1, // VVC_TASK_STAGE_SAO,
124  1, // VVC_TASK_STAGE_ALF,
125  };
126 
128  task->priority = priorities[t->stage];
129  ff_executor_execute(s->executor, task);
130 }
131 
132 static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
133 {
134  memset(t, 0, sizeof(*t));
135  t->stage = stage;
136  t->fc = fc;
137  t->rx = rx;
138  t->ry = ry;
139  t->rs = ry * fc->ft->ctu_width + rx;
140  for (int i = 0; i < FF_ARRAY_ELEMS(t->score); i++)
141  atomic_store(t->score + i, 0);
143 }
144 
145 static int task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
146 {
147  if (t->sc) {
148  // the task already inited, error bitstream
149  return AVERROR_INVALIDDATA;
150  }
151  t->sc = sc;
152  t->ep = ep;
153  t->ctu_idx = ctu_idx;
154 
155  return 0;
156 }
157 
158 static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
159 {
160  return atomic_fetch_add(&t->score[stage], 1) + 1;
161 }
162 
163 static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
164 {
165  return atomic_load(&t->score[stage]);
166 }
167 
168 //first row in tile or slice
169 static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
170 {
171  const VVCFrameThread *ft = fc->ft;
172  const VVCPPS *pps = fc->ps.pps;
173 
174  if (ry != pps->ctb_to_row_bd[ry]) {
175  const int rs = ry * ft->ctu_width + rx;
176  return fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - ft->ctu_width];
177  }
178  return 1;
179 }
180 
181 static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
182 {
183  // l:left, r:right, t: top, b: bottom
184  static const uint8_t target_score[] =
185  {
186  2, //VVC_TASK_STAGE_DEBLOCK_BS,need l + t parse
187  0, //VVC_TASK_STAGE_INTER, not used
188  2, //VVC_TASK_STAGE_RECON, need l + rt recon
189  3, //VVC_TASK_STAGE_LMCS, need r + b + rb recon
190  1, //VVC_TASK_STAGE_DEBLOCK_V, need l deblock v
191  2, //VVC_TASK_STAGE_DEBLOCK_H, need r deblock v + t deblock h
192  5, //VVC_TASK_STAGE_SAO, need l + r + lb + b + rb deblock h
193  8, //VVC_TASK_STAGE_ALF, need sao around the ctu
194  };
195  uint8_t target = 0;
196  VVCFrameContext *fc = t->fc;
197 
198  if (stage == VVC_TASK_STAGE_INIT)
199  return 1;
200 
201  if (stage == VVC_TASK_STAGE_PARSE) {
202  const H266RawSPS *rsps = fc->ps.sps->r;
203  const int wpp = rsps->sps_entropy_coding_sync_enabled_flag && !is_first_row(fc, t->rx, t->ry);
204  const int no_prev_stage = t->rs > 0;
205  target = 2 + wpp - no_prev_stage; //left parse + colocation + wpp - no_prev_stage
206  } else if (stage == VVC_TASK_STAGE_INTER) {
207  target = atomic_load(&t->target_inter_score);
208  } else {
209  target = target_score[stage - VVC_TASK_STAGE_DEBLOCK_BS];
210  }
211 
212  //+1 for previous stage
213  av_assert0(score <= target + 1);
214  return score == target + 1;
215 }
216 
218  const int rx, const int ry, const VVCTaskStage stage)
219 {
220  VVCTask *t = ft->tasks + ft->ctu_width * ry + rx;
221  uint8_t score;
222 
223  if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height)
224  return;
225 
226  score = task_add_score(t, stage);
227  if (task_has_target_score(t, stage, score)) {
228  av_assert0(s);
229  av_assert0(stage == t->stage);
230  add_task(s, t);
231  }
232 }
233 
234 static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
235 {
236  if (atomic_fetch_sub(scheduled, 1) == 1) {
237  ff_mutex_lock(&ft->lock);
238  ff_cond_signal(&ft->cond);
239  ff_mutex_unlock(&ft->lock);
240  }
241 }
242 
243 static void progress_done(VVCProgressListener *_l, const int type)
244 {
245  const ProgressListener *l = (ProgressListener *)_l;
246  const VVCTask *t = l->task;
247  VVCFrameThread *ft = t->fc->ft;
248 
249  frame_thread_add_score(l->s, ft, t->rx, t->ry, type);
251 }
252 
254 {
256 }
257 
259 {
261 }
262 
263 static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
264 {
265  const int is_inter = vp == VVC_PROGRESS_PIXEL;
266 
267  l->task = t;
268  l->s = s;
269  l->l.vp = vp;
270  l->l.y = y;
271  l->l.progress_done = is_inter ? pixel_done : mv_done;
272  if (is_inter)
274 }
275 
277  VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
278 {
279  VVCFrameThread *ft = t->fc->ft;
280 
282  listener_init(l, t, s, vp, y);
284 }
285 
287 {
288  VVCFrameThread *ft = fc->ft;
289  EntryPoint *ep = t->ep;
290  const VVCSPS *sps = fc->ps.sps;
291 
292  if (sps->r->sps_entropy_coding_sync_enabled_flag) {
293  if (t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]) {
294  EntryPoint *next = ep + 1;
295  if (next < sc->eps + sc->nb_eps && !is_first_row(fc, t->rx, t->ry + 1)) {
296  memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state));
297  ff_vvc_ep_init_stat_coeff(next, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag);
298  }
299  }
300  if (t->ry + 1 < ft->ctu_height && !is_first_row(fc, t->rx, t->ry + 1))
302  }
303 
304  if (t->ctu_idx + 1 < t->ep->ctu_end) {
305  const int next_rs = sc->sh.ctb_addr_in_curr_slice[t->ctu_idx + 1];
306  const int next_rx = next_rs % ft->ctu_width;
307  const int next_ry = next_rs / ft->ctu_width;
308  frame_thread_add_score(s, ft, next_rx, next_ry, VVC_TASK_STAGE_PARSE);
309  }
310 }
311 
312 static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
313 {
314  const VVCSH *sh = &sc->sh;
315 
316  if (!IS_I(sh->r)) {
317  CTU *ctu = fc->tab.ctus + rs;
318  for (int lx = 0; lx < 2; lx++) {
319  for (int i = 0; i < sh->r->num_ref_idx_active[lx]; i++) {
320  int y = ctu->max_y[lx][i];
321  VVCRefPic *refp = sc->rpl[lx].refs + i;
322  VVCFrame *ref = refp->ref;
323  if (ref && y >= 0) {
324  if (refp->is_scaled)
325  y = y * refp->scale[1] >> 14;
327  }
328  }
329  }
330  }
331 }
332 
333 static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry)
334 {
335  VVCFrameThread *ft = fc->ft;
336  const int rs = ry * ft->ctu_width + rx;
337  const int slice_idx = fc->tab.slice_idx[rs];
338  VVCTask *t = ft->tasks + rs;
339  const SliceContext *sc = fc->slices[slice_idx];
340 
341  schedule_next_parse(s, fc, sc, t);
342  schedule_inter(s, fc, sc, t, rs);
343 }
344 
345 static void task_stage_done(const VVCTask *t, VVCContext *s)
346 {
347  VVCFrameContext *fc = t->fc;
348  VVCFrameThread *ft = fc->ft;
349  const VVCTaskStage stage = t->stage;
350 
351 #define ADD(dx, dy, stage) frame_thread_add_score(s, ft, t->rx + (dx), t->ry + (dy), stage)
352 
353  //this is a reserve map of ready_score, ordered by zigzag
354  if (stage == VVC_TASK_STAGE_PARSE) {
357  if (t->rx < 0 || t->rx >= ft->ctu_width || t->ry < 0 || t->ry >= ft->ctu_height)
358  return;
359  parse_task_done(s, fc, t->rx, t->ry);
360  } else if (stage == VVC_TASK_STAGE_RECON) {
361  ADD(-1, 1, VVC_TASK_STAGE_RECON);
362  ADD( 1, 0, VVC_TASK_STAGE_RECON);
363  ADD(-1, -1, VVC_TASK_STAGE_LMCS);
364  ADD( 0, -1, VVC_TASK_STAGE_LMCS);
365  ADD(-1, 0, VVC_TASK_STAGE_LMCS);
366  } else if (stage == VVC_TASK_STAGE_DEBLOCK_V) {
369  } else if (stage == VVC_TASK_STAGE_DEBLOCK_H) {
371  ADD(-1, -1, VVC_TASK_STAGE_SAO);
372  ADD( 0, -1, VVC_TASK_STAGE_SAO);
373  ADD(-1, 0, VVC_TASK_STAGE_SAO);
374  ADD( 1, -1, VVC_TASK_STAGE_SAO);
375  ADD( 1, 0, VVC_TASK_STAGE_SAO);
376  } else if (stage == VVC_TASK_STAGE_SAO) {
377  ADD(-1, -1, VVC_TASK_STAGE_ALF);
378  ADD( 0, -1, VVC_TASK_STAGE_ALF);
379  ADD(-1, 0, VVC_TASK_STAGE_ALF);
380  ADD( 1, -1, VVC_TASK_STAGE_ALF);
381  ADD(-1, 1, VVC_TASK_STAGE_ALF);
382  ADD( 1, 0, VVC_TASK_STAGE_ALF);
383  ADD( 0, 1, VVC_TASK_STAGE_ALF);
384  ADD( 1, 1, VVC_TASK_STAGE_ALF);
385  }
386 }
387 
388 static int task_is_stage_ready(VVCTask *t, int add)
389 {
390  const VVCTaskStage stage = t->stage;
391  uint8_t score;
392  if (stage > VVC_TASK_STAGE_ALF)
393  return 0;
394  score = task_get_score(t, stage) + add;
395  return task_has_target_score(t, stage, score);
396 }
397 
399 {
400  const VVCFrameContext *fc = t->fc;
401 
402  if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) {
403  VVCFrame *col = fc->ref->collocated_ref;
404  const int first_col = t->rx == fc->ps.pps->ctb_to_col_bd[t->rx];
405  if (col && first_col) {
406  //we depend on bottom and right boundary, do not - 1 for y
407  const int y = (t->ry << fc->ps.sps->ctb_log2_size_y);
409  return;
410  }
411  }
413 }
414 
416 {
417  const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start];
418  VVCTask *t = ft->tasks + rs;
419 
421 }
422 
424 {
425  VVCFrameContext *fc = lc->fc;
426  VVCFrameThread *ft = fc->ft;
427  const int ret = ff_vvc_per_frame_init(fc);
428 
429  if (ret < 0)
430  return ret;
431 
432  for (int i = 0; i < fc->nb_slices; i++) {
433  SliceContext *sc = fc->slices[i];
434  for (int j = 0; j < sc->nb_eps; j++) {
435  EntryPoint *ep = sc->eps + j;
436  for (int k = ep->ctu_start; k < ep->ctu_end; k++) {
437  const int rs = sc->sh.ctb_addr_in_curr_slice[k];
438  VVCTask *t = ft->tasks + rs;
439  check_colocation(s, t);
440  }
441  submit_entry_point(s, ft, sc, ep);
442  }
443  }
444  return 0;
445 }
446 
448  const int ry, const VVCProgress idx)
449 {
450  VVCFrameThread *ft = fc->ft;
451  const int ctu_size = ft->ctu_size;
452  int old;
453 
454  if (atomic_fetch_add(&ft->rows[ry].col_progress[idx], 1) == ft->ctu_width - 1) {
455  int y;
456  ff_mutex_lock(&ft->lock);
457  y = old = ft->row_progress[idx];
458  while (y < ft->ctu_height && atomic_load(&ft->rows[y].col_progress[idx]) == ft->ctu_width)
459  y++;
460  if (old != y)
461  ft->row_progress[idx] = y;
462  // ff_vvc_report_progress will acquire other frames' locks, which could lead to a deadlock
463  // We need to unlock ft->lock first
464  ff_mutex_unlock(&ft->lock);
465 
466  if (old != y) {
467  const int progress = y == ft->ctu_height ? INT_MAX : y * ctu_size;
468  ff_vvc_report_progress(fc->ref, idx, progress);
469  }
470  }
471 }
472 
474 {
475  int ret;
476  VVCFrameContext *fc = lc->fc;
477  const int rs = t->rs;
478  const CTU *ctu = fc->tab.ctus + rs;
479 
480  lc->ep = t->ep;
481 
482  ret = ff_vvc_coding_tree_unit(lc, t->ctu_idx, rs, t->rx, t->ry);
483  if (ret < 0)
484  return ret;
485 
486  if (!ctu->has_dmvr)
488 
489  return 0;
490 }
491 
493 {
495  ff_vvc_deblock_bs(lc, t->rx, t->ry, t->rs);
496 
497  return 0;
498 }
499 
501 {
502  VVCFrameContext *fc = lc->fc;
503  const CTU *ctu = fc->tab.ctus + t->rs;
504  int ret;
505 
506  ret = ff_vvc_predict_inter(lc, t->rs);
507  if (ret < 0)
508  return ret;
509 
510  if (ctu->has_dmvr)
512 
513  return 0;
514 }
515 
517 {
518  return ff_vvc_reconstruct(lc, t->rs, t->rx, t->ry);
519 }
520 
522 {
523  VVCFrameContext *fc = lc->fc;
524  VVCFrameThread *ft = fc->ft;
525  const int ctu_size = ft->ctu_size;
526  const int x0 = t->rx * ctu_size;
527  const int y0 = t->ry * ctu_size;
528 
529  ff_vvc_lmcs_filter(lc, x0, y0);
530 
531  return 0;
532 }
533 
535 {
536  VVCFrameContext *fc = lc->fc;
537  VVCFrameThread *ft = fc->ft;
538  const int ctb_size = ft->ctu_size;
539  const int x0 = t->rx * ctb_size;
540  const int y0 = t->ry * ctb_size;
541 
543  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
544  ff_vvc_deblock_vertical(lc, x0, y0, t->rs);
545  }
546 
547  return 0;
548 }
549 
551 {
552  VVCFrameContext *fc = lc->fc;
553  VVCFrameThread *ft = fc->ft;
554  const int ctb_size = ft->ctu_size;
555  const int x0 = t->rx * ctb_size;
556  const int y0 = t->ry * ctb_size;
557 
559  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
560  ff_vvc_deblock_horizontal(lc, x0, y0, t->rs);
561  }
562  if (fc->ps.sps->r->sps_sao_enabled_flag)
563  ff_vvc_sao_copy_ctb_to_hv(lc, t->rx, t->ry, t->ry == ft->ctu_height - 1);
564 
565  return 0;
566 }
567 
569 {
570  VVCFrameContext *fc = lc->fc;
571  VVCFrameThread *ft = fc->ft;
572  const int ctb_size = ft->ctu_size;
573  const int x0 = t->rx * ctb_size;
574  const int y0 = t->ry * ctb_size;
575 
576  if (fc->ps.sps->r->sps_sao_enabled_flag) {
577  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
578  ff_vvc_sao_filter(lc, x0, y0);
579  }
580 
581  if (fc->ps.sps->r->sps_alf_enabled_flag)
582  ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0);
583 
584  return 0;
585 }
586 
588 {
589  VVCFrameContext *fc = lc->fc;
590  VVCFrameThread *ft = fc->ft;
591  const int ctu_size = ft->ctu_size;
592  const int x0 = t->rx * ctu_size;
593  const int y0 = t->ry * ctu_size;
594 
595  if (fc->ps.sps->r->sps_alf_enabled_flag) {
596  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
597  ff_vvc_alf_filter(lc, x0, y0);
598  }
600 
601  return 0;
602 }
603 
604 #define VVC_THREAD_DEBUG
605 #ifdef VVC_THREAD_DEBUG
606 const static char* task_name[] = {
607  "INIT",
608  "P",
609  "B",
610  "I",
611  "R",
612  "L",
613  "V",
614  "H",
615  "S",
616  "A"
617 };
618 #endif
619 
620 typedef int (*run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t);
621 
623 {
624  int ret;
625  VVCFrameContext *fc = t->fc;
626  VVCFrameThread *ft = fc->ft;
627  const VVCTaskStage stage = t->stage;
628  static const run_func run[] = {
629  run_init,
630  run_parse,
632  run_inter,
633  run_recon,
634  run_lmcs,
637  run_sao,
638  run_alf,
639  };
640 
641 #ifdef VVC_THREAD_DEBUG
642  av_log(s->avctx, AV_LOG_DEBUG, "frame %5d, %s(%3d, %3d)\r\n", (int)t->fc->decode_order, task_name[stage], t->rx, t->ry);
643 #endif
644 
645  lc->sc = t->sc;
646 
647  if (!atomic_load(&ft->ret)) {
648  if ((ret = run[stage](s, lc, t)) < 0) {
649 #ifdef COMPAT_ATOMICS_WIN32_STDATOMIC_H
650  intptr_t zero = 0;
651 #else
652  int zero = 0;
653 #endif
655  av_log(s->avctx, AV_LOG_ERROR,
656  "frame %5d, %s(%3d, %3d) failed with %d\r\n",
657  (int)fc->decode_order, task_name[stage], t->rx, t->ry, ret);
658  }
659  }
660 
661  task_stage_done(t, s);
662  return;
663 }
664 
665 static int task_run(FFTask *_t, void *local_context, void *user_data)
666 {
667  VVCTask *t = (VVCTask*)_t;
669  VVCLocalContext *lc = local_context;
670  VVCFrameThread *ft = t->fc->ft;
671 
672  lc->fc = t->fc;
673 
674  do {
675  task_run_stage(t, s, lc);
676  t->stage++;
677  } while (task_is_stage_ready(t, 1));
678 
679  if (t->stage != VVC_TASK_STAGE_LAST)
680  frame_thread_add_score(s, ft, t->rx, t->ry, t->stage);
681 
683 
684  return 0;
685 }
686 
687 FFExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
688 {
690  s,
691  sizeof(VVCLocalContext),
692  PRIORITY_LOWEST + 1,
693  task_run,
694  };
695  return ff_executor_alloc(&callbacks, thread_count);
696 }
697 
699 {
700  ff_executor_free(e);
701 }
702 
704 {
705  VVCFrameThread *ft = fc->ft;
706 
707  if (!ft)
708  return;
709 
710  ff_mutex_destroy(&ft->lock);
711  ff_cond_destroy(&ft->cond);
712  av_freep(&ft->rows);
713  av_freep(&ft->tasks);
714  av_freep(&ft);
715 }
716 
718 {
719  const VVCFrameThread *ft = fc->ft;
720  VVCTask task;
721 
722  task_init(&task, VVC_TASK_STAGE_PARSE, fc, 0, 0);
723 
724  for (int i = VVC_TASK_STAGE_PARSE; i < VVC_TASK_STAGE_LAST; i++) {
725  task.stage = i;
726 
727  for (task.rx = -1; task.rx <= ft->ctu_width; task.rx++) {
728  task.ry = -1; //top
729  task_stage_done(&task, NULL);
730  task.ry = ft->ctu_height; //bottom
731  task_stage_done(&task, NULL);
732  }
733 
734  for (task.ry = 0; task.ry < ft->ctu_height; task.ry++) {
735  task.rx = -1; //left
736  task_stage_done(&task, NULL);
737  task.rx = ft->ctu_width; //right
738  task_stage_done(&task, NULL);
739  }
740  }
741 }
742 
744 {
745  const VVCSPS *sps = fc->ps.sps;
746  const VVCPPS *pps = fc->ps.pps;
747  VVCFrameThread *ft = fc->ft;
748  int ret;
749 
750  if (!ft || ft->ctu_width != pps->ctb_width ||
751  ft->ctu_height != pps->ctb_height ||
752  ft->ctu_size != sps->ctb_size_y) {
753 
755  ft = av_calloc(1, sizeof(*fc->ft));
756  if (!ft)
757  return AVERROR(ENOMEM);
758 
759  ft->ctu_width = fc->ps.pps->ctb_width;
760  ft->ctu_height = fc->ps.pps->ctb_height;
761  ft->ctu_count = fc->ps.pps->ctb_count;
762  ft->ctu_size = fc->ps.sps->ctb_size_y;
763 
764  ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows));
765  if (!ft->rows)
766  goto fail;
767 
768  ft->tasks = av_malloc(ft->ctu_count * sizeof(*ft->tasks));
769  if (!ft->tasks)
770  goto fail;
771 
772  if ((ret = ff_cond_init(&ft->cond, NULL)))
773  goto fail;
774 
775  if ((ret = ff_mutex_init(&ft->lock, NULL))) {
776  ff_cond_destroy(&ft->cond);
777  goto fail;
778  }
779  }
780  fc->ft = ft;
781  ft->ret = 0;
782  for (int y = 0; y < ft->ctu_height; y++) {
783  VVCRowThread *row = ft->rows + y;
784  memset(row->col_progress, 0, sizeof(row->col_progress));
785  }
786 
787  for (int rs = 0; rs < ft->ctu_count; rs++) {
788  VVCTask *t = ft->tasks + rs;
790  }
791 
792  memset(&ft->row_progress[0], 0, sizeof(ft->row_progress));
793 
795 
796  return 0;
797 
798 fail:
799  if (ft) {
800  av_freep(&ft->rows);
801  av_freep(&ft->tasks);
802  av_freep(&ft);
803  }
804 
805  return AVERROR(ENOMEM);
806 }
807 
809 {
810  VVCFrameThread *ft = fc->ft;
811 
812  for (int i = 0; i < fc->nb_slices; i++) {
813  SliceContext *sc = fc->slices[i];
814  for (int j = 0; j < sc->nb_eps; j++) {
815  EntryPoint *ep = sc->eps + j;
816  for (int k = ep->ctu_start; k < ep->ctu_end; k++) {
817  const int rs = sc->sh.ctb_addr_in_curr_slice[k];
818  VVCTask *t = ft->tasks + rs;
819  const int ret = task_init_parse(t, sc, ep, k);
820  if (ret < 0)
821  return ret;
822  }
823  }
824  }
826 
827  return 0;
828 }
829 
831 {
832  VVCFrameThread *ft = fc->ft;
833 
834  ff_mutex_lock(&ft->lock);
835 
837  ff_cond_wait(&ft->cond, &ft->lock);
838 
839  ff_mutex_unlock(&ft->lock);
841 
842 #ifdef VVC_THREAD_DEBUG
843  av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order);
844 #endif
845  return ft->ret;
846 }
ff_vvc_reconstruct
int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry)
reconstruct a CTU
Definition: intra.c:661
VVCSPS
Definition: ps.h:58
run_deblock_h
static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:550
ff_mutex_init
static int ff_mutex_init(AVMutex *mutex, const void *attr)
Definition: thread.h:187
VVCPPS
Definition: ps.h:92
parse_task_done
static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:333
VVCFrameContext::decode_order
uint64_t decode_order
Definition: dec.h:139
atomic_store
#define atomic_store(object, desired)
Definition: stdatomic.h:85
VVC_TASK_STAGE_LAST
@ VVC_TASK_STAGE_LAST
Definition: thread.c:53
ff_vvc_sao_filter
void ff_vvc_sao_filter(VVCLocalContext *lc, int x0, int y0)
sao filter for the CTU
Definition: filter.c:314
VVC_PROGRESS_PIXEL
@ VVC_PROGRESS_PIXEL
Definition: refs.h:45
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
filter.h
run_sao
static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:568
atomic_fetch_add
#define atomic_fetch_add(object, operand)
Definition: stdatomic.h:137
thread.h
ff_vvc_report_frame_finished
void ff_vvc_report_frame_finished(VVCFrame *frame)
Definition: refs.c:547
VVCProgressListener::vp
VVCProgress vp
Definition: refs.h:53
listener_init
static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
Definition: thread.c:263
frame_thread_init_score
static void frame_thread_init_score(VVCFrameContext *fc)
Definition: thread.c:717
ff_vvc_predict_inter
int ff_vvc_predict_inter(VVCLocalContext *lc, const int rs)
Loop entire CTU to predict all inter coding blocks.
Definition: inter.c:1003
callbacks
static const OMX_CALLBACKTYPE callbacks
Definition: omx.c:340
VVCRefPic
Definition: dec.h:45
VVC_TASK_STAGE_DEBLOCK_V
@ VVC_TASK_STAGE_DEBLOCK_V
Definition: thread.c:49
ff_vvc_deblock_vertical
void ff_vvc_deblock_vertical(const VVCLocalContext *lc, const int x0, const int y0, const int rs)
vertical deblock filter for the CTU
Definition: filter.c:836
VVCLocalContext::sc
SliceContext * sc
Definition: ctu.h:434
run_lmcs
static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:521
task_init_parse
static int task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
Definition: thread.c:145
ff_vvc_alf_copy_ctu_to_hv
void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext *lc, const int x0, const int y0)
Definition: filter.c:1071
VVCTask::task
FFTask task
Definition: thread.c:59
ff_vvc_executor_alloc
FFExecutor * ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
Definition: thread.c:687
VVCFrameThread::ctu_size
int ctu_size
Definition: thread.c:92
atomic_int
intptr_t atomic_int
Definition: stdatomic.h:55
VVCSH::r
const H266RawSliceHeader * r
RefStruct reference.
Definition: ps.h:239
fc
#define fc(width, name, range_min, range_max)
Definition: cbs_av1.c:472
atomic_fetch_sub
#define atomic_fetch_sub(object, operand)
Definition: stdatomic.h:140
task_run_stage
static void task_run_stage(VVCTask *t, VVCContext *s, VVCLocalContext *lc)
Definition: thread.c:622
run_parse
static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:473
ff_vvc_deblock_horizontal
void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, const int x0, const int y0, const int rs)
horizontal deblock filter for the CTU
Definition: filter.c:841
ff_vvc_report_progress
void ff_vvc_report_progress(VVCFrame *frame, const VVCProgress vp, const int y)
Definition: refs.c:587
add_progress_listener
static void add_progress_listener(VVCFrame *ref, ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
Definition: thread.c:276
VVCProgress
VVCProgress
Definition: refs.h:43
progress_done
static void progress_done(VVCProgressListener *_l, const int type)
Definition: thread.c:243
FFTaskCallbacks
Definition: executor.h:38
VVCRefPic::ref
struct VVCFrame * ref
Definition: dec.h:46
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
ff_mutex_unlock
static int ff_mutex_unlock(AVMutex *mutex)
Definition: thread.h:189
FFTask
Definition: executor.h:33
ProgressListener::s
VVCContext * s
Definition: thread.c:39
ff_vvc_coding_tree_unit
int ff_vvc_coding_tree_unit(VVCLocalContext *lc, const int ctu_idx, const int rs, const int rx, const int ry)
parse a CTU
Definition: ctu.c:2455
VVCLocalContext::fc
VVCFrameContext * fc
Definition: ctu.h:435
fail
#define fail()
Definition: checkasm.h:188
VVCRowThread
Definition: thread.c:81
VVCTask::stage
VVCTaskStage stage
Definition: thread.c:62
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
task_name
const static char * task_name[]
Definition: thread.c:606
SliceContext::rpl
RefPicList * rpl
Definition: dec.h:113
ADD
#define ADD(dx, dy, stage)
ff_vvc_executor_free
void ff_vvc_executor_free(FFExecutor **e)
Definition: thread.c:698
ff_executor_free
void ff_executor_free(FFExecutor **executor)
Free executor.
Definition: executor.c:202
ff_vvc_frame_submit
int ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc)
Definition: thread.c:808
VVCFrameThread::cond
AVCond cond
Definition: thread.c:104
VVCFrameThread
Definition: thread.c:85
H266RawSliceHeader::num_ref_idx_active
uint8_t num_ref_idx_active[2]
NumRefIdxActive[].
Definition: cbs_h266.h:839
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:209
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
VVCFrameThread::nb_scheduled_tasks
atomic_int nb_scheduled_tasks
Definition: thread.c:98
RefPicList::refs
VVCRefPic refs[VVC_MAX_REF_ENTRIES]
Definition: dec.h:56
AVMutex
#define AVMutex
Definition: thread.h:184
ff_vvc_frame_wait
int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc)
Definition: thread.c:830
s
#define s(width, name)
Definition: cbs_vp9.c:198
FFExecutor
Definition: executor.c:56
EntryPoint::cabac_state
VVCCabacState cabac_state[VVC_CONTEXTS]
Definition: ctu.h:359
LUMA_EXTRA_AFTER
#define LUMA_EXTRA_AFTER
Definition: ctu.h:58
ff_cond_wait
static int ff_cond_wait(AVCond *cond, AVMutex *mutex)
Definition: thread.h:198
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:40
VVCSH
Definition: ps.h:238
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:230
AVCond
#define AVCond
Definition: thread.h:192
VVCFrameContext::ft
struct VVCFrameThread * ft
Definition: dec.h:137
ff_executor_execute
void ff_executor_execute(FFExecutor *e, FFTask *t)
Add task to executor.
Definition: executor.c:213
VVCRowThread::col_progress
atomic_int col_progress[VVC_PROGRESS_LAST]
Definition: thread.c:82
VVC_MAX_REF_ENTRIES
@ VVC_MAX_REF_ENTRIES
Definition: vvc.h:115
atomic_load
#define atomic_load(object)
Definition: stdatomic.h:93
sheduled_done
static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
Definition: thread.c:234
H266RawSPS
Definition: cbs_h266.h:308
VVCTask::sc
SliceContext * sc
Definition: thread.c:72
CTU
Definition: ctu.h:334
inter.h
NULL
#define NULL
Definition: coverity.c:32
run_func
int(* run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:620
ff_vvc_frame_thread_init
int ff_vvc_frame_thread_init(VVCFrameContext *fc)
Definition: thread.c:743
task_has_target_score
static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
Definition: thread.c:181
run
uint8_t run
Definition: svq3.c:204
VVCLocalContext
Definition: ctu.h:373
SliceContext::eps
struct EntryPoint * eps
Definition: dec.h:111
VVCTask::rx
int rx
Definition: thread.c:65
VVC_TASK_STAGE_DEBLOCK_H
@ VVC_TASK_STAGE_DEBLOCK_H
Definition: thread.c:50
FFTask::priority
int priority
Definition: executor.h:35
VVCRefPic::is_scaled
int is_scaled
RprConstraintsActiveFlag.
Definition: dec.h:51
VVCFrameThread::ret
atomic_int ret
Definition: thread.c:87
ProgressListener
Definition: thread.c:36
VVCFrameThread::nb_scheduled_listeners
atomic_int nb_scheduled_listeners
Definition: thread.c:99
ff_executor_alloc
FFExecutor * ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count)
Alloc executor.
Definition: executor.c:156
ff_vvc_alf_filter
void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0)
alf filter for the CTU
Definition: filter.c:1175
SliceContext
Definition: mss12.h:70
ff_mutex_destroy
static int ff_mutex_destroy(AVMutex *mutex)
Definition: thread.h:190
VVCFrameThread::rows
VVCRowThread * rows
Definition: thread.c:89
VVCTask::col_listener
ProgressListener col_listener
Definition: thread.c:68
ff_vvc_decode_neighbour
void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, const int rx, const int ry, const int rs)
Definition: ctu.c:2486
VVCTask::listener
ProgressListener listener[2][VVC_MAX_REF_ENTRIES]
Definition: thread.c:69
run_init
static int run_init(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:423
ff_vvc_lmcs_filter
void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x, const int y)
lmcs filter for the CTU
Definition: filter.c:1230
pixel_done
static void pixel_done(VVCProgressListener *l)
Definition: thread.c:253
VVCTask::u
union VVCTask::@292 u
VVCProgressListener::y
int y
Definition: refs.h:54
ff_vvc_deblock_bs
void ff_vvc_deblock_bs(VVCLocalContext *lc, const int rx, const int ry, const int rs)
derive boundary strength for the CTU
Definition: filter.c:674
ff_vvc_frame_thread_free
void ff_vvc_frame_thread_free(VVCFrameContext *fc)
Definition: thread.c:703
EntryPoint::ctu_end
int ctu_end
Definition: ctu.h:363
VVC_TASK_STAGE_INTER
@ VVC_TASK_STAGE_INTER
Definition: thread.c:46
schedule_inter
static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
Definition: thread.c:312
user_data
static int FUNC() user_data(CodedBitstreamContext *ctx, RWContext *rw, MPEG2RawUserData *current)
Definition: cbs_mpeg2_syntax_template.c:59
intra.h
run_alf
static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:587
refs.h
VVC_PROGRESS_LAST
@ VVC_PROGRESS_LAST
Definition: refs.h:46
VVCFrame
Definition: dec.h:71
CTU::has_dmvr
int has_dmvr
Definition: ctu.h:337
VVCSH::ctb_addr_in_curr_slice
const uint32_t * ctb_addr_in_curr_slice
CtbAddrInCurrSlice.
Definition: ps.h:244
ff_mutex_lock
static int ff_mutex_lock(AVMutex *mutex)
Definition: thread.h:188
VVC_TASK_STAGE_LMCS
@ VVC_TASK_STAGE_LMCS
Definition: thread.c:48
zero
static int zero(InterplayACMContext *s, unsigned ind, unsigned col)
Definition: interplayacm.c:121
VVCTask::ctu_idx
int ctu_idx
Definition: thread.c:74
task_add_score
static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
Definition: thread.c:158
atomic_uchar
intptr_t atomic_uchar
Definition: stdatomic.h:52
VVC_PROGRESS_MV
@ VVC_PROGRESS_MV
Definition: refs.h:44
CTU::max_y
int max_y[2][VVC_MAX_REF_ENTRIES]
Definition: ctu.h:335
task_get_score
static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
Definition: thread.c:163
task_init
static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:132
SliceContext::nb_eps
int nb_eps
Definition: dec.h:112
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
VVCFrameThread::lock
AVMutex lock
Definition: thread.c:103
submit_entry_point
static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep)
Definition: thread.c:415
VVCTask::next
struct VVCTask * next
Definition: thread.c:58
task_is_stage_ready
static int task_is_stage_ready(VVCTask *t, int add)
Definition: thread.c:388
report_frame_progress
static void report_frame_progress(VVCFrameContext *fc, const int ry, const VVCProgress idx)
Definition: thread.c:447
EntryPoint
Definition: ctu.h:354
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
VVCTask
Definition: thread.c:56
VVC_TASK_STAGE_RECON
@ VVC_TASK_STAGE_RECON
Definition: thread.c:47
atomic_compare_exchange_strong
static int atomic_compare_exchange_strong(intptr_t *object, intptr_t *expected, intptr_t desired)
Definition: stdatomic.h:109
run_deblock_bs
static int run_deblock_bs(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:492
ret
ret
Definition: filter_design.txt:187
sps
static int FUNC() sps(CodedBitstreamContext *ctx, RWContext *rw, H264RawSPS *current)
Definition: cbs_h264_syntax_template.c:260
run_deblock_v
static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:534
VVCProgressListener
Definition: refs.h:52
task_stage_done
static void task_stage_done(const VVCTask *t, VVCContext *s)
Definition: thread.c:345
run_recon
static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:516
VVCFrameThread::row_progress
int row_progress[VVC_PROGRESS_LAST]
Definition: thread.c:101
frame_thread_add_score
static void frame_thread_add_score(VVCContext *s, VVCFrameThread *ft, const int rx, const int ry, const VVCTaskStage stage)
Definition: thread.c:217
mv_done
static void mv_done(VVCProgressListener *l)
Definition: thread.c:258
VVC_TASK_STAGE_SAO
@ VVC_TASK_STAGE_SAO
Definition: thread.c:51
VVCFrameThread::tasks
VVCTask * tasks
Definition: thread.c:90
VVCFrameThread::ctu_count
int ctu_count
Definition: thread.c:95
executor.h
ProgressListener::task
struct VVCTask * task
Definition: thread.c:38
ref
static int ref[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:112
ff_cond_signal
static int ff_cond_signal(AVCond *cond)
Definition: thread.h:196
pps
uint64_t pps
Definition: dovi_rpuenc.c:35
H266RawSPS::sps_entropy_coding_sync_enabled_flag
uint8_t sps_entropy_coding_sync_enabled_flag
Definition: cbs_h266.h:348
VVCTask::score
atomic_uchar score[VVC_TASK_STAGE_LAST]
Definition: thread.c:77
VVCTask::ep
EntryPoint * ep
Definition: thread.c:73
ProgressListener::l
VVCProgressListener l
Definition: thread.c:37
VVCTask::target_inter_score
atomic_uchar target_inter_score
Definition: thread.c:78
VVCTask::fc
VVCFrameContext * fc
Definition: thread.c:66
PRIORITY_LOWEST
#define PRIORITY_LOWEST
Definition: thread.c:107
mem.h
VVC_TASK_STAGE_PARSE
@ VVC_TASK_STAGE_PARSE
Definition: thread.c:44
ff_cond_destroy
static int ff_cond_destroy(AVCond *cond)
Definition: thread.h:195
VVCProgressListener::progress_done
progress_done_fn progress_done
Definition: refs.h:55
SliceContext::sh
VVCSH sh
Definition: dec.h:110
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
add_task
static void add_task(VVCContext *s, VVCTask *t)
Definition: thread.c:108
VVCFrameContext
Definition: dec.h:117
VVC_TASK_STAGE_INIT
@ VVC_TASK_STAGE_INIT
Definition: thread.c:43
EntryPoint::ctu_start
int ctu_start
Definition: ctu.h:362
run_inter
static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: thread.c:500
VVCTask::ry
int ry
Definition: thread.c:65
IS_I
#define IS_I(rsh)
Definition: ps.h:38
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
thread.h
VVCFrameThread::ctu_width
int ctu_width
Definition: thread.c:93
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
ff_vvc_sao_copy_ctb_to_hv
void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int last_row)
Definition: filter.c:174
ff_cond_init
static int ff_cond_init(AVCond *cond, const void *attr)
Definition: thread.h:194
ctu.h
ff_vvc_per_frame_init
int ff_vvc_per_frame_init(VVCFrameContext *fc)
Definition: dec.c:412
VVC_TASK_STAGE_DEBLOCK_BS
@ VVC_TASK_STAGE_DEBLOCK_BS
Definition: thread.c:45
VVCLocalContext::ep
EntryPoint * ep
Definition: ctu.h:436
VVCTask::rs
int rs
Definition: thread.c:65
H266RawSliceHeader::sh_deblocking_filter_disabled_flag
uint8_t sh_deblocking_filter_disabled_flag
Definition: cbs_h266.h:817
ff_vvc_ep_init_stat_coeff
void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, const int bit_depth, const int persistent_rice_adaptation_enabled_flag)
Definition: ctu.c:2561
is_first_row
static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
Definition: thread.c:169
task_run
static int task_run(FFTask *_t, void *local_context, void *user_data)
Definition: thread.c:665
check_colocation
static void check_colocation(VVCContext *s, VVCTask *t)
Definition: thread.c:398
ff_vvc_add_progress_listener
void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l)
Definition: refs.c:608
VVCFrameThread::ctu_height
int ctu_height
Definition: thread.c:94
VVCRefPic::scale
int scale[2]
RefPicScale[].
Definition: dec.h:52
VVCContext
Definition: dec.h:214
schedule_next_parse
static void schedule_next_parse(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, const VVCTask *t)
Definition: thread.c:286
VVCTaskStage
VVCTaskStage
Definition: thread.c:42
VVC_TASK_STAGE_ALF
@ VVC_TASK_STAGE_ALF
Definition: thread.c:52