khronosfws/openmax_al/src/gst_adaptation/xaadaptationgst.c
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 04 Oct 2010 00:51:56 +0300
changeset 55 e267340986c9
parent 42 1fa3fb47b1e3
permissions -rw-r--r--
Revision: 201037 Kit: 201039

/*
 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of "Eclipse Public License v1.0"
 * which accompanies this distribution, and is available
 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 *
 * Initial Contributors:
 * Nokia Corporation - initial contribution.
 *
 * Contributors:
 *
 * Description: Base GStreamer Adaptation Source
 *
 */

#include <string.h>
#include <gst/gst.h>
#include <gst/gstbuffer.h>
#include <stdlib.h>
#ifdef INPUTSRCSELECTION_FEATURE
#include <gst/interfaces/gstaudioinputinterface.h>
#endif
#include "xaobjectitf.h"
#include "xaadaptationgst.h"
/*
 * XAAdaptationGstCtx* XAAdaptationGst_Create()
 * 1st phase initialization function for Adaptation Base context structure.
 * Reserves memory for base context and initializes GStreamer FW.
 */
XAresult XAAdaptationGst_Init(XAAdaptationGstCtx* pSelf, XAuint32 ctxId)
    {
    DEBUG_API("->XAAdaptationGst_Init");

    if (pSelf)
        {
        if (XAAdaptationBase_Init(&(pSelf->baseObj), ctxId)
                != XA_RESULT_SUCCESS)
            {
            DEBUG_ERR("Failed to init base context!!!");
            free(pSelf);
            pSelf = NULL;
            }
        else
            {
            GError* gerror = 0;
            /*         Add default handler for Gst-bus messages */
            //pSelf->busCb = XAAdaptationGst_GstBusCb;
            pSelf->cond_mutx_inited = XA_BOOLEAN_FALSE;
#ifdef PROFILE_HEAP_USAGE
            xa_heap_total_alloc_print("Before GST Init");
#endif
            sem_init(&(pSelf->semAsyncWait), 0, 0);
            if ( !gst_init_check( NULL, NULL, &gerror ) )
                {
                    DEBUG_ERR("Gst Initalization failure.");
                    return XA_RESULT_INTERNAL_ERROR;
                }
#ifdef PROFILE_HEAP_USAGE
            xa_heap_total_alloc_print("After GST Init");
#endif            
            }
        }
    else
        {
        DEBUG_ERR("Invalid Gst Base Context.");
        return XA_RESULT_PARAMETER_INVALID;
        }

    DEBUG_API("<-XAAdaptationGst_Init");
    return XA_RESULT_SUCCESS;
    }

/*
 * XAresult XAAdaptationGst_PostInit()
 * 2nd phase initialization for Adaptation Base.
 */
XAresult XAAdaptationGst_PostInit(XAAdaptationGstCtx* ctx)
    {
    XAresult ret = XA_RESULT_SUCCESS;
    DEBUG_API("->XAAdaptationGst_PostInit");
    XAAdaptationBase_PostInit(&ctx->baseObj);

    ctx->thread_launched = XA_BOOLEAN_FALSE;
    pthread_mutex_init(&(ctx->ds_mutex), NULL);
    pthread_cond_init(&(ctx->ds_condition), NULL);
    ctx->cond_mutx_inited = XA_BOOLEAN_TRUE;

    DEBUG_API("<-XAAdaptationGst_PostInit");
    return ret;
    }

/*
 * void XAAdaptationGst_Free( XAAdaptationGstCtx* ctx )
 * Frees all Base context variables .
 */
