diff -r 71ca22bcf22a -r 5a06f39ad45b khronosfws/openmax_al/src/adaptation/xametadataadaptation.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/khronosfws/openmax_al/src/adaptation/xametadataadaptation.c Fri Apr 16 15:29:42 2010 +0300 @@ -0,0 +1,1202 @@ +/* +* 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 "XAAdaptation.h" +#include "XAMetadataAdaptCtx.h" +#include "XAMediaPlayerAdaptCtx.h" +#include "XAMediaRecorderAdaptCtx.h" +#include "XAMetadataAdaptation.h" + +/* forward declarations of internal methods */ +XAMetadataAdaptVars* XAMetadataAdapt_GetMetadataVars(XAAdaptationBaseCtx *bCtx); +GstStructure* XAMetadataAdapt_GetChildStructure(XAMetadataAdaptVars* mdv, XAuint32 index); +gboolean XAMetadataAdapt_CountTags(GQuark gkey, const GValue *gvalue, gpointer counter); +void XAMetadataAdapt_ParseTag(const GstTagList* list, const gchar* tag, gpointer listptr); +gboolean XAMetadataAdapt_ParseItem(GQuark field_id, const GValue * value, gpointer listptr); +gboolean XAMetadataAdapt_GstTagCb( GstBus *bus, GstMessage *message, gpointer data ); + +/*NOTE: no way to query these dynamically?*/ +/*supported keys for metadata insertion*/ +static char* xaMetadataAdaptSupportedKeys[] = { + KHRONOS_TITLE, + KHRONOS_ALBUM, + KHRONOS_TRACK_NUMBER, + KHRONOS_ARTIST, + KHRONOS_GENRE, + KHRONOS_COMMENT, + KHRONOS_COPYRIGHT, + GST_TAG_TITLE, + GST_TAG_TITLE_SORTNAME, + GST_TAG_ARTIST, + GST_TAG_ARTIST_SORTNAME, + GST_TAG_ALBUM, + GST_TAG_ALBUM_SORTNAME, + GST_TAG_COMPOSER, + GST_TAG_DATE, + GST_TAG_GENRE, + GST_TAG_COMMENT, + GST_TAG_EXTENDED_COMMENT, + GST_TAG_TRACK_NUMBER, + GST_TAG_TRACK_COUNT, + GST_TAG_ALBUM_VOLUME_NUMBER, + GST_TAG_ALBUM_VOLUME_COUNT, + GST_TAG_LOCATION, + GST_TAG_DESCRIPTION, + GST_TAG_VERSION, + GST_TAG_ISRC, + GST_TAG_ORGANIZATION, + GST_TAG_COPYRIGHT, + GST_TAG_COPYRIGHT_URI, + GST_TAG_CONTACT, + GST_TAG_LICENSE, + GST_TAG_LICENSE_URI, + GST_TAG_PERFORMER, + NULL +}; + +/****************************************************************************** + * COMMONS + ******************************************************************************/ + + +/* const char* XAMetadataAdapt_ParseKhronosKey(const char* pKKey) + * Translates Khronos key string to adaptation-specific key string + * @return Translated key string + */ +const XAchar* XAMetadataAdapt_ParseKhronosKey(const XAchar* pKKey) +{ + const XAchar* newKey; + DEBUG_API_A1("->XAMetadataAdapt_ParseKhronosKey: \"%s\"", (char*)pKKey); + if( strcmp((char*)pKKey,KHRONOS_TITLE) ==0 ) newKey = (XAchar*)GST_TAG_TITLE; + else if( strcmp((char*)pKKey,KHRONOS_ALBUM) ==0 ) newKey = (XAchar*)GST_TAG_ALBUM; + else if( strcmp((char*)pKKey,KHRONOS_TRACK_NUMBER) ==0 ) newKey = (XAchar*)GST_TAG_TRACK_NUMBER; + else if( strcmp((char*)pKKey,KHRONOS_ARTIST) ==0 ) newKey = (XAchar*)GST_TAG_ARTIST; + else if( strcmp((char*)pKKey,KHRONOS_GENRE) ==0 ) newKey = (XAchar*)GST_TAG_GENRE; + else if( strcmp((char*)pKKey,KHRONOS_COMMENT) ==0 ) newKey = (XAchar*)GST_TAG_COMMENT; + else if( strcmp((char*)pKKey,KHRONOS_COPYRIGHT) ==0 ) newKey = (XAchar*)GST_TAG_COPYRIGHT; + else newKey = pKKey; +/* No Gst keys for following: + else if( strcmp(pKKey,KHRONOS_YEAR) ==0 ) newKey = ; + else if( strcmp(pKKey,KHRONOS_ARTIST_URL) ==0 ) newKey = ; + else if( strcmp(pKKey,KHRONOS_CONTENT_URL) ==0 ) newKey = ; + else if( strcmp(pKKey,KHRONOS_RATING) ==0 ) newKey = ; + else if( strcmp(pKKey,KHRONOS_ALBUM_ART) ==0 ) newKey = ; + */ + DEBUG_API_A1("<-XAMetadataAdapt_ParseKhronosKey: => \"%s\"", (char*)newKey); + return newKey; +} + +/* XAresult XAMetadataAdapt_PreInit(XAAdaptationBaseCtx *bCtx) + * Initialize values needed before preroll + * @return XAresult ret - Success value + */ +XAresult XAMetadataAdapt_PreInit(XAAdaptationBaseCtx *bCtx) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv; + DEBUG_API("->XAMetadataAdapt_PreInit"); + if( bCtx->ctxId == XAMDAdaptation || + bCtx->ctxId == XAMediaPlayerAdaptation ) + { + mdv = calloc(1, sizeof(XAMetadataAdaptVars)); + mdv->currentchild = &(mdv->generaltags); + mdv->traversemode=XA_METADATATRAVERSALMODE_NODE; + ((XAMediaPlayerAdaptationCtx*)bCtx)->metadatavars = mdv; + + } + else if ( bCtx->ctxId == XAMediaRecorderAdaptation ) + { + mdv = calloc(1, sizeof(XAMetadataAdaptVars)); + mdv->currentchild = &(mdv->generaltags); + mdv->traversemode=XA_METADATATRAVERSALMODE_NODE; + ((XAMediaRecorderAdaptationCtx*)bCtx)->metadatavars = mdv; + } + else + { + DEBUG_ERR("Unsupported metadata context!!"); + ret = XA_RESULT_FEATURE_UNSUPPORTED; + } + + if(ret == XA_RESULT_SUCCESS) + { + if(bCtx->bus) + { + g_signal_connect(bCtx->bus, "message::tag", G_CALLBACK(XAMetadataAdapt_GstTagCb), bCtx ); + } + else + { + DEBUG_ERR("No bus in context!!"); + ret = XA_RESULT_PRECONDITIONS_VIOLATED; + } + } + DEBUG_API_A1("<-XAMetadataAdapt_PreInit (%d)", (int)ret); + return ret; +} +/* XAresult XAMetadataAdapt_PostInit(XAAdaptationBaseCtx *bCtx) + * Initialize values after preroll + * @return XAresult ret - Success value + */ +XAresult XAMetadataAdapt_PostInit(XAAdaptationBaseCtx *bCtx) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + XAMediaPlayerAdaptationCtx* mCtx=NULL; + GstPad* tmppad=NULL; + GstCaps* tmpcaps=NULL; + GstElement* tmpelement=NULL; + XAAdaptEvent event = {XA_METADATAEVENTS, XA_ADAPT_MDE_TAGS_AVAILABLE, 0, NULL }; + + DEBUG_API("->XAMetadataAdapt_PostInit"); + if( bCtx->ctxId == XAMDAdaptation || + bCtx->ctxId == XAMediaPlayerAdaptation ) + { + mdv = XAMetadataAdapt_GetMetadataVars(bCtx); + if( mdv ) + { + mdv->childcount = 0; + /* try to dig out audio and video pads from decodebin for stream info tags */ + /* NOTE: currently no good deterministic way to dig out undecoded pads + * from decodebin's internal demuxer, this is just ugly way to try to cope + * with most demuxers. + */ + mCtx = ((XAMediaPlayerAdaptationCtx*)bCtx); + if(GST_IS_BIN(mCtx->codecbin)) + { + tmpelement=gst_bin_get_by_name(GST_BIN(mCtx->codecbin),"typefind"); + if(tmpelement) + { + tmppad = gst_element_get_static_pad(GST_ELEMENT(tmpelement),"src"); + tmpelement=NULL; + if(tmppad) + { + tmppad = gst_pad_get_peer(tmppad); + if(tmppad) + { + tmpelement = gst_pad_get_parent_element(tmppad); + } + } + /* now we have demuxer, if existing */ + if(tmpelement) + { + tmppad = gst_element_get_pad( tmpelement, "audio_00"); + if(tmppad) + { + tmpcaps = gst_pad_get_negotiated_caps( GST_PAD(tmppad) ); + if(tmpcaps==NULL || gst_caps_is_any(tmpcaps) || gst_caps_is_empty(tmpcaps)) + { + mdv->audiotags = NULL; + DEBUG_INFO("no usable audio properties found from pad !"); + } + else + { + mdv->audiotags = gst_caps_get_structure(tmpcaps,0); + mdv->childcount++; + DEBUG_INFO_A1("found audio node: %s",gst_caps_to_string(tmpcaps)); + } + } + tmppad = gst_element_get_pad( tmpelement, "video_00"); + if(tmppad) + { + tmpcaps = gst_pad_get_negotiated_caps( GST_PAD(tmppad) ); + if(tmpcaps==NULL || gst_caps_is_any(tmpcaps) || gst_caps_is_empty(tmpcaps)) + { + mdv->videotags = NULL; + DEBUG_INFO("no usable video properties found from pad !"); + } + else + { + mdv->videotags = gst_caps_get_structure(tmpcaps,0); + mdv->childcount++; + DEBUG_INFO_A1("found video node: %s",gst_caps_to_string(tmpcaps)); + } + } + } + } + } + XAAdaptationBase_SendAdaptEvents(bCtx, &event ); + } + } + + if ( tmpelement ) + { + gst_object_unref( tmpelement ); + } + if ( tmppad ) + { + gst_object_unref( tmppad ); + } + if ( tmpcaps ) + { + gst_object_unref( tmpcaps ); + } + + DEBUG_API_A1("<-XAMetadataAdapt_PostInit (%d)", (int)ret); + return ret; +} + +/* + * void XAMetadataAdapt_FreeImplTagList(XAMetadataImplTagList *list, XAboolean freeItems) + * deallocate elements inside taglist + */ +void XAMetadataAdapt_FreeImplTagList(XAMetadataImplTagList *list, XAboolean freeItems) +{ + XAuint16 i; + DEBUG_API("->XAMetadataExtractionItfAdapt_FreeTagList"); + if(list->mdeKeys) + { + for(i=0;iitemcount;i++) + { + if(list->mdeKeys[i]) + { + if(freeItems) + free(list->mdeKeys[i]); + list->mdeKeys[i]=NULL; + } + } + free(list->mdeKeys); + list->mdeKeys = NULL; + } + if(list->mdeValues) + { + for(i=0;iitemcount;i++) + { + if(list->mdeValues[i]) + { + if(freeItems) + free(list->mdeValues[i]); + list->mdeValues[i]=NULL; + } + } + free(list->mdeValues); + list->mdeValues = NULL; + } + list->itemcount = 0; + DEBUG_API("<-XAMetadataExtractionItfAdapt_FreeTagList"); +} + +/* + * void XAMetadataAdapt_FreeVars(XAMetadataAdaptVars *vars) + * deallocate XAMetadataAdaptVars + */ +void XAMetadataAdapt_FreeVars(XAMetadataAdaptVars *vars) +{ + DEBUG_API("->XAMetadataAdapt_FreeVars"); + if(vars) + { + if( vars->generaltags ) + { + gst_tag_list_free(vars->generaltags); + } + if( vars->audiotags ) + { + gst_structure_set_parent_refcount(vars->audiotags,NULL); + gst_structure_free(vars->audiotags); + } + if( vars->videotags ) + { + gst_structure_set_parent_refcount(vars->videotags,NULL); + gst_structure_free(vars->videotags); + } + free(vars); + } + DEBUG_API("<-XAMetadataAdapt_FreeVars"); +} + +/****************************************************************************** + * EXTRACTION + ******************************************************************************/ + +/* + * XAresult XAMetadataExtractionItfAdapt_FillTagList() + * @param tagList - contains pointer to tag list to be updated + * @return XAresult ret - Success value + */ +XAresult XAMetadataExtractionItfAdapt_FillTagList(XAAdaptationBaseCtx *bCtx, + XAMetadataImplTagList* tagList) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv; + XAuint32 newcount = 0; + DEBUG_API("->XAMetadataExtractionItfAdapt_FillTagList"); + + mdv = XAMetadataAdapt_GetMetadataVars(bCtx); + if( mdv ) + { + XAMetadataAdapt_FreeImplTagList(tagList, XA_BOOLEAN_TRUE); + /* get number of tags and allocate memory for them */ + if(mdv->traversemode==XA_METADATATRAVERSALMODE_ALL) + { + if(mdv->generaltags) + gst_structure_foreach(mdv->generaltags, XAMetadataAdapt_CountTags, &newcount); + if(mdv->audiotags) + gst_structure_foreach(mdv->audiotags, XAMetadataAdapt_CountTags, &newcount); + if(mdv->videotags) + gst_structure_foreach(mdv->videotags, XAMetadataAdapt_CountTags, &newcount); + } + else + { + if(mdv->currentchild && *(mdv->currentchild)) + gst_structure_foreach(*(mdv->currentchild), XAMetadataAdapt_CountTags, &newcount); + } + DEBUG_INFO_A1("tag count = %d",(int)newcount) + tagList->mdeKeys = calloc(newcount,sizeof(XAMetadataInfo*)); + tagList->mdeValues = calloc(newcount,sizeof(XAMetadataInfo*)); + + /* fill in given tag list */ + tagList->itemcount = 0; + if(mdv->traversemode==XA_METADATATRAVERSALMODE_ALL) + { + if(mdv->generaltags) + gst_tag_list_foreach(mdv->generaltags, XAMetadataAdapt_ParseTag, tagList); + if(mdv->audiotags) + gst_structure_foreach(mdv->audiotags, XAMetadataAdapt_ParseItem, tagList); + if(mdv->videotags) + gst_structure_foreach(mdv->videotags, XAMetadataAdapt_ParseItem, tagList); + } + else + { + if(mdv->currentchild && *(mdv->currentchild)) + { + if(GST_IS_TAG_LIST(*(mdv->currentchild))) + { + gst_tag_list_foreach(*(mdv->currentchild), XAMetadataAdapt_ParseTag, tagList); + } + else + { + gst_structure_foreach(*(mdv->currentchild), XAMetadataAdapt_ParseItem, tagList); + } + } + } + } + else + { + DEBUG_ERR("Metadata variables not found!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + + DEBUG_API_A1("<-XAMetadataExtractionItfAdapt_FillTagList (%d)", (int)ret); + return ret; +} + +/****************************************************************************** + * TRAVERSAL + *****************************************************************************/ + +/* + * XAresult XAMetadataTraversalItfAdapt_SetMode(XAAdaptationBaseCtx *bCtx, XAuint32 mode) + */ +XAresult XAMetadataTraversalItfAdapt_SetMode(XAAdaptationBaseCtx *bCtx, XAuint32 mode) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + XAAdaptEvent event = {XA_METADATAEVENTS, XA_ADAPT_MDE_TAGS_AVAILABLE, 0, NULL }; + + DEBUG_API("->XAMetadataTraversalItfAdapt_SetMode"); + mdv = XAMetadataAdapt_GetMetadataVars(bCtx); + if( mdv ) + { + mdv->traversemode = mode; + /* inform extractor to update tag list */ + XAAdaptationBase_SendAdaptEvents(bCtx, &event ); + if(mdv->traversemode==XA_METADATATRAVERSALMODE_ALL) + { + mdv->nodedepth = 0; + mdv->currentchild = &(mdv->generaltags); + } + } + else + { + DEBUG_ERR("Metadata variables not found!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + + DEBUG_API("<-XAMetadataTraversalItfAdapt_SetMode"); + return ret; +} + +/* + * XAresult XAMetadataTraversalItfAdapt_GetChildCount(XAAdaptationBaseCtx *bCtx, XAuint32 *pCount) + */ +XAresult XAMetadataTraversalItfAdapt_GetChildCount(XAAdaptationBaseCtx *bCtx, XAuint32 *pCount) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + + DEBUG_API("->XAMetadataTraversalItfAdapt_GetChildCount"); + mdv = XAMetadataAdapt_GetMetadataVars(bCtx); + if( mdv ) + { + /* only depth of 1 is supported */ + if( mdv->nodedepth == 0 ) + { + *pCount=mdv->childcount; + } + else + { + *pCount=0; + } + } + else + { + DEBUG_ERR("Metadata variables not found!!"); + ret = XA_RESULT_PARAMETER_INVALID; + *pCount=0; + } + DEBUG_API_A2("<-XAMetadataTraversalItfAdapt_GetChildCount(%d) count=%d", (int)ret, (int)*pCount); + return ret; +} + +/* + * XAresult XAMetadataTraversalItfAdapt_GetChildMIMETypeSize(XAAdaptationBaseCtx *bCtx, + * XAuint32 index, + * XAuint32 *pSize) + */ +XAresult XAMetadataTraversalItfAdapt_GetChildMIMETypeSize(XAAdaptationBaseCtx *bCtx, + XAuint32 index, + XAuint32 *pSize) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + + DEBUG_API("->XAMetadataTraversalItfAdapt_GetChildMIMETypeSize"); + mdv = XAMetadataAdapt_GetMetadataVars(bCtx); + if( mdv ) + { + GstStructure* child = XAMetadataAdapt_GetChildStructure(mdv,index); + if(child) + { + *pSize = strlen(gst_structure_get_name(child)); + } + } + else + { + DEBUG_ERR("Metadata variables not found!!"); + ret = XA_RESULT_PARAMETER_INVALID; + *pSize=0; + } + DEBUG_API("<-XAMetadataTraversalItfAdapt_GetChildMIMETypeSize"); + return ret; +} + +/* + * XAresult XAMetadataTraversalItfAdapt_GetChildInfo(XAAdaptationBaseCtx *bCtx, + * XAuint32 index, + * XAint32 *pNodeID, + * XAuint32 *pType, + * XAuint32 size, + * XAchar *pMimeType) + */ +XAresult XAMetadataTraversalItfAdapt_GetChildInfo(XAAdaptationBaseCtx *bCtx, + XAuint32 index, + XAint32 *pNodeID, + XAuint32 *pType, + XAuint32 size, + XAchar *pMimeType) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + const gchar* mime; + GstStructure* child; + + DEBUG_API("->XAMetadataTraversalItfAdapt_GetChildInfo"); + mdv = XAMetadataAdapt_GetMetadataVars(bCtx); + if( mdv ) + { + child = XAMetadataAdapt_GetChildStructure(mdv,index); + if(child) + { + *pNodeID = (XAint32)child; + mime = gst_structure_get_name(child); + if(strncmp(mime,"audio",5)==0) + { + *pType = XA_NODETYPE_AUDIO; + } + if(strncmp(mime,"video",5)==0) + { + *pType = XA_NODETYPE_VIDEO; + } + if(strncmp(mime,"image",5)==0) + { + *pType = XA_NODETYPE_IMAGE; + } + else + { + *pType = XA_NODETYPE_UNSPECIFIED; + } + strncpy((char*)pMimeType,mime,size); + } + } + else + { + DEBUG_ERR("Metadata variables not found!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + DEBUG_API("<-XAMetadataTraversalItfAdapt_GetChildInfo"); + return ret; +} + +/* + * XAresult XAMetadataTraversalItfAdapt_SetActiveNode(XAAdaptationBaseCtx *bCtx, + * XAuint32 index) + */ +XAresult XAMetadataTraversalItfAdapt_SetActiveNode(XAAdaptationBaseCtx *bCtx, + XAuint32 index) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + GstStructure* child; + + DEBUG_API("->XAMetadataTraversalItfAdapt_SetActiveNode"); + mdv = XAMetadataAdapt_GetMetadataVars(bCtx); + if( mdv ) + { + child = XAMetadataAdapt_GetChildStructure(mdv,index); + if(child) + { + mdv->currentchild = &child; + if(index==XA_NODE_PARENT) mdv->nodedepth--; + else mdv->nodedepth++; + } + } + else + { + DEBUG_ERR("Metadata variables not found!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + DEBUG_API("<-XAMetadataTraversalItfAdapt_SetActiveNode"); + return ret; +} + +/***************************************************************************** + * INSERTION + *****************************************************************************/ + +/* + * XAresult XAMetadataInsertionItfAdapt_CreateChildNode(XAAdaptationBaseCtx *bCtx, + * XAint32 parentNodeID, + * XAuint32 type, + * XAchar *mimeType, + * XAint32 *pChildNodeID) + */ +XAresult XAMetadataInsertionItfAdapt_CreateChildNode(XAAdaptationBaseCtx *bCtx, + XAint32 parentNodeID, + XAuint32 type, + XAchar *mimeType, + XAint32 *pChildNodeID) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + XAMediaRecorderAdaptationCtx* mCtx; + + DEBUG_API("->XAMetadataInsertionItfAdapt_CreateChildNode"); + if(bCtx && bCtx->ctxId == XAMediaRecorderAdaptation) + { + /* no support for more than 1 levels of childs*/ + if(parentNodeID==XA_ROOT_NODE_ID) + { + XAMediaType mediaType; + mCtx = (XAMediaRecorderAdaptationCtx*)bCtx; + + /* first check sink type */ + XACommon_CheckDataSource((XADataSource*)(mCtx->xaSink),&mediaType); + if( ((mediaType==XA_MEDIATYPE_AUDIO||mediaType==XA_MEDIATYPE_VIDEO) && type==XA_NODETYPE_IMAGE) || + (mediaType==XA_MEDIATYPE_IMAGE && (type==XA_NODETYPE_AUDIO || type==XA_NODETYPE_VIDEO)) ) + { + DEBUG_ERR("Nodetype not supported!"); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + DEBUG_API_A1("<-XAMetadataInsertionItfAdapt_CreateChildNode (%d)", (int)ret); + return ret; } + + mdv = mCtx->metadatavars; + if(!mdv) + { + DEBUG_ERR("Metadata variables not initialized!"); + ret = XA_RESULT_PRECONDITIONS_VIOLATED; + } + switch(type) + { + case XA_NODETYPE_IMAGE: + /*fall-through to use video pipe tags for image*/ + case XA_NODETYPE_VIDEO: + if(!mCtx->videosource) + { + DEBUG_ERR("Nodetype not found!"); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + } + else + { + if(!mdv->videotags) + { + if(strlen((char*)mimeType)==0) + { + mdv->videotags = gst_structure_empty_new("video/unknown"); + } + else + { + mdv->videotags = gst_structure_empty_new((gchar*)mimeType); + } + } + *pChildNodeID = (XAint32)mdv->videotags; + } + break; + case XA_NODETYPE_AUDIO: + if(!mCtx->audiosource) + { + DEBUG_ERR("Nodetype not found!"); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + } + else + { + if(!mdv->audiotags) + { + if(strlen((char*)mimeType)==0) + { + mdv->audiotags = gst_structure_empty_new("audio/unknown"); + } + else + { + mdv->audiotags = gst_structure_empty_new((gchar*)mimeType); + } + } + *pChildNodeID = (XAint32)mdv->audiotags; + } + break; + default: + DEBUG_ERR("Nodetype not found!"); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + break; + } + } + else + { + DEBUG_ERR("Only root childs supported!!"); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + } + } + else + { + DEBUG_ERR("Unsupported metadata insertion context!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + DEBUG_API_A1("<-XAMetadataInsertionItfAdapt_CreateChildNode (%d)", (int)ret); + return ret; +} + +/* + * XAresult XAMetadataInsertionItfAdapt_GetSupportedKeysCount(XAAdaptationBaseCtx *bCtx, + * XAint32 nodeID, + * XAboolean *pFreeKeys, + * XAuint32 *pKeyCount, + * XAuint32 *pEncodingCount) + */ +XAresult XAMetadataInsertionItfAdapt_GetSupportedKeysCount(XAAdaptationBaseCtx *bCtx, + XAint32 nodeID, + XAboolean *pFreeKeys, + XAuint32 *pKeyCount, + XAuint32 *pEncodingCount) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMediaRecorderAdaptationCtx* mCtx; + + DEBUG_API("->XAMetadataInsertionItfAdapt_GetSupportedKeysCount"); + if(bCtx && bCtx->ctxId == XAMediaRecorderAdaptation) + { + mCtx = (XAMediaRecorderAdaptationCtx*)bCtx; + *pKeyCount = 0; + *pFreeKeys = XA_BOOLEAN_FALSE; + while(xaMetadataAdaptSupportedKeys[*pKeyCount]) + { + (*pKeyCount)++; + } + *pEncodingCount = 1; + } + else + { + DEBUG_ERR("Unsupported metadata insertion context!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + DEBUG_API("<-XAMetadataInsertionItfAdapt_GetSupportedKeysCount"); + return ret; +} + +/* + * XAresult XAMetadataInsertionItfAdapt_GetKeySize(XAAdaptationBaseCtx *bCtx, + * XAint32 nodeID, + * XAuint32 keyIndex, + * XAuint32 *pKeySize) + */ +XAresult XAMetadataInsertionItfAdapt_GetKeySize(XAAdaptationBaseCtx *bCtx, + XAint32 nodeID, + XAuint32 keyIndex, + XAuint32 *pKeySize) +{ + XAresult ret = XA_RESULT_SUCCESS; + + DEBUG_API("->XAMetadataInsertionItfAdapt_GetKeySize"); + if(bCtx && bCtx->ctxId == XAMediaRecorderAdaptation) + { + if(xaMetadataAdaptSupportedKeys[keyIndex]) + { + *pKeySize = sizeof(XAMetadataInfo)+strlen(xaMetadataAdaptSupportedKeys[keyIndex]); + } + else + { + ret = XA_RESULT_PARAMETER_INVALID; + } + } + else + { + DEBUG_ERR("Unsupported metadata insertion context!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + DEBUG_API("<-XAMetadataInsertionItfAdapt_GetKeySize"); + return ret; +} + +/* + * XAresult XAMetadataInsertionItfAdapt_GetKey(XAAdaptationBaseCtx *bCtx, + * XAint32 nodeID, + * XAuint32 keyIndex, + * XAuint32 keySize, + * XAMetadataInfo *pKey) + */ +XAresult XAMetadataInsertionItfAdapt_GetKey(XAAdaptationBaseCtx *bCtx, + XAint32 nodeID, + XAuint32 keyIndex, + XAuint32 keySize, + XAMetadataInfo *pKey) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAuint32 datasize, neededsize; + + DEBUG_API("->XAMetadataInsertionItfAdapt_GetKey"); + if(bCtx && bCtx->ctxId == XAMediaRecorderAdaptation) + { + memset(pKey,0,keySize); + if(xaMetadataAdaptSupportedKeys[keyIndex]) + { + neededsize = strlen(xaMetadataAdaptSupportedKeys[keyIndex])+1; + datasize = keySize-sizeof(XAMetadataInfo)+1; + if(neededsize>datasize) + { + strncpy((char*)pKey->data,xaMetadataAdaptSupportedKeys[keyIndex],datasize); + pKey->size=datasize; + ret=XA_RESULT_BUFFER_INSUFFICIENT; + } + else + { + strncpy((char*)pKey->data,xaMetadataAdaptSupportedKeys[keyIndex],neededsize); + pKey->size=neededsize; + } + pKey->encoding = XA_CHARACTERENCODING_ASCII; + strcpy((char*)(pKey->langCountry),"en"); + } + else + { + ret = XA_RESULT_PARAMETER_INVALID; + } + } + else + { + DEBUG_ERR("Unsupported metadata insertion context!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + DEBUG_API("<-XAMetadataInsertionItfAdapt_GetKey"); + return ret; +} + +/* + * XAresult XAMetadataInsertionItfAdapt_GetFreeKeysEncoding(XAAdaptationBaseCtx *bCtx, + * XAint32 nodeID, + * XAuint32 encodingIndex, + * XAuint32 *pEncoding) + */ +XAresult XAMetadataInsertionItfAdapt_GetFreeKeysEncoding(XAAdaptationBaseCtx *bCtx, + XAint32 nodeID, + XAuint32 encodingIndex, + XAuint32 *pEncoding) +{ + DEBUG_API("->XAMetadataInsertionItfAdapt_GetFreeKeysEncoding"); + DEBUG_API("<-XAMetadataInsertionItfAdapt_GetFreeKeysEncoding"); + /* no free keys*/ + return XA_RESULT_PRECONDITIONS_VIOLATED; +} + +/* + * XAresult XAMetadataInsertionItfAdapt_InsertMetadataItem(XAAdaptationBaseCtx *bCtx, + * XAint32 nodeID, + * XAMetadataInfo *pKey, + * XAMetadataInfo *pValue, + * XAboolean overwrite) + */ +XAresult XAMetadataInsertionItfAdapt_InsertMetadataItem(XAAdaptationBaseCtx *bCtx, + XAint32 nodeID, + XAMetadataInfo *pKey, + XAMetadataInfo *pValue, + XAboolean overwrite) +{ + XAresult ret = XA_RESULT_SUCCESS; + XAMetadataAdaptVars* mdv=NULL; + XAMediaRecorderAdaptationCtx* mCtx; + GstTagMergeMode mode = (overwrite?GST_TAG_MERGE_REPLACE:GST_TAG_MERGE_KEEP); + gchar* parsedKey = (gchar*)XAMetadataAdapt_ParseKhronosKey((XAchar*)pKey->data); + DEBUG_API("->XAMetadataInsertionItfAdapt_InsertMetadataItem"); + if(bCtx && bCtx->ctxId == XAMediaRecorderAdaptation) + { + mCtx = (XAMediaRecorderAdaptationCtx*)bCtx; + mdv = mCtx->metadatavars; + if(mdv) + { + if( nodeID==XA_ROOT_NODE_ID ) + { + if(!mdv->generaltags) + { + mdv->generaltags = gst_tag_list_new(); + } + if (gst_tag_get_type (parsedKey) == G_TYPE_STRING) + { + gst_tag_list_add(mdv->generaltags, mode, parsedKey, (gchar*)pValue->data, NULL ); + } + else + { + gst_tag_list_add(mdv->generaltags, mode, parsedKey, atoi((gchar*)pValue->data), NULL ); + } + } + else + { + if(nodeID == (XAint32)mdv->audiotags || nodeID == (XAint32)mdv->videotags) + { + if (gst_tag_get_type (parsedKey) == G_TYPE_STRING) + { + gst_structure_set((GstStructure*)nodeID, parsedKey, G_TYPE_STRING, g_strdup((gchar*)pValue->data), NULL ); + } + else + { + gst_structure_set((GstStructure*)nodeID, parsedKey, gst_tag_get_type (parsedKey), atoi((gchar*)pValue->data), NULL ); + } + } + else + { + DEBUG_ERR("Unknown node!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + } + if( ret == XA_RESULT_SUCCESS ) + { /* let (possible) extraction itf to know new tags */ + XAAdaptEvent event = {XA_METADATAEVENTS, XA_ADAPT_MDE_TAGS_AVAILABLE, 0, NULL }; + XAAdaptationBase_SendAdaptEvents(bCtx, &event ); + } + } + else + { + DEBUG_ERR("Metadata not initialized!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + } + else + { + DEBUG_ERR("Unsupported metadata insertion context!!"); + ret = XA_RESULT_PARAMETER_INVALID; + } + DEBUG_API("<-XAMetadataInsertionItfAdapt_InsertMetadataItem"); + return ret; +} + +/***************************************************************************** + * INTERNALS + *****************************************************************************/ + + +/* + * GstStructure* XAMetadataAdapt_GetChildStructure(XAMetadataAdaptVars* mdv, XAuint32 index) + * helper to get gst structure corresponding to child index + */ +GstStructure* XAMetadataAdapt_GetChildStructure(XAMetadataAdaptVars* mdv, XAuint32 index) +{ + if(mdv->nodedepth == 0) + { /*root childs, if existent, the order is 1)video 2)audio*/ + if (index==0) + { + if(mdv->videotags) return mdv->videotags; + else if (mdv->audiotags) return mdv->audiotags; + } + else if (index==1) + { + if (mdv->audiotags) return mdv->audiotags; + } + } + else + { + if(index==XA_NODE_PARENT) + { + return GST_STRUCTURE(mdv->generaltags); + } + } + /*else*/ + return NULL; +} + +/* + * XAMetadataAdaptVars* XAMetadataAdapt_GetMetadataVars(XAAdaptationBaseCtx *bCtx) + * Return metadata variables from different types of context + */ +XAMetadataAdaptVars* XAMetadataAdapt_GetMetadataVars(XAAdaptationBaseCtx *bCtx) +{ + if( bCtx->ctxId == XAMDAdaptation || + bCtx->ctxId == XAMediaPlayerAdaptation ) + { + return ((XAMediaPlayerAdaptationCtx*)bCtx)->metadatavars; + } + else if( bCtx->ctxId == XAMediaRecorderAdaptation ) + { + return ((XAMediaRecorderAdaptationCtx*)bCtx)->metadatavars; + } + else + { + DEBUG_ERR("Invalid context for metadata"); + return NULL; + } +} + +/* + * gboolean XAMetadataAdapt_CountTags(GQuark field_id, const GValue* value, gpointer counter) + * GstStructureForeachFunc to count number of items inside list + */ +gboolean XAMetadataAdapt_CountTags(GQuark field_id, const GValue* value, gpointer counter) +{ + XAuint32* cnt = (XAuint32*)counter; + (*cnt)++; + return TRUE; +} + +/* + * void XAMetadataAdapt_ParseTag(const GstTagList* list, const gchar* tag, gpointer listptr) + * GstTaglistForeachFunc to parse items inside gsttaglist + */ +void XAMetadataAdapt_ParseTag(const GstTagList* list, const gchar* tag, gpointer listptr) +{ + XAMetadataImplTagList* tagList = (XAMetadataImplTagList*)listptr; + const gchar *key; + gchar *value; + guint32 keylen, valuelen; + + DEBUG_API("->XAMetadataAdapt_ParseTag"); + key = gst_tag_get_nick(tag); + /*include null-terminator*/ + keylen = strlen(key)+1; + tagList->mdeKeys[tagList->itemcount] = calloc(1,keylen+sizeof(XAMetadataInfo)); + tagList->mdeKeys[tagList->itemcount]->size = keylen; + strncpy((char*)tagList->mdeKeys[tagList->itemcount]->data,key,keylen-1); + + if (gst_tag_get_type (tag) == G_TYPE_STRING) + { + gst_tag_list_get_string_index(list, tag, 0, &value); + } + else + { + value = g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, 0)); + } + /*include null-terminator*/ + valuelen = strlen(value)+1; + tagList->mdeValues[tagList->itemcount] = calloc(1,valuelen+sizeof(XAMetadataInfo)); + tagList->mdeValues[tagList->itemcount]->size = valuelen; + strncpy((char*)tagList->mdeValues[tagList->itemcount]->data,value,valuelen-1); + + /* NOTE: for now, encoding and language fixed */ + tagList->mdeKeys[tagList->itemcount]->encoding = XA_CHARACTERENCODING_ASCII; + tagList->mdeValues[tagList->itemcount]->encoding = XA_CHARACTERENCODING_ASCII; + strcpy((char*)tagList->mdeKeys[tagList->itemcount]->langCountry,"en"); + strcpy((char*)tagList->mdeValues[tagList->itemcount]->langCountry,"en"); + tagList->itemcount++; + DEBUG_API_A2("<-XAMetadataAdapt_ParseTag: added %s : %s", + tagList->mdeKeys[tagList->itemcount-1]->data, + tagList->mdeValues[tagList->itemcount-1]->data); +} + +/* + * gboolean XAMetadataAdapt_ParseItem(GQuark gkey, + * const GValue* gvalue, + * gpointer listptr) + * GstStructureForeachFunc to parse items inside caps structure + */ +gboolean XAMetadataAdapt_ParseItem(GQuark gkey, + const GValue* gvalue, + gpointer listptr) +{ + XAMetadataImplTagList* tagList = (XAMetadataImplTagList*)listptr; + const gchar *key; + gchar *value; + guint32 keylen, valuelen; + + DEBUG_API("->XAMetadataAdapt_ParseItem"); + key = g_quark_to_string(gkey); + /*include null-terminator*/ + keylen = strlen(key)+1; + tagList->mdeKeys[tagList->itemcount] = calloc(1,keylen+sizeof(XAMetadataInfo)); + tagList->mdeKeys[tagList->itemcount]->size = keylen; + strncpy((char*)tagList->mdeKeys[tagList->itemcount]->data,key,keylen-1); + + value = gst_value_serialize(gvalue); + /*include null-terminator*/ + valuelen = strlen(value)+1; + tagList->mdeValues[tagList->itemcount] = calloc(1,valuelen+sizeof(XAMetadataInfo)); + tagList->mdeValues[tagList->itemcount]->size = valuelen; + strncpy((char*)tagList->mdeValues[tagList->itemcount]->data,value,valuelen-1); + + /* for Gst, encoding and language fixed */ + tagList->mdeKeys[tagList->itemcount]->encoding = XA_CHARACTERENCODING_ASCII; + tagList->mdeValues[tagList->itemcount]->encoding = XA_CHARACTERENCODING_ASCII; + strcpy((char*)tagList->mdeKeys[tagList->itemcount]->langCountry,"en"); + strcpy((char*)tagList->mdeValues[tagList->itemcount]->langCountry,"en"); + tagList->itemcount++; + DEBUG_API_A2("<-XAMetadataAdapt_ParseItem: added %15s : %s", + tagList->mdeKeys[tagList->itemcount-1]->data, + tagList->mdeValues[tagList->itemcount-1]->data); + return TRUE; +} + +/* + * gboolean XAMetadataAdapt_GstTagCb( GstBus *bus, GstMessage *message, gpointer data ) + * Metadata callback - called when new tags found from stream + */ +gboolean XAMetadataAdapt_GstTagCb( GstBus *bus, GstMessage *message, gpointer data ) +{ + GstTagList *new_tags; + GstTagList **old_tags = NULL; + XAAdaptationBaseCtx* bCtx; + GstTagMergeMode mode; + + bCtx = (XAAdaptationBaseCtx*)data; + if(GST_MESSAGE_TAG==GST_MESSAGE_TYPE(message)) + { + DEBUG_API_A2("->XAMetadataAdapt_GstTagCb:\"%s\" from object \"%s\"", + GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME(GST_MESSAGE_SRC(message))); + gst_message_parse_tag (message, &new_tags); + /* NOTE: only general tags received this way (not child nodes)*/ + if( bCtx->ctxId == XAMDAdaptation || + bCtx->ctxId == XAMediaPlayerAdaptation ) + { + old_tags = &((XAMediaPlayerAdaptationCtx*)bCtx)->metadatavars->generaltags; + mode = GST_TAG_MERGE_REPLACE; + } + else if( bCtx->ctxId == XAMediaRecorderAdaptation ) + { + old_tags = &((XAMediaRecorderAdaptationCtx*)bCtx)->metadatavars->generaltags; + /* keep user's tags */ + mode = GST_TAG_MERGE_KEEP; + } + else + { /*context not applicable*/ + return TRUE; + } + + if( *old_tags ) + *old_tags = gst_tag_list_merge (*old_tags, new_tags, mode); + else + *old_tags = new_tags; + + if( GST_STATE(bCtx->bin)==GST_STATE_PLAYING ) + { /* send event only for live stream, otherwise wait for preroll to complete */ + XAAdaptEvent event = {XA_METADATAEVENTS, XA_ADAPT_MDE_TAGS_AVAILABLE, 0, NULL }; + XAAdaptationBase_SendAdaptEvents(bCtx, &event ); + } + DEBUG_API("<-XAMetadataAdapt_GstTagCb"); + } + return TRUE; +} + +/* + * void XAMetadataAdapt_TryWriteTag(const GstTagList* list, const gchar* tag, gpointer taggerptr) + * GstTaglistForeachFunc to write single tag entries to stream + */ +void XAMetadataAdapt_TryWriteTag(const GstTagList* list, const gchar* tag, gpointer taggerptr) +{ + GstTagSetter* tagger = (GstTagSetter*)taggerptr; + const gchar *key; + gchar *value; + + DEBUG_API("->XAMetadataAdapt_ParseTag"); + key = gst_tag_get_nick(tag); + if (gst_tag_get_type (tag) == G_TYPE_STRING) + { + gst_tag_list_get_string_index(list, tag, 0, &value); + } + else + { + value = g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, 0)); + } + gst_tag_setter_add_tags(tagger, GST_TAG_MERGE_REPLACE, key, value, NULL); +} + +/* + * XAresult XAMetadataAdapt_TryWriteTags(XAAdaptationBaseCtx* bCtx, GstBin* binToWriteTo) + */ +XAresult XAMetadataAdapt_TryWriteTags(XAAdaptationBaseCtx* bCtx, GstBin* binToWriteTo) +{ + XAresult ret = XA_RESULT_SUCCESS; + GstTagSetter *tagger=NULL; + /* Find metadata tagger from bin (if any) */ + GstIterator *iter = gst_bin_iterate_all_by_interface( binToWriteTo, GST_TYPE_TAG_SETTER ); + gboolean done = FALSE; + XAMediaRecorderAdaptationCtx* mCtx; + XAAdaptEvent mdevent = {XA_METADATAEVENTS, XA_ADAPT_MDE_TAGS_WRITTEN, 0, NULL }; + + DEBUG_API("->XAMetadataAdapt_TryWriteTags"); + + if( bCtx->ctxId != XAMediaRecorderAdaptation ) + { + return XA_RESULT_FEATURE_UNSUPPORTED; + } + mCtx = ((XAMediaRecorderAdaptationCtx*)bCtx); + while (!done) + { + switch (gst_iterator_next(iter, (gpointer)&tagger)) + { + case GST_ITERATOR_OK: + if(mCtx->metadatavars) + { + if(mCtx->metadatavars->generaltags) + { + gst_tag_setter_merge_tags(tagger, mCtx->metadatavars->generaltags, GST_TAG_MERGE_REPLACE); + } + } + done = TRUE; + break; + case GST_ITERATOR_ERROR: + done = TRUE; + DEBUG_ERR("Pipeline does not support tag setting"); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + break; + case GST_ITERATOR_DONE: + done = TRUE; + DEBUG_ERR("Pipeline does not support tag setting"); + ret = XA_RESULT_CONTENT_UNSUPPORTED; + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync(iter); + break; + } + } + /*here datasize field is used for return value*/ + mdevent.datasize = ret; + XAAdaptationBase_SendAdaptEvents(bCtx, &mdevent ); + gst_iterator_free (iter); + DEBUG_API("<-XAMetadataAdapt_TryWriteTags"); + return ret; +}