diff -r 80975da52420 -r 43d09473c595 khronosfws/openmax_al/src/gst_adaptation/xamediarecorderadaptctx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/khronosfws/openmax_al/src/gst_adaptation/xamediarecorderadaptctx.c Fri May 14 16:22:35 2010 +0300 @@ -0,0 +1,1807 @@ +/* +* 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: +* +*/ + +#include +#include +#include +#include +#include "xacapabilitiesmgr.h" +#include "xamediarecorderadaptctx.h" +#include "xacameraadaptctx.h" + + +extern XAboolean cameraRealized; +extern XACameraAdaptationCtx_* cameraCtx; + +/*forward declarations*/ +GstElement* XAMediaRecorderAdapt_CreateEncodeBin( + XAMediaRecorderAdaptationCtx* ctx); +XAresult XAMediaRecorderAdapt_CreatePipeline( + XAMediaRecorderAdaptationCtx* ctx); +void XAMediaRecorderAdapt_BufferAvailable(GstElement* sink, + gpointer user_data); +/** Creates the caps gst element */ +static XAresult XAMediaRecorderAdapt_CreateCapsFilter( XAMediaRecorderAdaptationCtx* ctx ); + +/* + * gboolean XAMediaRecorderAdapt_GstBusCb( GstBus *bus, GstMessage *message, gpointer data ) + * MediaPlayer Gst-bus message handler (Callback) + */ +gboolean XAMediaRecorderAdapt_GstBusCb(GstBus *bus, GstMessage *message, + gpointer data) + { + XAAdaptationGstCtx* bCtx = (XAAdaptationGstCtx*) data; + /* only listen to pipeline messages */ + if (GST_MESSAGE_SRC(message)==GST_OBJECT(bCtx->bin)) + { + XAMediaRecorderAdaptationCtx* mCtx = NULL; + DEBUG_API_A2("->XAMediaRecorderAdapt_GstBusCb:\"%s\" from object \"%s\"", + GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME(GST_MESSAGE_SRC(message))); + mCtx = (XAMediaRecorderAdaptationCtx*)data; + + switch( GST_MESSAGE_TYPE(message)) + { + case GST_MESSAGE_EOS: + { + /* stop position tracking */ + if(mCtx->runpositiontimer > 0) + { + g_source_remove(mCtx->runpositiontimer); + mCtx->runpositiontimer=0; + } + + /* complete any ongoing client async operations */ + XAAdaptationGst_CompleteAsyncWait(bCtx); + + /* send needed events */ + { + XAAdaptEvent event = {XA_RECORDITFEVENTS, XA_RECORDEVENT_HEADATLIMIT, 0, NULL }; + XAAdaptationBase_SendAdaptEvents(&bCtx->baseObj, &event ); + } + if(mCtx->positionCb) + { + mCtx->positionCb(bCtx); + } + bCtx->binWantedState = GST_STATE_PAUSED; + break; + } + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldstate, newstate, pendingstate, gsttargetstate; + gst_message_parse_state_changed(message, &oldstate, &newstate, &pendingstate); + gsttargetstate = GST_STATE_TARGET(bCtx->bin); + DEBUG_INFO_A4("old %d -> new %d (-> pending %d -> gsttarget %d)", + oldstate, newstate, pendingstate, gsttargetstate); + if(gsttargetstate!=bCtx->binWantedState) + { + DEBUG_ERR_A1("WARNING: Gst target is not wanted target [%d]!!!", bCtx->binWantedState); + } + /* print out some more info */ + if( pendingstate == GST_STATE_VOID_PENDING ) + { + if( newstate != bCtx->binWantedState ) + { + DEBUG_INFO_A2("Gst in intermediate state transition (curr %d, target %d)", + newstate,bCtx->binWantedState); + } + else + { + DEBUG_INFO_A1("Gst in wanted target state (%d)",newstate); + } + } + if( oldstate!=GST_STATE_PLAYING && newstate==GST_STATE_PLAYING ) + { + XAAdaptEvent event = {XA_RECORDITFEVENTS, XA_RECORDEVENT_HEADMOVING, 0, NULL }; + /* send needed events */ + XAAdaptationBase_SendAdaptEvents(&bCtx->baseObj, &event ); + /* enable position tracking if needed */ + if( mCtx->runpositiontimer==0 && mCtx->trackpositionenabled && mCtx->positionCb ) + { + mCtx->runpositiontimer = g_timeout_add(XA_ADAPT_PU_INTERVAL, mCtx->positionCb, mCtx); + } + } + break; + } + + case GST_MESSAGE_ASYNC_DONE: + { + /* some async sequence ended */ + XAAdaptationGst_CompleteAsyncWait(bCtx); + break; + } + + case GST_MESSAGE_ERROR: + { + GError* error; + gchar* debug; + gst_message_parse_error(message, &error, &debug); + DEBUG_ERR_A1("Gst reports error \"%s\"", debug); + /* stop waiting any ongoing async operations */ + XAAdaptationGst_CompleteAsyncWait(bCtx); + break; + } + case GST_MESSAGE_WARNING: + { + GError* error; + gchar* debug; + gst_message_parse_warning(message, &error, &debug); + DEBUG_ERR_A1("Gst reports warning \"%s\"", debug); + /* stop waiting any ongoing async operations */ + XAAdaptationGst_CompleteAsyncWait(bCtx); + break; + } + default: + break; + } + DEBUG_API("<-XAMediaRecorderAdapt_GstBusCb"); + } + return TRUE; + } + +/* + * XAAdaptationGstCtx* XAMediaRecorderAdapt_Create() + * Allocates memory for Media Recorder Adaptation Context and makes 1st phase initialization + * @param XADataSource *pAudioSrc - pointer to OMX-AL audio source + * @param XADataSource *pImageVideoSrc - pointer image/video source + * @param XADataSink *pDataSnk - pointer to OMX-AL sink + * @returns XAMediaRecorderAdaptationCtx* - Pointer to created context, NULL if error occurs. + */ +XAAdaptationBaseCtx* XAMediaRecorderAdapt_Create(XADataSource* pAudioSrc, + XADataSource* pImageVideoSrc, XADataSink* pDataSnk, XAuint8 recModes) + { + XAMediaRecorderAdaptationCtx *pSelf = NULL; + XAuint32 locType = 0; + XADataLocator_IODevice *ioDevice; + DEBUG_API("->XAMediaRecorderAdapt_Create"); + + pSelf = (XAMediaRecorderAdaptationCtx*)calloc(1, sizeof(XAMediaRecorderAdaptationCtx)); + if (pSelf) + { + if (XAAdaptationGst_Init(&(pSelf->baseObj), + XAMediaRecorderAdaptation) != XA_RESULT_SUCCESS) + { + DEBUG_ERR("Failed to init base context!!!"); + free(pSelf); + pSelf = NULL; + DEBUG_API("<-XAMediaRecorderAdapt_Create"); + return NULL; + } + else + { + pSelf->baseObj.baseObj.fwtype = FWMgrFWGST; + pSelf->xaAudioSource = pAudioSrc; + pSelf->xaVideoSource = pImageVideoSrc; + pSelf->xaSink = pDataSnk; +/* pSelf->baseObj.pipeSinkThrCtx.state = CPStateNull;*/ + pSelf->xaRecordState = XA_RECORDSTATE_STOPPED; + pSelf->curMirror = XA_VIDEOMIRROR_NONE; + pSelf->curRotation = 0; + pSelf->recModes = recModes; + pSelf->isRecord = XA_BOOLEAN_FALSE; + + /* defaults from API spec */ + pSelf->imageEncSettings.width = 640; + pSelf->imageEncSettings.height = 480; + pSelf->imageEncSettings.compressionLevel = 0; + pSelf->imageEncSettings.encoderId = XA_IMAGECODEC_JPEG; + pSelf->imageEncSettings.colorFormat = XA_COLORFORMAT_UNUSED; + /* no specified defaults for rest, determined later from container type */ + pSelf->videoEncSettings.encoderId = XA_ADAPTID_UNINITED; + pSelf->videoEncSettings.width = 640; + pSelf->videoEncSettings.height = 480; + pSelf->videoEncSettings.frameRate = 15; + pSelf->audioEncSettings.encoderId = XA_ADAPTID_UNINITED; + pSelf->audioEncSettings.channelsIn = 2; + pSelf->audioEncSettings.channelsOut = 2; + pSelf->audioEncSettings.bitsPerSample = 8; + pSelf->audioEncSettings.bitRate = 128; + pSelf->audioEncSettings.sampleRate = 44100; + } + + if (pImageVideoSrc) + { + locType = *((XAuint32*) (pImageVideoSrc->pLocator)); + if (locType == XA_DATALOCATOR_IODEVICE) + { + ioDevice + = (XADataLocator_IODevice*) (pImageVideoSrc->pLocator); + if (ioDevice->deviceType == XA_IODEVICE_CAMERA + && !cameraRealized) + { + DEBUG_ERR("Preconditions violated - Camera object not realized"); + XAAdaptationBase_Free(&pSelf->baseObj.baseObj); + free(pSelf); + pSelf = NULL; + DEBUG_API("<-XAMediaRecorderAdapt_Create"); + return NULL; + } + } + } + } + + DEBUG_API("<-XAMediaRecorderAdapt_Create"); + return (XAAdaptationBaseCtx*) &pSelf->baseObj; + } + +/* + * XAresult XAMediaRecorderAdapt_PostInit() + * 2nd phase initialization of Media Recorder Adaptation Context + * @param XAAdaptationGstCtx* ctx - pointer to Media Recorder adaptation context + * @return XAresult - Success value + */ +XAresult XAMediaRecorderAdapt_PostInit(XAAdaptationGstCtx* bCtx) + { + GstStateChangeReturn gret; + + XAresult ret = XA_RESULT_SUCCESS; + XAMediaRecorderAdaptationCtx* ctx = NULL; + DEBUG_API("->XAMediaRecorderAdapt_PostInit"); + if (bCtx == NULL || bCtx->baseObj.ctxId != XAMediaRecorderAdaptation) + { + DEBUG_ERR("Invalid parameter!!");DEBUG_API("<-XAMediaRecorderAdapt_PostInit"); + return XA_RESULT_PARAMETER_INVALID; + } + ctx = (XAMediaRecorderAdaptationCtx*) bCtx; + + ret = XAAdaptationGst_PostInit(bCtx); + if (ret != XA_RESULT_SUCCESS) + { + DEBUG_ERR("Gst context postinit failed!!"); + return ret; + } + + /* top level bin for media recorder */ + bCtx->bin = gst_pipeline_new("media_recorder"); + + /* Create Gst bus listener. */ + ret = XAAdaptationGst_InitGstListener(bCtx); + if (ret != XA_RESULT_SUCCESS) + { + DEBUG_ERR("Bus listener creation failed!!"); + return ret; + } + /* Add Media Recorder specific handler */ + if (bCtx->bus) + { + bCtx->busCb = XAMediaRecorderAdapt_GstBusCb; + gst_bus_add_signal_watch(bCtx->bus); + g_signal_connect(bCtx->bus, "message::eos", G_CALLBACK(bCtx->busCb), ctx ); + g_signal_connect(bCtx->bus, "message::error", G_CALLBACK(bCtx->busCb), ctx ); + g_signal_connect(bCtx->bus, "message::warning", G_CALLBACK(bCtx->busCb), ctx ); + g_signal_connect(bCtx->bus, "message::state-changed", G_CALLBACK(bCtx->busCb), ctx ); + g_signal_connect(bCtx->bus, "message::segment-done", G_CALLBACK(bCtx->busCb), ctx ); + g_signal_connect(bCtx->bus, "message::async-done", G_CALLBACK(bCtx->busCb), ctx ); + } + else + { + DEBUG_ERR("Failed to create message bus"); + return XA_RESULT_INTERNAL_ERROR; + } + + XAMetadataAdapt_PreInit(bCtx); + + /* create pipeline */ + ret = XAMediaRecorderAdapt_CreatePipeline(ctx); + if (ret != XA_RESULT_SUCCESS) + { + DEBUG_ERR("Failed to create recorder pipeline"); + return XA_RESULT_INTERNAL_ERROR; + } + +#ifdef XA_IMPL_MEASURE_GST_DELAY + bCtx->startTime = clock(); +#endif /* XA_IMPL_MEASURE_GST_DELAY */ + /* roll up bin */ + bCtx->binWantedState = GST_STATE_PAUSED; + XAAdaptationGst_PrepareAsyncWait(bCtx); + gret = gst_element_set_state(GST_ELEMENT(bCtx->bin), bCtx->binWantedState); + if (gret == GST_STATE_CHANGE_ASYNC) + { + DEBUG_INFO("Wait for preroll"); + XAAdaptationGst_StartAsyncWait(bCtx);DEBUG_INFO("Preroll ready"); + } + else if (gret == GST_STATE_CHANGE_FAILURE) + { + DEBUG_ERR("Preroll FAILED"); + /*ret = XA_RESULT_INTERNAL_ERROR;*/ + } +#ifdef XA_IMPL_MEASURE_GST_DELAY + bCtx->endTime = clock(); + double diff = bCtx->endTime - bCtx->startTime; + diff = diff / CLOCKS_PER_SEC; + DEBUG_API_A1( "Starting up bin took %.4lf secs",diff); +#endif /* XA_IMPL_MEASURE_GST_DELAY */ + XAMetadataAdapt_PostInit(bCtx); + + bCtx->waitingasyncop = XA_BOOLEAN_FALSE; + + DEBUG_API("<-XAMediaRecorderAdapt_PostInit"); + return ret; + } + +/* + * void XAMediaRecorderAdapt_Destroy( XAAdaptationGstCtx* bCtx ) + * Destroys Media Recorder Adaptation Context + * @param ctx - Media Recorder Adaptation context to be destroyed + */ +void XAMediaRecorderAdapt_Destroy(XAAdaptationGstCtx* bCtx) + { + XAMediaRecorderAdaptationCtx* ctx = NULL; + char* fname = NULL; + DEBUG_API("->XAMediaRecorderAdapt_Destroy"); + + if (bCtx == NULL || bCtx->baseObj.ctxId != XAMediaRecorderAdaptation) + { + DEBUG_ERR("Invalid parameter!!");DEBUG_API("<-XAMediaRecorderAdapt_Destroy"); + return; + } + ctx = (XAMediaRecorderAdaptationCtx*) bCtx; + + if (ctx->isRecord == XA_BOOLEAN_FALSE) + { + DEBUG_INFO("Removing unnecessary file."); + + if (ctx->xaSink && *((XAuint32*) (ctx->xaSink->pLocator)) + == XA_DATALOCATOR_URI) + { + if (strncmp( + (char *) ((XADataLocator_URI*) (ctx->xaSink->pLocator))->URI, + "file:///", 8) == 0) + { + fname + = (char *) &(((XADataLocator_URI*) (ctx->xaSink->pLocator))->URI[8]); + } + else + { + fname + = (char *) ((XADataLocator_URI*) (ctx->xaSink->pLocator))->URI; + } + + if (remove(fname) != 0) + { + DEBUG_ERR_A1("Cannot remove file %s", fname); + } + } + } + + if (ctx->isobjvsrc && ctx->videosource) + { /* external source, unlink now */ + gst_element_unlink(ctx->videosource, ctx->codecbin); + GST_OBJECT_FLAG_SET(GST_OBJECT(ctx->videosource),GST_OBJECT_FLOATING); + } + if (bCtx->bus) + { + gst_bus_remove_signal_watch(bCtx->bus); + } + XAAdaptationGst_CancelAsyncWait(bCtx); + + if (ctx->runpositiontimer) + { + g_source_remove(ctx->runpositiontimer); + } + + XAAdaptationGst_Free(bCtx); + + free(ctx); + ctx = NULL; + + DEBUG_API("<-XAMediaRecorderAdapt_Destroy"); + } + +/***************** INTERNAL FUNCTIONS *******************************/ + + /* + * void XAMediaRecorderAdapt_CreatePipeline( XAMediaRecorderAdaptationCtx* ctx ); + */ +XAresult XAMediaRecorderAdapt_CreatePipeline( + XAMediaRecorderAdaptationCtx* ctx) + { + XAresult ret = XA_RESULT_SUCCESS; + DEBUG_API("->XAMediaRecorderAdapt_CreatePipeline"); + + /* Create the audio src */ + if ( ctx->xaAudioSource ) + { + /* create audio pipe source */ + ctx->audiosource = XAAdaptationGst_CreateGstSource( ctx->xaAudioSource, "audiosource", + &(ctx->isobjasrc), NULL, NULL ); + + if( ctx->audiosource ) + { + if (gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->audiosource)) + { + DEBUG_API("Added audiosource to bin"); + } + else + { + DEBUG_API("Could not add audiosource to bin"); + return XA_RESULT_INTERNAL_ERROR; + } + } + else + { + DEBUG_ERR("Could not create audio source!!!!"); + return XA_RESULT_INTERNAL_ERROR; + } + } + + /* create and add video pipeline */ + ctx->codecbin = XAMediaRecorderAdapt_CreateEncodeBin(ctx); + if (ctx->codecbin) + { + if (gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->codecbin)) + { + DEBUG_API("->XAMediaRecorderAdapt_CreatePipeline: gst_bin_add success"); + } + else + { + DEBUG_ERR("Could not add codec bin"); + return XA_RESULT_INTERNAL_ERROR; + } + } + else + { + DEBUG_ERR("Could not create encoding bin!!!"); + return XA_RESULT_INTERNAL_ERROR; + } + + /* create and add video pipeline if video source available and codec supports video */ + if (ctx->xaVideoSource + && gst_element_get_pad(ctx->codecbin, "v_sink")) + { + /* create video pipe source */ + ctx->videosource = XAAdaptationGst_CreateGstSource( + ctx->xaVideoSource, "videosource", &(ctx->isobjvsrc), + NULL, NULL ); + if (!ctx->videosource) + { + DEBUG_ERR("Could not create video source!!!!"); + return XA_RESULT_INTERNAL_ERROR; + } + + if (!ctx->isobjvsrc) + { /* Add other than camera sources to media recorder bin */ + gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->videosource); + } + else + { /* Don't add camera source to media recorder bin */ + GstCaps * encSrcCaps; + encSrcCaps = gst_caps_new_simple( "video/x-raw-yuv", + "format", GST_TYPE_FOURCC,GST_MAKE_FOURCC('I','4','2','0'), + "framerate", GST_TYPE_FRACTION, ctx->videoEncSettings.frameRate, 1, + NULL); + DEBUG_INFO_A1("new camera encoding filter: %s",gst_caps_to_string(encSrcCaps)); + g_object_set( G_OBJECT(ctx->videosource), "filter-caps",encSrcCaps,NULL); + gst_caps_unref(encSrcCaps); + } + /* create video filter for video encoder settings */ + ctx->videofilter = gst_element_factory_make("capsfilter", "videofilter"); + if( ctx->videofilter ) + { + GstCaps* encSrcCaps; + gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->videofilter); + encSrcCaps = gst_caps_new_simple("video/x-raw-yuv", + "width", G_TYPE_INT, ctx->videoEncSettings.width, + "height", G_TYPE_INT, ctx->videoEncSettings.height, + "framerate", GST_TYPE_FRACTION, ctx->videoEncSettings.frameRate, 1, + NULL); + DEBUG_INFO_A1("video encoder config from settings: %s",gst_caps_to_string(encSrcCaps)); + g_object_set( G_OBJECT(ctx->videofilter), "caps",encSrcCaps,NULL); + gst_caps_unref(encSrcCaps); + if ( ! ctx->isobjvsrc ) + { + if(!gst_element_link(ctx->videosource, ctx->videofilter)) + { + DEBUG_ERR("Could not link videopp to videofilter!!"); + return XA_RESULT_INTERNAL_ERROR; + } + } + else + { /* For camera source used ghost-pads for linking, because elements are in different bins */ + GstStateChangeReturn gret; + GstElement *camTee=NULL; + GstPad *cameraBinGhostPad=NULL; + GstPad *ghost=NULL; + GstPad *mrGhostSink=NULL; + + DEBUG_INFO("Set ext-source PAUSED for pipeline manipulation"); + gret = gst_element_set_state( GST_ELEMENT(ctx->videosource), GST_STATE_READY); + if(gret == GST_STATE_CHANGE_SUCCESS) + { + gret = gst_element_get_state( GST_ELEMENT(ctx->videosource), NULL,NULL,XA_ADAPT_ASYNC_TIMEOUT_SHORT_NSEC); + } + + /* Add new ghost-pad to external camera source */ + camTee = gst_bin_get_by_name( GST_BIN(ctx->videosource), "CamTee"); + if ( !camTee ) + { + DEBUG_ERR("Could not get tee-element from camera"); + } + cameraBinGhostPad = gst_element_get_request_pad( camTee, "src%d" ); + if ( !cameraBinGhostPad ) + { + DEBUG_ERR("Could not get new src-pad from CamTee element"); + } + gst_element_add_pad(ctx->videosource, gst_ghost_pad_new("MRObjSrc",cameraBinGhostPad)); + ghost = gst_element_get_static_pad( GST_ELEMENT(ctx->videosource), "MRObjSrc" ); + DEBUG_INFO_A2("Setting element:%s pad:%s to blocking.", + gst_element_get_name(ctx->baseObj.bin), + gst_pad_get_name(ghost)); + /* Set newly created pad to blocking */ + gst_pad_set_blocked_async(ghost, TRUE, XAAdaptationGst_PadBlockCb, NULL); + + mrGhostSink = gst_element_get_static_pad( GST_ELEMENT(ctx->videofilter), "sink"); + gst_element_add_pad(ctx->baseObj.bin, gst_ghost_pad_new("MRObjSink",mrGhostSink)); + if ( !gst_element_link_pads( GST_ELEMENT(ctx->videosource), "MRObjSrc", + GST_ELEMENT(ctx->baseObj.bin), "MRObjSink") ) + { + DEBUG_ERR("Could not link camera:MRObjSrc to videofilter:MRObjSink"); + return XA_RESULT_INTERNAL_ERROR; + } + + if ( cameraBinGhostPad ) + { + gst_object_unref( cameraBinGhostPad ); + } + if ( ghost ) + { + gst_object_unref( ghost ); + } + if ( mrGhostSink ) + { + gst_object_unref( mrGhostSink ); + } + if ( camTee ) + { + gst_object_unref( camTee ); + } + } + } + /* create video processing pipeline */ +#ifdef XA_IMPL_FIXED_VIDEO_SIZE + ctx->videoppbin = XAAdaptationGst_CreateFixedSizeVideoPP( ); +#else + ctx->videoppbin = XAAdaptationGst_CreateVideoPP( ); +#endif + if( ctx->videoppbin ) + { + gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->videoppbin); + if(!gst_element_link(ctx->videofilter, ctx->videoppbin)) + { + DEBUG_ERR("Could not link videofilter to videopp!!"); + return XA_RESULT_INTERNAL_ERROR; + } + } + else + { + DEBUG_ERR("Could not create video pp bin!!!!"); + return XA_RESULT_INTERNAL_ERROR; + } + /* create identity to extract buffers from */ + ctx->videoextract = gst_element_factory_make("identity", "videoextract"); + if( ctx->videoextract ) + { + gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->videoextract); + if(!gst_element_link(ctx->videoppbin, ctx->videoextract)) + { + DEBUG_ERR("Could not link videopp to videoextract!!"); + return XA_RESULT_INTERNAL_ERROR; + } + } + else + { + DEBUG_ERR("Could not create videoextract!!!!"); + return XA_RESULT_INTERNAL_ERROR; + } + if( ctx->videoextract ) + { + if( !gst_element_link_pads(ctx->videoextract, "src", ctx->codecbin, "v_sink") ) + { + DEBUG_INFO("Warning: could not link videoextract to codec!!"); + } + } + } + else + { + DEBUG_INFO("No video input"); + } + + /* create and add audio pipeline */ + if ( ctx->audiosource ) + { +#ifdef USE_AUDIO_PP + /* create audio processing pipeline */ + ctx->audioppbin = XAAdaptationGst_CreateAudioPP( ); + if( ctx->audioppbin ) + { + if (gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->audioppbin)) + { + DEBUG_INFO("Added audioppbin to bin"); + } + else + { + DEBUG_ERR("Could not add audioppbin to bin"); + return XA_RESULT_INTERNAL_ERROR; + } + if(!gst_element_link(ctx->audiosource, ctx->audioppbin)) + { + DEBUG_ERR("Could not link audiofilter to audiopp!!"); + return XA_RESULT_INTERNAL_ERROR; + } + + + } + else + { + DEBUG_ERR("Could not create audio pp bin!!!!"); + return XA_RESULT_INTERNAL_ERROR; + } +#endif //USE_AUDIO_PP + + /* create audio filter for audio encoder settings */ + ret = XAMediaRecorderAdapt_CreateCapsFilter(ctx); + if ( XA_RESULT_SUCCESS != ret ) + { + DEBUG_ERR("cannot create caps filter"); + return ret; + } + + /*LINK : audiosource -> audiofilter */ + if(!gst_element_link(ctx->audiosource, ctx->audiofilter)) + { + DEBUG_ERR("Could not link audiosource to audiofilter!!"); + return XA_RESULT_INTERNAL_ERROR; + } + +/* if( !gst_element_link_pads_filtered(ctx->audiofilter, "src", ctx->codecbin, "sink", encSrcCaps) ) + { + DEBUG_INFO("Warning: could not link audiopp to codec!!"); + return XA_RESULT_INTERNAL_ERROR; + } +*/ +#ifdef USE_AUDIO_PP + if (!gst_element_link_filtered( ctx->audiofilter , ctx->audioppbin ,encSrcCaps)) + { + DEBUG_INFO("Warning: could not link audiosource to audiopp!!"); + return XA_RESULT_INTERNAL_ERROR; + } + if(!gst_element_link(ctx->audioppbin, ctx->codecbin)) + { + DEBUG_INFO("Warning: could not link audioppbin to codecbin!!"); + return XA_RESULT_INTERNAL_ERROR; + } +#else + if(!gst_element_link(ctx->audiofilter, ctx->codecbin )) + { + DEBUG_ERR("Could not link audiosource to codecbin!!"); + return XA_RESULT_INTERNAL_ERROR; + } + else + { + DEBUG_INFO("Warning: link audiosource to codecbin is successfull with muxcaps!!"); + } +#endif // USE_AUDIO_PP + } + else + { + DEBUG_INFO("No audio input"); + } + + /* create and add data sink */ + ctx->datasink = XAAdaptationGst_CreateGstSink( ctx->xaSink, "datasink", &(ctx->isobjsink) ); + if( ctx->datasink ) + { + if ( GST_IS_APP_SINK(ctx->datasink) ) + { + gst_app_sink_set_emit_signals( GST_APP_SINK(ctx->datasink), TRUE ); + } + if (gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->datasink)) + { + DEBUG_INFO("Added datasink to bin"); + } + else + { + DEBUG_ERR("Could not add datasink to bin"); + return XA_RESULT_INTERNAL_ERROR; + } + if(!gst_element_link(ctx->codecbin, ctx->datasink)) + { + DEBUG_ERR("Could not link codec to sink!!"); + return XA_RESULT_INTERNAL_ERROR; + } + /* NOTE: no actual object sinks applicable, variable used to imply appsrc (recording to memory)*/ + if(ctx->isobjsink) + { + g_signal_connect(ctx->datasink, "new-buffer", + G_CALLBACK (XAMediaRecorderAdapt_BufferAvailable),ctx); + + ret = XAImpl_CreateSemaphore( &(ctx->recThrCtx.bufInsufficientSem)); + if ( ret != XA_RESULT_SUCCESS ) + { + DEBUG_ERR("WARN: Could not create semaphore for recorder event handler!"); + } + XAImpl_CreateThreadHandle( &(ctx->recordingEventThr) ); + } + } + else + { + DEBUG_ERR("Could not create data sink!!!"); + return XA_RESULT_INTERNAL_ERROR; + } + DEBUG_API("<-XAMediaRecorderAdapt_CreatePipeline"); + return ret; + } + + /* + * XAresult XAMediaRecorderAdapt_CheckCodec( XAMediaRecorderAdaptationCtx_* mCtx ); + * Check codec compatibility and support with initiated datasink content + */ + XAresult XAMediaRecorderAdapt_CheckCodec( XAMediaRecorderAdaptationCtx_* mCtx, XACapsType encType, XAuint32 encoderId ) + { + XAresult ret = XA_RESULT_SUCCESS; + XAuint32 format; + XACapabilities temp; + + DEBUG_API("->XAMediaRecorderAdapt_CheckCodec"); + + /*first, check if codec supported at all*/ + ret = XACapabilitiesMgr_GetCapsById(mCtx->baseObj.baseObj.capslist,(XACapsType)(XACAP_ENCODER|encType), encoderId, &temp); + + if( ret==XA_RESULT_SUCCESS ) + { + if(encType & (XACAP_VIDEO|XACAP_AUDIO)) + { + if(mCtx->xaSink && mCtx->xaSink->pFormat) + { + format = *(XAuint32*)(mCtx->xaSink->pFormat); + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + } + else + { + if(mCtx->snapshotVars.xaSink && mCtx->snapshotVars.xaSink->pFormat) + { + format = *(XAuint32*)(mCtx->snapshotVars.xaSink->pFormat); + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + } + if(ret==XA_RESULT_SUCCESS) switch ( format ) + { + case XA_DATAFORMAT_PCM: + if ( (encType == XACAP_AUDIO) && (encoderId == XA_AUDIOCODEC_PCM) ) + { + ret=XA_RESULT_SUCCESS; + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + break; + + case XA_DATAFORMAT_RAWIMAGE: + if ( (encType == XACAP_IMAGE) && (encoderId == XA_IMAGECODEC_RAW) ) + { + ret=XA_RESULT_SUCCESS; + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + break; + + case XA_DATAFORMAT_MIME: + DEBUG_INFO("XA_DATAFORMAT_MIME "); + { + XADataFormat_MIME* mime = ((XADataFormat_MIME*)mCtx->xaSink->pFormat); + DEBUG_INFO_A1("mime->containerType:%u",(int)mime->containerType); + DEBUG_INFO_A1("mime->mimeType:%s",mime->mimeType); + switch ( mime->containerType ) + { + case XA_CONTAINERTYPE_RAW: + if( ((encType == XACAP_AUDIO) && (encoderId == XA_AUDIOCODEC_PCM)) || + ((encType == XACAP_VIDEO) && (encoderId == XA_ADAPTID_RAWVIDEO)) || + ((encType == XACAP_IMAGE) && (encoderId == XA_IMAGECODEC_RAW)) ) + { + ret=XA_RESULT_SUCCESS; + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + break; + + case XA_CONTAINERTYPE_AVI: + if(encType == XACAP_VIDEO) + { + switch(encoderId) + { + case XA_ADAPTID_MOTIONJPEG: + case XA_ADAPTID_RAWVIDEO: + ret=XA_RESULT_SUCCESS; + break; + default: + ret=XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + } + else if(encType == XACAP_AUDIO) + { + switch(encoderId) + { + case XA_AUDIOCODEC_PCM: + ret=XA_RESULT_SUCCESS; + break; + default: + ret=XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + break; + + case XA_CONTAINERTYPE_WAV: + if(encType == XACAP_AUDIO) + { + switch(encoderId) + { + case XA_AUDIOCODEC_PCM: + ret=XA_RESULT_SUCCESS; + break; + default: + ret=XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + break; + + case XA_CONTAINERTYPE_JPG: + if(encType == XACAP_VIDEO) + { + switch(encoderId) + { + case XA_ADAPTID_MOTIONJPEG: + ret=XA_RESULT_SUCCESS; + break; + default: + ret=XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + } + else if(encType == XACAP_IMAGE) + { + switch(encoderId) + { + case XA_IMAGECODEC_JPEG: + ret=XA_RESULT_SUCCESS; + break; + default: + ret=XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + break; + + case XA_CONTAINERTYPE_UNSPECIFIED: + if(strstr( (char *) mime->mimeType, "/ogg") != 0) + { + if(encType == XACAP_VIDEO) + { + switch(encoderId) + { + case XA_ADAPTID_THEORA: + ret=XA_RESULT_SUCCESS; + break; + default: + ret=XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + } + else if(encType == XACAP_AUDIO) + { + switch(encoderId) + { + case XA_ADAPTID_VORBIS: + ret=XA_RESULT_SUCCESS; + break; + default: + ret=XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + } + else + { + ret=XA_RESULT_FEATURE_UNSUPPORTED; + } + break; + + default: /*switch (containertype)*/ + ret = XA_RESULT_FEATURE_UNSUPPORTED; + break; + } + break; + } + default: /*switch (format)*/ + ret = XA_RESULT_CONTENT_UNSUPPORTED; + break; + } + } + if( ret!=XA_RESULT_SUCCESS ) + { + DEBUG_ERR("cannot accommodate given codec & datasink pair!!!"); + } + DEBUG_API("<-XAMediaRecorderAdapt_CheckCodec"); + return ret; + } +/* + * XAresult XAMediaRecorderAdapt_ChangeEncoders( XAMediaRecorderAdaptationCtx* mCtx ); + * re-create encodebin based on new encoder settings + */ +XAresult XAMediaRecorderAdapt_ChangeEncoders( XAMediaRecorderAdaptationCtx* mCtx ) + { + XAresult ret = XA_RESULT_SUCCESS; + XAAdaptationGstCtx* bCtx = &(mCtx->baseObj); + + DEBUG_API("->XAMediaRecorderAdapt_ChangeEncoders"); + /* check state */ + if(GST_STATE(mCtx->baseObj.bin)binWantedState = GST_STATE_READY; + gret = gst_element_set_state( GST_ELEMENT(bCtx->bin), bCtx->binWantedState); + gret = gst_element_get_state( GST_ELEMENT(bCtx->bin), NULL, NULL, XA_ADAPT_ASYNC_TIMEOUT_SHORT_NSEC); + + /*set new stream settings*/ + if( mCtx->videofilter ) + { + encSrcCaps = gst_caps_new_simple("video/x-raw-yuv", + "format", GST_TYPE_FOURCC,GST_MAKE_FOURCC('I','4','2','0'), + "width", G_TYPE_INT, mCtx->videoEncSettings.width, + "height", G_TYPE_INT, mCtx->videoEncSettings.height, + "framerate", GST_TYPE_FRACTION, mCtx->videoEncSettings.frameRate, 1, + NULL); + DEBUG_INFO_A1("new video encoder config from settings: %s",gst_caps_to_string(encSrcCaps)); + g_object_set( G_OBJECT(mCtx->videofilter), "caps",encSrcCaps,NULL); + gst_caps_unref(encSrcCaps); + } + if( mCtx->audiofilter ) + { + encSrcCaps = gst_caps_new_full( + gst_structure_new("audio/x-raw-int", + "channels", G_TYPE_INT, mCtx->audioEncSettings.channelsOut, + "rate", G_TYPE_INT, mCtx->audioEncSettings.sampleRate, + "bitrate", G_TYPE_INT, mCtx->audioEncSettings.bitRate, + NULL), + gst_structure_new("audio/x-raw-float", + "channels", G_TYPE_INT, mCtx->audioEncSettings.channelsOut, + "width", G_TYPE_INT, mCtx->audioEncSettings.bitsPerSample, + "rate", G_TYPE_INT, mCtx->audioEncSettings.sampleRate, + "bitrate", G_TYPE_INT, mCtx->audioEncSettings.bitRate, + NULL), + NULL); + DEBUG_INFO_A1("new audio encoder config from settings: %s",gst_caps_to_string(encSrcCaps)); + g_object_set( G_OBJECT(mCtx->audiofilter), "caps",encSrcCaps,NULL); + gst_caps_unref(encSrcCaps); + } + + if(mCtx->isobjvsrc) + { + moSrc = gst_element_get_static_pad(mCtx->videosource,"MRObjSrc"); + moSink = gst_pad_get_peer(moSrc); + if(moSink) + { + gst_pad_unlink(moSrc,moSink); + } + moSrc = gst_element_get_static_pad(mCtx->videosource,"mediaobjectsrc"); + encSrcCaps = gst_caps_new_simple("video/x-raw-yuv", + "format", GST_TYPE_FOURCC,GST_MAKE_FOURCC('I','4','2','0'), + "framerate", GST_TYPE_FRACTION, mCtx->videoEncSettings.frameRate, 1, + NULL); + DEBUG_INFO_A1("new camera encoding filter: %s",gst_caps_to_string(encSrcCaps)); + g_object_set( G_OBJECT(mCtx->videosource), "filter-caps",encSrcCaps,NULL); + gst_caps_unref(encSrcCaps); + } + + DEBUG_INFO("Unlink and remove old encodebin"); + asink = gst_element_get_static_pad(mCtx->codecbin,"sink"); + if(asink) + { + linkedasrc = gst_pad_get_peer(asink); + if(linkedasrc) + { + gst_pad_unlink(linkedasrc,asink); + } + } + vsink = gst_element_get_static_pad(mCtx->codecbin,"v_sink"); + if(vsink) + { + linkedvsrc = gst_pad_get_peer(vsink); + if(linkedvsrc) + { + gst_pad_unlink(linkedvsrc,vsink); + } + } + src = gst_element_get_static_pad(mCtx->codecbin,"src"); + if(src) + { + linkedsink = gst_pad_get_peer(src); + if(linkedsink) + { + gst_pad_unlink(src,linkedsink); + } + } + + gst_element_set_state( GST_ELEMENT(mCtx->codecbin), GST_STATE_NULL ); + gst_element_get_state( GST_ELEMENT(mCtx->codecbin),NULL,NULL,1000 ); + gst_bin_remove( GST_BIN(bCtx->bin), mCtx->codecbin ); + /*reset filesink too*/ + gst_element_set_state(GST_ELEMENT(mCtx->datasink),GST_STATE_NULL); + gst_element_sync_state_with_parent(mCtx->datasink); + gst_element_get_state(mCtx->datasink,NULL,NULL,XA_ADAPT_ASYNC_TIMEOUT_SHORT_NSEC); + + DEBUG_INFO("Link new encodebin"); + mCtx->codecbin = newBin; + gst_bin_add(GST_BIN(bCtx->bin), mCtx->codecbin); + asink = gst_element_get_static_pad(mCtx->codecbin,"sink"); + if(asink && linkedasrc) + { + gst_pad_link(linkedasrc,asink); + } + vsink = gst_element_get_static_pad(mCtx->codecbin,"v_sink"); + if(vsink && linkedvsrc) + { + gst_pad_link(linkedvsrc,vsink); + } + src = gst_element_get_static_pad(mCtx->codecbin,"src"); + if(src && linkedsink) + { + gst_pad_link(src,linkedsink); + } + + if(mCtx->isobjvsrc) + { + moSrc = gst_element_get_static_pad(mCtx->videosource,"MRObjSrc"); + if(moSink&&moSrc) + { + gst_pad_link(moSrc,moSink); + } + } + + /*re-roll*/ + DEBUG_INFO("Reroll pipeline"); + bCtx->binWantedState = GST_STATE_PAUSED; + gret = gst_element_set_state( GST_ELEMENT(bCtx->bin), bCtx->binWantedState); + if( gret == GST_STATE_CHANGE_ASYNC ) + { + DEBUG_INFO("Wait for reroll"); + XAAdaptationGst_StartAsyncWait(bCtx); + } + else if( gret == GST_STATE_CHANGE_FAILURE ) + { + DEBUG_ERR("reroll FAILED"); + ret = XA_RESULT_INTERNAL_ERROR; + } + bCtx->waitingasyncop = XA_BOOLEAN_FALSE; + DEBUG_INFO_A1("Pipeline in state %s",gst_element_state_get_name(GST_STATE(bCtx->bin))); + } + else + { + /* could not support wanted encoders */ + DEBUG_ERR("Failed to create encodebin with new settings, using old one!"); + ret = XA_RESULT_FEATURE_UNSUPPORTED; + } + } + else + { /* n/a while playing */ + DEBUG_ERR("Cannot change encoder when recording ongoing!"); + ret = XA_RESULT_PRECONDITIONS_VIOLATED; + } + DEBUG_API("<-XAMediaRecorderAdapt_ChangeEncoders"); + return ret; + } + +/** + * GstElement* XAMediaRecorderAdapt_CreateEncodeBin( XAMediaRecorderAdaptationCtx* ctx ) + * @return GstElement* - pointer to created bin element + * Decription: Create encoder/muxer element based on given format and encoder settings + */ +GstElement* XAMediaRecorderAdapt_CreateEncodeBin( XAMediaRecorderAdaptationCtx* ctx ) + { + XAresult ret = XA_RESULT_SUCCESS; + GstElement *audioenc = NULL, *videoenc=NULL, *mux=NULL; + GstElement *codecbin = gst_bin_new( "mrcodecbin" ); + GstPad *ghostsrc = NULL, *ghostaudiosink = NULL, *ghostvideosink = NULL; + XAuint32 format; + XACapabilities temp; + + DEBUG_API("->XAMediaRecorderAdapt_CreateEncodeBin"); + if(ctx->recModes & XA_RECMODE_STREAM) + { + if(ctx->xaSink && ctx->xaSink->pFormat) + { + format = *(XAuint32*)(ctx->xaSink->pFormat); + switch ( format ) + { + case XA_DATAFORMAT_PCM: + DEBUG_INFO("XA_DATAFORMAT_PCM"); + { + XADataFormat_PCM* pcm = ((XADataFormat_PCM*)ctx->xaSink->pFormat); + if(!ctx->xaAudioSource) + { + DEBUG_ERR("Unsupported dataformat for given data sources"); + return NULL; + } + /* no need for codec, just pass data on */ + mux = gst_element_factory_make("identity", "mux"); + gst_bin_add(GST_BIN(codecbin), mux); + ghostsrc = gst_element_get_static_pad(mux,"src"); + ghostaudiosink = gst_element_get_static_pad(mux,"sink"); + /*set usable audio settings from the sink structure*/ + ctx->audioEncSettings.encoderId = XA_AUDIOCODEC_PCM; + ctx->audioEncSettings.channelsOut = pcm->numChannels; + ctx->audioEncSettings.bitsPerSample = pcm->bitsPerSample; + } + break; + + case XA_DATAFORMAT_RAWIMAGE: + DEBUG_INFO("XA_DATAFORMAT_RAWIMAGE"); + { + XADataFormat_RawImage* img = ((XADataFormat_RawImage*)ctx->xaSink->pFormat); + if(!ctx->xaVideoSource) + { + DEBUG_ERR("Unsupported dataformat for given data sources"); + return NULL; + } + /* no need for codec, just pass data on */ + mux = gst_element_factory_make("identity", "mux"); + gst_bin_add(GST_BIN(codecbin), mux); + ghostsrc = gst_element_get_static_pad(mux,"src"); + ghostvideosink = gst_element_get_static_pad(mux,"sink"); + /*set needed image settings from the sink structure*/ + ctx->imageEncSettings.encoderId = XA_IMAGECODEC_RAW; + ctx->imageEncSettings.width = img->width; + ctx->imageEncSettings.height = img->height; + ctx->imageEncSettings.colorFormat = img->colorFormat; + } + break; + + case XA_DATAFORMAT_MIME: + DEBUG_INFO("XA_DATAFORMAT_MIME "); + { + XADataFormat_MIME* mime = ((XADataFormat_MIME*)ctx->xaSink->pFormat); + DEBUG_INFO_A1("mime->containerType:%u",(int)mime->containerType); + DEBUG_INFO_A1("mime->mimeType:%s",mime->mimeType); + switch ( mime->containerType ) + { + case XA_CONTAINERTYPE_AVI: + DEBUG_INFO("XA_CONTAINERTYPE_AVI"); + mux = gst_element_factory_make("avimux", "mux"); + if(mux) + { + if (gst_bin_add(GST_BIN(codecbin), mux)) + { + DEBUG_INFO("Added mux to codecbin"); + } + else + { + DEBUG_ERR("Could not add mux to codecbin"); + return NULL; + } + /*use this as linkpoint*/ + ghostsrc = gst_element_get_static_pad(mux,"src"); + } + /* Add and link audio/video codecs */ + /*set video defaults*/ + if(ctx->videoEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->videoEncSettings.encoderId = XA_ADAPTID_MOTIONJPEG; + if(ctx->xaVideoSource) + { + if(XACapabilitiesMgr_GetCapsById(NULL, (XACapsType)(XACAP_ENCODER|XACAP_VIDEO), ctx->videoEncSettings.encoderId, &temp) == XA_RESULT_SUCCESS) + { + if(temp.adaptId) + { + videoenc = gst_element_factory_make((char*)temp.adaptId, "videoenc"); + } + } + if(videoenc) + { + gst_bin_add(GST_BIN(codecbin), videoenc); + if(!gst_element_link(videoenc, mux)) + { + DEBUG_ERR("Could not link videoenc to mux!!"); + DEBUG_API("<-XAMediaRecorderAdapt_CreateEncodeBin"); + return NULL; + } + ghostvideosink = gst_element_get_static_pad(videoenc,"sink"); + } + else + { + /*no video codec but video source = raw video case, request video pad directly from mux*/ + ghostvideosink = gst_element_get_request_pad(mux,"video_%d"); + } + } + /*set audio defaults*/ + if(ctx->audioEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->audioEncSettings.encoderId = XA_AUDIOCODEC_PCM; + if(ctx->xaAudioSource) + { + if(XACapabilitiesMgr_GetCapsById(NULL, (XACapsType)(XACAP_ENCODER|XACAP_AUDIO), ctx->audioEncSettings.encoderId, &temp) == XA_RESULT_SUCCESS) + { + if(temp.adaptId) + { + audioenc = gst_element_factory_make((char*)temp.adaptId, "audioenc"); + } + } + if(audioenc) + { + gst_bin_add(GST_BIN(codecbin), audioenc); + if(!gst_element_link(audioenc, mux)) + { + DEBUG_ERR("Could not link audioenc to mux!!"); + DEBUG_API("<-XAMediaRecorderAdapt_CreateEncodeBin"); + return NULL; + } + ghostaudiosink = gst_element_get_static_pad(audioenc,"sink"); + } + else + { + /*no audio codec but audio source = PCM case, explicity request audio pad*/ + ghostaudiosink = gst_element_get_request_pad(mux,"audio_%d"); + } + } + break; + + case XA_CONTAINERTYPE_WAV: + DEBUG_INFO("XA_CONTAINERTYPE_WAV"); + audioenc = gst_element_factory_make("wavenc", "audioenc"); + if(audioenc) + { + if (gst_bin_add(GST_BIN(codecbin), audioenc)) + { + DEBUG_INFO("added audioenc to codecbin"); + } + else + { + DEBUG_ERR("Could not add audioenc to codecbin"); + return NULL; + } + /*use this as linkpoint*/ + ghostsrc = gst_element_get_static_pad(audioenc,"src"); + ghostaudiosink = gst_element_get_static_pad(audioenc,"sink"); + if ( ghostsrc == NULL || ghostaudiosink == NULL) + { + DEBUG_ERR("Could not get src or sink ghoset element(s)"); + return NULL; + } + } + /* no other codecs needed */ + break; + case XA_CONTAINERTYPE_JPG: + /*motion jpeg*/ + DEBUG_INFO("XA_CONTAINERTYPE_JPG"); + /*set defaults*/ + if(ctx->videoEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->videoEncSettings.encoderId = XA_ADAPTID_MOTIONJPEG; + if(XACapabilitiesMgr_GetCapsById(NULL, (XACapsType)(XACAP_ENCODER|XACAP_VIDEO), ctx->videoEncSettings.encoderId, &temp) == XA_RESULT_SUCCESS) + { + videoenc = gst_element_factory_make((char*)temp.adaptId, "videoenc"); + } + if(videoenc) + { + gst_bin_add(GST_BIN(codecbin), videoenc); + /*use this as linkpoint*/ + ghostsrc = gst_element_get_static_pad(videoenc,"src"); + ghostvideosink = gst_element_get_static_pad(videoenc,"sink"); + } + /* no other codecs needed */ + break; + case XA_CONTAINERTYPE_RAW: + DEBUG_INFO("XA_CONTAINERTYPE_RAW"); + /* no need for codec, just pass data on */ + if(strncmp((char *)mime->mimeType, "video", 5) == 0 && ctx->xaVideoSource) + { + mux = gst_element_factory_make("identity", "mux"); + gst_bin_add(GST_BIN(codecbin), mux); + ghostvideosink = gst_element_get_static_pad(mux,"sink"); + } + else if (strncmp((char *)mime->mimeType, "audio", 5) == 0 && ctx->xaAudioSource) + { + mux = gst_element_factory_make("identity", "mux"); + gst_bin_add(GST_BIN(codecbin), mux); + ghostaudiosink = gst_element_get_static_pad(mux,"sink"); + } + else + { + ret = XA_RESULT_CONTENT_UNSUPPORTED; + DEBUG_ERR("Content mismatch with given sources!!!") + } + ghostsrc = gst_element_get_static_pad(mux,"src"); + break; + case XA_CONTAINERTYPE_UNSPECIFIED: + DEBUG_INFO("No support for requested encoder...try to select encoder from mime string"); + if(strstr( (char *) mime->mimeType, "/ogg") != 0) + { + DEBUG_INFO("XA_CONTAINERTYPE_UNSPECIFIED - mimetype ogg detected"); + mux = gst_element_factory_make("oggmux", "mux"); + if(mux) + { + gst_bin_add(GST_BIN(codecbin), mux); + /*use this as linkpoint*/ + ghostsrc = gst_element_get_static_pad(mux,"src"); + /*set defaults*/ + if(ctx->audioEncSettings.encoderId == XA_ADAPTID_UNINITED) + { + ctx->audioEncSettings.encoderId = XA_ADAPTID_VORBIS; + ctx->audioEncSettings.bitsPerSample=32; + } + if(ctx->videoEncSettings.encoderId == XA_ADAPTID_UNINITED) + { + ctx->videoEncSettings.encoderId = XA_ADAPTID_THEORA; + } + if(ctx->xaAudioSource) + { + if(XACapabilitiesMgr_GetCapsById(NULL, (XACapsType)(XACAP_ENCODER|XACAP_AUDIO), ctx->audioEncSettings.encoderId, &temp) == XA_RESULT_SUCCESS) + { + audioenc = gst_element_factory_make((char*)temp.adaptId, "audioenc"); + } + if(audioenc) + { + gst_bin_add(GST_BIN(codecbin), audioenc); + if(!gst_element_link(audioenc, mux)) + { + DEBUG_ERR("Could not link audioenc to mux!!"); + DEBUG_API("<-XAMediaRecorderAdapt_CreateEncodeBin"); + return NULL; + } + ghostaudiosink = gst_element_get_static_pad(audioenc,"sink"); + } + } + if(strncmp((char *)mime->mimeType, "video", 5) == 0 && ctx->xaVideoSource) + { + if(XACapabilitiesMgr_GetCapsById(NULL, (XACapsType)(XACAP_ENCODER|XACAP_VIDEO), ctx->videoEncSettings.encoderId, &temp) == XA_RESULT_SUCCESS) + { + videoenc = gst_element_factory_make((char*)temp.adaptId, "videoenc"); + } + if(videoenc) + { + gst_bin_add(GST_BIN(codecbin), videoenc); + if(!gst_element_link(videoenc, mux)) + { + DEBUG_ERR("Could not link videoenc to mux!!"); + DEBUG_API("<-XAMediaRecorderAdapt_CreateEncodeBin"); + return NULL; + } + ghostvideosink = gst_element_get_static_pad(videoenc,"sink"); + } + } + } + } + else + { + DEBUG_INFO("No support for requested mime/container type."); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + } + break; + case XA_CONTAINERTYPE_MOBILE_DLS: + case XA_CONTAINERTYPE_MP4: + DEBUG_INFO("XA_CONTAINERTYPE_MP4"); + mux = gst_element_factory_make("mp4mux", "mp4mux"); + if(mux) + { + if (gst_bin_add(GST_BIN(codecbin), mux)) + { + DEBUG_INFO("Added mux to codecbin"); + } + else + { + DEBUG_ERR("Could not add mux to codecbin"); + return NULL; + } + /*use this as linkpoint*/ + ghostsrc = gst_element_get_static_pad(mux,"src"); + } + /* Add and link audio/video codecs */ + /*set video defaults*/ + if(ctx->videoEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->videoEncSettings.encoderId = XA_ADAPTID_MOTIONJPEG; + if(ctx->xaVideoSource) + { + if(XACapabilitiesMgr_GetCapsById(ctx->baseObj.baseObj.capslist, (XACapsType)(XACAP_ENCODER|XACAP_VIDEO), ctx->videoEncSettings.encoderId, &temp) == XA_RESULT_SUCCESS) + { + if(temp.adaptId) + { + videoenc = gst_element_factory_make((char*)temp.adaptId, "videoenc"); + } + } + if(videoenc) + { + gst_bin_add(GST_BIN(codecbin), videoenc); + if(!gst_element_link(videoenc, mux)) + { + DEBUG_ERR("Could not link videoenc to mux!!"); + DEBUG_API("<-XAMediaRecorderAdapt_CreateEncodeBin"); + return NULL; + } + ghostvideosink = gst_element_get_static_pad(videoenc,"sink"); + } + else + { + /*no video codec but video source = raw video case, request video pad directly from mux*/ + ghostvideosink = gst_element_get_request_pad(mux,"video_%d"); + } + } + /*set audio defaults*/ + if(ctx->audioEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->audioEncSettings.encoderId = XA_AUDIOCODEC_AAC; + if(ctx->xaAudioSource) + { + if(XACapabilitiesMgr_GetCapsById(ctx->baseObj.baseObj.capslist, (XACapsType)(XACAP_ENCODER|XACAP_AUDIO), ctx->audioEncSettings.encoderId, &temp) == XA_RESULT_SUCCESS) + { + if(temp.adaptId) + { + audioenc = gst_element_factory_make((char*)temp.adaptId, "nokiaaacenc"); + } + } + if(audioenc) + { + GstCaps* caps = gst_caps_new_simple((const char*)mime->mimeType, + "mpegversion", G_TYPE_INT, 4, + "channels", G_TYPE_INT, 1, + "rate", G_TYPE_INT, 16000, + NULL); + gst_bin_add(GST_BIN(codecbin), audioenc); + if(!gst_element_link_filtered(audioenc, mux,caps)) + { + DEBUG_ERR("Could not link audioenc to mux!!"); + DEBUG_API("<-XAMediaRecorderAdapt_CreateEncodeBin"); + return NULL; + } + ghostaudiosink = gst_element_get_static_pad(audioenc,"sink"); + } + else + { + /*no audio codec but audio source = PCM case, explicity request audio pad*/ + ghostaudiosink = gst_element_get_request_pad(mux,"audio_%d"); + } + } + break; + case XA_CONTAINERTYPE_AMR: + DEBUG_INFO("XA_CONTAINERTYPE_AMR"); + if(ctx->audioEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->audioEncSettings.encoderId = XA_AUDIOCODEC_AMR; + mux = gst_element_factory_make("amrmux", "mux"); + if(mux) + { + if (gst_bin_add(GST_BIN(codecbin), mux)) + { + DEBUG_INFO("Added mux to codecbin"); + /*Setting the buffer size on src since amr generates + * small amounts of data */ + g_object_set (G_OBJECT (ctx->audiosource), + "blocksize", 1280, + NULL); + } + else + { + DEBUG_ERR("Could not add mux to codecbin"); + return NULL; + } + /*use this as linkpoint*/ + ghostsrc = gst_element_get_static_pad(mux,"src"); + } + /*set audio defaults*/ + if(ctx->xaAudioSource) + { + /*no audio codec but audio source = PCM case, explicity request audio pad*/ + ghostaudiosink = gst_element_get_static_pad(mux,"sink"); + } + break; + case XA_CONTAINERTYPE_3GPP: + case XA_CONTAINERTYPE_BMP: + case XA_CONTAINERTYPE_ASF: + case XA_CONTAINERTYPE_M4A: + case XA_CONTAINERTYPE_MP3: + case XA_CONTAINERTYPE_JPG2000: + case XA_CONTAINERTYPE_MPEG_ES: + case XA_CONTAINERTYPE_MPEG_PS: + case XA_CONTAINERTYPE_MPEG_TS: + case XA_CONTAINERTYPE_QT: + case XA_CONTAINERTYPE_XMF_0: + case XA_CONTAINERTYPE_XMF_1: + case XA_CONTAINERTYPE_XMF_2: + case XA_CONTAINERTYPE_XMF_3: + case XA_CONTAINERTYPE_XMF_GENERIC: + case XA_CONTAINERTYPE_AAC: + case XA_CONTAINERTYPE_3GA: + case XA_CONTAINERTYPE_RM: + case XA_CONTAINERTYPE_DMF: + default: + DEBUG_INFO("No support for requested container type."); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + break; + } + break; + } + default: + DEBUG_ERR("Incorrect data format type."); + ret = XA_RESULT_PARAMETER_INVALID; + break; + } + } + else + { + DEBUG_ERR("Invalid data sink for stream recording!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + } + else + {/* stream recording not requested, datasink ignored, use uncoded recordstream*/ + mux = gst_element_factory_make("identity", "mux"); + gst_bin_add(GST_BIN(codecbin), mux); + ghostsrc = gst_element_get_static_pad(mux,"src"); + ghostvideosink = gst_element_get_static_pad(mux,"sink"); + } + + /*set default codecs for unrecognized*/ + if(ctx->audioEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->audioEncSettings.encoderId = XA_AUDIOCODEC_PCM; + if(ctx->imageEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->imageEncSettings.encoderId = XA_IMAGECODEC_RAW; + if(ctx->videoEncSettings.encoderId == XA_ADAPTID_UNINITED) + ctx->videoEncSettings.encoderId = XA_ADAPTID_RAWVIDEO; + + if ( ret != XA_RESULT_SUCCESS ) + { + gst_object_unref(codecbin); + codecbin=NULL; + } + else + { + /*add ghost pad(s) to link to*/ + if(ghostsrc) + { + gst_element_add_pad(codecbin, gst_ghost_pad_new("src",ghostsrc)); + gst_object_unref(GST_OBJECT(ghostsrc)); + } + if(ghostaudiosink) + { + gst_element_add_pad(codecbin, gst_ghost_pad_new("sink",ghostaudiosink)); + gst_object_unref(GST_OBJECT(ghostaudiosink)); + } + if(ghostvideosink) + { + gst_element_add_pad(codecbin, gst_ghost_pad_new("v_sink",ghostvideosink)); + gst_object_unref(GST_OBJECT(ghostvideosink)); + } + DEBUG_INFO_A1("Created encoder bin at %x", (int)codecbin); + } + + DEBUG_API("<-XAMediaRecorderAdapt_CreateEncodeBin"); + return codecbin; + + } + +/* + * void XAMediaRecorderAdapt_BufferAvailable(GstElement* sink, gpointer user_data) + * called when new buffer is available at appsink + */ +void XAMediaRecorderAdapt_BufferAvailable(GstElement* sink, gpointer user_data) + { + GstBuffer *buffer=NULL; + XAMediaRecorderAdaptationCtx* mCtx = (XAMediaRecorderAdaptationCtx*)user_data; + DEBUG_API("->XAMediaRecorderAdapt_BufferAvailable"); + if(!mCtx || !mCtx->xaSink) + { + DEBUG_ERR("Invalid context") + return; + } + /* get the buffer */ + buffer = gst_app_sink_pull_buffer(GST_APP_SINK(sink)); + if(buffer) + { + guint size; + XADataLocator_Address* address; + size = GST_BUFFER_SIZE(buffer); + DEBUG_INFO_A1("Pulled new buffer of size %d", size); + address = (XADataLocator_Address*)(mCtx->xaSink->pLocator); + if( !address || *(XAuint32*)address != XA_DATALOCATOR_ADDRESS ) + { + DEBUG_ERR("Invalid address datalocator") + return; + } + + if(mCtx->writepos + size < address->length ) + { /*enough room in buffer*/ + memcpy(((char*)(address->pAddress) + mCtx->writepos), + GST_BUFFER_DATA (buffer), size); + mCtx->writepos+=size; + } + else + { /*not enough room in buffer*/ + XAAdaptEvent event = + {XA_RECORDITFEVENTS, XA_RECORDEVENT_BUFFER_FULL, 0, NULL}; + + size = address->length - mCtx->writepos; + memcpy(((char*)(address->pAddress) + mCtx->writepos), + GST_BUFFER_DATA (buffer), size); + DEBUG_INFO_A1("Buffer insufficient, wrote %d bytes", size); + /* send event */ + XAAdaptationBase_SendAdaptEvents(&(mCtx->baseObj.baseObj), &event); + /* "recordhead to start" i.e. reset write position */ + mCtx->writepos=0; + mCtx->recThrCtx.buffer_insufficient = XA_BOOLEAN_TRUE; + if ( XAImpl_PostSemaphore( mCtx->recThrCtx.bufInsufficientSem ) != XA_RESULT_SUCCESS) + { + DEBUG_ERR("Posting buffer-insufficien semaphore FAILED!"); + } + } + gst_buffer_unref (buffer); + } + else + { + DEBUG_ERR("Could not pull buffer from appsink!"); + } + DEBUG_API("<-XAMediaRecorderAdapt_BufferAvailable"); + } + +/* + * void* XAMediaRecorderAdapt_RecordEventThr( void* ctx ) + */ +void* XAMediaRecorderAdapt_RecordEventThr( void* ctx ) + { + XAMediaRecorderAdaptationCtx* mrCtx = (XAMediaRecorderAdaptationCtx*)ctx; + GstStateChangeReturn gret; + XAresult ret; + DEBUG_API("->XAMediaRecorderAdapt_RecordEventThr"); + + /* Wait semaphore here */ + ret = XAImpl_WaitSemaphore( mrCtx->recThrCtx.bufInsufficientSem ); + if ( ret != XA_RESULT_SUCCESS) + { + DEBUG_ERR("Could not start semaphore"); + } + + if(mrCtx->recThrCtx.buffer_insufficient) + { + mrCtx->baseObj.binWantedState = GST_STATE_PAUSED; + XAAdaptationGst_PrepareAsyncWait(&(mrCtx->baseObj)); + gret = gst_element_set_state( GST_ELEMENT(mrCtx->baseObj.bin), mrCtx->baseObj.binWantedState); + if( gret == GST_STATE_CHANGE_ASYNC ) + { + DEBUG_INFO("Start to wait recoder state change."); + XAAdaptationGst_StartAsyncWait(&(mrCtx->baseObj)); + DEBUG_INFO("Recorder state change async. SUCCESFULL."); + } + else if( gret == GST_STATE_CHANGE_FAILURE ) + { + DEBUG_INFO("Recorder state change FAILED"); + /*ret = XA_RESULT_INTERNAL_ERROR;*/ + } + else + { + DEBUG_INFO("Recorder state change SUCCESFULL") + } + + mrCtx->baseObj.waitingasyncop= XA_BOOLEAN_FALSE; + } + DEBUG_API("<-XAMediaRecorderAdapt_RecordEventThr"); + return NULL; + } + +XAresult XAMediaRecorderAdapt_CreateCapsFilter( XAMediaRecorderAdaptationCtx* ctx ) +{ + + GstCaps* encSrcCaps = NULL; + XADataFormat_MIME *pMime = 0; + + if (!ctx ) + return XA_RESULT_PARAMETER_INVALID; + + DEBUG_INFO("create capsfilter"); + ctx->audiofilter = gst_element_factory_make("capsfilter", "audiofilter"); + if( !ctx->audiofilter ) + { + return XA_RESULT_INTERNAL_ERROR; + } + // GstCaps* encSrcCaps; TL + if (gst_bin_add(GST_BIN(ctx->baseObj.bin), ctx->audiofilter)) + { + DEBUG_INFO("Added audiofilter to bin"); + } + else + { + DEBUG_ERR("Could not add audio filter to bin"); + return XA_RESULT_INTERNAL_ERROR; + } + + pMime = (XADataFormat_MIME*) ctx->xaSink->pFormat; + if(!strcmp((const char*)pMime->mimeType, "audio/amr")) + { + encSrcCaps = gst_caps_new_simple ("audio/amr", + "width", G_TYPE_INT, 8, + "depth", G_TYPE_INT, 8, + "signed",G_TYPE_BOOLEAN, TRUE, + "endianness",G_TYPE_INT, G_BYTE_ORDER, + "rate", G_TYPE_INT, 8000, + "channels", G_TYPE_INT, 1, NULL); + } + else + { + encSrcCaps = gst_caps_new_full( + gst_structure_new("audio/x-raw-int", + "width", G_TYPE_INT, 16, + "depth", G_TYPE_INT, 16, + "signed",G_TYPE_BOOLEAN, 1, + "endianness",G_TYPE_INT, 1234, + "rate", G_TYPE_INT, 16000, + "bitrate", G_TYPE_INT, ctx->audioEncSettings.bitRate, + "channels", G_TYPE_INT, 1, NULL), + /*gst_structure_new("audio/x-raw-int", + "channels", G_TYPE_INT, ctx->audioEncSettings.channelsOut, + "rate", G_TYPE_INT, ctx->audioEncSettings.sampleRate, + "bitrate", G_TYPE_INT, ctx->audioEncSettings.bitRate, + NULL),*/ + gst_structure_new("audio/x-raw-float", + "channels", G_TYPE_INT, ctx->audioEncSettings.channelsOut, + "width", G_TYPE_INT, ctx->audioEncSettings.bitsPerSample, + "rate", G_TYPE_INT, ctx->audioEncSettings.sampleRate, + "bitrate", G_TYPE_INT, ctx->audioEncSettings.bitRate, + NULL), + NULL); + + } + + + DEBUG_INFO_A1("audio encoder config from settings: %s",gst_caps_to_string(encSrcCaps)); + g_object_set( G_OBJECT(ctx->audiofilter), "caps",encSrcCaps,NULL); + + gst_caps_unref(encSrcCaps); + return XA_RESULT_SUCCESS; +} +