void XAAdaptationGst_Free(XAAdaptationGstCtx* ctx)
    {
    GstElement* fakesink = NULL;
    DEBUG_API("->XAAdaptationGst_Free");

    if (ctx->bin)
        {
        fakesink = gst_bin_get_by_name(GST_BIN(ctx->bin), "fakesink");
        if (fakesink)
            {
            gst_element_set_state(GST_ELEMENT(fakesink), GST_STATE_NULL);
            gst_object_unref(fakesink);
            }

        if (gst_element_set_state(GST_ELEMENT(ctx->bin), GST_STATE_NULL)
                != GST_STATE_CHANGE_SUCCESS)
            { /*not much we can do*/
            DEBUG_ERR("WARNING: Failed to change to NULL state before deletion!!");
            }
        }

    if (ctx->asynctimer)
        { /*cancel timer*/
        g_source_remove(ctx->asynctimer);
        }
    sem_post(&(ctx->semAsyncWait));
    sem_destroy(&(ctx->semAsyncWait));

    XAAdaptationGst_StopGstListener(ctx);

    if (ctx->bin)
        {
        gst_object_unref(ctx->bin);
        }

    if (ctx->cond_mutx_inited == XA_BOOLEAN_TRUE)
        {
        ctx->thread_launched = XA_BOOLEAN_FALSE;
        pthread_mutex_destroy(&(ctx->ds_mutex));
        pthread_cond_destroy(&(ctx->ds_condition));
        ctx->cond_mutx_inited = XA_BOOLEAN_FALSE;
        }
    XAAdaptationBase_Free(&ctx->baseObj);

    DEBUG_API("<-XAAdaptationGst_Free");
    }

XAresult XAAdaptationGst_InitGstListener(XAAdaptationGstCtx* ctx)
    {
    int ret;
    DEBUG_API("->XAAdaptationGst_InitGstListener");
    if (ctx->bin)
        {
        ctx->bus = gst_pipeline_get_bus(GST_PIPELINE( ctx->bin ));
        }
    if (!ctx->bus)
        {
        DEBUG_ERR("could not get gst bus!");
        return XA_RESULT_INTERNAL_ERROR;
        }
    ret = pthread_create(&(ctx->busloopThr), NULL,
            (XAAdaptationGst_LaunchGstListener), (void*) ctx);
    if (ctx->thread_launched == XA_BOOLEAN_FALSE)
        {
        // Wait until the thread is created
        pthread_mutex_lock(&(ctx->ds_mutex));
        pthread_cond_wait(&(ctx->ds_condition), &(ctx->ds_mutex));
        pthread_mutex_unlock(&(ctx->ds_mutex));
        }
    if (ret)
        {
        DEBUG_ERR_A1("could not create thread!! (%d)",ret);
        return XA_RESULT_INTERNAL_ERROR;
        }
    DEBUG_API("<-XAAdaptationGst_InitGstListener");
    return XA_RESULT_SUCCESS;
    }

void * XAAdaptationGst_LaunchGstListener(void* args)
    {
    XAAdaptationGstCtx* ctx = (XAAdaptationGstCtx*) args;
    DEBUG_API("->XAAdaptationGst_LaunchGstListener");
    // Signal calling thread that this thread creation is completed
    ctx->thread_launched = XA_BOOLEAN_TRUE;
    pthread_mutex_lock(&(ctx->ds_mutex));
    pthread_cond_signal(&(ctx->ds_condition));
    pthread_mutex_unlock(&(ctx->ds_mutex));

    ctx->busloop = g_main_loop_new(NULL, FALSE);
    if (!ctx->busloop)
        {
        DEBUG_ERR("Glib main loop failure.");
        DEBUG_API("<-XAAdaptationGst_LaunchGstListener");
        }
    else
        {
        DEBUG_INFO("Start Glib main loop");
        g_main_loop_run(ctx->busloop);
        DEBUG_INFO("Glib main loop stopped - exiting thread");
        DEBUG_API("<-XAAdaptationGst_LaunchGstListener");
        pthread_exit(NULL);
        }
    return NULL;
    }

void XAAdaptationGst_StopGstListener(XAAdaptationGstCtx* ctx)
    {
    DEBUG_API("->XAAdaptationGst_StopGstListener");
    if (ctx->busloop)
        {
        g_main_loop_quit(ctx->busloop);
        g_main_loop_unref(ctx->busloop);
        }
    if (ctx->bus)
        {
        gst_object_unref(ctx->bus);
        ctx->bus = NULL;
        }
    DEBUG_API("<-XAAdaptationGst_StopGstListener");
    }

/*
 * ASynchronous operation managing
 **/

/* NOTE: This should NOT be called from gst callbacks - danger of deadlock!!
 */
