/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 | } |