Directory: | ../../../ffmpeg/ |
---|---|
File: | src/libavcodec/pthread_slice.c |
Date: | 2022-07-04 00:18:54 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 39 | 127 | 30.7% |
Branches: | 15 | 56 | 26.8% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * This file is part of FFmpeg. | ||
3 | * | ||
4 | * FFmpeg is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU Lesser General Public | ||
6 | * License as published by the Free Software Foundation; either | ||
7 | * version 2.1 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * FFmpeg is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | * Lesser General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU Lesser General Public | ||
15 | * License along with FFmpeg; if not, write to the Free Software | ||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
17 | */ | ||
18 | |||
19 | /** | ||
20 | * @file | ||
21 | * Slice multithreading support functions | ||
22 | * @see doc/multithreading.txt | ||
23 | */ | ||
24 | |||
25 | #include "config.h" | ||
26 | |||
27 | #include "avcodec.h" | ||
28 | #include "codec_internal.h" | ||
29 | #include "internal.h" | ||
30 | #include "pthread_internal.h" | ||
31 | #include "thread.h" | ||
32 | |||
33 | #include "libavutil/avassert.h" | ||
34 | #include "libavutil/common.h" | ||
35 | #include "libavutil/cpu.h" | ||
36 | #include "libavutil/mem.h" | ||
37 | #include "libavutil/thread.h" | ||
38 | #include "libavutil/slicethread.h" | ||
39 | |||
40 | typedef int (action_func)(AVCodecContext *c, void *arg); | ||
41 | typedef int (action_func2)(AVCodecContext *c, void *arg, int jobnr, int threadnr); | ||
42 | typedef int (main_func)(AVCodecContext *c); | ||
43 | |||
44 | typedef struct Progress { | ||
45 | pthread_cond_t cond; | ||
46 | pthread_mutex_t mutex; | ||
47 | } Progress; | ||
48 | |||
49 | typedef struct SliceThreadContext { | ||
50 | AVSliceThread *thread; | ||
51 | action_func *func; | ||
52 | action_func2 *func2; | ||
53 | main_func *mainfunc; | ||
54 | void *args; | ||
55 | int *rets; | ||
56 | int job_size; | ||
57 | |||
58 | int *entries; | ||
59 | int entries_count; | ||
60 | int thread_count; | ||
61 | Progress *progress; | ||
62 | } SliceThreadContext; | ||
63 | |||
64 | ✗ | static void main_function(void *priv) { | |
65 | ✗ | AVCodecContext *avctx = priv; | |
66 | ✗ | SliceThreadContext *c = avctx->internal->thread_ctx; | |
67 | ✗ | c->mainfunc(avctx); | |
68 | } | ||
69 | |||
70 | 8687 | static void worker_func(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads) | |
71 | { | ||
72 | 8687 | AVCodecContext *avctx = priv; | |
73 | 8687 | SliceThreadContext *c = avctx->internal->thread_ctx; | |
74 | int ret; | ||
75 | |||
76 | 8687 | ret = c->func ? c->func(avctx, (char *)c->args + c->job_size * jobnr) | |
77 |
1/2✓ Branch 0 taken 8687 times.
✗ Branch 1 not taken.
|
8687 | : c->func2(avctx, c->args, jobnr, threadnr); |
78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8687 times.
|
8687 | if (c->rets) |
79 | ✗ | c->rets[jobnr] = ret; | |
80 | 8687 | } | |
81 | |||
82 | 17 | void ff_slice_thread_free(AVCodecContext *avctx) | |
83 | { | ||
84 | 17 | SliceThreadContext *c = avctx->internal->thread_ctx; | |
85 | int i; | ||
86 | |||
87 | 17 | avpriv_slicethread_free(&c->thread); | |
88 | |||
89 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | for (i = 0; i < c->thread_count; i++) { |
90 | ✗ | Progress *const progress = &c->progress[i]; | |
91 | ✗ | pthread_mutex_destroy(&progress->mutex); | |
92 | ✗ | pthread_cond_destroy(&progress->cond); | |
93 | } | ||
94 | |||
95 | 17 | av_freep(&c->entries); | |
96 | 17 | av_freep(&c->progress); | |
97 | 17 | av_freep(&avctx->internal->thread_ctx); | |
98 | 17 | } | |
99 | |||
100 | 2223 | static int thread_execute(AVCodecContext *avctx, action_func* func, void *arg, int *ret, int job_count, int job_size) | |
101 | { | ||
102 | 2223 | SliceThreadContext *c = avctx->internal->thread_ctx; | |
103 | |||
104 |
2/4✓ Branch 0 taken 2223 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2223 times.
|
2223 | if (!(avctx->active_thread_type&FF_THREAD_SLICE) || avctx->thread_count <= 1) |
105 | ✗ | return avcodec_default_execute(avctx, func, arg, ret, job_count, job_size); | |
106 | |||
107 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2219 times.
|
2223 | if (job_count <= 0) |
108 | 4 | return 0; | |
109 | |||
110 | 2219 | c->job_size = job_size; | |
111 | 2219 | c->args = arg; | |
112 | 2219 | c->func = func; | |
113 | 2219 | c->rets = ret; | |
114 | |||
115 | 2219 | avpriv_slicethread_execute(c->thread, job_count, !!c->mainfunc ); | |
116 | 2219 | return 0; | |
117 | } | ||
118 | |||
119 | ✗ | static int thread_execute2(AVCodecContext *avctx, action_func2* func2, void *arg, int *ret, int job_count) | |
120 | { | ||
121 | ✗ | SliceThreadContext *c = avctx->internal->thread_ctx; | |
122 | ✗ | c->func2 = func2; | |
123 | ✗ | return thread_execute(avctx, NULL, arg, ret, job_count, 0); | |
124 | } | ||
125 | |||
126 | ✗ | int ff_slice_thread_execute_with_mainfunc(AVCodecContext *avctx, action_func2* func2, main_func *mainfunc, void *arg, int *ret, int job_count) | |
127 | { | ||
128 | ✗ | SliceThreadContext *c = avctx->internal->thread_ctx; | |
129 | ✗ | c->func2 = func2; | |
130 | ✗ | c->mainfunc = mainfunc; | |
131 | ✗ | return thread_execute(avctx, NULL, arg, ret, job_count, 0); | |
132 | } | ||
133 | |||
134 | 17 | int ff_slice_thread_init(AVCodecContext *avctx) | |
135 | { | ||
136 | SliceThreadContext *c; | ||
137 | 17 | int thread_count = avctx->thread_count; | |
138 | void (*mainfunc)(void *); | ||
139 | |||
140 | // We cannot do this in the encoder init as the threads are created before | ||
141 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 5 times.
|
17 | if (av_codec_is_encoder(avctx->codec) && |
142 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO && |
143 | ✗ | avctx->height > 2800) | |
144 | ✗ | thread_count = avctx->thread_count = 1; | |
145 | |||
146 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (!thread_count) { |
147 | ✗ | int nb_cpus = av_cpu_count(); | |
148 | ✗ | if (avctx->height) | |
149 | ✗ | nb_cpus = FFMIN(nb_cpus, (avctx->height+15)/16); | |
150 | // use number of cores + 1 as thread count if there is more than one | ||
151 | ✗ | if (nb_cpus > 1) | |
152 | ✗ | thread_count = avctx->thread_count = FFMIN(nb_cpus + 1, MAX_AUTO_THREADS); | |
153 | else | ||
154 | ✗ | thread_count = avctx->thread_count = 1; | |
155 | } | ||
156 | |||
157 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (thread_count <= 1) { |
158 | ✗ | avctx->active_thread_type = 0; | |
159 | ✗ | return 0; | |
160 | } | ||
161 | |||
162 | 17 | avctx->internal->thread_ctx = c = av_mallocz(sizeof(*c)); | |
163 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
|
17 | mainfunc = ffcodec(avctx->codec)->caps_internal & FF_CODEC_CAP_SLICE_THREAD_HAS_MF ? &main_function : NULL; |
164 |
2/4✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
|
17 | if (!c || (thread_count = avpriv_slicethread_create(&c->thread, avctx, worker_func, mainfunc, thread_count)) <= 1) { |
165 | ✗ | if (c) | |
166 | ✗ | avpriv_slicethread_free(&c->thread); | |
167 | ✗ | av_freep(&avctx->internal->thread_ctx); | |
168 | ✗ | avctx->thread_count = 1; | |
169 | ✗ | avctx->active_thread_type = 0; | |
170 | ✗ | return 0; | |
171 | } | ||
172 | 17 | avctx->thread_count = thread_count; | |
173 | |||
174 | 17 | avctx->execute = thread_execute; | |
175 | 17 | avctx->execute2 = thread_execute2; | |
176 | 17 | return 0; | |
177 | } | ||
178 | |||
179 | ✗ | int av_cold ff_slice_thread_init_progress(AVCodecContext *avctx) | |
180 | { | ||
181 | ✗ | SliceThreadContext *const p = avctx->internal->thread_ctx; | |
182 | ✗ | int err, i = 0, thread_count = avctx->thread_count; | |
183 | |||
184 | ✗ | p->progress = av_calloc(thread_count, sizeof(*p->progress)); | |
185 | ✗ | if (!p->progress) { | |
186 | ✗ | err = AVERROR(ENOMEM); | |
187 | ✗ | goto fail; | |
188 | } | ||
189 | |||
190 | ✗ | for (; i < thread_count; i++) { | |
191 | ✗ | Progress *const progress = &p->progress[i]; | |
192 | ✗ | err = pthread_mutex_init(&progress->mutex, NULL); | |
193 | ✗ | if (err) { | |
194 | ✗ | err = AVERROR(err); | |
195 | ✗ | goto fail; | |
196 | } | ||
197 | ✗ | err = pthread_cond_init (&progress->cond, NULL); | |
198 | ✗ | if (err) { | |
199 | ✗ | err = AVERROR(err); | |
200 | ✗ | pthread_mutex_destroy(&progress->mutex); | |
201 | ✗ | goto fail; | |
202 | } | ||
203 | } | ||
204 | ✗ | err = 0; | |
205 | ✗ | fail: | |
206 | ✗ | p->thread_count = i; | |
207 | ✗ | return err; | |
208 | } | ||
209 | |||
210 | ✗ | void ff_thread_report_progress2(AVCodecContext *avctx, int field, int thread, int n) | |
211 | { | ||
212 | ✗ | SliceThreadContext *p = avctx->internal->thread_ctx; | |
213 | ✗ | Progress *const progress = &p->progress[thread]; | |
214 | ✗ | int *entries = p->entries; | |
215 | |||
216 | ✗ | pthread_mutex_lock(&progress->mutex); | |
217 | ✗ | entries[field] +=n; | |
218 | ✗ | pthread_cond_signal(&progress->cond); | |
219 | ✗ | pthread_mutex_unlock(&progress->mutex); | |
220 | } | ||
221 | |||
222 | ✗ | void ff_thread_await_progress2(AVCodecContext *avctx, int field, int thread, int shift) | |
223 | { | ||
224 | ✗ | SliceThreadContext *p = avctx->internal->thread_ctx; | |
225 | Progress *progress; | ||
226 | ✗ | int *entries = p->entries; | |
227 | |||
228 | ✗ | if (!entries || !field) return; | |
229 | |||
230 | ✗ | thread = thread ? thread - 1 : p->thread_count - 1; | |
231 | ✗ | progress = &p->progress[thread]; | |
232 | |||
233 | ✗ | pthread_mutex_lock(&progress->mutex); | |
234 | ✗ | while ((entries[field - 1] - entries[field]) < shift){ | |
235 | ✗ | pthread_cond_wait(&progress->cond, &progress->mutex); | |
236 | } | ||
237 | ✗ | pthread_mutex_unlock(&progress->mutex); | |
238 | } | ||
239 | |||
240 | ✗ | int ff_alloc_entries(AVCodecContext *avctx, int count) | |
241 | { | ||
242 | ✗ | if (avctx->active_thread_type & FF_THREAD_SLICE) { | |
243 | ✗ | SliceThreadContext *p = avctx->internal->thread_ctx; | |
244 | |||
245 | ✗ | if (p->entries) { | |
246 | ✗ | av_freep(&p->entries); | |
247 | } | ||
248 | |||
249 | ✗ | p->entries = av_calloc(count, sizeof(*p->entries)); | |
250 | ✗ | if (!p->entries) { | |
251 | ✗ | p->entries_count = 0; | |
252 | ✗ | return AVERROR(ENOMEM); | |
253 | } | ||
254 | ✗ | p->entries_count = count; | |
255 | } | ||
256 | |||
257 | ✗ | return 0; | |
258 | } | ||
259 | |||
260 | ✗ | void ff_reset_entries(AVCodecContext *avctx) | |
261 | { | ||
262 | ✗ | SliceThreadContext *p = avctx->internal->thread_ctx; | |
263 | ✗ | memset(p->entries, 0, p->entries_count * sizeof(int)); | |
264 | } | ||
265 |