void XAAdaptationGst_PrepareAsyncWait(XAAdaptationGstCtx* ctx)
    {
    DEBUG_API("->XAAdaptationGst_PrepareAsyncWait");

    if (ctx->waitingasyncop)
        { /*wait previous async op*/
        DEBUG_INFO("::WARNING:: previous asynch still ongoing!!!");
        DEBUG_INFO(">>>>  WAIT PREVIOUS");
        sem_wait(&(ctx->semAsyncWait));
        DEBUG_INFO("<<<<  PREVIOUS COMPLETED");
        }
    sem_init(&(ctx->semAsyncWait), 0, 0);

    ctx->waitingasyncop = XA_BOOLEAN_TRUE;
    DEBUG_API("<-XAAdaptationGst_PrepareAsyncWait");
    }

void XAAdaptationGst_StartAsyncWait(XAAdaptationGstCtx* ctx)
    {
    DEBUG_API("->XAAdaptationGst_StartAsyncWait");

    /* timeout to try to avoid gst freeze in rollup */
    ctx->asynctimer = g_timeout_add(XA_ADAPT_ASYNC_TIMEOUT,
            XAAdaptationGst_CancelAsyncWait, ctx);
    /* check flag once again if callback already happened before wait */
    if (ctx->waitingasyncop)
        {
        DEBUG_INFO(">>>>  ASYNC STARTS");
        sem_wait(&(ctx->semAsyncWait));
        DEBUG_INFO("<<<<  ASYNC COMPLETED");
        }
    else
        {
        DEBUG_INFO("<> async completed already");
        }
    /*cancel timer*/
    if (ctx->asynctimer)
        {
        g_source_remove(ctx->asynctimer);
        }
    ctx->waitingasyncop = XA_BOOLEAN_FALSE;

    DEBUG_API("<-XAAdaptationGst_StartAsyncWait");
    }

/* async operation timeout callback*/
gboolean XAAdaptationGst_CancelAsyncWait(gpointer ctx)
    {
    XAAdaptationGstCtx* bCtx = (XAAdaptationGstCtx*) ctx;
    DEBUG_API("->XAAdaptationGst_CancelAsyncWait");
    if (bCtx->waitingasyncop)
        {
        DEBUG_ERR_A3("ASYNC TIMED OUT : current %d, gsttarget %d, wanted %d",
                GST_STATE(bCtx->bin), GST_STATE_TARGET(bCtx->bin), bCtx->binWantedState);
        bCtx->waitingasyncop = XA_BOOLEAN_FALSE;
        sem_post(&(bCtx->semAsyncWait));
        }
    DEBUG_API("<-XAAdaptationGst_CancelAsyncWait");
    /* return false to remove timer */
    return FALSE;
    }

void XAAdaptationGst_CompleteAsyncWait(XAAdaptationGstCtx* ctx)
    {
    DEBUG_API("->XAAdaptationGst_CompleteAsyncWait");
    if (ctx->waitingasyncop)
        {
        int i;
        ctx->waitingasyncop = XA_BOOLEAN_FALSE;
        sem_getvalue(&(ctx->semAsyncWait), &i);
        DEBUG_INFO_A1("Asynch operation succeeded, sem value %d",i);
        if (i <= 0)
            { /* only post if locked */
            sem_post(&(ctx->semAsyncWait));
            }
        else if (i > 0)
            { /* should not be, reset semaphore */
            sem_init(&(ctx->semAsyncWait), 0, 0);
            }
        }
    DEBUG_API("<-XAAdaptationGst_CompleteAsyncWait");
    }

/**
 * GstElement* XAAdaptationGst_CreateGstSource( XADataSource* xaSrc, const XAchar *name )
 * @param XADataSource* xaSnk - XADataSource defining gst source to create
 * @param const XAchar *name - string for naming the gst element
 * @param XAboolean *isobj - (out param) is source another XA object?
 * @return GstElement* - return newly created gst source element
 * Description: Create gst source element corresponding to XA source structure
 */
