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 41typedef struct{ 42 void *indata; 43 void *outdata; 44 int64_t return_code; 45 unsigned index; 46} Task; 47 48typedef 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 67static 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 } 110end: 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 119int 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 // huffyuv does not support these with multiple frame threads currently 146 if (avctx->context_model > 0 || (avctx->flags & CODEC_FLAG_PASS1)) { 147 av_log(avctx, AV_LOG_WARNING, 148 "Forcing thread count to 1 for huffyuv encoding with first pass or context 1\n"); 149 avctx->thread_count = 1; 150 } 151 } 152 153 if(!avctx->thread_count) { 154 avctx->thread_count = av_cpu_count(); 155 avctx->thread_count = FFMIN(avctx->thread_count, MAX_THREADS); 156 } 157 158 if(avctx->thread_count <= 1) 159 return 0; 160 161 if(avctx->thread_count > MAX_THREADS) 162 return AVERROR(EINVAL); 163 164 av_assert0(!avctx->internal->frame_thread_encoder); 165 c = avctx->internal->frame_thread_encoder = av_mallocz(sizeof(ThreadContext)); 166 if(!c) 167 return AVERROR(ENOMEM); 168 169 c->parent_avctx = avctx; 170 171 c->task_fifo = av_fifo_alloc_array(BUFFER_SIZE, sizeof(Task)); 172 if(!c->task_fifo) 173 goto fail; 174 175 pthread_mutex_init(&c->task_fifo_mutex, NULL); 176 pthread_mutex_init(&c->finished_task_mutex, NULL); 177 pthread_mutex_init(&c->buffer_mutex, NULL); 178 pthread_cond_init(&c->task_fifo_cond, NULL); 179 pthread_cond_init(&c->finished_task_cond, NULL); 180 181 for(i=0; i<avctx->thread_count ; i++){ 182 AVDictionary *tmp = NULL; 183 void *tmpv; 184 AVCodecContext *thread_avctx = avcodec_alloc_context3(avctx->codec); 185 if(!thread_avctx) 186 goto fail; 187 tmpv = thread_avctx->priv_data; 188 *thread_avctx = *avctx; 189 thread_avctx->priv_data = tmpv; 190 thread_avctx->internal = NULL; 191 memcpy(thread_avctx->priv_data, avctx->priv_data, avctx->codec->priv_data_size); 192 thread_avctx->thread_count = 1; 193 thread_avctx->active_thread_type &= ~FF_THREAD_FRAME; 194 195 av_dict_copy(&tmp, options, 0); 196 av_dict_set(&tmp, "threads", "1", 0); 197 if(avcodec_open2(thread_avctx, avctx->codec, &tmp) < 0) { 198 av_dict_free(&tmp); 199 goto fail; 200 } 201 av_dict_free(&tmp); 202 av_assert0(!thread_avctx->internal->frame_thread_encoder); 203 thread_avctx->internal->frame_thread_encoder = c; 204 if(pthread_create(&c->worker[i], NULL, worker, thread_avctx)) { 205 goto fail; 206 } 207 } 208 209 avctx->active_thread_type = FF_THREAD_FRAME; 210 211 return 0; 212fail: 213 avctx->thread_count = i; 214 av_log(avctx, AV_LOG_ERROR, "ff_frame_thread_encoder_init failed\n"); 215 ff_frame_thread_encoder_free(avctx); 216 return -1; 217} 218 219void ff_frame_thread_encoder_free(AVCodecContext *avctx){ 220 int i; 221 ThreadContext *c= avctx->internal->frame_thread_encoder; 222 223 pthread_mutex_lock(&c->task_fifo_mutex); 224 c->exit = 1; 225 pthread_cond_broadcast(&c->task_fifo_cond); 226 pthread_mutex_unlock(&c->task_fifo_mutex); 227 228 for (i=0; i<avctx->thread_count; i++) { 229 pthread_join(c->worker[i], NULL); 230 } 231 232 pthread_mutex_destroy(&c->task_fifo_mutex); 233 pthread_mutex_destroy(&c->finished_task_mutex); 234 pthread_mutex_destroy(&c->buffer_mutex); 235 pthread_cond_destroy(&c->task_fifo_cond); 236 pthread_cond_destroy(&c->finished_task_cond); 237 av_fifo_freep(&c->task_fifo); 238 av_freep(&avctx->internal->frame_thread_encoder); 239} 240 241int ff_thread_video_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet_ptr){ 242 ThreadContext *c = avctx->internal->frame_thread_encoder; 243 Task task; 244 int ret; 245 246 av_assert1(!*got_packet_ptr); 247 248 if(frame){ 249 if(!(avctx->flags & CODEC_FLAG_INPUT_PRESERVED)){ 250 AVFrame *new = av_frame_alloc(); 251 if(!new) 252 return AVERROR(ENOMEM); 253 pthread_mutex_lock(&c->buffer_mutex); 254 ret = ff_get_buffer(c->parent_avctx, new, 0); 255 pthread_mutex_unlock(&c->buffer_mutex); 256 if(ret<0) 257 return ret; 258 new->pts = frame->pts; 259 new->quality = frame->quality; 260 new->pict_type = frame->pict_type; 261 av_image_copy(new->data, new->linesize, (const uint8_t **)frame->data, frame->linesize, 262 avctx->pix_fmt, avctx->width, avctx->height); 263 frame = new; 264 } 265 266 task.index = c->task_index; 267 task.indata = (void*)frame; 268 pthread_mutex_lock(&c->task_fifo_mutex); 269 av_fifo_generic_write(c->task_fifo, &task, sizeof(task), NULL); 270 pthread_cond_signal(&c->task_fifo_cond); 271 pthread_mutex_unlock(&c->task_fifo_mutex); 272 273 c->task_index = (c->task_index+1) % BUFFER_SIZE; 274 275 if(!c->finished_tasks[c->finished_task_index].outdata && (c->task_index - c->finished_task_index) % BUFFER_SIZE <= avctx->thread_count) 276 return 0; 277 } 278 279 if(c->task_index == c->finished_task_index) 280 return 0; 281 282 pthread_mutex_lock(&c->finished_task_mutex); 283 while (!c->finished_tasks[c->finished_task_index].outdata) { 284 pthread_cond_wait(&c->finished_task_cond, &c->finished_task_mutex); 285 } 286 task = c->finished_tasks[c->finished_task_index]; 287 *pkt = *(AVPacket*)(task.outdata); 288 if(pkt->data) 289 *got_packet_ptr = 1; 290 av_freep(&c->finished_tasks[c->finished_task_index].outdata); 291 c->finished_task_index = (c->finished_task_index+1) % BUFFER_SIZE; 292 pthread_mutex_unlock(&c->finished_task_mutex); 293 294 return task.return_code; 295} 296