mirror of https://code.videolan.org/videolan/vlc
323 lines
8.6 KiB
C
323 lines
8.6 KiB
C
/*****************************************************************************
|
|
* thumbnailer.c: Thumbnailing API
|
|
*****************************************************************************
|
|
* Copyright (C) 2018 VLC authors and VideoLAN
|
|
*
|
|
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <vlc_thumbnailer.h>
|
|
#include <vlc_executor.h>
|
|
#include "input_internal.h"
|
|
|
|
struct vlc_thumbnailer_t
|
|
{
|
|
vlc_object_t* parent;
|
|
vlc_executor_t *executor;
|
|
};
|
|
|
|
struct seek_target
|
|
{
|
|
enum
|
|
{
|
|
VLC_THUMBNAILER_SEEK_TIME,
|
|
VLC_THUMBNAILER_SEEK_POS,
|
|
} type;
|
|
union
|
|
{
|
|
vlc_tick_t time;
|
|
double pos;
|
|
};
|
|
};
|
|
|
|
/* We may not rename vlc_thumbnailer_request_t because it is exposed in the
|
|
* public API */
|
|
typedef struct vlc_thumbnailer_request_t task_t;
|
|
|
|
struct vlc_thumbnailer_request_t
|
|
{
|
|
vlc_atomic_rc_t rc;
|
|
vlc_thumbnailer_t *thumbnailer;
|
|
|
|
struct seek_target seek_target;
|
|
bool fast_seek;
|
|
input_item_t *item;
|
|
/**
|
|
* A positive value will be used as the timeout duration
|
|
* VLC_TICK_INVALID means no timeout
|
|
*/
|
|
vlc_tick_t timeout;
|
|
vlc_thumbnailer_cb cb;
|
|
void* userdata;
|
|
|
|
vlc_mutex_t lock;
|
|
vlc_cond_t cond_ended;
|
|
enum
|
|
{
|
|
RUNNING,
|
|
INTERRUPTED,
|
|
ENDED,
|
|
} status;
|
|
picture_t *pic;
|
|
|
|
struct vlc_runnable runnable; /**< to be passed to the executor */
|
|
};
|
|
|
|
static void RunnableRun(void *);
|
|
|
|
static task_t *
|
|
TaskNew(vlc_thumbnailer_t *thumbnailer, input_item_t *item,
|
|
struct seek_target seek_target, bool fast_seek,
|
|
vlc_thumbnailer_cb cb, void *userdata, vlc_tick_t timeout)
|
|
{
|
|
task_t *task = malloc(sizeof(*task));
|
|
if (!task)
|
|
return NULL;
|
|
|
|
vlc_atomic_rc_init(&task->rc);
|
|
task->thumbnailer = thumbnailer;
|
|
task->item = item;
|
|
task->seek_target = seek_target;
|
|
task->fast_seek = fast_seek;
|
|
task->cb = cb;
|
|
task->userdata = userdata;
|
|
task->timeout = timeout;
|
|
|
|
vlc_mutex_init(&task->lock);
|
|
vlc_cond_init(&task->cond_ended);
|
|
task->status = RUNNING;
|
|
task->pic = NULL;
|
|
|
|
task->runnable.run = RunnableRun;
|
|
task->runnable.userdata = task;
|
|
|
|
input_item_Hold(item);
|
|
|
|
return task;
|
|
}
|
|
|
|
static void
|
|
TaskRelease(task_t *task)
|
|
{
|
|
if (!vlc_atomic_rc_dec(&task->rc))
|
|
return;
|
|
input_item_Release(task->item);
|
|
free(task);
|
|
}
|
|
|
|
static void NotifyThumbnail(task_t *task, picture_t *pic)
|
|
{
|
|
assert(task->cb);
|
|
task->cb(task->userdata, pic);
|
|
}
|
|
|
|
static void
|
|
on_thumbnailer_input_event( input_thread_t *input,
|
|
const struct vlc_input_event *event, void *userdata )
|
|
{
|
|
VLC_UNUSED(input);
|
|
if ( event->type != INPUT_EVENT_THUMBNAIL_READY &&
|
|
( event->type != INPUT_EVENT_STATE || ( event->state.value != ERROR_S &&
|
|
event->state.value != END_S ) ) )
|
|
return;
|
|
|
|
task_t *task = userdata;
|
|
|
|
vlc_mutex_lock(&task->lock);
|
|
if (task->status != RUNNING)
|
|
{
|
|
/* We may receive a THUMBNAIL_READY event followed by an
|
|
* INPUT_EVENT_STATE (end of stream), we must only consider the first
|
|
* one. */
|
|
vlc_mutex_unlock(&task->lock);
|
|
return;
|
|
}
|
|
|
|
task->status = ENDED;
|
|
|
|
if (event->type == INPUT_EVENT_THUMBNAIL_READY)
|
|
task->pic = picture_Hold(event->thumbnail);
|
|
|
|
vlc_cond_signal(&task->cond_ended);
|
|
vlc_mutex_unlock(&task->lock);
|
|
}
|
|
|
|
static void
|
|
RunnableRun(void *userdata)
|
|
{
|
|
vlc_thread_set_name("vlc-run-thumb");
|
|
|
|
task_t *task = userdata;
|
|
vlc_thumbnailer_t *thumbnailer = task->thumbnailer;
|
|
|
|
vlc_tick_t now = vlc_tick_now();
|
|
|
|
input_thread_t* input =
|
|
input_Create( thumbnailer->parent, on_thumbnailer_input_event, task,
|
|
task->item, INPUT_TYPE_THUMBNAILING, NULL, NULL );
|
|
if (!input)
|
|
goto error;
|
|
|
|
if (task->seek_target.type == VLC_THUMBNAILER_SEEK_TIME)
|
|
input_SetTime(input, task->seek_target.time, task->fast_seek);
|
|
else
|
|
{
|
|
assert(task->seek_target.type == VLC_THUMBNAILER_SEEK_POS);
|
|
input_SetPosition(input, task->seek_target.pos, task->fast_seek);
|
|
}
|
|
|
|
int ret = input_Start(input);
|
|
if (ret != VLC_SUCCESS)
|
|
{
|
|
input_Close(input);
|
|
goto error;
|
|
}
|
|
|
|
vlc_mutex_lock(&task->lock);
|
|
if (task->timeout == VLC_TICK_INVALID)
|
|
{
|
|
while (task->status == RUNNING)
|
|
vlc_cond_wait(&task->cond_ended, &task->lock);
|
|
}
|
|
else
|
|
{
|
|
vlc_tick_t deadline = now + task->timeout;
|
|
int timeout = 0;
|
|
while (task->status == RUNNING && timeout == 0)
|
|
timeout =
|
|
vlc_cond_timedwait(&task->cond_ended, &task->lock, deadline);
|
|
}
|
|
picture_t* pic = task->pic;
|
|
task->pic = NULL;
|
|
|
|
bool notify = task->status != INTERRUPTED;
|
|
vlc_mutex_unlock(&task->lock);
|
|
|
|
if (notify)
|
|
NotifyThumbnail(task, pic);
|
|
|
|
if (pic)
|
|
picture_Release(pic);
|
|
|
|
input_Stop(input);
|
|
input_Close(input);
|
|
|
|
error:
|
|
TaskRelease(task);
|
|
}
|
|
|
|
static void
|
|
Interrupt(task_t *task)
|
|
{
|
|
/* Wake up RunnableRun() which will call input_Stop() */
|
|
vlc_mutex_lock(&task->lock);
|
|
task->status = INTERRUPTED;
|
|
vlc_cond_signal(&task->cond_ended);
|
|
vlc_mutex_unlock(&task->lock);
|
|
}
|
|
|
|
static task_t *
|
|
RequestCommon(vlc_thumbnailer_t *thumbnailer, struct seek_target seek_target,
|
|
enum vlc_thumbnailer_seek_speed speed, input_item_t *item,
|
|
vlc_tick_t timeout, vlc_thumbnailer_cb cb, void *userdata)
|
|
{
|
|
bool fast_seek = speed == VLC_THUMBNAILER_SEEK_FAST;
|
|
task_t *task = TaskNew(thumbnailer, item, seek_target, fast_seek, cb,
|
|
userdata, timeout);
|
|
if (!task)
|
|
return NULL;
|
|
|
|
/* One ref for the executor */
|
|
vlc_atomic_rc_inc(&task->rc);
|
|
vlc_executor_Submit(thumbnailer->executor, &task->runnable);
|
|
|
|
return task;
|
|
}
|
|
|
|
task_t *
|
|
vlc_thumbnailer_RequestByTime( vlc_thumbnailer_t *thumbnailer,
|
|
vlc_tick_t time,
|
|
enum vlc_thumbnailer_seek_speed speed,
|
|
input_item_t *item, vlc_tick_t timeout,
|
|
vlc_thumbnailer_cb cb, void* userdata )
|
|
{
|
|
struct seek_target seek_target = {
|
|
.type = VLC_THUMBNAILER_SEEK_TIME,
|
|
.time = time,
|
|
};
|
|
return RequestCommon(thumbnailer, seek_target, speed, item, timeout, cb,
|
|
userdata);
|
|
}
|
|
|
|
task_t *
|
|
vlc_thumbnailer_RequestByPos( vlc_thumbnailer_t *thumbnailer,
|
|
double pos, enum vlc_thumbnailer_seek_speed speed,
|
|
input_item_t *item, vlc_tick_t timeout,
|
|
vlc_thumbnailer_cb cb, void* userdata )
|
|
{
|
|
struct seek_target seek_target = {
|
|
.type = VLC_THUMBNAILER_SEEK_POS,
|
|
.pos = pos,
|
|
};
|
|
return RequestCommon(thumbnailer, seek_target, speed, item, timeout, cb,
|
|
userdata);
|
|
}
|
|
|
|
void vlc_thumbnailer_DestroyRequest( vlc_thumbnailer_t* thumbnailer, task_t* task )
|
|
{
|
|
bool canceled = vlc_executor_Cancel(thumbnailer->executor, &task->runnable);
|
|
if (canceled)
|
|
{
|
|
/* Release the executor reference (since it won't run) */
|
|
bool ret = vlc_atomic_rc_dec(&task->rc);
|
|
/* Assert that only the caller got the reference */
|
|
assert(!ret); (void) ret;
|
|
}
|
|
else
|
|
Interrupt(task);
|
|
|
|
TaskRelease(task);
|
|
}
|
|
|
|
vlc_thumbnailer_t *vlc_thumbnailer_Create( vlc_object_t* parent)
|
|
{
|
|
vlc_thumbnailer_t *thumbnailer = malloc( sizeof( *thumbnailer ) );
|
|
if ( unlikely( thumbnailer == NULL ) )
|
|
return NULL;
|
|
|
|
thumbnailer->executor = vlc_executor_New(1);
|
|
if (!thumbnailer->executor)
|
|
{
|
|
free(thumbnailer);
|
|
return NULL;
|
|
}
|
|
|
|
thumbnailer->parent = parent;
|
|
|
|
return thumbnailer;
|
|
}
|
|
|
|
void vlc_thumbnailer_Release( vlc_thumbnailer_t *thumbnailer )
|
|
{
|
|
vlc_executor_Delete(thumbnailer->executor);
|
|
free( thumbnailer );
|
|
}
|