GstElement* XAAdaptationGst_CreateGstSource(XADataSource* xaSrc,
        const char *name, XAboolean *isPCM )
    {
    XAuint32 locType = 0;
    GstElement* gstSrc = NULL;
#ifdef INPUTSRCSELECTION_FEATURE    
    GstAudioInputConfigIntfc *audioInputIntfc = NULL;
#endif
    char* fname = NULL;
    XADataLocator_URI* uri = NULL;
    XADataLocator_IODevice* ioDevice = NULL;

    DEBUG_API("->XAAdaptationGst_CreateGstSource");
    if (!xaSrc || !xaSrc->pLocator )
        {
        return NULL;
        }

    if (xaSrc && xaSrc->pFormat && *((XAuint32*) (xaSrc->pFormat))
            ==XA_DATAFORMAT_PCM && isPCM)
        {
        *isPCM = XA_BOOLEAN_TRUE;
        }

    locType = *((XAuint32*) (xaSrc->pLocator));
    switch (locType)
        {
        case XA_DATALOCATOR_URI:
            {
            DEBUG_INFO("XA_DATALOCATOR_URI");
            uri = (XADataLocator_URI*) xaSrc->pLocator;
            gstSrc = gst_element_factory_make("filesrc", name);
            if (uri->URI != NULL)
                {
                DEBUG_INFO_A1_STR("URI: %s", (char *)uri->URI);
                if (strncmp((char *) uri->URI, "file:///", 8) == 0)
                    {
                    fname = (char *) &((uri->URI)[8]);
                    }
                else
                    {
                    fname = (char *) uri->URI;
                    }
                DEBUG_INFO_A1_STR("->filesystem path %s", (char *)fname);
                g_object_set(G_OBJECT(gstSrc), "location", fname, NULL);
                /*check for pcm - decodebin does not know how to handle raw PCM files */
                if (isPCM && strstr(fname, ".pcm"))
                    {
                    DEBUG_INFO("PCM file detected");
                    *isPCM = XA_BOOLEAN_TRUE;
                    }
                }
            else
                {
                DEBUG_ERR("No uri specified.");
                return NULL;
                }
            break; /* XA_DATALOCATOR_URI */
            }
        case XA_DATALOCATOR_IODEVICE:
            {
            DEBUG_INFO("XA_DATALOCATOR_IODEVICE");
            ioDevice = (XADataLocator_IODevice*) (xaSrc->pLocator);
            switch (ioDevice->deviceType)
                {
                case XA_IODEVICE_AUDIOINPUT:
                    {
                    DEBUG_INFO("XA_IODEVICE_AUDIOINPUT");
                    DEBUG_INFO_A1("ioDevice->deviceID: %x", ioDevice->deviceID);
                    switch (ioDevice->deviceID)
                        {
                        case XA_ADAPTID_DEVSOUNDSRC_MIC:
                            DEBUG_INFO("devsoundsrc");
                            gstSrc = gst_element_factory_make("devsoundsrc",
                                    name);
#ifdef INPUTSRCSELECTION_FEATURE
                            if(gstSrc)
                                {
                                audioInputIntfc = GST_AUDIO_INPUT_CONFIG_GET_IFACE(gstSrc);
                                if(audioInputIntfc)
                                    {
                                    audioInputIntfc->SetAudioInputs(EDefaultMicrophone);
                                    }
                                }
#endif
                            break;
                        case XA_ADAPTID_DEVSOUNDSRC_FMRX:
                            DEBUG_INFO("devsoundsrc");
                            gstSrc = gst_element_factory_make("devsoundsrc",
                                    name);
#ifdef INPUTSRCSELECTION_FEATURE
                            if(gstSrc)
                                {
                                audioInputIntfc = GST_AUDIO_INPUT_CONFIG_GET_IFACE(gstSrc);
                                if(audioInputIntfc)
                                    {
                                    audioInputIntfc->SetAudioInputs(EFmRadio);
                                    }
                                }
#endif
                            break;
                        case XA_ADAPTID_DEVSOUNDSRC_CALL:
                            DEBUG_INFO("devsoundsrc");
                            gstSrc = gst_element_factory_make("devsoundsrc",
                                    name);
#ifdef INPUTSRCSELECTION_FEATURE
                            if(gstSrc)
                                {
                                audioInputIntfc = GST_AUDIO_INPUT_CONFIG_GET_IFACE(gstSrc);
                                if(audioInputIntfc)
                                    {
                                    audioInputIntfc->SetAudioInputs(EVoiceCall);
                                    }
                                }
#endif
                            break;

                        case XA_ADAPTID_AUDIOTESTSRC:
                            /*fall through*/
                        default:
                            DEBUG_INFO("audiotestsrc");
                            gstSrc = gst_element_factory_make("audiotestsrc",
                                    name);
                            break;
                        }
                    break;
                    }

                default:
                    {
                    DEBUG_ERR("Unsupported IODevice.");
                    break;
                    }
                }
            break; /* XA_DATALOCATOR_IODEVICE */
            }
        case XA_DATALOCATOR_ADDRESS:
        default:
            DEBUG_ERR("Incorrect data locator for source.");
            break;
        }

    if (gstSrc)
        {
        DEBUG_INFO_A1("Created gstreamer source element at %x", (int)gstSrc);
        }

    DEBUG_API("<-XAAdaptationGst_CreateGstSource");
    return gstSrc;
    }

