|
|
|
|
Quelle resource.c
Sprache: C
|
|
/*****************************************************************************
* resource.c
*****************************************************************************
* Copyright (C) 2008 Laurent Aimar
*
* 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 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.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <assert.h>
#include <vlc_common.h>
#include <vlc_atomic.h>
#include <vlc_decoder.h>
#include <vlc_vout.h>
#include <vlc_spu.h>
#include <vlc_aout.h>
#include <vlc_sout.h>
#include "../libvlc.h"
#include "../stream_output/stream_output.h"
#include "../audio_output/aout_internal.h"
#include "../video_output/vout_internal.h"
#include "input_interface.h"
#include "event.h"
#include "resource.h"
struct vout_resource
{
vout_thread_t *vout;
enum vlc_vout_order order;
bool started;
char *psz_prev_title;
struct vlc_list node;
};
struct input_resource_t
{
vlc_atomic_rc_t rc;
vlc_object_t *p_parent;
/* This lock is used to serialize request and protect
* our variables */
vlc_mutex_t lock;
/* */
input_thread_t *p_input;
sout_stream_t *p_sout;
char *psz_sout;
vout_thread_t *p_vout_dummy;
struct vout_resource *vout_rsc_free;
/* This lock is used to protect vout resources access (for hold)
* It is a special case because of embed video (possible deadlock
* between vout window request and vout holds in some(qt) interface)
*/
vlc_mutex_t lock_hold;
/* You need lock+lock_hold to write to the following variables and
* only lock or lock_hold to read them */
struct vlc_list vout_rscs;
bool b_aout_busy;
audio_output_t *p_aout;
};
#define resource_GetFirstVoutRsc(resource) \
vlc_list_first_entry_or_null(&resource->vout_rscs, struct vout_resource, node)
static inline struct vout_resource *
resource_GetVoutRsc(input_resource_t *resource, vout_thread_t *vout)
{
struct vout_resource *vout_rsc;
vlc_list_foreach(vout_rsc, &resource->vout_rscs, node)
if (vout_rsc->vout == vout)
return vout_rsc;
vlc_assert_unreachable();
}
static inline struct vout_resource *
vout_resource_Create(vout_thread_t *vout)
{
struct vout_resource *vout_rsc = malloc(sizeof(*vout_rsc));
if (unlikely(vout_rsc == NULL))
return NULL;
vout_rsc->started = false;
vout_rsc->vout = vout;
vout_rsc->psz_prev_title = NULL;
return vout_rsc;
}
static inline void
vout_resource_Add(struct vout_resource *vout_rsc, input_resource_t *resource)
{
vlc_list_append(&vout_rsc->node, &resource->vout_rscs);
}
static inline vout_thread_t *
vout_resource_Remove(struct vout_resource *vout_rsc)
{
vlc_list_remove(&vout_rsc->node);
vout_thread_t *vout = vout_rsc->vout;
free(vout_rsc->psz_prev_title);
free(vout_rsc);
assert(vout);
return vout;
}
/* */
static void DestroySout( input_resource_t *p_resource )
{
if( p_resource->p_sout )
{
msg_Dbg( p_resource->p_parent, "destroying stream output" );
sout_StreamChainDelete( p_resource->p_sout, NULL );
free(p_resource->psz_sout);
}
p_resource->p_sout = NULL;
}
/* */
static void DestroyVout( input_resource_t *p_resource )
{
assert( vlc_list_is_empty( &p_resource->vout_rscs )
|| p_resource->vout_rsc_free == resource_GetFirstVoutRsc( p_resource ) );
if( p_resource->vout_rsc_free )
{
vlc_mutex_lock(&p_resource->lock_hold);
vout_thread_t *vout = vout_resource_Remove( p_resource->vout_rsc_free );
vlc_mutex_unlock(&p_resource->lock_hold);
vout_Close( vout );
p_resource->vout_rsc_free = NULL;
}
}
static void VoutSetAndDeduplicateTitle( vout_thread_t *p_vout,
char **ppsz_title,
char **ppsz_prev_title )
{
if( !*ppsz_prev_title || strcmp( *ppsz_prev_title, *ppsz_title ) )
{
vout_DisplayTitle( p_vout, *ppsz_title );
free( *ppsz_prev_title );
*ppsz_prev_title = *ppsz_title;
*ppsz_title = NULL;
}
}
static void DisplayVoutTitle( input_resource_t *p_resource,
vout_thread_t *p_vout,
char ** ppsz_prev_title )
{
if( p_resource->p_input == NULL )
return;
/* TODO display the title only one time for the same input ? */
input_item_t *p_item = input_GetItem( p_resource->p_input );
char *psz_nowplaying = input_item_GetNowPlayingFb( p_item );
if( psz_nowplaying && *psz_nowplaying )
{
VoutSetAndDeduplicateTitle( p_vout, &psz_nowplaying, ppsz_prev_title );
}
else
{
char *psz_artist = input_item_GetArtist( p_item );
char *psz_name = input_item_GetTitle( p_item );
if( !psz_name || *psz_name == '\0' )
{
free( psz_name );
psz_name = input_item_GetName( p_item );
}
if( psz_artist && *psz_artist )
{
char *psz_string = NULL;
if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) > 0 )
{
VoutSetAndDeduplicateTitle( p_vout, &psz_string, ppsz_prev_title );
free( psz_string );
}
}
else if( psz_name )
{
VoutSetAndDeduplicateTitle( p_vout, &psz_name, ppsz_prev_title );
}
free( psz_name );
free( psz_artist );
}
free( psz_nowplaying );
}
/* Audio output */
audio_output_t *input_resource_GetAout( input_resource_t *p_resource )
{
audio_output_t *p_aout;
vlc_mutex_lock( &p_resource->lock_hold );
p_aout = p_resource->p_aout;
if( p_aout == NULL || p_resource->b_aout_busy )
{
msg_Dbg( p_resource->p_parent, "creating audio output" );
vlc_mutex_unlock( &p_resource->lock_hold );
p_aout = aout_New( p_resource->p_parent );
if( p_aout == NULL )
return NULL; /* failed */
vlc_mutex_lock( &p_resource->lock_hold );
if( p_resource->p_aout == NULL )
p_resource->p_aout = p_aout;
}
else
msg_Dbg( p_resource->p_parent, "reusing audio output" );
if( p_resource->p_aout == p_aout )
{
assert( !p_resource->b_aout_busy );
p_resource->b_aout_busy = true;
}
vlc_mutex_unlock( &p_resource->lock_hold );
return p_aout;
}
void input_resource_PutAout( input_resource_t *p_resource,
audio_output_t *p_aout )
{
assert( p_aout != NULL );
vlc_mutex_lock( &p_resource->lock_hold );
if( p_aout == p_resource->p_aout )
{
assert( p_resource->b_aout_busy );
p_resource->b_aout_busy = false;
msg_Dbg( p_resource->p_parent, "keeping audio output" );
p_aout = NULL;
}
else
msg_Dbg( p_resource->p_parent, "destroying extra audio output" );
vlc_mutex_unlock( &p_resource->lock_hold );
if( p_aout != NULL )
aout_Release( p_aout );
}
audio_output_t *input_resource_HoldAout( input_resource_t *p_resource )
{
audio_output_t *p_aout;
vlc_mutex_lock( &p_resource->lock_hold );
p_aout = p_resource->p_aout;
if( p_aout != NULL )
aout_Hold(p_aout);
vlc_mutex_unlock( &p_resource->lock_hold );
return p_aout;
}
void input_resource_ResetAout( input_resource_t *p_resource )
{
audio_output_t *p_aout = NULL;
vlc_mutex_lock( &p_resource->lock_hold );
if( !p_resource->b_aout_busy )
p_aout = p_resource->p_aout;
p_resource->p_aout = NULL;
p_resource->b_aout_busy = false;
vlc_mutex_unlock( &p_resource->lock_hold );
if( p_aout != NULL )
aout_Release( p_aout );
}
/* Common */
input_resource_t *input_resource_New( vlc_object_t *p_parent )
{
input_resource_t *p_resource = calloc( 1, sizeof(*p_resource) );
if( !p_resource )
return NULL;
p_resource->p_vout_dummy = vout_CreateDummy(p_parent);
if( !p_resource->p_vout_dummy )
{
free( p_resource );
return NULL;
}
vlc_list_init( &p_resource->vout_rscs );
vlc_atomic_rc_init( &p_resource->rc );
p_resource->p_parent = p_parent;
vlc_mutex_init( &p_resource->lock );
vlc_mutex_init( &p_resource->lock_hold );
return p_resource;
}
void input_resource_Release( input_resource_t *p_resource )
{
if( !vlc_atomic_rc_dec( &p_resource->rc ) )
return;
DestroySout( p_resource );
DestroyVout( p_resource );
if( p_resource->p_aout != NULL )
aout_Release( p_resource->p_aout );
vout_Release( p_resource->p_vout_dummy );
free( p_resource );
}
input_resource_t *input_resource_Hold( input_resource_t *p_resource )
{
vlc_atomic_rc_inc( &p_resource->rc );
return p_resource;
}
void input_resource_SetInput( input_resource_t *p_resource, input_thread_t *p_input )
{
vlc_mutex_lock( &p_resource->lock );
if( p_resource->p_input && !p_input )
assert( vlc_list_is_empty( &p_resource->vout_rscs )
|| p_resource->vout_rsc_free == resource_GetFirstVoutRsc( p_resource ) );
/* */
p_resource->p_input = p_input;
vlc_mutex_unlock( &p_resource->lock );
}
static void input_resource_PutVoutLocked(input_resource_t *p_resource,
vout_thread_t *vout,
enum input_resource_vout_state *vout_state)
{
assert(vout != NULL);
struct vout_resource *vout_rsc = resource_GetVoutRsc(p_resource, vout);
assert(vout_rsc != NULL);
if (vout_state != NULL)
*vout_state = vout_rsc->started ? INPUT_RESOURCE_VOUT_STOPPED
: INPUT_RESOURCE_VOUT_NOTCHANGED;
if (vout_rsc->started)
{
vout_StopDisplay(vout_rsc->vout);
vout_rsc->started = false;
}
if (vout_rsc == resource_GetFirstVoutRsc(p_resource))
{
assert(p_resource->vout_rsc_free == NULL || p_resource->vout_rsc_free == vout_rsc);
msg_Dbg(p_resource->p_parent, "saving a free vout");
p_resource->vout_rsc_free = vout_rsc;
}
else
{
msg_Dbg(p_resource->p_parent, "destroying vout (already one saved or active)");
vlc_mutex_lock(&p_resource->lock_hold);
vout_resource_Remove(vout_rsc);
vlc_mutex_unlock(&p_resource->lock_hold);
vout_Stop(vout);
vout_Close(vout);
}
}
void input_resource_PutVout(input_resource_t *p_resource,
vout_thread_t *vout,
enum input_resource_vout_state *vout_state)
{
vlc_mutex_lock( &p_resource->lock );
input_resource_PutVoutLocked( p_resource, vout, vout_state );
vlc_mutex_unlock( &p_resource->lock );
}
static struct vout_resource *
RequestVoutRsc(input_resource_t *p_resource)
{
struct vout_resource *vout_rsc = NULL;
if( p_resource->vout_rsc_free != NULL )
{
/* The free vout is always the first one */
msg_Dbg(p_resource->p_parent, "trying to reuse free vout");
vout_rsc = p_resource->vout_rsc_free;
p_resource->vout_rsc_free = NULL;
}
else
{
/* Use the dummy vout as the parent of the future main vout. This
* will allow the future vout to inherit all parameters
* pre-configured on this dummy vout. */
vlc_object_t *parent = vlc_list_is_empty( &p_resource->vout_rscs ) ?
VLC_OBJECT(p_resource->p_vout_dummy) : p_resource->p_parent;
vout_thread_t *vout = vout_Create(parent);
if (vout == NULL)
return NULL;
vout_rsc = vout_resource_Create(vout);
if (vout_rsc == NULL)
{
vout_Close(vout);
return NULL;
}
vout_rsc->order = vlc_list_is_empty( &p_resource->vout_rscs ) ?
VLC_VOUT_ORDER_PRIMARY : VLC_VOUT_ORDER_SECONDARY;
vlc_mutex_lock(&p_resource->lock_hold);
vout_resource_Add(vout_rsc, p_resource);
vlc_mutex_unlock(&p_resource->lock_hold);
}
return vout_rsc;
}
vout_thread_t *input_resource_RequestVout(input_resource_t *p_resource,
vlc_video_context *vctx,
const vout_configuration_t *cfg,
enum vlc_vout_order *order,
enum input_resource_vout_state *vout_state)
{
vlc_mutex_lock( &p_resource->lock );
struct vout_resource *vout_rsc = NULL;
if (vout_state != NULL)
*vout_state = INPUT_RESOURCE_VOUT_NOTCHANGED;
vout_configuration_t dcfg = *cfg;
if (dcfg.vout == NULL)
{
vout_rsc = RequestVoutRsc(p_resource);
if (vout_rsc == NULL)
{
vlc_mutex_unlock( &p_resource->lock );
return NULL;
}
dcfg.vout = vout_rsc->vout;
}
else
{
vout_rsc = resource_GetVoutRsc(p_resource, dcfg.vout);
assert(vout_rsc != NULL);
/* the caller is going to reuse the free vout, it's not free anymore */
if (p_resource->vout_rsc_free == vout_rsc)
p_resource->vout_rsc_free = NULL;
}
if (order != NULL)
*order = vout_rsc->order;
if (dcfg.fmt == NULL)
{
/* A NULL fmt means that only the vout creation is requested, do not
* start it with vout_Request(). */
vlc_mutex_unlock(&p_resource->lock);
return dcfg.vout;
}
if (vout_Request(&dcfg, vctx, p_resource->p_input)) {
if (vout_rsc->started && vout_state != NULL)
*vout_state = INPUT_RESOURCE_VOUT_STOPPED;
vout_rsc->started = false;
input_resource_PutVoutLocked(p_resource, dcfg.vout, NULL);
vlc_mutex_unlock(&p_resource->lock);
return NULL;
}
if (!vout_rsc->started)
{
vout_rsc->started = true;
if (vout_state != NULL)
*vout_state = INPUT_RESOURCE_VOUT_STARTED;
DisplayVoutTitle(p_resource, cfg->vout, &vout_rsc->psz_prev_title);
/* Send original viewpoint to the input in order to update other ESes */
if (p_resource->p_input != NULL)
{
input_control_param_t param = { .viewpoint = cfg->fmt->pose };
input_ControlPush(p_resource->p_input, INPUT_CONTROL_SET_INITIAL_VIEWPOINT,
¶m);
}
}
vlc_mutex_unlock( &p_resource->lock );
return dcfg.vout;
}
vout_thread_t *input_resource_HoldVout( input_resource_t *p_resource )
{
vlc_mutex_lock( &p_resource->lock_hold );
struct vout_resource *vout_rsc = resource_GetFirstVoutRsc( p_resource );
vout_thread_t *p_vout = vout_rsc != NULL ? vout_rsc->vout : NULL;
if( p_vout )
vout_Hold(p_vout);
vlc_mutex_unlock( &p_resource->lock_hold );
return p_vout;
}
vout_thread_t *input_resource_HoldDummyVout( input_resource_t *p_resource )
{
return vout_Hold(p_resource->p_vout_dummy);
}
void input_resource_HoldVouts( input_resource_t *p_resource, vout_thread_t ***ppp_vout,
size_t *pi_vout )
{
vout_thread_t **pp_vout;
*pi_vout = 0;
*ppp_vout = NULL;
vlc_mutex_lock( &p_resource->lock_hold );
if( vlc_list_is_empty( &p_resource->vout_rscs ) )
goto exit;
size_t count = 0;
struct vout_resource *vout_rsc;
vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
count ++;
pp_vout = vlc_alloc( count, sizeof(*pp_vout) );
if( !pp_vout )
goto exit;
*ppp_vout = pp_vout;
*pi_vout = count;
count = 0;
vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
pp_vout[count++] = vout_Hold( vout_rsc->vout );
exit:
vlc_mutex_unlock( &p_resource->lock_hold );
}
void input_resource_StopFreeVout(input_resource_t *p_resource)
{
vlc_mutex_lock(&p_resource->lock);
if (p_resource->vout_rsc_free != NULL)
{
msg_Dbg(p_resource->vout_rsc_free->vout, "stop free vout");
vout_Stop(p_resource->vout_rsc_free->vout);
}
vlc_mutex_unlock(&p_resource->lock);
}
/* */
sout_stream_t *input_resource_RequestSout( input_resource_t *p_resource, const char *psz_sout )
{
sout_stream_t *sout;
assert(psz_sout != NULL);
vlc_mutex_lock( &p_resource->lock );
/* Check the validity of the sout */
if (p_resource->p_sout != NULL
&& strcmp(p_resource->psz_sout, psz_sout) != 0)
{
msg_Dbg(p_resource->p_parent, "destroying unusable sout");
DestroySout(p_resource);
}
sout = p_resource->p_sout;
if (sout != NULL)
{
/* Reuse it */
msg_Dbg(p_resource->p_parent, "reusing sout");
msg_Dbg(p_resource->p_parent, "you probably want to use gather stream_out");
p_resource->p_sout = NULL;
}
else
{
/* Create a new one */
p_resource->psz_sout = strdup(psz_sout);
if (likely(p_resource->psz_sout != NULL)) {
sout = sout_NewInstance(p_resource->p_parent, psz_sout);
if (sout == NULL)
free(p_resource->psz_sout);
}
}
vlc_mutex_unlock( &p_resource->lock );
return sout;
}
void input_resource_PutSout(input_resource_t *resource, sout_stream_t *sout)
{
if (sout == NULL)
{
input_resource_TerminateSout(resource);
return;
}
vlc_mutex_lock(&resource->lock);
assert(resource->p_sout == NULL);
resource->p_sout = sout;
vlc_mutex_unlock(&resource->lock);
}
void input_resource_TerminateSout( input_resource_t *p_resource )
{
vlc_mutex_lock( &p_resource->lock );
DestroySout(p_resource);
vlc_mutex_unlock( &p_resource->lock );
}
¤ Dauer der Verarbeitung: 0.7 Sekunden
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104842 |
| #Domains=499162 |
|
|