Coverage Report

Created: 2023-06-07 06:30

/src/vlc/src/input/thumbnailer.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * thumbnailer.c: Thumbnailing API
3
 *****************************************************************************
4
 * Copyright (C) 2018 VLC authors and VideoLAN
5
 *
6
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program 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
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
#ifdef HAVE_CONFIG_H
24
# include "config.h"
25
#endif
26
27
#include <vlc_thumbnailer.h>
28
#include <vlc_executor.h>
29
#include "input_internal.h"
30
31
struct vlc_thumbnailer_t
32
{
33
    vlc_object_t* parent;
34
    vlc_executor_t *executor;
35
};
36
37
struct seek_target
38
{
39
    enum
40
    {
41
        VLC_THUMBNAILER_SEEK_TIME,
42
        VLC_THUMBNAILER_SEEK_POS,
43
    } type;
44
    union
45
    {
46
        vlc_tick_t time;
47
        double pos;
48
    };
49
};
50
51
/* We may not rename vlc_thumbnailer_request_t because it is exposed in the
52
 * public API */
53
typedef struct vlc_thumbnailer_request_t task_t;
54
55
struct vlc_thumbnailer_request_t
56
{
57
    vlc_atomic_rc_t rc;
58
    vlc_thumbnailer_t *thumbnailer;
59
60
    struct seek_target seek_target;
61
    bool fast_seek;
62
    input_item_t *item;
63
    /**
64
     * A positive value will be used as the timeout duration
65
     * VLC_TICK_INVALID means no timeout
66
     */
67
    vlc_tick_t timeout;
68
    vlc_thumbnailer_cb cb;
69
    void* userdata;
70
71
    vlc_mutex_t lock;
72
    vlc_cond_t cond_ended;
73
    enum
74
    {
75
        RUNNING,
76
        INTERRUPTED,
77
        ENDED,
78
    } status;
79
    picture_t *pic;
80
81
    struct vlc_runnable runnable; /**< to be passed to the executor */
82
};
83
84
static void RunnableRun(void *);
85
86
static task_t *
87
TaskNew(vlc_thumbnailer_t *thumbnailer, input_item_t *item,
88
        struct seek_target seek_target, bool fast_seek,
89
        vlc_thumbnailer_cb cb, void *userdata, vlc_tick_t timeout)
90
0
{
91
0
    task_t *task = malloc(sizeof(*task));
92
0
    if (!task)
93
0
        return NULL;
94
95
0
    vlc_atomic_rc_init(&task->rc);
96
0
    task->thumbnailer = thumbnailer;
97
0
    task->item = item;
98
0
    task->seek_target = seek_target;
99
0
    task->fast_seek = fast_seek;
100
0
    task->cb = cb;
101
0
    task->userdata = userdata;
102
0
    task->timeout = timeout;
103
104
0
    vlc_mutex_init(&task->lock);
105
0
    vlc_cond_init(&task->cond_ended);
106
0
    task->status = RUNNING;
107
0
    task->pic = NULL;
108
109
0
    task->runnable.run = RunnableRun;
110
0
    task->runnable.userdata = task;
111
112
0
    input_item_Hold(item);
113
114
0
    return task;
115
0
}
116
117
static void
118
TaskRelease(task_t *task)
119
0
{
120
0
    if (!vlc_atomic_rc_dec(&task->rc))
121
0
        return;
122
0
    input_item_Release(task->item);
123
0
    free(task);
124
0
}
125
126
static void NotifyThumbnail(task_t *task, picture_t *pic)
127
0
{
128
0
    assert(task->cb);
129
0
    task->cb(task->userdata, pic);
130
0
}
131
132
static void
133
on_thumbnailer_input_event( input_thread_t *input,
134
                            const struct vlc_input_event *event, void *userdata )