/**
 * GstElement* XAAdaptationGst_CreateGstSink( XADataSink* xaSnk, const XAchar *name )
 * @param XADataSink* xaSnk - XADataSink defining gst sink to create
 * @param const XAchar *name - string for naming the gst element
 * @return GstElement* - return newly created gst sink element
 * Description: Create gst sink element corresponding to XA sink structure
 */
GstElement* XAAdaptationGst_CreateGstSink(XADataSink* xaSnk,
        const char *name)
    {
    XAuint32 locType = 0;
    GstElement* gstSnk = NULL;
    XADataLocator_URI* uri = NULL;
    DEBUG_API("->XAAdaptationGst_CreateGstSink");
    if (!xaSnk || !xaSnk->pLocator)
        {
        DEBUG_INFO("Warning! No sink specified, use fakesink");
        gstSnk = gst_element_factory_make("fakesink", name);
        if (!gstSnk)
            {
            DEBUG_ERR("Cannot create sink!");
            return NULL;
            }
        g_object_set(G_OBJECT(gstSnk), "async", FALSE, NULL);
        }
    else
        {
        locType = *((XAuint32*) (xaSnk->pLocator));
        switch (locType)
            {
            case XA_DATALOCATOR_URI:
                {
                DEBUG_INFO("XA_DATALOCATOR_URI");
                uri = (XADataLocator_URI*) xaSnk->pLocator;
                gstSnk = gst_element_factory_make("filesink", name);
                if (!gstSnk)
                    {
                    DEBUG_ERR("Cannot create sink!");
                    return NULL;
                    }
                if (uri->URI != NULL)
                    {
                    XAchar *fname;
                    DEBUG_INFO_A1_STR("URI: %s", (char *)uri->URI);
                    if (strncmp((char *) uri->URI, "file:///", 8) == 0)
                        {
                        fname = &((uri->URI)[8]);
                        }
                    else
                        {
                        fname = uri->URI;
                        }
                    DEBUG_INFO_A1_STR("->filesystem path %s", (char *)fname);
                    g_object_set(G_OBJECT(gstSnk), "location", fname,
                            "async", FALSE, "qos", FALSE, "max-lateness",
                            (gint64) (-1), NULL);
                    }
                else
                    {
                    DEBUG_ERR("No recording output uri specified.");
                    return NULL;
                    }
                break;
                }
/*            case XA_DATALOCATOR_ADDRESS:
            case XA_DATALOCATOR_NATIVEDISPLAY:
            case XA_DATALOCATOR_OUTPUTMIX:
            case XA_DATALOCATOR_IODEVICE:*/
                /* when only valid IOdevice sinks vibra and LED sinks implemented
                 * at adaptation level, add handling here (in this implementation,
                 * no handling needed as only dummy implementations for those)
                 **/
            default:
                DEBUG_ERR("Incorrect data locator for sink.");
                break;
            }
        }
    if (gstSnk)
        {
        DEBUG_INFO_A1("Created gstreamer sink element at %x", (int)gstSnk);
        }
    DEBUG_API("<-XAAdaptationGst_CreateGstSink");
    return gstSnk;
    }

/* called when pad is actually blocking/ gets unblocked*/
void XAAdaptationGst_PadBlockCb(GstPad *pad, gboolean blocked,
        gpointer user_data)
    {
    DEBUG_API_A2_STR("->XAAdaptationGst_PadBlockCb   pad \"%s\" of \"%s\" ",
            GST_OBJECT_NAME(pad),
            GST_OBJECT_NAME(gst_pad_get_parent_element(pad)) );
    DEBUG_API_A1("<-XAAdaptationGst_PadBlockCb   blocked:%d",blocked);
    }