Commit a7a12139 authored by Laurent Aimar's avatar Laurent Aimar

Moved out snapshot code to a standalone file.

parent 5174ad04
......@@ -344,6 +344,8 @@ SOURCES_libvlc_common = \
input/stream_memory.c \
input/subtitles.c \
input/var.c \
video_output/snapshot.c \
video_output/snapshot.h \
video_output/video_output.c \
video_output/vout_pictures.c \
video_output/vout_pictures.h \
......
/*****************************************************************************
* snapshot.c : vout internal snapshot
*****************************************************************************
* Copyright (C) 2009 Laurent Aimar
* $Id$
*
* Authors: Gildas Bazin <gbazin _AT_ videolan _DOT_ org>
* Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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_common.h>
#include <vlc_charset.h>
#include <vlc_strings.h>
#include <vlc_block.h>
#include "snapshot.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
/* */
void vout_snapshot_Init(vout_snapshot_t *snap)
{
vlc_mutex_init(&snap->lock);
vlc_cond_init(&snap->wait);
snap->is_available = true;
snap->request_count = 0;
snap->picture = NULL;
}
void vout_snapshot_Clean(vout_snapshot_t *snap)
{
picture_t *picture = snap->picture;
while (picture) {
picture_t *next = picture->p_next;
picture_Release(picture);
picture = next;
}
vlc_cond_destroy(&snap->wait);
vlc_mutex_destroy(&snap->lock);
}
void vout_snapshot_End(vout_snapshot_t *snap)
{
vlc_mutex_lock(&snap->lock);
snap->is_available = false;
vlc_cond_broadcast(&snap->wait);
vlc_mutex_unlock(&snap->lock);
}
/* */
picture_t *vout_snapshot_Get(vout_snapshot_t *snap, mtime_t timeout)
{
vlc_mutex_lock(&snap->lock);
/* */
snap->request_count++;
/* */
const mtime_t deadline = mdate() + timeout;
while (snap->is_available && !snap->picture && mdate() < deadline)
vlc_cond_timedwait(&snap->wait, &snap->lock, deadline);
/* */
picture_t *picture = snap->picture;
if (picture)
snap->picture = picture->p_next;
else if (snap->request_count > 0)
snap->request_count--;
vlc_mutex_unlock(&snap->lock);
return picture;
}
/* */
bool vout_snapshot_IsRequested(vout_snapshot_t *snap)
{
bool has_request = false;
if (!vlc_mutex_trylock(&snap->lock)) {
has_request = snap->request_count > 0;
vlc_mutex_unlock(&snap->lock);
}
return has_request;
}
void vout_snapshot_Set(vout_snapshot_t *snap,
const video_format_t *fmt,
const picture_t *picture)
{
if (!fmt)
fmt = &picture->format;
vlc_mutex_lock(&snap->lock);
while (snap->request_count > 0) {
picture_t *dup = picture_NewFromFormat(fmt);
if (!dup)
break;
picture_Copy(dup, picture);
dup->p_next = snap->picture;
snap->picture = dup;
snap->request_count--;
}
vlc_cond_broadcast(&snap->wait);
vlc_mutex_unlock(&snap->lock);
}
/* */
char *vout_snapshot_GetDirectory(void)
{
char *psz_path = NULL;
#if defined(__APPLE__) || defined(SYS_BEOS)
if (asprintf(&psz_path, "%s/Desktop",
config_GetHomeDir()) == -1)
psz_path = NULL;
#elif defined(WIN32) && !defined(UNDER_CE)
/* Get the My Pictures folder path */
char *p_mypicturesdir = NULL;
typedef HRESULT (WINAPI *SHGETFOLDERPATH)(HWND, int, HANDLE, DWORD,
LPWSTR);
#ifndef CSIDL_FLAG_CREATE
# define CSIDL_FLAG_CREATE 0x8000
#endif
#ifndef CSIDL_MYPICTURES
# define CSIDL_MYPICTURES 0x27
#endif
#ifndef SHGFP_TYPE_CURRENT
# define SHGFP_TYPE_CURRENT 0
#endif
HINSTANCE shfolder_dll;
SHGETFOLDERPATH SHGetFolderPath ;
/* load the shfolder dll to retrieve SHGetFolderPath */
if ((shfolder_dll = LoadLibrary(_T("SHFolder.dll"))) != NULL)
{
wchar_t wdir[PATH_MAX];
SHGetFolderPath = (void *)GetProcAddress(shfolder_dll,
_T("SHGetFolderPathW"));
if ((SHGetFolderPath != NULL)
&& SUCCEEDED (SHGetFolderPath (NULL,
CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
NULL, SHGFP_TYPE_CURRENT,
wdir)))
p_mypicturesdir = FromWide (wdir);
FreeLibrary(shfolder_dll);
}
if (p_mypicturesdir == NULL)
psz_path = strdup(config_GetHomeDir());
else
psz_path = p_mypicturesdir;
#else
/* XXX: This saves in the data directory. Shouldn't we try saving
* to psz_homedir/Desktop or something nicer ? */
char *psz_datadir = config_GetUserDataDir();
if (psz_datadir)
{
if (asprintf(&psz_path, "%s", psz_datadir) == -1)
psz_path = NULL;
free(psz_datadir);
}
#endif
return psz_path;
}
/* */
int vout_snapshot_SaveImage(char **name, int *sequential,
const block_t *image,
vlc_object_t *object,
const vout_snapshot_save_cfg_t *cfg)
{
/* */
char *filename;
DIR *pathdir = utf8_opendir(cfg->path);
if (pathdir != NULL) {
/* The use specified a directory path */
closedir(pathdir);
/* */
char *prefix = NULL;
if (cfg->prefix_fmt)
prefix = str_format(object, cfg->prefix_fmt);
if (!prefix) {
prefix = strdup("vlcsnap-");
if (!prefix)
goto error;
}
if (cfg->is_sequential) {
for (int num = cfg->sequence; ; num++) {
struct stat st;
if (asprintf(&filename, "%s" DIR_SEP "%s%05d.%s",
cfg->path, prefix, num, cfg->format) < 0) {
free(prefix);
goto error;
}
if (utf8_stat(filename, &st)) {
*sequential = num;
break;
}
free(filename);
}
} else {
struct tm curtime;
time_t lcurtime = time(NULL) ;
if (!localtime_r(&lcurtime, &curtime)) {
const unsigned int id = (image->i_pts / 100000) & 0xFFFFFF;
msg_Warn(object, "failed to get current time. Falling back to legacy snapshot naming");
if (asprintf(&filename, "%s" DIR_SEP "%s%u.%s",
cfg->path, prefix, id, cfg->format) < 0)
filename = NULL;
} else {
/* suffix with the last decimal digit in 10s of seconds resolution
* FIXME gni ? */
const int id = (image->i_pts / (100*1000)) & 0xFF;
char buffer[128];
if (!strftime(buffer, sizeof(buffer), "%Y-%m-%d-%Hh%Mm%Ss", &curtime))
strcpy(buffer, "error");
if (asprintf(&filename, "%s" DIR_SEP "%s%s%1u.%s",
cfg->path, prefix, buffer, id, cfg->format) < 0)
filename = NULL;
}
}
free(prefix);
} else {
/* The user specified a full path name (including file name) */
filename = str_format(object, cfg->path);
path_sanitize(filename);
}
if (!filename)
goto error;
/* Save the snapshot */
FILE *file = utf8_fopen(filename, "wb");
if (!file) {
msg_Err(object, "Failed to open '%s'", filename);
free(filename);
goto error;
}
if (fwrite(image->p_buffer, image->i_buffer, 1, file) != 1) {
msg_Err(object, "Failed to write to '%s'", filename);
fclose(file);
free(filename);
goto error;
}
fclose(file);
/* */
if (name)
*name = filename;
else
free(filename);
return VLC_SUCCESS;
error:
msg_Err(object, "could not save snapshot");
return VLC_EGENERIC;
}
/*****************************************************************************
* snapshot.h : vout internal snapshot
*****************************************************************************
* Copyright (C) 2009 Laurent Aimar
* $Id$
*
* Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.
*****************************************************************************/
#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__)
# error This header file can only be included from LibVLC.
#endif
#ifndef _VOUT_INTERNAL_SNAPSHOT_H
#define _VOUT_INTERNAL_SNAPSHOT_H
#include <vlc_picture.h>
typedef struct {
vlc_mutex_t lock;
vlc_cond_t wait;
bool is_available;
int request_count;
picture_t *picture;
} vout_snapshot_t;
/* */
void vout_snapshot_Init(vout_snapshot_t *);
void vout_snapshot_Clean(vout_snapshot_t *);
void vout_snapshot_End(vout_snapshot_t *);
/* */
picture_t *vout_snapshot_Get(vout_snapshot_t *, mtime_t timeout);
/**
* It tells if they are pending snapshot request
*/
bool vout_snapshot_IsRequested(vout_snapshot_t *);
/**
* It set the picture used to create the snapshots.
*
* The given picture is only copied and not released.
* If p_fmt is non NULL it will override the format of the p_picture (mainly
* used because of aspect/crop problems).
*/
void vout_snapshot_Set(vout_snapshot_t *, const video_format_t *, const picture_t *);
/**
* This function will return the directory used for snapshots
*/
char *vout_snapshot_GetDirectory(void);
typedef struct {
bool is_sequential;
int sequence;
char *path;
char *format;
char *prefix_fmt;
} vout_snapshot_save_cfg_t;
/**
* This function will write an image to the disk an return the file name created.
*/
int vout_snapshot_SaveImage(char **name, int *sequential,
const block_t *image,
vlc_object_t *object,
const vout_snapshot_save_cfg_t *cfg);
#endif
......@@ -398,11 +398,7 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
p_vout->p->b_picture_empty = false;
p_vout->p->i_picture_qtype = QTYPE_NONE;
p_vout->p->snapshot.b_available = true;
p_vout->p->snapshot.i_request = 0;
p_vout->p->snapshot.p_picture = NULL;
vlc_mutex_init( &p_vout->p->snapshot.lock );
vlc_cond_init( &p_vout->p->snapshot.wait );
vout_snapshot_Init( &p_vout->p->snapshot );
/* Initialize locks */
vlc_mutex_init( &p_vout->picture_lock );
......@@ -563,10 +559,7 @@ void vout_Close( vout_thread_t *p_vout )
vlc_cond_signal( &p_vout->p->change_wait );
vlc_mutex_unlock( &p_vout->change_lock );
vlc_mutex_lock( &p_vout->p->snapshot.lock );
p_vout->p->snapshot.b_available = false;
vlc_cond_broadcast( &p_vout->p->snapshot.wait );
vlc_mutex_unlock( &p_vout->p->snapshot.lock );
vout_snapshot_End( &p_vout->p->snapshot );
vlc_join( p_vout->p->thread, NULL );
module_unneed( p_vout, p_vout->p_module );
......@@ -593,18 +586,7 @@ static void vout_Destructor( vlc_object_t * p_this )
vlc_mutex_destroy( &p_vout->p->vfilter_lock );
/* */
for( ;; )
{
picture_t *p_picture = p_vout->p->snapshot.p_picture;
if( !p_picture )
break;
p_vout->p->snapshot.p_picture = p_picture->p_next;
picture_Release( p_picture );
}
vlc_cond_destroy( &p_vout->p->snapshot.wait );
vlc_mutex_destroy( &p_vout->p->snapshot.lock );
vout_snapshot_Clean( &p_vout->p->snapshot );
/* */
free( p_vout->p->psz_filter_chain );
......@@ -1166,13 +1148,7 @@ static void* RunThread( void *p_this )
p_filtered_picture = filter_chain_VideoFilter( p_vout->p->p_vf2_chain,
p_picture );
bool b_snapshot = false;
if( vlc_mutex_trylock( &p_vout->p->snapshot.lock ) == 0 )
{
b_snapshot = p_vout->p->snapshot.i_request > 0
&& p_picture != NULL;
vlc_mutex_unlock( &p_vout->p->snapshot.lock );
}
const bool b_snapshot = vout_snapshot_IsRequested( &p_vout->p->snapshot );
/*
* Check for subpictures to display
......@@ -1200,30 +1176,8 @@ static void* RunThread( void *p_this )
* Take a snapshot if requested
*/
if( p_directbuffer && b_snapshot )
{
vlc_mutex_lock( &p_vout->p->snapshot.lock );
assert( p_vout->p->snapshot.i_request > 0 );
while( p_vout->p->snapshot.i_request > 0 )
{
picture_t *p_pic = picture_New( p_vout->fmt_out.i_chroma,
p_vout->fmt_out.i_width,
p_vout->fmt_out.i_height,
p_vout->fmt_out.i_aspect );
if( !p_pic )
break;
picture_Copy( p_pic, p_directbuffer );
p_pic->format.i_sar_num = p_vout->fmt_out.i_sar_num;
p_pic->format.i_sar_den = p_vout->fmt_out.i_sar_den;
p_pic->p_next = p_vout->p->snapshot.p_picture;
p_vout->p->snapshot.p_picture = p_pic;
p_vout->p->snapshot.i_request--;
}
vlc_cond_broadcast( &p_vout->p->snapshot.wait );
vlc_mutex_unlock( &p_vout->p->snapshot.lock );
}
vout_snapshot_Set( &p_vout->p->snapshot,
&p_vout->fmt_out, p_directbuffer );
/*
* Call the plugin-specific rendering method if there is one
......
......@@ -31,6 +31,7 @@
#define _VOUT_INTERNAL_H 1
#include "vout_control.h"
#include "snapshot.h"
/* Number of pictures required to computes the FPS rate */
#define VOUT_FPS_SAMPLES 20
......@@ -88,14 +89,7 @@ struct vout_thread_sys_t
char *psz_vf2;
/* Snapshot interface */
struct
{
bool b_available;
int i_request;
picture_t *p_picture;
vlc_mutex_t lock;
vlc_cond_t wait;
} snapshot;
vout_snapshot_t snapshot;
/* Show media title on videoutput */
bool b_title_show;
......
......@@ -400,187 +400,6 @@ static int VoutSnapshotPip( vout_thread_t *p_vout, picture_t *p_pic )
spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
return VLC_SUCCESS;
}
/**
* This function will return the default directory used for snapshots
*/
static char *VoutSnapshotGetDefaultDirectory( void )
{
char *psz_path = NULL;
#if defined(__APPLE__) || defined(SYS_BEOS)
if( asprintf( &psz_path, "%s/Desktop",
config_GetHomeDir() ) == -1 )
psz_path = NULL;
#elif defined(WIN32) && !defined(UNDER_CE)
/* Get the My Pictures folder path */
char *p_mypicturesdir = NULL;
typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
LPWSTR );
#ifndef CSIDL_FLAG_CREATE
# define CSIDL_FLAG_CREATE 0x8000
#endif
#ifndef CSIDL_MYPICTURES
# define CSIDL_MYPICTURES 0x27
#endif
#ifndef SHGFP_TYPE_CURRENT
# define SHGFP_TYPE_CURRENT 0
#endif
HINSTANCE shfolder_dll;
SHGETFOLDERPATH SHGetFolderPath ;
/* load the shfolder dll to retrieve SHGetFolderPath */
if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
{
wchar_t wdir[PATH_MAX];
SHGetFolderPath = (void *)GetProcAddress( shfolder_dll,
_T("SHGetFolderPathW") );
if ((SHGetFolderPath != NULL )
&& SUCCEEDED (SHGetFolderPath (NULL,
CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
NULL, SHGFP_TYPE_CURRENT,
wdir)))
p_mypicturesdir = FromWide (wdir);
FreeLibrary( shfolder_dll );
}
if( p_mypicturesdir == NULL )
psz_path = strdup( config_GetHomeDir() );
else
psz_path = p_mypicturesdir;
#else
/* XXX: This saves in the data directory. Shouldn't we try saving
* to psz_homedir/Desktop or something nicer ? */
psz_path = config_GetUserDataDir();
#endif
return psz_path;
}
/**
* This function will save a video snapshot to a file
*/
static int VoutWriteSnapshot( vout_thread_t *p_vout, char **ppsz_filename,
const block_t *p_image,
const char *psz_path,
const char *psz_format,
const char *psz_prefix_fmt )
{
/* */
char *psz_filename;
DIR *p_path = utf8_opendir( psz_path );
if( p_path != NULL )
{
/* The use specified a directory path */
closedir( p_path );
/* */
char *psz_prefix = NULL;
if( psz_prefix_fmt )
psz_prefix = str_format( p_vout, psz_prefix_fmt );
if( !psz_prefix )
{
psz_prefix = strdup( "vlcsnap-" );
if( !psz_prefix )
goto error;
}
if( var_GetBool( p_vout, "snapshot-sequential" ) )
{
int i_num = var_GetInteger( p_vout, "snapshot-num" );
for( ; ; i_num++ )
{
struct stat st;
if( asprintf( &psz_filename, "%s" DIR_SEP "%s%05d.%s",
psz_path, psz_prefix, i_num++, psz_format ) < 0 )
{
free( psz_prefix );
goto error;
}
if( utf8_stat( psz_filename, &st ) )
break;
free( psz_filename );
}
var_SetInteger( p_vout, "snapshot-num", i_num );
}
else
{
struct tm curtime;
time_t lcurtime = time( NULL ) ;
if( !localtime_r( &lcurtime, &curtime ) )
{
const unsigned int i_id = (p_image->i_pts / 100000) & 0xFFFFFF;
msg_Warn( p_vout, "failed to get current time. Falling back to legacy snapshot naming" );
if( asprintf( &psz_filename, "%s" DIR_SEP "%s%u.%s",
psz_path, psz_prefix, i_id, psz_format ) < 0 )
psz_filename = NULL;
}
else
{
/* suffix with the last decimal digit in 10s of seconds resolution
* FIXME gni ? */
const int i_id = (p_image->i_pts / (100*1000)) & 0xFF;
char psz_curtime[128];