FFmpeg
vvc_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 "libavutil/executor.h"
26 #include "libavutil/thread.h"
27 
28 #include "vvc_thread.h"
29 #include "vvc_ctu.h"
30 #include "vvc_filter.h"
31 #include "vvc_inter.h"
32 #include "vvc_intra.h"
33 #include "vvc_refs.h"
34 
35 typedef struct ProgressListener {
37  struct VVCTask *task;
40 
41 typedef enum VVCTaskStage {
51 } VVCTaskStage;
52 
53 typedef struct VVCTask {
54  union {
55  struct VVCTask *next; //for executor debug only
57  } u;
58 
60 
61  // ctu x, y, and raster scan order
62  int rx, ry, rs;
64 
67 
68  // for parse task only
71  int ctu_idx; //ctu idx in the current slice
72 
73  // tasks with target scores met are ready for scheduling
76 } VVCTask;
77 
78 typedef struct VVCRowThread {
80 } VVCRowThread;
81 
82 typedef struct VVCFrameThread {
83  // error return for tasks
85 
88 
89  int ctu_size;
90  int ctu_width;
92  int ctu_count;
93 
94  //protected by lock
97 
99 
103 
104 static void add_task(VVCContext *s, VVCTask *t)
105 {
106  VVCFrameThread *ft = t->fc->ft;
107 
109 
110  av_executor_execute(s->executor, &t->u.task);
111 }
112 
113 static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
114 {
115  memset(t, 0, sizeof(*t));
116  t->stage = stage;
117  t->fc = fc;
118  t->rx = rx;
119  t->ry = ry;
120  t->rs = ry * fc->ft->ctu_width + rx;
121  for (int i = 0; i < FF_ARRAY_ELEMS(t->score); i++)
122  atomic_store(t->score + i, 0);
124 }
125 
126 static void task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
127 {
128  t->sc = sc;
129  t->ep = ep;
130  t->ctu_idx = ctu_idx;
131 }
132 
133 static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
134 {
135  return atomic_fetch_add(&t->score[stage], 1) + 1;
136 }
137 
138 static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
139 {
140  return atomic_load(&t->score[stage]);
141 }
142 
143 //first row in tile or slice
144 static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
145 {
146  const VVCFrameThread *ft = fc->ft;
147  const VVCPPS *pps = fc->ps.pps;
148 
149  if (ry != pps->ctb_to_row_bd[ry]) {
150  const int rs = ry * ft->ctu_width + rx;
151  return fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - ft->ctu_width];
152  }
153  return 1;
154 }
155 
156 static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
157 {
158  // l:left, r:right, t: top, b: bottom
159  static const uint8_t target_score[] =
160  {
161  2, //VVC_TASK_STAGE_RECON, need l + rt recon
162  3, //VVC_TASK_STAGE_LMCS, need r + b + rb recon
163  1, //VVC_TASK_STAGE_DEBLOCK_V, need l deblock v
164  2, //VVC_TASK_STAGE_DEBLOCK_H, need r deblock v + t deblock h
165  5, //VVC_TASK_STAGE_SAO, need l + r + lb + b + rb deblock h
166  8, //VVC_TASK_STAGE_ALF, need sao around the ctu
167  };
168  uint8_t target = 0;
169  VVCFrameContext *fc = t->fc;
170 
171  if (stage == VVC_TASK_STAGE_PARSE) {
172  const H266RawSPS *rsps = fc->ps.sps->r;
173  const int wpp = rsps->sps_entropy_coding_sync_enabled_flag && !is_first_row(fc, t->rx, t->ry);
174  target = 2 + wpp - 1; //left parse + colocation + wpp - no previous stage
175  } else if (stage == VVC_TASK_STAGE_INTER) {
176  target = atomic_load(&t->target_inter_score);
177  } else {
178  target = target_score[stage - VVC_TASK_STAGE_RECON];
179  }
180 
181  //+1 for previous stage
182  av_assert0(score <= target + 1);
183  return score == target + 1;
184 }
185 
187  const int rx, const int ry, const VVCTaskStage stage)
188 {
189  VVCTask *t = ft->tasks + ft->ctu_width * ry + rx;
190  uint8_t score;
191 
192  if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height)
193  return;
194 
195  score = task_add_score(t, stage);
196  if (task_has_target_score(t, stage, score)) {
197  av_assert0(s);
198  av_assert0(stage == t->stage);
199  add_task(s, t);
200  }
201 }
202 
203 static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
204 {
205  if (atomic_fetch_sub(scheduled, 1) == 1) {
206  ff_mutex_lock(&ft->lock);
207  ff_cond_signal(&ft->cond);
208  ff_mutex_unlock(&ft->lock);
209  }
210 }
211 
212 static void progress_done(VVCProgressListener *_l, const int type)
213 {
214  const ProgressListener *l = (ProgressListener *)_l;
215  const VVCTask *t = l->task;
216  VVCFrameThread *ft = t->fc->ft;
217 
218  frame_thread_add_score(l->s, ft, t->rx, t->ry, type);
220 }
221 
223 {
225 }
226 
228 {
230 }
231 
232 static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
233 {
234  const int is_inter = vp == VVC_PROGRESS_PIXEL;
235 
236  l->task = t;
237  l->s = s;
238  l->l.vp = vp;
239  l->l.y = y;
240  l->l.progress_done = is_inter ? pixel_done : mv_done;
241  if (is_inter)
243 }
244 
246  VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
247 {
248  VVCFrameThread *ft = t->fc->ft;
249 
251  listener_init(l, t, s, vp, y);
253 }
254 
256 {
257  VVCFrameThread *ft = fc->ft;
258  EntryPoint *ep = t->ep;
259  const VVCSPS *sps = fc->ps.sps;
260 
261  if (sps->r->sps_entropy_coding_sync_enabled_flag) {
262  if (t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]) {
263  EntryPoint *next = ep + 1;
264  if (next < sc->eps + sc->nb_eps && !is_first_row(fc, t->rx, t->ry + 1)) {
265  memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state));
266  ff_vvc_ep_init_stat_coeff(next, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag);
267  }
268  }
269  if (t->ry + 1 < ft->ctu_height && !is_first_row(fc, t->rx, t->ry + 1))
271  }
272 
273  if (t->ctu_idx + 1 < t->ep->ctu_end) {
274  const int next_rs = sc->sh.ctb_addr_in_curr_slice[t->ctu_idx + 1];
275  const int next_rx = next_rs % ft->ctu_width;
276  const int next_ry = next_rs / ft->ctu_width;
277  frame_thread_add_score(s, ft, next_rx, next_ry, VVC_TASK_STAGE_PARSE);
278  }
279 }
280 
281 static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
282 {
283  const VVCSH *sh = &sc->sh;
284 
285  if (!IS_I(sh->r)) {
286  CTU *ctu = fc->tab.ctus + rs;
287  for (int lx = 0; lx < 2; lx++) {
288  for (int i = 0; i < sh->r->num_ref_idx_active[lx]; i++) {
289  const int y = ctu->max_y[lx][i];
290  VVCFrame *ref = sc->rpl[lx].ref[i];
291  if (ref && y >= 0)
293  }
294  }
295  }
296 }
297 
298 static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry)
299 {
300  VVCFrameThread *ft = fc->ft;
301  const int rs = ry * ft->ctu_width + rx;
302  const int slice_idx = fc->tab.slice_idx[rs];
303  VVCTask *t = ft->tasks + rs;
304  const SliceContext *sc = fc->slices[slice_idx];
305 
306  schedule_next_parse(s, fc, sc, t);
307  schedule_inter(s, fc, sc, t, rs);
308 }
309 
310 static void task_stage_done(const VVCTask *t, VVCContext *s)
311 {
312  VVCFrameContext *fc = t->fc;
313  VVCFrameThread *ft = fc->ft;
314  const VVCTaskStage stage = t->stage;
315 
316 #define ADD(dx, dy, stage) frame_thread_add_score(s, ft, t->rx + (dx), t->ry + (dy), stage)
317 
318  //this is a reserve map of ready_score, ordered by zigzag
319  if (stage == VVC_TASK_STAGE_PARSE) {
320  parse_task_done(s, fc, t->rx, t->ry);
321  } else if (stage == VVC_TASK_STAGE_RECON) {
322  ADD(-1, 1, VVC_TASK_STAGE_RECON);
323  ADD( 1, 0, VVC_TASK_STAGE_RECON);
324  ADD(-1, -1, VVC_TASK_STAGE_LMCS);
325  ADD( 0, -1, VVC_TASK_STAGE_LMCS);
326  ADD(-1, 0, VVC_TASK_STAGE_LMCS);
327  } else if (stage == VVC_TASK_STAGE_DEBLOCK_V) {
330  } else if (stage == VVC_TASK_STAGE_DEBLOCK_H) {
332  ADD(-1, -1, VVC_TASK_STAGE_SAO);
333  ADD( 0, -1, VVC_TASK_STAGE_SAO);
334  ADD(-1, 0, VVC_TASK_STAGE_SAO);
335  ADD( 1, -1, VVC_TASK_STAGE_SAO);
336  ADD( 1, 0, VVC_TASK_STAGE_SAO);
337  } else if (stage == VVC_TASK_STAGE_SAO) {
338  ADD(-1, -1, VVC_TASK_STAGE_ALF);
339  ADD( 0, -1, VVC_TASK_STAGE_ALF);
340  ADD(-1, 0, VVC_TASK_STAGE_ALF);
341  ADD( 1, -1, VVC_TASK_STAGE_ALF);
342  ADD(-1, 1, VVC_TASK_STAGE_ALF);
343  ADD( 1, 0, VVC_TASK_STAGE_ALF);
344  ADD( 0, 1, VVC_TASK_STAGE_ALF);
345  ADD( 1, 1, VVC_TASK_STAGE_ALF);
346  }
347 }
348 
349 static int task_is_stage_ready(VVCTask *t, int add)
350 {
351  const VVCTaskStage stage = t->stage;
352  uint8_t score;
353  if (stage > VVC_TASK_STAGE_ALF)
354  return 0;
355  score = task_get_score(t, stage) + add;
356  return task_has_target_score(t, stage, score);
357 }
358 
359 static int task_ready(const AVTask *_t, void *user_data)
360 {
361  VVCTask *t = (VVCTask*)_t;
362 
363  return task_is_stage_ready(t, 0);
364 }
365 
366 #define CHECK(a, b) \
367  do { \
368  if ((a) != (b)) \
369  return (a) < (b); \
370  } while (0)
371 
372 static int task_priority_higher(const AVTask *_a, const AVTask *_b)
373 {
374  const VVCTask *a = (const VVCTask*)_a;
375  const VVCTask *b = (const VVCTask*)_b;
376 
377  CHECK(a->fc->decode_order, b->fc->decode_order); //decode order
378 
379  if (a->stage == VVC_TASK_STAGE_PARSE || b->stage == VVC_TASK_STAGE_PARSE) {
380  CHECK(a->stage, b->stage);
381  CHECK(a->ry, b->ry);
382  return a->rx < b->rx;
383  }
384 
385  CHECK(a->rx + a->ry + a->stage, b->rx + b->ry + b->stage); //zigzag with type
386  CHECK(a->rx + a->ry, b->rx + b->ry); //zigzag
387  return a->ry < b->ry;
388 }
389 
391  const int ry, const VVCProgress idx)
392 {
393  VVCFrameThread *ft = fc->ft;
394  const int ctu_size = ft->ctu_size;
395  int old;
396 
397  if (atomic_fetch_add(&ft->rows[ry].col_progress[idx], 1) == ft->ctu_width - 1) {
398  int y;
399  ff_mutex_lock(&ft->lock);
400  y = old = ft->row_progress[idx];
401  while (y < ft->ctu_height && atomic_load(&ft->rows[y].col_progress[idx]) == ft->ctu_width)
402  y++;
403  if (old != y) {
404  const int progress = y == ft->ctu_height ? INT_MAX : y * ctu_size;
405  ft->row_progress[idx] = y;
406  ff_vvc_report_progress(fc->ref, idx, progress);
407  }
408  ff_mutex_unlock(&ft->lock);
409  }
410 }
411 
413 {
414  int ret;
415  VVCFrameContext *fc = lc->fc;
416  const int rs = t->rs;
417  const CTU *ctu = fc->tab.ctus + rs;
418 
419  lc->sc = t->sc;
420  lc->ep = t->ep;
421 
422  ret = ff_vvc_coding_tree_unit(lc, t->ctu_idx, rs, t->rx, t->ry);
423  if (ret < 0)
424  return ret;
425 
426  if (!ctu->has_dmvr)
428 
429  return 0;
430 }
431 
433 {
434  VVCFrameContext *fc = lc->fc;
435  VVCFrameThread *ft = fc->ft;
436  const int rs = t->ry * ft->ctu_width + t->rx;
437  const CTU *ctu = fc->tab.ctus + rs;
438  const int slice_idx = fc->tab.slice_idx[rs];
439 
440  if (slice_idx != -1) {
441  lc->sc = fc->slices[slice_idx];
442  ff_vvc_predict_inter(lc, rs);
443  }
444 
445  if (ctu->has_dmvr)
447 
448  return 0;
449 }
450 
452 {
453  VVCFrameContext *fc = lc->fc;
454  const int rs = t->rs;
455  const int slice_idx = fc->tab.slice_idx[rs];
456 
457  if (slice_idx != -1) {
458  lc->sc = fc->slices[slice_idx];
459  ff_vvc_reconstruct(lc, rs, t->rx, t->ry);
460  }
461 
462  return 0;
463 }
464 
466 {
467  VVCFrameContext *fc = lc->fc;
468  VVCFrameThread *ft = fc->ft;
469  const int ctu_size = ft->ctu_size;
470  const int x0 = t->rx * ctu_size;
471  const int y0 = t->ry * ctu_size;
472  const int rs = t->ry * ft->ctu_width + t->rx;
473  const int slice_idx = fc->tab.slice_idx[rs];
474 
475  if (slice_idx != -1) {
476  lc->sc = fc->slices[slice_idx];
477  ff_vvc_lmcs_filter(lc, x0, y0);
478  }
479 
480  return 0;
481 }
482 
484 {
485  VVCFrameContext *fc = lc->fc;
486  VVCFrameThread *ft = fc->ft;
487  const int rs = t->ry * ft->ctu_width + t->rx;
488  const int ctb_size = ft->ctu_size;
489  const int x0 = t->rx * ctb_size;
490  const int y0 = t->ry * ctb_size;
491  const int slice_idx = fc->tab.slice_idx[rs];
492 
493  if (slice_idx != -1) {
494  lc->sc = fc->slices[slice_idx];
496  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs);
497  ff_vvc_deblock_vertical(lc, x0, y0);
498  }
499  }
500 
501  return 0;
502 }
503 
505 {
506  VVCFrameContext *fc = lc->fc;
507  VVCFrameThread *ft = fc->ft;
508  const int ctb_size = ft->ctu_size;
509  const int rs = t->ry * ft->ctu_width + t->rx;
510  const int x0 = t->rx * ctb_size;
511  const int y0 = t->ry * ctb_size;
512  const int slice_idx = fc->tab.slice_idx[rs];
513 
514  if (slice_idx != -1) {
515  lc->sc = fc->slices[slice_idx];
517  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs);
518  ff_vvc_deblock_horizontal(lc, x0, y0);
519  }
520  if (fc->ps.sps->r->sps_sao_enabled_flag)
521  ff_vvc_sao_copy_ctb_to_hv(lc, t->rx, t->ry, t->ry == ft->ctu_height - 1);
522  }
523 
524  return 0;
525 }
526 
528 {
529  VVCFrameContext *fc = lc->fc;
530  VVCFrameThread *ft = fc->ft;
531  const int rs = t->ry * fc->ps.pps->ctb_width + t->rx;
532  const int ctb_size = ft->ctu_size;
533  const int x0 = t->rx * ctb_size;
534  const int y0 = t->ry * ctb_size;
535 
536  if (fc->ps.sps->r->sps_sao_enabled_flag) {
537  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs);
538  ff_vvc_sao_filter(lc, x0, y0);
539  }
540 
541  if (fc->ps.sps->r->sps_alf_enabled_flag)
542  ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0);
543 
544  return 0;
545 }
546 
548 {
549  VVCFrameContext *fc = lc->fc;
550  VVCFrameThread *ft = fc->ft;
551  const int ctu_size = ft->ctu_size;
552  const int x0 = t->rx * ctu_size;
553  const int y0 = t->ry * ctu_size;
554 
555  if (fc->ps.sps->r->sps_alf_enabled_flag) {
556  const int slice_idx = CTB(fc->tab.slice_idx, t->rx, t->ry);
557  if (slice_idx != -1) {
558  lc->sc = fc->slices[slice_idx];
559  ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs);
560  ff_vvc_alf_filter(lc, x0, y0);
561  }
562  }
564 
565  return 0;
566 }
567 
568 #define VVC_THREAD_DEBUG
569 #ifdef VVC_THREAD_DEBUG
570 const static char* task_name[] = {
571  "P",
572  "I",
573  "R",
574  "L",
575  "V",
576  "H",
577  "S",
578  "A"
579 };
580 #endif
581 
583 
585 {
586  int ret;
587  VVCFrameContext *fc = t->fc;
588  VVCFrameThread *ft = fc->ft;
589  const VVCTaskStage stage = t->stage;
590  run_func run[] = {
591  run_parse,
592  run_inter,
593  run_recon,
594  run_lmcs,
597  run_sao,
598  run_alf,
599  };
600 
601 #ifdef VVC_THREAD_DEBUG
602  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);
603 #endif
604 
605  if (!atomic_load(&ft->ret)) {
606  if ((ret = run[stage](s, lc, t)) < 0) {
607 #ifdef COMPAT_ATOMICS_WIN32_STDATOMIC_H
608  intptr_t zero = 0;
609 #else
610  int zero = 0;
611 #endif
613  av_log(s->avctx, AV_LOG_ERROR,
614  "frame %5d, %s(%3d, %3d) failed with %d\r\n",
615  (int)fc->decode_order, task_name[stage], t->rx, t->ry, ret);
616  }
617  }
618 
619  task_stage_done(t, s);
620  return;
621 }
622 
623 static int task_run(AVTask *_t, void *local_context, void *user_data)
624 {
625  VVCTask *t = (VVCTask*)_t;
627  VVCLocalContext *lc = local_context;
628  VVCFrameThread *ft = t->fc->ft;
629 
630  lc->fc = t->fc;
631 
632  do {
633  task_run_stage(t, s, lc);
634  t->stage++;
635  } while (task_is_stage_ready(t, 1));
636 
637  if (t->stage != VVC_TASK_STAGE_LAST)
638  frame_thread_add_score(s, ft, t->rx, t->ry, t->stage);
639 
641 
642  return 0;
643 }
644 
645 AVExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
646 {
648  s,
649  sizeof(VVCLocalContext),
651  task_ready,
652  task_run,
653  };
654  return av_executor_alloc(&callbacks, thread_count);
655 }
656 
658 {
659  av_executor_free(e);
660 }
661 
663 {
664  VVCFrameThread *ft = fc->ft;
665 
666  if (!ft)
667  return;
668 
669  ff_mutex_destroy(&ft->lock);
670  ff_cond_destroy(&ft->cond);
671  av_freep(&ft->rows);
672  av_freep(&ft->tasks);
673  av_freep(&ft);
674 }
675 
677 {
678  const VVCFrameThread *ft = fc->ft;
679  VVCTask task;
680 
681  task_init(&task, VVC_TASK_STAGE_RECON, fc, 0, 0);
682 
683  for (int i = VVC_TASK_STAGE_RECON; i < VVC_TASK_STAGE_LAST; i++) {
684  task.stage = i;
685 
686  for (task.rx = -1; task.rx <= ft->ctu_width; task.rx++) {
687  task.ry = -1; //top
688  task_stage_done(&task, NULL);
689  task.ry = ft->ctu_height; //bottom
690  task_stage_done(&task, NULL);
691  }
692 
693  for (task.ry = 0; task.ry < ft->ctu_height; task.ry++) {
694  task.rx = -1; //left
695  task_stage_done(&task, NULL);
696  task.rx = ft->ctu_width; //right
697  task_stage_done(&task, NULL);
698  }
699  }
700 }
701 
703 {
704  const VVCSPS *sps = fc->ps.sps;
705  const VVCPPS *pps = fc->ps.pps;
706  VVCFrameThread *ft = fc->ft;
707  int ret;
708 
709  if (!ft || ft->ctu_width != pps->ctb_width ||
710  ft->ctu_height != pps->ctb_height ||
711  ft->ctu_size != sps->ctb_size_y) {
712 
714  ft = av_calloc(1, sizeof(*fc->ft));
715  if (!ft)
716  return AVERROR(ENOMEM);
717 
718  ft->ctu_width = fc->ps.pps->ctb_width;
719  ft->ctu_height = fc->ps.pps->ctb_height;
720  ft->ctu_count = fc->ps.pps->ctb_count;
721  ft->ctu_size = fc->ps.sps->ctb_size_y;
722 
723  ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows));
724  if (!ft->rows)
725  goto fail;
726 
727  ft->tasks = av_malloc(ft->ctu_count * sizeof(*ft->tasks));
728  if (!ft->tasks)
729  goto fail;
730 
731  if ((ret = ff_cond_init(&ft->cond, NULL)))
732  goto fail;
733 
734  if ((ret = ff_mutex_init(&ft->lock, NULL))) {
735  ff_cond_destroy(&ft->cond);
736  goto fail;
737  }
738  }
739  fc->ft = ft;
740  ft->ret = 0;
741  for (int y = 0; y < ft->ctu_height; y++) {
742  VVCRowThread *row = ft->rows + y;
743  memset(row->col_progress, 0, sizeof(row->col_progress));
744  }
745 
746  for (int rs = 0; rs < ft->ctu_count; rs++) {
747  VVCTask *t = ft->tasks + rs;
748  task_init(t, VVC_TASK_STAGE_PARSE, fc, rs % ft->ctu_width, rs / ft->ctu_width);
749  }
750 
751  memset(&ft->row_progress[0], 0, sizeof(ft->row_progress));
752 
754 
755  return 0;
756 
757 fail:
758  if (ft) {
759  av_freep(&ft->rows);
760  av_freep(&ft->tasks);
761  av_freep(&ft);
762  }
763 
764  return AVERROR(ENOMEM);
765 }
766 
768 {
769  const VVCFrameContext *fc = t->fc;
770 
771  if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) {
772  VVCFrame *col = fc->ref->collocated_ref;
773  const int first_col = t->rx == fc->ps.pps->ctb_to_col_bd[t->rx];
774  if (col && first_col) {
775  //we depend on bottom and right boundary, do not - 1 for y
776  const int y = (t->ry << fc->ps.sps->ctb_log2_size_y);
778  return;
779  }
780  }
782 }
783 
785 {
786  const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start];
787  VVCTask *t = ft->tasks + rs;
788 
790 }
791 
793 {
794  VVCFrameThread *ft = fc->ft;
795 
796  for (int i = 0; i < fc->nb_slices; i++) {
797  SliceContext *sc = fc->slices[i];
798  for (int j = 0; j < sc->nb_eps; j++) {
799  EntryPoint *ep = sc->eps + j;
800  for (int k = ep->ctu_start; k < ep->ctu_end; k++) {
801  const int rs = sc->sh.ctb_addr_in_curr_slice[k];
802  VVCTask *t = ft->tasks + rs;
803 
804  task_init_parse(t, sc, ep, k);
805  check_colocation(s, t);
806  }
807  submit_entry_point(s, ft, sc, ep);
808  }
809  }
810 }
811 
813 {
814  VVCFrameThread *ft = fc->ft;
815 
816  ff_mutex_lock(&ft->lock);
817 
819  ff_cond_wait(&ft->cond, &ft->lock);
820 
821  ff_mutex_unlock(&ft->lock);
823 
824 #ifdef VVC_THREAD_DEBUG
825  av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order);
826 #endif
827  return ft->ret;
828 }
task_get_score
static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage)
Definition: vvc_thread.c:138
VVCSPS
Definition: vvc_ps.h:58
ff_vvc_sao_filter
void ff_vvc_sao_filter(VVCLocalContext *lc, int x, int y)
sao filter for the CTU
Definition: vvc_filter.c:154
add_task
static void add_task(VVCContext *s, VVCTask *t)
Definition: vvc_thread.c:104
VVC_MAX_REF_ENTRIES
@ VVC_MAX_REF_ENTRIES
Definition: vvc.h:115
ff_mutex_init
static int ff_mutex_init(AVMutex *mutex, const void *attr)
Definition: thread.h:187
VVCPPS
Definition: vvc_ps.h:92
VVCFrameContext::decode_order
uint64_t decode_order
Definition: vvcdec.h:114
atomic_store
#define atomic_store(object, desired)
Definition: stdatomic.h:85
ff_vvc_lmcs_filter
void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x, const int y)
lmcs filter for the CTU
Definition: vvc_filter.c:1269
listener_init
static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
Definition: vvc_thread.c:232
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
task_init_parse
static void task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx)
Definition: vvc_thread.c:126
ff_vvc_report_frame_finished
void ff_vvc_report_frame_finished(VVCFrame *frame)
Definition: vvc_refs.c:494
task_name
const static char * task_name[]
Definition: vvc_thread.c:570
thread.h
VVCProgressListener::vp
VVCProgress vp
Definition: vvc_refs.h:48
ff_vvc_alf_filter
void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0)
alf filter for the CTU
Definition: vvc_filter.c:1201
run_parse
static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:412
ff_vvc_predict_inter
int ff_vvc_predict_inter(VVCLocalContext *lc, const int rs)
Loop entire CTU to predict all inter coding blocks.
Definition: vvc_inter.c:902
VVC_TASK_STAGE_PARSE
@ VVC_TASK_STAGE_PARSE
Definition: vvc_thread.c:42
callbacks
static const OMX_CALLBACKTYPE callbacks
Definition: omx.c:339
task_is_stage_ready
static int task_is_stage_ready(VVCTask *t, int add)
Definition: vvc_thread.c:349
VVCLocalContext::sc
SliceContext * sc
Definition: vvc_ctu.h:430
b
#define b
Definition: input.c:41
VVCFrameThread::ctu_size
int ctu_size
Definition: vvc_thread.c:89
atomic_int
intptr_t atomic_int
Definition: stdatomic.h:55
VVCSH::r
const H266RawSliceHeader * r
RefStruct reference.
Definition: vvc_ps.h:225
schedule_next_parse
static void schedule_next_parse(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, const VVCTask *t)
Definition: vvc_thread.c:255
fc
#define fc(width, name, range_min, range_max)
Definition: cbs_av1.c:472
run_recon
static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:451
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
vvc_intra.h
ff_mutex_unlock
static int ff_mutex_unlock(AVMutex *mutex)
Definition: thread.h:189
AVTaskCallbacks
Definition: executor.h:31
VVCTask::u
union VVCTask::@226 u
ProgressListener::s
VVCContext * s
Definition: vvc_thread.c:38
VVCLocalContext::fc
VVCFrameContext * fc
Definition: vvc_ctu.h:431
schedule_inter
static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs)
Definition: vvc_thread.c:281
fail
#define fail()
Definition: checkasm.h:179
VVCTaskStage
VVCTaskStage
Definition: vvc_thread.c:41
VVCRowThread
Definition: vvc_thread.c:78
vvc_refs.h
VVC_TASK_STAGE_ALF
@ VVC_TASK_STAGE_ALF
Definition: vvc_thread.c:49
task_ready
static int task_ready(const AVTask *_t, void *user_data)
Definition: vvc_thread.c:359
VVCTask::stage
VVCTaskStage stage
Definition: vvc_thread.c:59
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
SliceContext::rpl
RefPicList * rpl
Definition: vvcdec.h:88
atomic_fetch_sub
#define atomic_fetch_sub(object, operand)
Definition: stdatomic.h:137
VVC_TASK_STAGE_DEBLOCK_V
@ VVC_TASK_STAGE_DEBLOCK_V
Definition: vvc_thread.c:46
VVCFrameThread::cond
AVCond cond
Definition: vvc_thread.c:101
VVCFrameThread
Definition: vvc_thread.c:82
H266RawSliceHeader::num_ref_idx_active
uint8_t num_ref_idx_active[2]
NumRefIdxActive[].
Definition: cbs_h266.h:837
av_executor_alloc
AVExecutor * av_executor_alloc(const AVTaskCallbacks *cb, int thread_count)
Alloc executor.
Definition: executor.c:135
ff_vvc_report_progress
void ff_vvc_report_progress(VVCFrame *frame, const VVCProgress vp, const int y)
Definition: vvc_refs.c:534
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
VVCFrameThread::nb_scheduled_tasks
atomic_int nb_scheduled_tasks
Definition: vvc_thread.c:95
AVMutex
#define AVMutex
Definition: thread.h:184
frame_thread_init_score
static void frame_thread_init_score(VVCFrameContext *fc)
Definition: vvc_thread.c:676
parse_task_done
static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry)
Definition: vvc_thread.c:298
s
#define s(width, name)
Definition: cbs_vp9.c:198
progress_done
static void progress_done(VVCProgressListener *_l, const int type)
Definition: vvc_thread.c:212
EntryPoint::cabac_state
VVCCabacState cabac_state[VVC_CONTEXTS]
Definition: vvc_ctu.h:354
VVC_TASK_STAGE_LAST
@ VVC_TASK_STAGE_LAST
Definition: vvc_thread.c:50
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: vvc_ps.h:224
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: vvc_ctu.c:2441
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
AVCond
#define AVCond
Definition: thread.h:192
VVCFrameContext::ft
struct VVCFrameThread * ft
Definition: vvcdec.h:112
pixel_done
static void pixel_done(VVCProgressListener *l)
Definition: vvc_thread.c:222
RefPicList::ref
struct HEVCFrame * ref[HEVC_MAX_REFS]
Definition: hevcdec.h:190
VVCRowThread::col_progress
atomic_int col_progress[VVC_PROGRESS_LAST]
Definition: vvc_thread.c:79
ff_vvc_frame_wait
int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc)
Definition: vvc_thread.c:812
atomic_load
#define atomic_load(object)
Definition: stdatomic.h:93
AVTask
Definition: executor.h:27
VVC_PROGRESS_MV
@ VVC_PROGRESS_MV
Definition: vvc_refs.h:39
run_lmcs
static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:465
LUMA_EXTRA_AFTER
#define LUMA_EXTRA_AFTER
Definition: vvc_ctu.h:55
H266RawSPS
Definition: cbs_h266.h:308
VVCTask::sc
SliceContext * sc
Definition: vvc_thread.c:69
CTU
Definition: vvc_ctu.h:328
add_progress_listener
static void add_progress_listener(VVCFrame *ref, ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y)
Definition: vvc_thread.c:245
NULL
#define NULL
Definition: coverity.c:32
AVExecutor
Definition: executor.c:47
run
uint8_t run
Definition: svq3.c:203
VVCLocalContext
Definition: vvc_ctu.h:368
frame_thread_add_score
static void frame_thread_add_score(VVCContext *s, VVCFrameThread *ft, const int rx, const int ry, const VVCTaskStage stage)
Definition: vvc_thread.c:186
mv_done
static void mv_done(VVCProgressListener *l)
Definition: vvc_thread.c:227
task_init
static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
Definition: vvc_thread.c:113
SliceContext::eps
struct EntryPoint * eps
Definition: vvcdec.h:86
submit_entry_point
static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep)
Definition: vvc_thread.c:784
VVCTask::rx
int rx
Definition: vvc_thread.c:62
ff_vvc_frame_thread_free
void ff_vvc_frame_thread_free(VVCFrameContext *fc)
Definition: vvc_thread.c:662
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: vvc_filter.c:143
VVC_TASK_STAGE_LMCS
@ VVC_TASK_STAGE_LMCS
Definition: vvc_thread.c:45
VVCFrameThread::ret
atomic_int ret
Definition: vvc_thread.c:84
ProgressListener
Definition: vvc_thread.c:35
VVCFrameThread::nb_scheduled_listeners
atomic_int nb_scheduled_listeners
Definition: vvc_thread.c:96
SliceContext
Definition: mss12.h:70
ff_mutex_destroy
static int ff_mutex_destroy(AVMutex *mutex)
Definition: thread.h:190
atomic_compare_exchange_strong
#define atomic_compare_exchange_strong(object, expected, desired)
Definition: stdatomic.h:114
VVCFrameThread::rows
VVCRowThread * rows
Definition: vvc_thread.c:86
VVCTask::col_listener
ProgressListener col_listener
Definition: vvc_thread.c:65
VVCTask::listener
ProgressListener listener[2][VVC_MAX_REF_ENTRIES]
Definition: vvc_thread.c:66
run_deblock_v
static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:483
pps
static int FUNC() pps(CodedBitstreamContext *ctx, RWContext *rw, H264RawPPS *current)
Definition: cbs_h264_syntax_template.c:404
ff_vvc_deblock_vertical
void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0)
vertical deblock filter for the CTU
Definition: vvc_filter.c:808
report_frame_progress
static void report_frame_progress(VVCFrameContext *fc, const int ry, const VVCProgress idx)
Definition: vvc_thread.c:390
ff_vvc_alf_copy_ctu_to_hv
void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext *lc, const int x0, const int y0)
Definition: vvc_filter.c:1177
VVCProgressListener::y
int y
Definition: vvc_refs.h:49
CTB
#define CTB(tab, x, y)
Definition: hevc_filter.c:254
EntryPoint::ctu_end
int ctu_end
Definition: vvc_ctu.h:358
executor.h
user_data
static int FUNC() user_data(CodedBitstreamContext *ctx, RWContext *rw, MPEG2RawUserData *current)
Definition: cbs_mpeg2_syntax_template.c:59
run_alf
static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:547
VVC_PROGRESS_PIXEL
@ VVC_PROGRESS_PIXEL
Definition: vvc_refs.h:40
vvc_thread.h
VVCFrame
Definition: vvcdec.h:56
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
CTU::has_dmvr
int has_dmvr
Definition: vvc_ctu.h:332
task_add_score
static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage)
Definition: vvc_thread.c:133
run_func
int(* run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:582
VVCSH::ctb_addr_in_curr_slice
const uint32_t * ctb_addr_in_curr_slice
CtbAddrInCurrSlice.
Definition: vvc_ps.h:230
ff_mutex_lock
static int ff_mutex_lock(AVMutex *mutex)
Definition: thread.h:188
is_first_row
static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry)
Definition: vvc_thread.c:144
VVC_TASK_STAGE_INTER
@ VVC_TASK_STAGE_INTER
Definition: vvc_thread.c:43
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: vvc_ctu.c:2544
vvc_filter.h
VVCTask::task
AVTask task
Definition: vvc_thread.c:56
VVCTask::ctu_idx
int ctu_idx
Definition: vvc_thread.c:71
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: vvc_ctu.c:2472
atomic_uchar
intptr_t atomic_uchar
Definition: stdatomic.h:52
CTU::max_y
int max_y[2][VVC_MAX_REF_ENTRIES]
Definition: vvc_ctu.h:330
VVC_PROGRESS_LAST
@ VVC_PROGRESS_LAST
Definition: vvc_refs.h:41
SliceContext::nb_eps
int nb_eps
Definition: vvcdec.h:87
task_priority_higher
static int task_priority_higher(const AVTask *_a, const AVTask *_b)
Definition: vvc_thread.c:372
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:255
run_sao
static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:527
vvc_ctu.h
ff_vvc_frame_thread_init
int ff_vvc_frame_thread_init(VVCFrameContext *fc)
Definition: vvc_thread.c:702
VVCFrameThread::lock
AVMutex lock
Definition: vvc_thread.c:100
VVCTask::next
struct VVCTask * next
Definition: vvc_thread.c:55
EntryPoint
Definition: vvc_ctu.h:349
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:262
VVCTask
Definition: vvc_thread.c:53
ret
ret
Definition: filter_design.txt:187
CHECK
#define CHECK(a, b)
Definition: vvc_thread.c:366
sps
static int FUNC() sps(CodedBitstreamContext *ctx, RWContext *rw, H264RawSPS *current)
Definition: cbs_h264_syntax_template.c:260
ff_vvc_executor_free
void ff_vvc_executor_free(AVExecutor **e)
Definition: vvc_thread.c:657
VVCProgressListener
Definition: vvc_refs.h:47
av_executor_free
void av_executor_free(AVExecutor **executor)
Free executor.
Definition: executor.c:174
VVCFrameThread::row_progress
int row_progress[VVC_PROGRESS_LAST]
Definition: vvc_thread.c:98
task_run
static int task_run(AVTask *_t, void *local_context, void *user_data)
Definition: vvc_thread.c:623
VVCFrameThread::tasks
VVCTask * tasks
Definition: vvc_thread.c:87
VVCFrameThread::ctu_count
int ctu_count
Definition: vvc_thread.c:92
ff_vvc_frame_submit
void ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc)
Definition: vvc_thread.c:792
ff_vvc_deblock_horizontal
void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0)
horizontal deblock filter for the CTU
Definition: vvc_filter.c:878
av_executor_execute
void av_executor_execute(AVExecutor *e, AVTask *t)
Add task to executor.
Definition: executor.c:182
VVC_TASK_STAGE_SAO
@ VVC_TASK_STAGE_SAO
Definition: vvc_thread.c:48
atomic_fetch_add
#define atomic_fetch_add(object, operand)
Definition: stdatomic.h:131
ff_vvc_reconstruct
int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry)
reconstruct a CTU
Definition: vvc_intra.c:659
ProgressListener::task
struct VVCTask * task
Definition: vvc_thread.c:37
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
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: vvc_thread.c:74
VVCProgress
VVCProgress
Definition: vvc_refs.h:38
VVCTask::ep
EntryPoint * ep
Definition: vvc_thread.c:70
ProgressListener::l
VVCProgressListener l
Definition: vvc_thread.c:36
IS_I
#define IS_I(rsh)
Definition: vvc_ps.h:38
VVCTask::target_inter_score
atomic_uchar target_inter_score
Definition: vvc_thread.c:75
run_inter
static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:432
VVCTask::fc
VVCFrameContext * fc
Definition: vvc_thread.c:63
zero
#define zero
Definition: regdef.h:64
VVC_TASK_STAGE_DEBLOCK_H
@ VVC_TASK_STAGE_DEBLOCK_H
Definition: vvc_thread.c:47
task_run_stage
static void task_run_stage(VVCTask *t, VVCContext *s, VVCLocalContext *lc)
Definition: vvc_thread.c:584
check_colocation
static void check_colocation(VVCContext *s, VVCTask *t)
Definition: vvc_thread.c:767
task_stage_done
static void task_stage_done(const VVCTask *t, VVCContext *s)
Definition: vvc_thread.c:310
sheduled_done
static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled)
Definition: vvc_thread.c:203
ff_cond_destroy
static int ff_cond_destroy(AVCond *cond)
Definition: thread.h:195
ADD
#define ADD(dx, dy, stage)
VVCProgressListener::progress_done
progress_done_fn progress_done
Definition: vvc_refs.h:50
SliceContext::sh
VVCSH sh
Definition: vvcdec.h:85
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
run_deblock_h
static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
Definition: vvc_thread.c:504
VVCFrameContext
Definition: vvcdec.h:92
EntryPoint::ctu_start
int ctu_start
Definition: vvc_ctu.h:357
VVCTask::ry
int ry
Definition: vvc_thread.c:62
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
VVCFrameThread::ctu_width
int ctu_width
Definition: vvc_thread.c:90
ff_cond_init
static int ff_cond_init(AVCond *cond, const void *attr)
Definition: thread.h:194
VVCLocalContext::ep
EntryPoint * ep
Definition: vvc_ctu.h:432
VVCTask::rs
int rs
Definition: vvc_thread.c:62
H266RawSliceHeader::sh_deblocking_filter_disabled_flag
uint8_t sh_deblocking_filter_disabled_flag
Definition: cbs_h266.h:815
ff_vvc_executor_alloc
AVExecutor * ff_vvc_executor_alloc(VVCContext *s, const int thread_count)
Definition: vvc_thread.c:645
int
int
Definition: ffmpeg_filter.c:409
VVCFrameThread::ctu_height
int ctu_height
Definition: vvc_thread.c:91
VVCContext
Definition: vvcdec.h:195
VVC_TASK_STAGE_RECON
@ VVC_TASK_STAGE_RECON
Definition: vvc_thread.c:44
ff_vvc_add_progress_listener
void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l)
Definition: vvc_refs.c:554
vvc_inter.h
task_has_target_score
static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score)
Definition: vvc_thread.c:156