135
0
{
136
0
    VLC_UNUSED(input);
137
0
    if ( event->type != INPUT_EVENT_THUMBNAIL_READY &&
138
0
         ( event->type != INPUT_EVENT_STATE || ( event->state.value != ERROR_S &&
139
0
                                                 event->state.value != END_S ) ) )
140
0
         return;
141
142
0
    task_t *task = userdata;
143
144
0
    vlc_mutex_lock(&task->lock);
145
0
    if (task->status != RUNNING)
146
0
    {
147
        /* We may receive a THUMBNAIL_READY event followed by an
148
         * INPUT_EVENT_STATE (end of stream), we must only consider the first
149
         * one. */
150
0
        vlc_mutex_unlock(&task->lock);
151
0
        return;
152
0
    }
153
154
0
    task->status = ENDED;
155
156
0
    if (event->type == INPUT_EVENT_THUMBNAIL_READY)
157
0
        task->pic = picture_Hold(event->thumbnail);
158
159
0
    vlc_cond_signal(&task->cond_ended);
160
0
    vlc_mutex_unlock(&task->lock);
161
0
}
162
163
static void
164
RunnableRun(void *userdata)
165
0
{
166
0
    vlc_thread_set_name("vlc-run-thumb");
167
168
0
    task_t *task = userdata;
169
0
    vlc_thumbnailer_t *thumbnailer = task->thumbnailer;
170
171
0
    vlc_tick_t now = vlc_tick_now();
172
173
0
    input_thread_t* input =
174
0
            input_Create( thumbnailer->parent, on_thumbnailer_input_event, task,
175
0
                          task->item, INPUT_TYPE_THUMBNAILING, NULL, NULL );
176
0
    if (!input)
177
0
        goto error;
178
179
0
    if (task->seek_target.type == VLC_THUMBNAILER_SEEK_TIME)
180
0
        input_SetTime(input, task->seek_target.time, task->fast_seek);
181
0
    else
182
0
    {
183
0
        assert(task->seek_target.type == VLC_THUMBNAILER_SEEK_POS);
184
0
        input_SetPosition(input, task->seek_target.pos, task->fast_seek);
185
0
    }
186
187
0
    int ret = input_Start(input);
188
0
    if (ret != VLC_SUCCESS)
189
0
    {
190
0
        input_Close(input);
191
0
        goto error;
192
0
    }
193
194
0
    vlc_mutex_lock(&task->lock);
195
0
    if (task->timeout == VLC_TICK_INVALID)
196
0
    {
197
0
        while (task->status == RUNNING)
198
0
            vlc_cond_wait(&task->cond_ended, &task->lock);
199
0
    }
200
0
    else
201
0
    {
202
0
        vlc_tick_t deadline = now + task->timeout;
203
0
        int timeout = 0;
204
0
        while (task->status == RUNNING && timeout == 0)
205
0
            timeout =
206
0
                vlc_cond_timedwait(&task->cond_ended, &task->lock, deadline);
207
0
    }
208
0
    picture_t* pic = task->pic;
209
0
    task->pic = NULL;
210
211
0
    bool notify = task->status != INTERRUPTED;
212
0
    vlc_mutex_unlock(&task->lock);
213
214
0
    if (notify)
215
0
        NotifyThumbnail(task, pic);
216
217
0
    if (pic)
218
0
        picture_Release(pic);
219
220
0
    input_Stop(input);
221
0
    input_Close(input);
222
223
0
error:
224
0
    TaskRelease(task);
225
0
}
226
227
static void
228
Interrupt(task_t *task)
229
0
{
230
    /* Wake up RunnableRun() which will call input_Stop() */
231
0
    vlc_mutex_lock(&task->lock);
232
0
    task->status = INTERRUPTED;
233
0
    vlc_cond_signal(&task->cond_ended);
234
0
    vlc_mutex_unlock(&task->lock);
235
0
}
236
237
static task_t *
238
RequestCommon(vlc_thumbnailer_t *thumbnailer, struct seek_target seek_target,
239
              enum vlc_thumbnailer_seek_speed speed, input_item_t *item,
240
              vlc_tick_t timeout, vlc_thumbnailer_cb cb, void *userdata)
241
0
{
242
0
    bool fast_seek = speed == VLC_THUMBNAILER_SEEK_FAST;
243
0
    task_t *task = TaskNew(thumbnailer, item, seek_target, fast_seek, cb,
244
0
                           userdata, timeout);
245
0
    if (!task)
246
0
        return NULL;
247
248
    /* One ref for the executor */
249
0
    vlc_atomic_rc_inc(&task->rc);
250
0
    vlc_executor_Submit(thumbnailer->executor, &task->runnable);
251
252
0
    return task;
253
0
}
254
255
task_t *
256
vlc_thumbnailer_RequestByTime( vlc_thumbnailer_t *thumbnailer,
257
                               vlc_tick_t time,
258
                               enum vlc_thumbnailer_seek_speed speed,
259
                               input_item_t *item, vlc_tick_t timeout,
260
                               vlc_thumbnailer_cb cb, void* userdata )
261
0
{
262
0
    struct seek_target seek_target = {
263
0
        .type = VLC_THUMBNAILER_SEEK_TIME,
264
0
        .time = time,
265
0
    };
266
0
    return RequestCommon(thumbnailer, seek_target, speed, item, timeout, cb,
267
0
                         userdata);
268
0
}
269
270
task_t *
271
vlc_thumbnailer_RequestByPos( vlc_thumbnailer_t *thumbnailer,
272
                              double pos, enum vlc_thumbnailer_seek_speed speed,
273
                              input_item_t *item, vlc_tick_t timeout,
274
                              vlc_thumbnailer_cb cb, void* userdata )
275
0
{
276
0
    struct seek_target seek_target = {
277
0
        .type = VLC_THUMBNAILER_SEEK_POS,
278
0
        .pos = pos,
279
0
    };
280
0
    return RequestCommon(thumbnailer, seek_target, speed, item, timeout, cb,
281
0
                         userdata);
282
0
}
283
284
void vlc_thumbnailer_DestroyRequest( vlc_thumbnailer_t* thumbnailer, task_t* task )
285
0
{
286
0
    bool canceled = vlc_executor_Cancel(thumbnailer->executor, &task->runnable);
287
0
    if (canceled)
288
0
    {
289
        /* Release the executor reference (since it won't run) */
290
0
        bool ret = vlc_atomic_rc_dec(&task->rc);
291
        /* Assert that only the caller got the reference */
292
0
        assert(!ret); (void) ret;
293
0
    }
294
0
    else
295
0
        Interrupt(task);
296
297
0
    TaskRelease(task);
298
0
}
299
300
vlc_thumbnailer_t *vlc_thumbnailer_Create( vlc_object_t* parent)
301
2
{
302
2
    vlc_thumbnailer_t *thumbnailer = malloc( sizeof( *thumbnailer ) );
303
2
    if ( unlikely( thumbnailer == NULL ) )
304
0
        return NULL;
305
306
2
    thumbnailer->executor = vlc_executor_New(1);
307
2
    if (!thumbnailer->executor)
308
0
    {
309
0
        free(thumbnailer);
310
0
        return NULL;
311
0
    }
312
313
2
    thumbnailer->parent = parent;
314
315
2
    return thumbnailer;
316
2
}
317
318
void vlc_thumbnailer_Release( vlc_thumbnailer_t *thumbnailer )
319
0
{
320
0
    vlc_executor_Delete(thumbnailer->executor);
321
0
    free( thumbnailer );
322
0
}