1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Copyright (c) 2012 Michael Niedermayer <michaelni@gmx.at>
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * This file is part of FFmpeg.
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
8cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14cabdff1aSopenharmony_ci * Lesser General Public License for more details.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
17cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
18cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19cabdff1aSopenharmony_ci */
20cabdff1aSopenharmony_ci
21cabdff1aSopenharmony_ci#include <stdatomic.h>
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci#include "frame_thread_encoder.h"
24cabdff1aSopenharmony_ci
25cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
26cabdff1aSopenharmony_ci#include "libavutil/cpu.h"
27cabdff1aSopenharmony_ci#include "libavutil/imgutils.h"
28cabdff1aSopenharmony_ci#include "libavutil/opt.h"
29cabdff1aSopenharmony_ci#include "libavutil/thread.h"
30cabdff1aSopenharmony_ci#include "avcodec.h"
31cabdff1aSopenharmony_ci#include "codec_internal.h"
32cabdff1aSopenharmony_ci#include "internal.h"
33cabdff1aSopenharmony_ci#include "pthread_internal.h"
34cabdff1aSopenharmony_ci#include "thread.h"
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_ci#define MAX_THREADS 64
37cabdff1aSopenharmony_ci/* There can be as many as MAX_THREADS + 1 outstanding tasks.
38cabdff1aSopenharmony_ci * An additional + 1 is needed so that one can distinguish
39cabdff1aSopenharmony_ci * the case of zero and MAX_THREADS + 1 outstanding tasks modulo
40cabdff1aSopenharmony_ci * the number of buffers. */
41cabdff1aSopenharmony_ci#define BUFFER_SIZE (MAX_THREADS + 2)
42cabdff1aSopenharmony_ci
43cabdff1aSopenharmony_citypedef struct{
44cabdff1aSopenharmony_ci    AVFrame  *indata;
45cabdff1aSopenharmony_ci    AVPacket *outdata;
46cabdff1aSopenharmony_ci    int       return_code;
47cabdff1aSopenharmony_ci    int       finished;
48cabdff1aSopenharmony_ci} Task;
49cabdff1aSopenharmony_ci
50cabdff1aSopenharmony_citypedef struct{
51cabdff1aSopenharmony_ci    AVCodecContext *parent_avctx;
52cabdff1aSopenharmony_ci    pthread_mutex_t buffer_mutex;
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_ci    pthread_mutex_t task_fifo_mutex; /* Used to guard (next_)task_index */
55cabdff1aSopenharmony_ci    pthread_cond_t task_fifo_cond;
56cabdff1aSopenharmony_ci
57cabdff1aSopenharmony_ci    unsigned pthread_init_cnt;
58cabdff1aSopenharmony_ci    unsigned max_tasks;
59cabdff1aSopenharmony_ci    Task tasks[BUFFER_SIZE];
60cabdff1aSopenharmony_ci    pthread_mutex_t finished_task_mutex; /* Guards tasks[i].finished */
61cabdff1aSopenharmony_ci    pthread_cond_t finished_task_cond;
62cabdff1aSopenharmony_ci
63cabdff1aSopenharmony_ci    unsigned next_task_index;
64cabdff1aSopenharmony_ci    unsigned task_index;
65cabdff1aSopenharmony_ci    unsigned finished_task_index;
66cabdff1aSopenharmony_ci
67cabdff1aSopenharmony_ci    pthread_t worker[MAX_THREADS];
68cabdff1aSopenharmony_ci    atomic_int exit;
69cabdff1aSopenharmony_ci} ThreadContext;
70cabdff1aSopenharmony_ci
71cabdff1aSopenharmony_ci#define OFF(member) offsetof(ThreadContext, member)
72cabdff1aSopenharmony_ciDEFINE_OFFSET_ARRAY(ThreadContext, thread_ctx, pthread_init_cnt,
73cabdff1aSopenharmony_ci                    (OFF(buffer_mutex), OFF(task_fifo_mutex), OFF(finished_task_mutex)),
74cabdff1aSopenharmony_ci                    (OFF(task_fifo_cond), OFF(finished_task_cond)));
75cabdff1aSopenharmony_ci#undef OFF
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_cistatic void * attribute_align_arg worker(void *v){
78cabdff1aSopenharmony_ci    AVCodecContext *avctx = v;
79cabdff1aSopenharmony_ci    ThreadContext *c = avctx->internal->frame_thread_encoder;
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_ci    while (!atomic_load(&c->exit)) {
82cabdff1aSopenharmony_ci        int got_packet = 0, ret;
83cabdff1aSopenharmony_ci        AVPacket *pkt;
84cabdff1aSopenharmony_ci        AVFrame *frame;
85cabdff1aSopenharmony_ci        Task *task;
86cabdff1aSopenharmony_ci        unsigned task_index;
87cabdff1aSopenharmony_ci
88cabdff1aSopenharmony_ci        pthread_mutex_lock(&c->task_fifo_mutex);
89cabdff1aSopenharmony_ci        while (c->next_task_index == c->task_index || atomic_load(&c->exit)) {
90cabdff1aSopenharmony_ci            if (atomic_load(&c->exit)) {
91cabdff1aSopenharmony_ci                pthread_mutex_unlock(&c->task_fifo_mutex);
92cabdff1aSopenharmony_ci                goto end;
93cabdff1aSopenharmony_ci            }
94cabdff1aSopenharmony_ci            pthread_cond_wait(&c->task_fifo_cond, &c->task_fifo_mutex);
95cabdff1aSopenharmony_ci        }
96cabdff1aSopenharmony_ci        task_index         = c->next_task_index;
97cabdff1aSopenharmony_ci        c->next_task_index = (c->next_task_index + 1) % c->max_tasks;
98cabdff1aSopenharmony_ci        pthread_mutex_unlock(&c->task_fifo_mutex);
99cabdff1aSopenharmony_ci        /* The main thread ensures that any two outstanding tasks have
100cabdff1aSopenharmony_ci         * different indices, ergo each worker thread owns its element
101cabdff1aSopenharmony_ci         * of c->tasks with the exception of finished, which is shared
102cabdff1aSopenharmony_ci         * with the main thread and guarded by finished_task_mutex. */
103cabdff1aSopenharmony_ci        task  = &c->tasks[task_index];
104cabdff1aSopenharmony_ci        frame = task->indata;
105cabdff1aSopenharmony_ci        pkt   = task->outdata;
106cabdff1aSopenharmony_ci
107cabdff1aSopenharmony_ci        ret = ffcodec(avctx->codec)->cb.encode(avctx, pkt, frame, &got_packet);
108cabdff1aSopenharmony_ci        if(got_packet) {
109cabdff1aSopenharmony_ci            int ret2 = av_packet_make_refcounted(pkt);
110cabdff1aSopenharmony_ci            if (ret >= 0 && ret2 < 0)
111cabdff1aSopenharmony_ci                ret = ret2;
112cabdff1aSopenharmony_ci            pkt->pts = pkt->dts = frame->pts;
113cabdff1aSopenharmony_ci        } else {
114cabdff1aSopenharmony_ci            pkt->data = NULL;
115cabdff1aSopenharmony_ci            pkt->size = 0;
116cabdff1aSopenharmony_ci        }
117cabdff1aSopenharmony_ci        pthread_mutex_lock(&c->buffer_mutex);
118cabdff1aSopenharmony_ci        av_frame_unref(frame);
119cabdff1aSopenharmony_ci        pthread_mutex_unlock(&c->buffer_mutex);
120cabdff1aSopenharmony_ci        pthread_mutex_lock(&c->finished_task_mutex);
121cabdff1aSopenharmony_ci        task->return_code = ret;
122cabdff1aSopenharmony_ci        task->finished    = 1;
123cabdff1aSopenharmony_ci        pthread_cond_signal(&c->finished_task_cond);
124cabdff1aSopenharmony_ci        pthread_mutex_unlock(&c->finished_task_mutex);
125cabdff1aSopenharmony_ci    }
126cabdff1aSopenharmony_ciend:
127cabdff1aSopenharmony_ci    pthread_mutex_lock(&c->buffer_mutex);
128cabdff1aSopenharmony_ci    avcodec_close(avctx);
129cabdff1aSopenharmony_ci    pthread_mutex_unlock(&c->buffer_mutex);
130cabdff1aSopenharmony_ci    av_freep(&avctx);
131cabdff1aSopenharmony_ci    return NULL;
132cabdff1aSopenharmony_ci}
133cabdff1aSopenharmony_ci
134cabdff1aSopenharmony_ciav_cold int ff_frame_thread_encoder_init(AVCodecContext *avctx)
135cabdff1aSopenharmony_ci{
136cabdff1aSopenharmony_ci    int i=0;
137cabdff1aSopenharmony_ci    ThreadContext *c;
138cabdff1aSopenharmony_ci    AVCodecContext *thread_avctx = NULL;
139cabdff1aSopenharmony_ci    int ret;
140cabdff1aSopenharmony_ci
141cabdff1aSopenharmony_ci    if(   !(avctx->thread_type & FF_THREAD_FRAME)
142cabdff1aSopenharmony_ci       || !(avctx->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS))
143cabdff1aSopenharmony_ci        return 0;
144cabdff1aSopenharmony_ci
145cabdff1aSopenharmony_ci    if(   !avctx->thread_count
146cabdff1aSopenharmony_ci       && avctx->codec_id == AV_CODEC_ID_MJPEG
147cabdff1aSopenharmony_ci       && !(avctx->flags & AV_CODEC_FLAG_QSCALE)) {
148cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_DEBUG,
149cabdff1aSopenharmony_ci               "Forcing thread count to 1 for MJPEG encoding, use -thread_type slice "
150cabdff1aSopenharmony_ci               "or a constant quantizer if you want to use multiple cpu cores\n");
151cabdff1aSopenharmony_ci        avctx->thread_count = 1;
152cabdff1aSopenharmony_ci    }
153cabdff1aSopenharmony_ci    if(   avctx->thread_count > 1
154cabdff1aSopenharmony_ci       && avctx->codec_id == AV_CODEC_ID_MJPEG
155cabdff1aSopenharmony_ci       && !(avctx->flags & AV_CODEC_FLAG_QSCALE))
156cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_WARNING,
157cabdff1aSopenharmony_ci               "MJPEG CBR encoding works badly with frame multi-threading, consider "
158cabdff1aSopenharmony_ci               "using -threads 1, -thread_type slice or a constant quantizer.\n");
159cabdff1aSopenharmony_ci
160cabdff1aSopenharmony_ci    if (avctx->codec_id == AV_CODEC_ID_HUFFYUV ||
161cabdff1aSopenharmony_ci        avctx->codec_id == AV_CODEC_ID_FFVHUFF) {
162cabdff1aSopenharmony_ci        int warn = 0;
163cabdff1aSopenharmony_ci        int64_t tmp;
164cabdff1aSopenharmony_ci
165cabdff1aSopenharmony_ci        if (avctx->flags & AV_CODEC_FLAG_PASS1)
166cabdff1aSopenharmony_ci            warn = 1;
167cabdff1aSopenharmony_ci        else if (av_opt_get_int(avctx->priv_data, "context", 0, &tmp) >= 0 &&
168cabdff1aSopenharmony_ci                 tmp > 0) {
169cabdff1aSopenharmony_ci            warn = av_opt_get_int(avctx->priv_data, "non_deterministic", 0, &tmp) < 0
170cabdff1aSopenharmony_ci                   || !tmp;
171cabdff1aSopenharmony_ci        }
172cabdff1aSopenharmony_ci        // huffyuv does not support these with multiple frame threads currently
173cabdff1aSopenharmony_ci        if (warn) {
174cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_WARNING,
175cabdff1aSopenharmony_ci               "Forcing thread count to 1 for huffyuv encoding with first pass or context 1\n");
176cabdff1aSopenharmony_ci            avctx->thread_count = 1;
177cabdff1aSopenharmony_ci        }
178cabdff1aSopenharmony_ci    }
179cabdff1aSopenharmony_ci
180cabdff1aSopenharmony_ci    if(!avctx->thread_count) {
181cabdff1aSopenharmony_ci        avctx->thread_count = av_cpu_count();
182cabdff1aSopenharmony_ci        avctx->thread_count = FFMIN(avctx->thread_count, MAX_THREADS);
183cabdff1aSopenharmony_ci    }
184cabdff1aSopenharmony_ci
185cabdff1aSopenharmony_ci    if(avctx->thread_count <= 1)
186cabdff1aSopenharmony_ci        return 0;
187cabdff1aSopenharmony_ci
188cabdff1aSopenharmony_ci    if(avctx->thread_count > MAX_THREADS)
189cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
190cabdff1aSopenharmony_ci
191cabdff1aSopenharmony_ci    av_assert0(!avctx->internal->frame_thread_encoder);
192cabdff1aSopenharmony_ci    c = avctx->internal->frame_thread_encoder = av_mallocz(sizeof(ThreadContext));
193cabdff1aSopenharmony_ci    if(!c)
194cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
195cabdff1aSopenharmony_ci
196cabdff1aSopenharmony_ci    c->parent_avctx = avctx;
197cabdff1aSopenharmony_ci
198cabdff1aSopenharmony_ci    ret = ff_pthread_init(c, thread_ctx_offsets);
199cabdff1aSopenharmony_ci    if (ret < 0)
200cabdff1aSopenharmony_ci        goto fail;
201cabdff1aSopenharmony_ci    atomic_init(&c->exit, 0);
202cabdff1aSopenharmony_ci
203cabdff1aSopenharmony_ci    c->max_tasks = avctx->thread_count + 2;
204cabdff1aSopenharmony_ci    for (unsigned j = 0; j < c->max_tasks; j++) {
205cabdff1aSopenharmony_ci        if (!(c->tasks[j].indata  = av_frame_alloc()) ||
206cabdff1aSopenharmony_ci            !(c->tasks[j].outdata = av_packet_alloc())) {
207cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
208cabdff1aSopenharmony_ci            goto fail;
209cabdff1aSopenharmony_ci        }
210cabdff1aSopenharmony_ci    }
211cabdff1aSopenharmony_ci
212cabdff1aSopenharmony_ci    for(i=0; i<avctx->thread_count ; i++){
213cabdff1aSopenharmony_ci        void *tmpv;
214cabdff1aSopenharmony_ci        thread_avctx = avcodec_alloc_context3(avctx->codec);
215cabdff1aSopenharmony_ci        if (!thread_avctx) {
216cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
217cabdff1aSopenharmony_ci            goto fail;
218cabdff1aSopenharmony_ci        }
219cabdff1aSopenharmony_ci        tmpv = thread_avctx->priv_data;
220cabdff1aSopenharmony_ci        *thread_avctx = *avctx;
221cabdff1aSopenharmony_ci        thread_avctx->priv_data = tmpv;
222cabdff1aSopenharmony_ci        thread_avctx->internal = NULL;
223cabdff1aSopenharmony_ci        thread_avctx->hw_frames_ctx = NULL;
224cabdff1aSopenharmony_ci        ret = av_opt_copy(thread_avctx, avctx);
225cabdff1aSopenharmony_ci        if (ret < 0)
226cabdff1aSopenharmony_ci            goto fail;
227cabdff1aSopenharmony_ci        if (avctx->codec->priv_class) {
228cabdff1aSopenharmony_ci            ret = av_opt_copy(thread_avctx->priv_data, avctx->priv_data);
229cabdff1aSopenharmony_ci            if (ret < 0)
230cabdff1aSopenharmony_ci                goto fail;
231cabdff1aSopenharmony_ci        }
232cabdff1aSopenharmony_ci        thread_avctx->thread_count = 1;
233cabdff1aSopenharmony_ci        thread_avctx->active_thread_type &= ~FF_THREAD_FRAME;
234cabdff1aSopenharmony_ci
235cabdff1aSopenharmony_ci        if ((ret = avcodec_open2(thread_avctx, avctx->codec, NULL)) < 0)
236cabdff1aSopenharmony_ci            goto fail;
237cabdff1aSopenharmony_ci        av_assert0(!thread_avctx->internal->frame_thread_encoder);
238cabdff1aSopenharmony_ci        thread_avctx->internal->frame_thread_encoder = c;
239cabdff1aSopenharmony_ci        if ((ret = pthread_create(&c->worker[i], NULL, worker, thread_avctx))) {
240cabdff1aSopenharmony_ci            ret = AVERROR(ret);
241cabdff1aSopenharmony_ci            goto fail;
242cabdff1aSopenharmony_ci        }
243cabdff1aSopenharmony_ci    }
244cabdff1aSopenharmony_ci
245cabdff1aSopenharmony_ci    avctx->active_thread_type = FF_THREAD_FRAME;
246cabdff1aSopenharmony_ci
247cabdff1aSopenharmony_ci    return 0;
248cabdff1aSopenharmony_cifail:
249cabdff1aSopenharmony_ci    avcodec_close(thread_avctx);
250cabdff1aSopenharmony_ci    av_freep(&thread_avctx);
251cabdff1aSopenharmony_ci    avctx->thread_count = i;
252cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_ERROR, "ff_frame_thread_encoder_init failed\n");
253cabdff1aSopenharmony_ci    ff_frame_thread_encoder_free(avctx);
254cabdff1aSopenharmony_ci    return ret;
255cabdff1aSopenharmony_ci}
256cabdff1aSopenharmony_ci
257cabdff1aSopenharmony_ciav_cold void ff_frame_thread_encoder_free(AVCodecContext *avctx)
258cabdff1aSopenharmony_ci{
259cabdff1aSopenharmony_ci    ThreadContext *c= avctx->internal->frame_thread_encoder;
260cabdff1aSopenharmony_ci
261cabdff1aSopenharmony_ci    /* In case initializing the mutexes/condition variables failed,
262cabdff1aSopenharmony_ci     * they must not be used. In this case the thread_count is zero
263cabdff1aSopenharmony_ci     * as no thread has been initialized yet. */
264cabdff1aSopenharmony_ci    if (avctx->thread_count > 0) {
265cabdff1aSopenharmony_ci        pthread_mutex_lock(&c->task_fifo_mutex);
266cabdff1aSopenharmony_ci        atomic_store(&c->exit, 1);
267cabdff1aSopenharmony_ci        pthread_cond_broadcast(&c->task_fifo_cond);
268cabdff1aSopenharmony_ci        pthread_mutex_unlock(&c->task_fifo_mutex);
269cabdff1aSopenharmony_ci
270cabdff1aSopenharmony_ci        for (int i = 0; i < avctx->thread_count; i++)
271cabdff1aSopenharmony_ci            pthread_join(c->worker[i], NULL);
272cabdff1aSopenharmony_ci    }
273cabdff1aSopenharmony_ci
274cabdff1aSopenharmony_ci    for (unsigned i = 0; i < c->max_tasks; i++) {
275cabdff1aSopenharmony_ci        av_frame_free(&c->tasks[i].indata);
276cabdff1aSopenharmony_ci        av_packet_free(&c->tasks[i].outdata);
277cabdff1aSopenharmony_ci    }
278cabdff1aSopenharmony_ci
279cabdff1aSopenharmony_ci    ff_pthread_free(c, thread_ctx_offsets);
280cabdff1aSopenharmony_ci    av_freep(&avctx->internal->frame_thread_encoder);
281cabdff1aSopenharmony_ci}
282cabdff1aSopenharmony_ci
283cabdff1aSopenharmony_ciint ff_thread_video_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
284cabdff1aSopenharmony_ci                                 AVFrame *frame, int *got_packet_ptr)
285cabdff1aSopenharmony_ci{
286cabdff1aSopenharmony_ci    ThreadContext *c = avctx->internal->frame_thread_encoder;
287cabdff1aSopenharmony_ci    Task *outtask;
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ci    av_assert1(!*got_packet_ptr);
290cabdff1aSopenharmony_ci
291cabdff1aSopenharmony_ci    if(frame){
292cabdff1aSopenharmony_ci        av_frame_move_ref(c->tasks[c->task_index].indata, frame);
293cabdff1aSopenharmony_ci
294cabdff1aSopenharmony_ci        pthread_mutex_lock(&c->task_fifo_mutex);
295cabdff1aSopenharmony_ci        c->task_index = (c->task_index + 1) % c->max_tasks;
296cabdff1aSopenharmony_ci        pthread_cond_signal(&c->task_fifo_cond);
297cabdff1aSopenharmony_ci        pthread_mutex_unlock(&c->task_fifo_mutex);
298cabdff1aSopenharmony_ci    }
299cabdff1aSopenharmony_ci
300cabdff1aSopenharmony_ci    outtask = &c->tasks[c->finished_task_index];
301cabdff1aSopenharmony_ci    pthread_mutex_lock(&c->finished_task_mutex);
302cabdff1aSopenharmony_ci    /* The access to task_index in the following code is ok,
303cabdff1aSopenharmony_ci     * because it is only ever changed by the main thread. */
304cabdff1aSopenharmony_ci    if (c->task_index == c->finished_task_index ||
305cabdff1aSopenharmony_ci        (frame && !outtask->finished &&
306cabdff1aSopenharmony_ci         (c->task_index - c->finished_task_index + c->max_tasks) % c->max_tasks <= avctx->thread_count)) {
307cabdff1aSopenharmony_ci            pthread_mutex_unlock(&c->finished_task_mutex);
308cabdff1aSopenharmony_ci            return 0;
309cabdff1aSopenharmony_ci        }
310cabdff1aSopenharmony_ci    while (!outtask->finished) {
311cabdff1aSopenharmony_ci        pthread_cond_wait(&c->finished_task_cond, &c->finished_task_mutex);
312cabdff1aSopenharmony_ci    }
313cabdff1aSopenharmony_ci    pthread_mutex_unlock(&c->finished_task_mutex);
314cabdff1aSopenharmony_ci    /* We now own outtask completely: No worker thread touches it any more,
315cabdff1aSopenharmony_ci     * because there is no outstanding task with this index. */
316cabdff1aSopenharmony_ci    outtask->finished = 0;
317cabdff1aSopenharmony_ci    av_packet_move_ref(pkt, outtask->outdata);
318cabdff1aSopenharmony_ci    if(pkt->data)
319cabdff1aSopenharmony_ci        *got_packet_ptr = 1;
320cabdff1aSopenharmony_ci    c->finished_task_index = (c->finished_task_index + 1) % c->max_tasks;
321cabdff1aSopenharmony_ci
322cabdff1aSopenharmony_ci    return outtask->return_code;
323cabdff1aSopenharmony_ci}
324