1 /*****************************************************************************
2 * thumbnailer.c: Thumbnailing API
3 *****************************************************************************
4 * Copyright (C) 2018 VLC authors and VideoLAN
6 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
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.
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.
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 *****************************************************************************/
27 #include <vlc_thumbnailer.h>
28 #include "input_internal.h"
29 #include "misc/background_worker.h"
31 struct vlc_thumbnailer_t
34 struct background_worker
* worker
;
37 typedef struct vlc_thumbnailer_params_t
46 VLC_THUMBNAILER_SEEK_TIME
,
47 VLC_THUMBNAILER_SEEK_POS
,
50 input_item_t
* input_item
;
52 * A positive value will be used as the timeout duration
53 * VLC_TICK_INVALID means no timeout
56 vlc_thumbnailer_cb cb
;
58 } vlc_thumbnailer_params_t
;
60 struct vlc_thumbnailer_request_t
62 vlc_thumbnailer_t
*thumbnailer
;
63 input_thread_t
*input_thread
;
65 vlc_thumbnailer_params_t params
;
72 on_thumbnailer_input_event( input_thread_t
*input
,
73 const struct vlc_input_event
*event
, void *userdata
)
76 if ( event
->type
!= INPUT_EVENT_THUMBNAIL_READY
&&
77 ( event
->type
!= INPUT_EVENT_STATE
|| ( event
->state
.value
!= ERROR_S
&&
78 event
->state
.value
!= END_S
) ) )
81 vlc_thumbnailer_request_t
* request
= userdata
;
82 picture_t
*pic
= NULL
;
84 if ( event
->type
== INPUT_EVENT_THUMBNAIL_READY
)
87 * Stop the input thread ASAP, delegate its release to
88 * thumbnailer_request_Release
90 input_Stop( request
->input_thread
);
91 pic
= event
->thumbnail
;
93 vlc_mutex_lock( &request
->lock
);
96 * If the request has not been cancelled, we can invoke the completion
99 if ( request
->params
.cb
)
101 request
->params
.cb( request
->params
.user_data
, pic
);
102 request
->params
.cb
= NULL
;
104 vlc_mutex_unlock( &request
->lock
);
105 background_worker_RequestProbe( request
->thumbnailer
->worker
);
108 static void thumbnailer_request_Hold( void* data
)
113 static void thumbnailer_request_Release( void* data
)
115 vlc_thumbnailer_request_t
* request
= data
;
116 if ( request
->input_thread
)
117 input_Close( request
->input_thread
);
119 input_item_Release( request
->params
.input_item
);
123 static int thumbnailer_request_Start( void* owner
, void* entity
, void** out
)
125 vlc_thumbnailer_t
* thumbnailer
= owner
;
126 vlc_thumbnailer_request_t
* request
= entity
;
127 input_thread_t
* input
= request
->input_thread
=
128 input_CreateThumbnailer( thumbnailer
->parent
,
129 on_thumbnailer_input_event
, request
,
130 request
->params
.input_item
);
131 if ( unlikely( input
== NULL
) )
133 request
->params
.cb( request
->params
.user_data
, NULL
);
136 if ( request
->params
.type
== VLC_THUMBNAILER_SEEK_TIME
)
138 input_SetTime( input
, request
->params
.time
,
139 request
->params
.fast_seek
);
143 assert( request
->params
.type
== VLC_THUMBNAILER_SEEK_POS
);
144 input_SetPosition( input
, request
->params
.pos
,
145 request
->params
.fast_seek
);
147 if ( input_Start( input
) != VLC_SUCCESS
)
149 request
->params
.cb( request
->params
.user_data
, NULL
);
156 static void thumbnailer_request_Stop( void* owner
, void* handle
)
160 vlc_thumbnailer_request_t
*request
= handle
;
161 vlc_mutex_lock( &request
->lock
);
163 * If the callback hasn't been invoked yet, we assume a timeout and
164 * signal it back to the user
166 if ( request
->params
.cb
!= NULL
)
168 request
->params
.cb( request
->params
.user_data
, NULL
);
169 request
->params
.cb
= NULL
;
171 vlc_mutex_unlock( &request
->lock
);
172 assert( request
->input_thread
!= NULL
);
173 input_Stop( request
->input_thread
);
176 static int thumbnailer_request_Probe( void* owner
, void* handle
)
179 vlc_thumbnailer_request_t
*request
= handle
;
180 vlc_mutex_lock( &request
->lock
);
181 int res
= request
->done
;
182 vlc_mutex_unlock( &request
->lock
);
186 static vlc_thumbnailer_request_t
*
187 thumbnailer_RequestCommon( vlc_thumbnailer_t
* thumbnailer
,
188 const vlc_thumbnailer_params_t
* params
)
190 vlc_thumbnailer_request_t
*request
= malloc( sizeof( *request
) );
191 if ( unlikely( request
== NULL
) )
193 request
->thumbnailer
= thumbnailer
;
194 request
->input_thread
= NULL
;
195 request
->params
= *(vlc_thumbnailer_params_t
*)params
;
196 request
->done
= false;
197 input_item_Hold( request
->params
.input_item
);
198 vlc_mutex_init( &request
->lock
);
200 int timeout
= params
->timeout
== VLC_TICK_INVALID
?
201 0 : MS_FROM_VLC_TICK( params
->timeout
);
202 if ( background_worker_Push( thumbnailer
->worker
, request
, request
,
203 timeout
) != VLC_SUCCESS
)
205 thumbnailer_request_Release( request
);
211 vlc_thumbnailer_request_t
*
212 vlc_thumbnailer_RequestByTime( vlc_thumbnailer_t
*thumbnailer
,
214 enum vlc_thumbnailer_seek_speed speed
,
215 input_item_t
*input_item
, vlc_tick_t timeout
,
216 vlc_thumbnailer_cb cb
, void* user_data
)
218 return thumbnailer_RequestCommon( thumbnailer
,
219 &(const vlc_thumbnailer_params_t
){
221 .type
= VLC_THUMBNAILER_SEEK_TIME
,
222 .fast_seek
= speed
== VLC_THUMBNAILER_SEEK_FAST
,
223 .input_item
= input_item
,
226 .user_data
= user_data
,
230 vlc_thumbnailer_request_t
*
231 vlc_thumbnailer_RequestByPos( vlc_thumbnailer_t
*thumbnailer
,
232 float pos
, enum vlc_thumbnailer_seek_speed speed
,
233 input_item_t
*input_item
, vlc_tick_t timeout
,
234 vlc_thumbnailer_cb cb
, void* user_data
)
236 return thumbnailer_RequestCommon( thumbnailer
,
237 &(const vlc_thumbnailer_params_t
){
239 .type
= VLC_THUMBNAILER_SEEK_POS
,
240 .fast_seek
= speed
== VLC_THUMBNAILER_SEEK_FAST
,
241 .input_item
= input_item
,
244 .user_data
= user_data
,
248 void vlc_thumbnailer_Cancel( vlc_thumbnailer_t
* thumbnailer
,
249 vlc_thumbnailer_request_t
* req
)
251 vlc_mutex_lock( &req
->lock
);
252 /* Ensure we won't invoke the callback if the input was running. */
253 req
->params
.cb
= NULL
;
254 vlc_mutex_unlock( &req
->lock
);
255 background_worker_Cancel( thumbnailer
->worker
, req
);
258 vlc_thumbnailer_t
*vlc_thumbnailer_Create( vlc_object_t
* parent
)
260 vlc_thumbnailer_t
*thumbnailer
= malloc( sizeof( *thumbnailer
) );
261 if ( unlikely( thumbnailer
== NULL
) )
263 thumbnailer
->parent
= parent
;
264 struct background_worker_config cfg
= {
265 .default_timeout
= -1,
267 .pf_release
= thumbnailer_request_Release
,
268 .pf_hold
= thumbnailer_request_Hold
,
269 .pf_start
= thumbnailer_request_Start
,
270 .pf_probe
= thumbnailer_request_Probe
,
271 .pf_stop
= thumbnailer_request_Stop
,
273 thumbnailer
->worker
= background_worker_New( thumbnailer
, &cfg
);
274 if ( unlikely( thumbnailer
->worker
== NULL
) )
282 void vlc_thumbnailer_Release( vlc_thumbnailer_t
*thumbnailer
)
284 background_worker_Delete( thumbnailer
->worker
);