Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Copyright (c) 2012 Michael Niedermayer <michaelni@gmx.at> | |
3 | * | |
4 | * This file is part of FFmpeg. | |
5 | * | |
6 | * FFmpeg is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * FFmpeg is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with FFmpeg; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #include "frame_thread_encoder.h" | |
22 | ||
23 | #include "libavutil/fifo.h" | |
24 | #include "libavutil/avassert.h" | |
25 | #include "libavutil/imgutils.h" | |
26 | #include "avcodec.h" | |
27 | #include "internal.h" | |
28 | #include "thread.h" | |
29 | ||
30 | #if HAVE_PTHREADS | |
31 | #include <pthread.h> | |
32 | #elif HAVE_W32THREADS | |
33 | #include "compat/w32pthreads.h" | |
34 | #elif HAVE_OS2THREADS | |
35 | #include "compat/os2threads.h" | |
36 | #endif | |
37 | ||
38 | #define MAX_THREADS 64 | |
39 | #define BUFFER_SIZE (2*MAX_THREADS) | |
40 | ||
41 | typedef struct{ | |
42 | void *indata; | |
43 | void *outdata; | |
44 | int64_t return_code; | |
45 | unsigned index; | |
46 | } Task; | |
47 | ||
48 | typedef struct{ | |
49 | AVCodecContext *parent_avctx; | |
50 | pthread_mutex_t buffer_mutex; | |
51 | ||
52 | AVFifoBuffer *task_fifo; | |
53 | pthread_mutex_t task_fifo_mutex; | |
54 | pthread_cond_t task_fifo_cond; | |
55 | ||
56 | Task finished_tasks[BUFFER_SIZE]; | |
57 | pthread_mutex_t finished_task_mutex; | |
58 | pthread_cond_t finished_task_cond; | |
59 | ||
60 | unsigned task_index; | |
61 | unsigned finished_task_index; | |
62 | ||
63 | pthread_t worker[MAX_THREADS]; | |
64 | int exit; | |
65 | } ThreadContext; | |
66 | ||
67 | static void * attribute_align_arg worker(void *v){ | |
68 | AVCodecContext *avctx = v; | |
69 | ThreadContext *c = avctx->internal->frame_thread_encoder; | |
70 | AVPacket *pkt = NULL; | |
71 | ||
72 | while(!c->exit){ | |
73 | int got_packet, ret; | |
74 | AVFrame *frame; | |
75 | Task task; | |
76 | ||
77 | if(!pkt) pkt= av_mallocz(sizeof(*pkt)); | |
78 | if(!pkt) continue; | |
79 | av_init_packet(pkt); | |
80 | ||
81 | pthread_mutex_lock(&c->task_fifo_mutex); | |
82 | while (av_fifo_size(c->task_fifo) <= 0 || c->exit) { | |
83 | if(c->exit){ | |
84 | pthread_mutex_unlock(&c->task_fifo_mutex); | |
85 | goto end; | |
86 | } | |
87 | pthread_cond_wait(&c->task_fifo_cond, &c->task_fifo_mutex); | |
88 | } | |
89 | av_fifo_generic_read(c->task_fifo, &task, sizeof(task), NULL); | |
90 | pthread_mutex_unlock(&c->task_fifo_mutex); | |
91 | frame = task.indata; | |
92 | ||
93 | ret = avcodec_encode_video2(avctx, pkt, frame, &got_packet); | |
94 | pthread_mutex_lock(&c->buffer_mutex); | |
95 | av_frame_unref(frame); | |
96 | pthread_mutex_unlock(&c->buffer_mutex); | |
97 | av_frame_free(&frame); | |
98 | if(got_packet) { | |
99 | av_dup_packet(pkt); | |
100 | } else { | |
101 | pkt->data = NULL; | |
102 | pkt->size = 0; | |
103 | } | |
104 | pthread_mutex_lock(&c->finished_task_mutex); | |
105 | c->finished_tasks[task.index].outdata = pkt; pkt = NULL; | |
106 | c->finished_tasks[task.index].return_code = ret; | |
107 | pthread_cond_signal(&c->finished_task_cond); | |
108 | pthread_mutex_unlock(&c->finished_task_mutex); | |
109 | } | |
110 | end: | |
111 | av_free(pkt); | |
112 | pthread_mutex_lock(&c->buffer_mutex); | |
113 | avcodec_close(avctx); | |
114 | pthread_mutex_unlock(&c->buffer_mutex); | |
115 | av_freep(&avctx); | |
116 | return NULL; | |
117 | } | |
118 | ||
119 | int ff_frame_thread_encoder_init(AVCodecContext *avctx, AVDictionary *options){ | |
120 | int i=0; | |
121 | ThreadContext *c; | |
122 | ||
123 | ||
124 | if( !(avctx->thread_type & FF_THREAD_FRAME) | |
125 | || !(avctx->codec->capabilities & CODEC_CAP_INTRA_ONLY)) | |
126 | return 0; | |
127 | ||
128 | if( !avctx->thread_count | |
129 | && avctx->codec_id == AV_CODEC_ID_MJPEG | |
130 | && !(avctx->flags & CODEC_FLAG_QSCALE)) { | |
131 | av_log(avctx, AV_LOG_DEBUG, | |
132 | "Forcing thread count to 1 for MJPEG encoding, use -thread_type slice " | |
133 | "or a constant quantizer if you want to use multiple cpu cores\n"); | |
134 | avctx->thread_count = 1; | |
135 | } | |
136 | if( avctx->thread_count > 1 | |
137 | && avctx->codec_id == AV_CODEC_ID_MJPEG | |
138 | && !(avctx->flags & CODEC_FLAG_QSCALE)) | |
139 | av_log(avctx, AV_LOG_WARNING, | |
140 | "MJPEG CBR encoding works badly with frame multi-threading, consider " | |
141 | "using -threads 1, -thread_type slice or a constant quantizer.\n"); | |
142 | ||
143 | if (avctx->codec_id == AV_CODEC_ID_HUFFYUV || | |
144 | avctx->codec_id == AV_CODEC_ID_FFVHUFF) { | |
145 | int warn = 0; | |
146 | if (avctx->flags & CODEC_FLAG_PASS1) | |
147 | warn = 1; | |
148 | else if(avctx->context_model > 0) { | |
149 | AVDictionaryEntry *t = av_dict_get(options, "non_deterministic", | |
150 | NULL, AV_DICT_MATCH_CASE); | |
151 | warn = !t || !t->value || !atoi(t->value) ? 1 : 0; | |
152 | } | |
153 | // huffyuv does not support these with multiple frame threads currently | |
154 | if (warn) { | |
155 | av_log(avctx, AV_LOG_WARNING, | |
156 | "Forcing thread count to 1 for huffyuv encoding with first pass or context 1\n"); | |
157 | avctx->thread_count = 1; | |
158 | } | |
159 | } | |
160 | ||
161 | if(!avctx->thread_count) { | |
162 | avctx->thread_count = av_cpu_count(); | |
163 | avctx->thread_count = FFMIN(avctx->thread_count, MAX_THREADS); | |
164 | } | |
165 | ||
166 | if(avctx->thread_count <= 1) | |
167 | return 0; | |
168 | ||
169 | if(avctx->thread_count > MAX_THREADS) | |
170 | return AVERROR(EINVAL); | |
171 | ||
172 | av_assert0(!avctx->internal->frame_thread_encoder); | |
173 | c = avctx->internal->frame_thread_encoder = av_mallocz(sizeof(ThreadContext)); | |
174 | if(!c) | |
175 | return AVERROR(ENOMEM); | |
176 | ||
177 | c->parent_avctx = avctx; | |
178 | ||
179 | c->task_fifo = av_fifo_alloc_array(BUFFER_SIZE, sizeof(Task)); | |
180 | if(!c->task_fifo) | |
181 | goto fail; | |
182 | ||
183 | pthread_mutex_init(&c->task_fifo_mutex, NULL); | |
184 | pthread_mutex_init(&c->finished_task_mutex, NULL); | |
185 | pthread_mutex_init(&c->buffer_mutex, NULL); | |
186 | pthread_cond_init(&c->task_fifo_cond, NULL); | |
187 | pthread_cond_init(&c->finished_task_cond, NULL); | |
188 | ||
189 | for(i=0; i<avctx->thread_count ; i++){ | |
190 | AVDictionary *tmp = NULL; | |
191 | void *tmpv; | |
192 | AVCodecContext *thread_avctx = avcodec_alloc_context3(avctx->codec); | |
193 | if(!thread_avctx) | |
194 | goto fail; | |
195 | tmpv = thread_avctx->priv_data; | |
196 | *thread_avctx = *avctx; | |
197 | thread_avctx->priv_data = tmpv; | |
198 | thread_avctx->internal = NULL; | |
199 | memcpy(thread_avctx->priv_data, avctx->priv_data, avctx->codec->priv_data_size); | |
200 | thread_avctx->thread_count = 1; | |
201 | thread_avctx->active_thread_type &= ~FF_THREAD_FRAME; | |
202 | ||
203 | av_dict_copy(&tmp, options, 0); | |
204 | av_dict_set(&tmp, "threads", "1", 0); | |
205 | if(avcodec_open2(thread_avctx, avctx->codec, &tmp) < 0) { | |
206 | av_dict_free(&tmp); | |
207 | goto fail; | |
208 | } | |
209 | av_dict_free(&tmp); | |
210 | av_assert0(!thread_avctx->internal->frame_thread_encoder); | |
211 | thread_avctx->internal->frame_thread_encoder = c; | |
212 | if(pthread_create(&c->worker[i], NULL, worker, thread_avctx)) { | |
213 | goto fail; | |
214 | } | |
215 | } | |
216 | ||
217 | avctx->active_thread_type = FF_THREAD_FRAME; | |
218 | ||
219 | return 0; | |
220 | fail: | |
221 | avctx->thread_count = i; | |
222 | av_log(avctx, AV_LOG_ERROR, "ff_frame_thread_encoder_init failed\n"); | |
223 | ff_frame_thread_encoder_free(avctx); | |
224 | return -1; | |
225 | } | |
226 | ||
227 | void ff_frame_thread_encoder_free(AVCodecContext *avctx){ | |
228 | int i; | |
229 | ThreadContext *c= avctx->internal->frame_thread_encoder; | |
230 | ||
231 | pthread_mutex_lock(&c->task_fifo_mutex); | |
232 | c->exit = 1; | |
233 | pthread_cond_broadcast(&c->task_fifo_cond); | |
234 | pthread_mutex_unlock(&c->task_fifo_mutex); | |
235 | ||
236 | for (i=0; i<avctx->thread_count; i++) { | |
237 | pthread_join(c->worker[i], NULL); | |
238 | } | |
239 | ||
240 | pthread_mutex_destroy(&c->task_fifo_mutex); | |
241 | pthread_mutex_destroy(&c->finished_task_mutex); | |
242 | pthread_mutex_destroy(&c->buffer_mutex); | |
243 | pthread_cond_destroy(&c->task_fifo_cond); | |
244 | pthread_cond_destroy(&c->finished_task_cond); | |
245 | av_fifo_freep(&c->task_fifo); | |
246 | av_freep(&avctx->internal->frame_thread_encoder); | |
247 | } | |
248 | ||
249 | int ff_thread_video_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet_ptr){ | |
250 | ThreadContext *c = avctx->internal->frame_thread_encoder; | |
251 | Task task; | |
252 | int ret; | |
253 | ||
254 | av_assert1(!*got_packet_ptr); | |
255 | ||
256 | if(frame){ | |
257 | if(!(avctx->flags & CODEC_FLAG_INPUT_PRESERVED)){ | |
258 | AVFrame *new = av_frame_alloc(); | |
259 | if(!new) | |
260 | return AVERROR(ENOMEM); | |
261 | pthread_mutex_lock(&c->buffer_mutex); | |
262 | ret = ff_get_buffer(c->parent_avctx, new, 0); | |
263 | pthread_mutex_unlock(&c->buffer_mutex); | |
264 | if(ret<0) | |
265 | return ret; | |
266 | new->pts = frame->pts; | |
267 | new->quality = frame->quality; | |
268 | new->pict_type = frame->pict_type; | |
269 | av_image_copy(new->data, new->linesize, (const uint8_t **)frame->data, frame->linesize, | |
270 | avctx->pix_fmt, avctx->width, avctx->height); | |
271 | frame = new; | |
272 | } | |
273 | ||
274 | task.index = c->task_index; | |
275 | task.indata = (void*)frame; | |
276 | pthread_mutex_lock(&c->task_fifo_mutex); | |
277 | av_fifo_generic_write(c->task_fifo, &task, sizeof(task), NULL); | |
278 | pthread_cond_signal(&c->task_fifo_cond); | |
279 | pthread_mutex_unlock(&c->task_fifo_mutex); | |
280 | ||
281 | c->task_index = (c->task_index+1) % BUFFER_SIZE; | |
282 | ||
283 | if(!c->finished_tasks[c->finished_task_index].outdata && (c->task_index - c->finished_task_index) % BUFFER_SIZE <= avctx->thread_count) | |
284 | return 0; | |
285 | } | |
286 | ||
287 | if(c->task_index == c->finished_task_index) | |
288 | return 0; | |
289 | ||
290 | pthread_mutex_lock(&c->finished_task_mutex); | |
291 | while (!c->finished_tasks[c->finished_task_index].outdata) { | |
292 | pthread_cond_wait(&c->finished_task_cond, &c->finished_task_mutex); | |
293 | } | |
294 | task = c->finished_tasks[c->finished_task_index]; | |
295 | *pkt = *(AVPacket*)(task.outdata); | |
296 | if(pkt->data) | |
297 | *got_packet_ptr = 1; | |
298 | av_freep(&c->finished_tasks[c->finished_task_index].outdata); | |
299 | c->finished_task_index = (c->finished_task_index+1) % BUFFER_SIZE; | |
300 | pthread_mutex_unlock(&c->finished_task_mutex); | |
301 | ||
302 | return task.return_code; | |
303 | } |