/***************************************************************************** * omxil.c: Video decoder module making use of OpenMAX IL components. ***************************************************************************** * Copyright (C) 2010 VLC authors and VideoLAN * $Id$ * * Authors: Gildas Bazin <gbazin@videolan.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 <dlfcn.h> #if defined(USE_IOMX) /* On dll_open, just check that the OMX_Init symbol already is loaded */ # define dll_open(name) dlsym(RTLD_DEFAULT, "OMX_Init") # define dll_close(handle) do { } while (0) # define dlsym(handle, name) dlsym(RTLD_DEFAULT, "I" name) #else # define dll_open(name) dlopen( name, RTLD_NOW ) # define dll_close(handle) dlclose(handle) #endif #include <vlc_common.h> #include <vlc_plugin.h> #include <vlc_codec.h> #include <vlc_block_helper.h> #include <vlc_cpu.h> #include "omxil.h" #include "omxil_core.h" /***************************************************************************** * List of OpenMAX IL core we will try in order *****************************************************************************/ static const char *ppsz_dll_list[] = { #if defined(USE_IOMX) "libiomx.so", /* Not used when using IOMX, the lib should already be loaded */ #elif defined(RPI_OMX) "/opt/vc/lib/libopenmaxil.so", /* Broadcom IL core */ #else "libOMX_Core.so", /* TI OMAP IL core */ "libOmxCore.so", /* Qualcomm IL core */ "libomxil-bellagio.so", /* Bellagio IL core */ "libnvomx.so", /* Tegra3 IL core */ #endif 0 }; #ifdef RPI_OMX static const char *ppsz_extra_dll_list[] = { "/opt/vc/lib/libbcm_host.so", /* Broadcom host library */ 0 }; #endif /***************************************************************************** * Global OMX Core instance, shared between module instances *****************************************************************************/ static vlc_mutex_t omx_core_mutex = VLC_STATIC_MUTEX; static unsigned int omx_refcount = 0; static void *dll_handle; OMX_ERRORTYPE (*pf_init) (void); OMX_ERRORTYPE (*pf_deinit) (void); OMX_ERRORTYPE (*pf_get_handle) (OMX_HANDLETYPE *, OMX_STRING, OMX_PTR, OMX_CALLBACKTYPE *); OMX_ERRORTYPE (*pf_free_handle) (OMX_HANDLETYPE); OMX_ERRORTYPE (*pf_component_enum)(OMX_STRING, OMX_U32, OMX_U32); OMX_ERRORTYPE (*pf_get_roles_of_component)(OMX_STRING, OMX_U32 *, OMX_U8 **); OMX_ERRORTYPE (*pf_enable_graphic_buffers)(OMX_HANDLETYPE, OMX_U32, OMX_BOOL); OMX_ERRORTYPE (*pf_get_graphic_buffer_usage)(OMX_HANDLETYPE, OMX_U32, OMX_U32*); int (*pf_omx_hwbuffer_connect) (void *); int (*pf_omx_hwbuffer_disconnect) (void *); int (*pf_omx_hwbuffer_setup) (void *, int, int, int, int ); int (*pf_omx_hwbuffer_get_min_undequeued) (void *, unsigned int *); int (*pf_omx_hwbuffer_set_buffer_count) (void *, unsigned int ); int (*pf_omx_hwbuffer_setcrop) (void *, int, int, int, int); int (*pf_omx_hwbuffer_dequeue) (void *, void **); int (*pf_omx_hwbuffer_lock) (void *, void *); int (*pf_omx_hwbuffer_queue) (void *, void *); int (*pf_omx_hwbuffer_cancel) (void *, void *); #ifdef RPI_OMX static void *extra_dll_handle; static void (*pf_host_init)(void); static void (*pf_host_deinit)(void); static void CloseExtraDll() { if (pf_host_deinit) pf_host_deinit(); // Intentionally not unloading the host library, since it cannot be // unloaded cleanly after it has been initialized. } #else #define CloseExtraDll() #endif int InitOmxCore(vlc_object_t *p_this) { int i; vlc_mutex_lock( &omx_core_mutex ); if( omx_refcount > 0 ) { omx_refcount++; vlc_mutex_unlock( &omx_core_mutex ); return VLC_SUCCESS; } #ifdef RPI_OMX /* Load an extra library first, if available */ extra_dll_handle = NULL; for( i = 0; ppsz_extra_dll_list[i]; i++ ) { extra_dll_handle = dll_open( ppsz_extra_dll_list[i] ); if( extra_dll_handle ) break; } if( extra_dll_handle ) { pf_host_init = dlsym( extra_dll_handle, "bcm_host_init" ); pf_host_deinit = dlsym( extra_dll_handle, "bcm_host_deinit" ); if (pf_host_init) pf_host_init(); } #endif /* Load the OMX core */ for( i = 0; ppsz_dll_list[i]; i++ ) { dll_handle = dll_open( ppsz_dll_list[i] ); if( dll_handle ) break; } if( !dll_handle ) { CloseExtraDll(); vlc_mutex_unlock( &omx_core_mutex ); return VLC_EGENERIC; } pf_init = dlsym( dll_handle, "OMX_Init" ); pf_deinit = dlsym( dll_handle, "OMX_Deinit" ); pf_get_handle = dlsym( dll_handle, "OMX_GetHandle" ); pf_free_handle = dlsym( dll_handle, "OMX_FreeHandle" ); pf_component_enum = dlsym( dll_handle, "OMX_ComponentNameEnum" ); pf_get_roles_of_component = dlsym( dll_handle, "OMX_GetRolesOfComponent" ); if( !pf_init || !pf_deinit || !pf_get_handle || !pf_free_handle || !pf_component_enum || !pf_get_roles_of_component ) { msg_Warn( p_this, "cannot find OMX_* symbols in `%s' (%s)", ppsz_dll_list[i], dlerror() ); dll_close(dll_handle); CloseExtraDll(); vlc_mutex_unlock( &omx_core_mutex ); return VLC_EGENERIC; } #if defined(USE_IOMX) pf_enable_graphic_buffers = dlsym( dll_handle, "OMXAndroid_EnableGraphicBuffers" ); pf_get_graphic_buffer_usage = dlsym( dll_handle, "OMXAndroid_GetGraphicBufferUsage" ); pf_omx_hwbuffer_connect = dlsym( dll_handle, "OMXHWBuffer_Connect" ); pf_omx_hwbuffer_disconnect = dlsym( dll_handle, "OMXHWBuffer_Disconnect" ); pf_omx_hwbuffer_setup = dlsym( dll_handle, "OMXHWBuffer_Setup" ); pf_omx_hwbuffer_get_min_undequeued = dlsym( dll_handle, "OMXHWBuffer_GetMinUndequeued" ); pf_omx_hwbuffer_set_buffer_count = dlsym( dll_handle, "OMXHWBuffer_SetBufferCount" ); pf_omx_hwbuffer_setcrop = dlsym( dll_handle, "OMXHWBuffer_Setcrop" ); pf_omx_hwbuffer_dequeue = dlsym( dll_handle, "OMXHWBuffer_Dequeue" ); pf_omx_hwbuffer_lock = dlsym( dll_handle, "OMXHWBuffer_Lock" ); pf_omx_hwbuffer_queue = dlsym( dll_handle, "OMXHWBuffer_Queue" ); pf_omx_hwbuffer_cancel = dlsym( dll_handle, "OMXHWBuffer_Cancel" ); #endif /* Initialise the OMX core */ OMX_ERRORTYPE omx_error = pf_init(); if(omx_error != OMX_ErrorNone) { msg_Warn( p_this, "OMX_Init failed (%x: %s)", omx_error, ErrorToString(omx_error) ); dll_close(dll_handle); CloseExtraDll(); vlc_mutex_unlock( &omx_core_mutex ); return VLC_EGENERIC; } omx_refcount++; vlc_mutex_unlock( &omx_core_mutex ); return VLC_SUCCESS; } void DeinitOmxCore(void) { vlc_mutex_lock( &omx_core_mutex ); omx_refcount--; if( omx_refcount == 0 ) { pf_deinit(); dll_close( dll_handle ); CloseExtraDll(); } vlc_mutex_unlock( &omx_core_mutex ); } /***************************************************************************** * CreateComponentsList: creates a list of components matching the given role *****************************************************************************/ static const struct { const char *psz_role; const char *psz_name; } role_mappings[] = { #ifdef RPI_OMX { "video_decoder.avc", "OMX.broadcom.video_decode" }, { "video_decoder.mpeg2", "OMX.broadcom.video_decode" }, { "iv_renderer", "OMX.broadcom.video_render" }, #endif { 0, 0 } }; int CreateComponentsList(vlc_object_t *p_this, const char *psz_role, char ppsz_components[MAX_COMPONENTS_LIST_SIZE][OMX_MAX_STRINGNAME_SIZE]) { char psz_name[OMX_MAX_STRINGNAME_SIZE]; OMX_ERRORTYPE omx_error; OMX_U32 roles = 0; OMX_U8 **ppsz_roles = 0; unsigned int i, j, components = 0; if(!psz_role) goto end; for( i = 0; ; i++ ) { bool b_found = false; omx_error = pf_component_enum(psz_name, OMX_MAX_STRINGNAME_SIZE, i); if(omx_error != OMX_ErrorNone) break; msg_Dbg(p_this, "component %s", psz_name); for( unsigned int j = 0; role_mappings[j].psz_role; j++ ) { if( !strcmp( psz_role, role_mappings[j].psz_role ) && !strcmp( psz_name, role_mappings[j].psz_name ) ) { goto found; } } omx_error = pf_get_roles_of_component(psz_name, &roles, 0); if(omx_error != OMX_ErrorNone || !roles) continue; ppsz_roles = malloc(roles * (sizeof(OMX_U8*) + OMX_MAX_STRINGNAME_SIZE)); if(!ppsz_roles) continue; for( j = 0; j < roles; j++ ) ppsz_roles[j] = ((OMX_U8 *)(&ppsz_roles[roles])) + j * OMX_MAX_STRINGNAME_SIZE; omx_error = pf_get_roles_of_component(psz_name, &roles, ppsz_roles); if(omx_error != OMX_ErrorNone) roles = 0; for(j = 0; j < roles; j++) { msg_Dbg(p_this, " - role: %s", ppsz_roles[j]); if(!strcmp((char *)ppsz_roles[j], psz_role)) b_found = true; } free(ppsz_roles); if(!b_found) continue; found: if(components >= MAX_COMPONENTS_LIST_SIZE) { msg_Dbg(p_this, "too many matching components"); continue; } strncpy(ppsz_components[components], psz_name, OMX_MAX_STRINGNAME_SIZE-1); components++; } end: msg_Dbg(p_this, "found %i matching components for role %s", components, psz_role); for( i = 0; i < components; i++ ) msg_Dbg(p_this, "- %s", ppsz_components[i]); return components; }