/*****************************************************************************
* picture_pool.c : picture pool functions
*****************************************************************************
- * Copyright (C) 2009 the VideoLAN team
+ * Copyright (C) 2009 VLC authors and VideoLAN
* Copyright (C) 2009 Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
* $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
+ * 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 General Public License for more details.
+ * 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 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.
+ * 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.
*****************************************************************************/
/*****************************************************************************
/*****************************************************************************
*
*****************************************************************************/
-struct picture_release_sys_t
-{
- /* Saved release */
- void (*pf_release)( picture_t * );
- picture_release_sys_t *p_release_sys;
+struct picture_gc_sys_t {
+ picture_pool_t *pool;
+ picture_t *picture;
+ bool in_use;
+ uint64_t tick;
+};
+struct picture_pool_t {
+ uint64_t tick;
/* */
- int (*pf_lock)( picture_t * );
- void (*pf_unlock)( picture_t * );
+ unsigned picture_count;
+ picture_t **picture;
- /* */
- int64_t i_tick;
+ int (*pic_lock)(picture_t *);
+ void (*pic_unlock)(picture_t *);
+ unsigned refs;
+ vlc_mutex_t lock;
};
-struct picture_pool_t
+void picture_pool_Release(picture_pool_t *pool)
{
- int64_t i_tick;
+ bool destroy;
- int i_picture;
- picture_t **pp_picture;
-};
+ vlc_mutex_lock(&pool->lock);
+ assert(pool->refs > 0);
+ destroy = --pool->refs == 0;
+ vlc_mutex_unlock(&pool->lock);
+
+ if (likely(!destroy))
+ return;
+
+ for (unsigned i = 0; i < pool->picture_count; i++) {
+ picture_t *picture = pool->picture[i];
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+
+ picture_Release(sys->picture);
+ free(sys);
+ free(picture);
+ }
+
+ vlc_mutex_destroy(&pool->lock);
+ free(pool->picture);
+ free(pool);
+}
+
+static void picture_pool_ReleasePicture(picture_t *picture)
+{
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+ picture_pool_t *pool = sys->pool;
+
+ if (pool->pic_unlock != NULL)
+ pool->pic_unlock(picture);
-static void PicturePoolPictureRelease( picture_t * );
+ vlc_mutex_lock(&pool->lock);
+ assert(sys->in_use);
+ sys->in_use = false;
+ vlc_mutex_unlock(&pool->lock);
-picture_pool_t *picture_pool_NewExtended( const picture_pool_configuration_t *cfg )
+ picture_pool_Release(pool);
+}
+
+static picture_t *picture_pool_ClonePicture(picture_pool_t *pool,
+ picture_t *picture)
+{
+ picture_gc_sys_t *sys = malloc(sizeof(*sys));
+ if (unlikely(sys == NULL))
+ return NULL;
+
+ sys->pool = pool;
+ sys->picture = picture;
+ sys->in_use = false;
+ sys->tick = 0;
+
+ picture_resource_t res = {
+ .p_sys = picture->p_sys,
+ .pf_destroy = picture_pool_ReleasePicture,
+ };
+
+ for (int i = 0; i < picture->i_planes; i++) {
+ res.p[i].p_pixels = picture->p[i].p_pixels;
+ res.p[i].i_lines = picture->p[i].i_lines;
+ res.p[i].i_pitch = picture->p[i].i_pitch;
+ }
+
+ picture_t *clone = picture_NewFromResource(&picture->format, &res);
+ if (likely(clone != NULL))
+ clone->gc.p_sys = sys;
+ else
+ free(sys);
+
+ return clone;
+}
+
+static picture_pool_t *Create(int picture_count)
{
- picture_pool_t *p_pool = calloc( 1, sizeof(*p_pool) );
- if( !p_pool )
+ picture_pool_t *pool = calloc(1, sizeof(*pool));
+ if (!pool)
return NULL;
- p_pool->i_tick = 1;
- p_pool->i_picture = cfg->picture_count;
- p_pool->pp_picture = calloc( p_pool->i_picture, sizeof(*p_pool->pp_picture) );
- if( !p_pool->pp_picture )
- {
- free( p_pool );
+ pool->tick = 1;
+ pool->picture_count = picture_count;
+ pool->picture = calloc(pool->picture_count, sizeof(*pool->picture));
+ if (!pool->picture) {
+ free(pool->picture);
+ free(pool);
return NULL;
}
+ pool->refs = 1;
+ vlc_mutex_init(&pool->lock);
+ return pool;
+}
- for( int i = 0; i < cfg->picture_count; i++ )
- {
- picture_t *p_picture = cfg->picture[i];
+picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
+{
+ picture_pool_t *pool = Create(cfg->picture_count);
+ if (!pool)
+ return NULL;
- /* The pool must be the only owner of the picture */
- assert( p_picture->i_refcount == 1 );
+ pool->pic_lock = cfg->lock;
+ pool->pic_unlock = cfg->unlock;
- /* Install the new release callback */
- picture_release_sys_t *p_release_sys = malloc( sizeof(*p_release_sys) );
- if( !p_release_sys )
+ for (unsigned i = 0; i < cfg->picture_count; i++) {
+ picture_t *picture = picture_pool_ClonePicture(pool, cfg->picture[i]);
+ if (unlikely(picture == NULL))
abort();
- p_release_sys->pf_release = p_picture->pf_release;
- p_release_sys->p_release_sys = p_picture->p_release_sys;
- p_release_sys->pf_lock = cfg->lock;
- p_release_sys->pf_unlock = cfg->unlock;
- p_release_sys->i_tick = 0;
-
- p_picture->i_refcount = 0;
- p_picture->pf_release = PicturePoolPictureRelease;
- p_picture->p_release_sys = p_release_sys;
-
- /* */
- p_pool->pp_picture[i] = p_picture;
+
+ atomic_init(&picture->gc.refcount, 0);
+
+ pool->picture[i] = picture;
}
- return p_pool;
+ return pool;
}
-picture_pool_t *picture_pool_New( int i_picture, picture_t *pp_picture[] )
+picture_pool_t *picture_pool_New(unsigned count, picture_t *const *tab)
{
picture_pool_configuration_t cfg;
- memset( &cfg, 0, sizeof(cfg) );
- cfg.picture_count = i_picture;
- cfg.picture = pp_picture;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.picture_count = count;
+ cfg.picture = tab;
- return picture_pool_NewExtended( &cfg );
+ return picture_pool_NewExtended(&cfg);
}
-picture_pool_t *picture_pool_NewFromFormat( const video_format_t *p_fmt, int i_picture )
+picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt,
+ unsigned count)
{
- picture_t *pp_picture[i_picture];
-
- for( int i = 0; i < i_picture; i++ )
- {
- pp_picture[i] = picture_New( p_fmt->i_chroma,
- p_fmt->i_width, p_fmt->i_height,
- p_fmt->i_aspect );
- if( !pp_picture[i] )
+ picture_t *picture[count ? count : 1];
+ unsigned i;
+
+ for (i = 0; i < count; i++) {
+ picture[i] = picture_NewFromFormat(fmt);
+ if (picture[i] == NULL)
goto error;
}
- picture_pool_t *p_pool = picture_pool_New( i_picture, pp_picture );
- if( !p_pool )
+
+ picture_pool_t *pool = picture_pool_New(count, picture);
+ if (!pool)
goto error;
- return p_pool;
+ return pool;
error:
- for( int i = 0; i < i_picture; i++ )
- {
- if( !pp_picture[i] )
- break;
- picture_Release( pp_picture[i] );
- }
+ while (i > 0)
+ picture_Release(picture[--i]);
return NULL;
}
-void picture_pool_Delete( picture_pool_t *p_pool )
+picture_pool_t *picture_pool_Reserve(picture_pool_t *master, unsigned count)
{
- for( int i = 0; i < p_pool->i_picture; i++ )
- {
- picture_t *p_picture = p_pool->pp_picture[i];
- picture_release_sys_t *p_release_sys = p_picture->p_release_sys;
+ picture_t *picture[count ? count : 1];
+ unsigned i;
- assert( p_picture->i_refcount == 0 );
+ for (i = 0; i < count; i++) {
+ picture[i] = picture_pool_Get(master);
+ if (picture[i] == NULL)
+ goto error;
+ }
- /* Restore old release callback */
- p_picture->i_refcount = 1;
- p_picture->pf_release = p_release_sys->pf_release;
- p_picture->p_release_sys = p_release_sys->p_release_sys;
+ picture_pool_t *pool = picture_pool_New(count, picture);
+ if (!pool)
+ goto error;
- picture_Release( p_picture );
+ pool->pic_lock = master->pic_lock;
+ pool->pic_unlock = master->pic_unlock;
+ return pool;
- free( p_release_sys );
- }
- free( p_pool->pp_picture );
- free( p_pool );
+error:
+ while (i > 0)
+ picture_Release(picture[--i]);
+ return NULL;
}
-picture_t *picture_pool_Get( picture_pool_t *p_pool )
+picture_t *picture_pool_Get(picture_pool_t *pool)
{
- for( int i = 0; i < p_pool->i_picture; i++ )
- {
- picture_t *p_picture = p_pool->pp_picture[i];
- if( p_picture->i_refcount > 0 )
+ vlc_mutex_lock(&pool->lock);
+ assert(pool->refs > 0);
+
+ for (unsigned i = 0; i < pool->picture_count; i++) {
+ picture_t *picture = pool->picture[i];
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+ uint64_t tick;
+
+ if (sys->in_use)
continue;
- picture_release_sys_t *p_release_sys = p_picture->p_release_sys;
- if( p_release_sys->pf_lock && p_release_sys->pf_lock(p_picture) )
+ pool->refs++;
+ tick = ++pool->tick;
+ sys->in_use = true;
+ vlc_mutex_unlock(&pool->lock);
+
+ if (pool->pic_lock != NULL && pool->pic_lock(picture) != 0) {
+ vlc_mutex_lock(&pool->lock);
+ sys->in_use = false;
+ pool->refs--;
+ vlc_mutex_unlock(&pool->lock);
continue;
+ }
+
+ sys->tick = tick;
- /* */
- p_picture->p_release_sys->i_tick = p_pool->i_tick++;
- picture_Hold( p_picture );
- return p_picture;
+ assert(atomic_load(&picture->gc.refcount) == 0);
+ atomic_init(&picture->gc.refcount, 1);
+ picture->p_next = NULL;
+ return picture;
}
+
+ vlc_mutex_unlock(&pool->lock);
return NULL;
}
-void picture_pool_NonEmpty( picture_pool_t *p_pool, bool b_reset )
+unsigned picture_pool_Reset(picture_pool_t *pool)
{
- picture_t *p_old = NULL;
-
- for( int i = 0; i < p_pool->i_picture; i++ )
- {
- picture_t *p_picture = p_pool->pp_picture[i];
-
- if( b_reset )
- p_picture->i_refcount = 0;
- else if( p_picture->i_refcount == 0 )
- return;
- else if( !p_old || p_picture->p_release_sys->i_tick < p_old->p_release_sys->i_tick )
- p_old = p_picture;
+ unsigned ret = 0;
+retry:
+ vlc_mutex_lock(&pool->lock);
+ assert(pool->refs > 0);
+
+ for (unsigned i = 0; i < pool->picture_count; i++) {
+ picture_t *picture = pool->picture[i];
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+
+ if (sys->in_use) {
+ vlc_mutex_unlock(&pool->lock);
+ picture_Release(picture);
+ ret++;
+ goto retry;
+ }
}
- if( !b_reset && p_old )
- p_old->i_refcount = 0;
+ vlc_mutex_unlock(&pool->lock);
+
+ return ret;
}
-static void PicturePoolPictureRelease( picture_t *p_picture )
+void picture_pool_NonEmpty(picture_pool_t *pool)
{
- assert( p_picture->i_refcount > 0 );
+ picture_t *oldest = NULL;
+ uint64_t tick = 0;
- if( --p_picture->i_refcount > 0 )
- return;
+ vlc_mutex_lock(&pool->lock);
+ assert(pool->refs > 0);
+
+ for (unsigned i = 0; i < pool->picture_count; i++) {
+ picture_t *picture = pool->picture[i];
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+
+ if (!sys->in_use) {
+ vlc_mutex_unlock(&pool->lock);
+ return; /* Nothing to do */
+ }
- picture_release_sys_t *p_release_sys = p_picture->p_release_sys;
- if( p_release_sys->pf_unlock )
- p_release_sys->pf_unlock( p_picture );
+ if (picture->gc.p_sys->tick < tick) {
+ oldest = picture;
+ tick = picture->gc.p_sys->tick;
+ }
+ }
+
+ if (oldest != NULL) {
+ while (oldest->gc.p_sys->in_use) {
+ vlc_mutex_unlock(&pool->lock);
+ picture_Release(oldest);
+ vlc_mutex_lock(&pool->lock);
+ }
+ }
+
+ vlc_mutex_unlock(&pool->lock);
}
+unsigned picture_pool_GetSize(const picture_pool_t *pool)
+{
+ return pool->picture_count;
+}