| /* ------------------------------------------------------------------ |
| * Copyright (C) 1998-2009 PacketVideo |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| * express or implied. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * ------------------------------------------------------------------- |
| */ |
| #ifndef PVMF_MP4FFPARSER_NODE_H_INCLUDED |
| #include "pvmf_mp4ffparser_node.h" |
| #endif |
| #ifndef OSCL_MIME_STRING_UTILS_H_INCLUDED |
| #include "pv_mime_string_utils.h" |
| #endif |
| #ifndef PVMI_FILEIO_KVP_H_INCLUDED |
| #include "pvmi_fileio_kvp.h" |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // Capability and config interface related constants and definitions |
| // - based on pv_player_engine.h |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| struct MP4ParserNodeKeyStringData |
| { |
| char iString[64]; |
| PvmiKvpType iType; |
| PvmiKvpValueType iValueType; |
| }; |
| |
| |
| // The number of characters to allocate for the key string |
| #define MP4CONFIG_KEYSTRING_SIZE 128 |
| |
| //The base selection keys for file IO,leading tag needs to be fileio |
| static const MP4ParserNodeKeyStringData MP4ParserNodeConfig_FileIO_Keys[] = |
| { |
| {"pv-cache-size", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32}, |
| {"async-read-buffer-size", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32}, |
| {"logger-enable", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_BOOL}, |
| {"logger-stats-enable", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_BOOL}, |
| {"native-access-mode", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32}, |
| {"file-handle", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_KSV} |
| }; |
| |
| //The base selection keys for file IO,leading tag needs to be x-pvmf\net |
| static const MP4ParserNodeKeyStringData MP4ParserNodeConfig_Net_Keys[] = |
| { |
| {"delay", PVMI_KVPTYPE_VALUE, PVMI_KVPVALTYPE_UINT32} |
| }; |
| |
| static const uint MP4ParserNodeConfig_Num_FileIO_Keys = |
| (sizeof(MP4ParserNodeConfig_FileIO_Keys) / |
| sizeof(MP4ParserNodeKeyStringData)); |
| |
| static const uint MP4ParserNodeConfig_Num_Net_Keys = |
| (sizeof(MP4ParserNodeConfig_Net_Keys) / |
| sizeof(MP4ParserNodeKeyStringData)); |
| |
| |
| enum BaseFileIOKeys_IndexMapType |
| { |
| PV_CACHE_SIZE = 0, |
| ASYNC_READ_BUFFER_SIZE, |
| BASEKEY_PVLOGGER_ENABLE, |
| PVLOGGER_STATS_ENABLE, |
| NATIVE_ACCESS_MODE, |
| FILE_HANDLE, |
| }; |
| |
| enum BaseNetKeys_IndexMapType |
| { |
| DELAY = 0 |
| }; |
| |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| PVMFStatus PVMFMP4FFParserNode::getParametersSync( |
| PvmiMIOSession aSession, PvmiKeyType aIdentifier, |
| PvmiKvp*& aParameters, int& aNumParamElements, |
| PvmiCapabilityContext aContext) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(aContext); |
| |
| // Initialize the output parameters |
| aNumParamElements = 0; |
| aParameters = NULL; |
| |
| // Count the number of components and parameters in the key |
| int compcount = pv_mime_string_compcnt(aIdentifier); |
| // Retrieve the first component from the key string |
| char* compstr = NULL; |
| pv_mime_string_extract_type(0, aIdentifier, compstr); |
| |
| iBaseKey = INVALID; |
| |
| if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("fileio")) < 0) || compcount < 2) |
| { |
| if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) < 0) || compcount < 2) |
| { |
| |
| // First component should be "fileio" or "x-pvmf" and there must |
| // be at least two components to go past fileio and x-pvmf |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::getParametersSync() Invalid key string")); |
| return PVMFErrArgument; |
| |
| } |
| else |
| { |
| // Retrieve the second component from the key string |
| pv_mime_string_extract_type(1, aIdentifier, compstr); |
| |
| // Check if it is key string for streaming |
| if (pv_mime_strcmp(compstr, _STRLIT_CHAR("net")) < 0) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFMP4FFParserNode::getParametersSync() Unsupported key")); |
| return PVMFFailure; |
| } |
| iBaseKey = NET; |
| } |
| } |
| else |
| { |
| //Since the key string matches that of fileio |
| iBaseKey = FILE_IO; |
| } |
| |
| //If the leading tag is fileio then component count should be 2 |
| if ((iBaseKey == FILE_IO) && (compcount == 2)) |
| { |
| //BaseKey's leading tag is fileio; |
| pv_mime_string_extract_type(1, aIdentifier, compstr); |
| |
| // Determine what is requested |
| PvmiKvpAttr reqattr = GetAttrTypeFromKeyString(aIdentifier); |
| if (reqattr == PVMI_KVPATTR_UNKNOWN) |
| { |
| reqattr = PVMI_KVPATTR_CUR; |
| } |
| uint i; |
| for (i = 0; i < MP4ParserNodeConfig_Num_FileIO_Keys; i++) |
| { |
| if (pv_mime_strcmp(compstr, (char*)(MP4ParserNodeConfig_FileIO_Keys[i].iString)) >= 0) |
| { |
| break; |
| } |
| } |
| |
| if (i == MP4ParserNodeConfig_Num_FileIO_Keys) |
| { |
| // no match found |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::getParametersSync() Unsupported key")); |
| return PVMFErrNoMemory; |
| } |
| |
| PVMFStatus retval = GetConfigParameter(aParameters, aNumParamElements, i, reqattr); |
| if (retval != PVMFSuccess) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::getParametersSync() " |
| "Retrieving mp4 parser node parameter failed")); |
| return retval; |
| } |
| } |
| else if ((iBaseKey == NET) && ((compcount == 2) || (compcount == 3))) |
| { |
| // //BaseKey's leading tag is x-pvmf\net |
| |
| if (compcount == 2) |
| { |
| // Since key is "x-pvmf/net" return all |
| // nodes available at this level. Ignore attribute |
| // since capability is only allowed |
| |
| // Allocate memory for the KVP list |
| aParameters = (PvmiKvp*)oscl_malloc(MP4ParserNodeConfig_Num_Net_Keys * sizeof(PvmiKvp)); |
| if (aParameters == NULL) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFMP4FFParserNode::getParametersSync() Memory allocation for KVP failed")); |
| return PVMFErrNoMemory; |
| } |
| oscl_memset(aParameters, 0, MP4ParserNodeConfig_Num_Net_Keys*sizeof(PvmiKvp)); |
| // Allocate memory for the key strings in each KVP |
| PvmiKeyType memblock = (PvmiKeyType)oscl_malloc(MP4ParserNodeConfig_Num_Net_Keys * MP4CONFIG_KEYSTRING_SIZE * sizeof(char)); |
| if (memblock == NULL) |
| { |
| oscl_free(aParameters); |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFMP4FFParserNode::getParametersSync() Memory allocation for key string failed")); |
| return PVMFErrNoMemory; |
| } |
| oscl_strset(memblock, 0, MP4ParserNodeConfig_Num_Net_Keys*MP4CONFIG_KEYSTRING_SIZE*sizeof(char)); |
| // Assign the key string buffer to each KVP |
| int32 j; |
| for (j = 0; j < (int32)MP4ParserNodeConfig_Num_Net_Keys; ++j) |
| { |
| aParameters[j].key = memblock + (j * MP4CONFIG_KEYSTRING_SIZE); |
| } |
| // Copy the requested info |
| for (j = 0; j < (int32)MP4ParserNodeConfig_Num_Net_Keys; ++j) |
| { |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR("x-pvmf/net/"), 11); |
| oscl_strncat(aParameters[j].key, MP4ParserNodeConfig_Net_Keys[j].iString, oscl_strlen(MP4ParserNodeConfig_Net_Keys[j].iString)); |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(";type="), 6); |
| switch (MP4ParserNodeConfig_Net_Keys[j].iType) |
| { |
| case PVMI_KVPTYPE_AGGREGATE: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPTYPE_AGGREGATE_STRING), oscl_strlen(PVMI_KVPTYPE_AGGREGATE_STRING)); |
| break; |
| |
| case PVMI_KVPTYPE_POINTER: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPTYPE_POINTER_STRING), oscl_strlen(PVMI_KVPTYPE_POINTER_STRING)); |
| break; |
| |
| case PVMI_KVPTYPE_VALUE: |
| default: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPTYPE_VALUE_STRING), oscl_strlen(PVMI_KVPTYPE_VALUE_STRING)); |
| break; |
| } |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(";valtype="), 9); |
| switch (MP4ParserNodeConfig_Net_Keys[j].iValueType) |
| { |
| case PVMI_KVPVALTYPE_RANGE_INT32: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_RANGE_INT32_STRING), oscl_strlen(PVMI_KVPVALTYPE_RANGE_INT32_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_KSV: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_KSV_STRING), oscl_strlen(PVMI_KVPVALTYPE_KSV_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_CHARPTR: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_CHARPTR_STRING), oscl_strlen(PVMI_KVPVALTYPE_CHARPTR_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_BOOL: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_BOOL_STRING), oscl_strlen(PVMI_KVPVALTYPE_BOOL_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_UINT32: |
| default: |
| oscl_strncat(aParameters[j].key, _STRLIT_CHAR(PVMI_KVPVALTYPE_UINT32_STRING), oscl_strlen(PVMI_KVPVALTYPE_UINT32_STRING)); |
| break; |
| } |
| aParameters[j].key[MP4CONFIG_KEYSTRING_SIZE-1] = 0; |
| } |
| |
| aNumParamElements = MP4ParserNodeConfig_Num_Net_Keys; |
| } |
| else if (compcount == 3) |
| { |
| pv_mime_string_extract_type(2, aIdentifier, compstr); |
| |
| // Determine what is requested |
| PvmiKvpAttr reqattr = GetAttrTypeFromKeyString(aIdentifier); |
| if (reqattr == PVMI_KVPATTR_UNKNOWN) |
| { |
| reqattr = PVMI_KVPATTR_CUR; |
| } |
| int32 i; |
| for (i = 0; i < (int32)MP4ParserNodeConfig_Num_Net_Keys; i++) |
| { |
| if (pv_mime_strcmp(compstr, (char*)(MP4ParserNodeConfig_Net_Keys[i].iString)) >= 0) |
| { |
| break; |
| } |
| } |
| |
| if (i == (int32)MP4ParserNodeConfig_Num_Net_Keys) |
| { |
| // no match found |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, |
| (0, "PVMFMP4FFParserNode::getParametersSync() Unsupported key")); |
| return PVMFErrNoMemory; |
| } |
| |
| PVMFStatus retval = GetConfigParameter(aParameters, aNumParamElements, i, reqattr); |
| if (retval != PVMFSuccess) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, |
| (0, "PVMFMP4FFParserNode::getParametersSync() " |
| "Retrieving streaming manager parameter failed")); |
| return retval; |
| } |
| } |
| } |
| else |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::getParametersSync() Unsupported key")); |
| return PVMFErrArgument; |
| } |
| |
| return PVMFSuccess; |
| } |
| |
| PVMFStatus PVMFMP4FFParserNode::releaseParameters(PvmiMIOSession aSession, |
| PvmiKvp* aParameters, |
| int num_elements) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::releaseParameters() In")); |
| |
| if (aParameters == NULL || num_elements < 1) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::releaseParameters() KVP list is NULL or number of elements is 0")); |
| return PVMFErrArgument; |
| } |
| |
| int32 compcount = pv_mime_string_compcnt(aParameters[0].key); |
| |
| // Retrieve the first component from the key string |
| char* compstr = NULL; |
| |
| pv_mime_string_extract_type(0, aParameters[0].key, compstr); |
| |
| BaseKeys_SelectionType basekey = INVALID; |
| |
| if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("fileio")) < 0) || compcount < 2) |
| { |
| if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) < 0) || compcount < 2) |
| { |
| // First component should be "fileio" or "x-pvmf" and there must |
| // be at least two components to go past fileio and x-pvmf |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::getParametersSync() Invalid key string")); |
| return PVMFErrArgument; |
| } |
| else |
| { |
| // Retrieve the second component from the key string |
| pv_mime_string_extract_type(1, aParameters[0].key, compstr); |
| |
| // Check if it is key string for streaming |
| if (pv_mime_strcmp(compstr, _STRLIT_CHAR("net")) < 0) |
| { |
| PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVMFMP4FFParserNode::getParametersSync() Unsupported key")); |
| return PVMFErrArgument; |
| } |
| basekey = NET; |
| } |
| } |
| else |
| { |
| //Since the key string matches that of fileio |
| basekey = FILE_IO; |
| } |
| |
| pv_mime_string_extract_type(0, aParameters[0].key, compstr); |
| |
| //It does not effect in releasing memory what are the base keys either fileio or x-pvmf. |
| if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("fileio")) >= 0) || (pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) >= 0)) |
| { |
| // Go through each KVP and release memory for value if allocated from heap |
| for (int32 i = 0; i < num_elements; ++i) |
| { |
| // Next check if it is a value type that allocated memory |
| PvmiKvpType kvptype = GetTypeFromKeyString(aParameters[i].key); |
| if (kvptype == PVMI_KVPTYPE_VALUE || kvptype == PVMI_KVPTYPE_UNKNOWN) |
| { |
| PvmiKvpValueType keyvaltype = GetValTypeFromKeyString(aParameters[i].key); |
| if (keyvaltype == PVMI_KVPVALTYPE_UNKNOWN) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::releaseParameters() Valtype not specified in key string")); |
| return PVMFErrArgument; |
| } |
| |
| if (keyvaltype == PVMI_KVPVALTYPE_CHARPTR && aParameters[i].value.pChar_value != NULL) |
| { |
| oscl_free(aParameters[i].value.pChar_value); |
| aParameters[i].value.pChar_value = NULL; |
| } |
| else if (keyvaltype == PVMI_KVPVALTYPE_WCHARPTR && aParameters[i].value.pWChar_value != NULL) |
| { |
| oscl_free(aParameters[i].value.pWChar_value); |
| aParameters[i].value.pWChar_value = NULL; |
| } |
| else if (keyvaltype == PVMI_KVPVALTYPE_CHARPTR && aParameters[i].value.pChar_value != NULL) |
| { |
| oscl_free(aParameters[i].value.pChar_value); |
| aParameters[i].value.pChar_value = NULL; |
| } |
| else if (keyvaltype == PVMI_KVPVALTYPE_KSV && aParameters[i].value.key_specific_value != NULL) |
| { |
| oscl_free(aParameters[i].value.key_specific_value); |
| aParameters[i].value.key_specific_value = NULL; |
| } |
| else if (keyvaltype == PVMI_KVPVALTYPE_RANGE_INT32 && aParameters[i].value.key_specific_value != NULL) |
| { |
| range_int32* ri32 = (range_int32*)aParameters[i].value.key_specific_value; |
| aParameters[i].value.key_specific_value = NULL; |
| oscl_free(ri32); |
| } |
| else if (keyvaltype == PVMI_KVPVALTYPE_RANGE_UINT32 && aParameters[i].value.key_specific_value != NULL) |
| { |
| range_uint32* rui32 = (range_uint32*)aParameters[i].value.key_specific_value; |
| aParameters[i].value.key_specific_value = NULL; |
| oscl_free(rui32); |
| } |
| } |
| } |
| |
| oscl_free(aParameters[0].key); |
| |
| // Free memory for the parameter list |
| oscl_free(aParameters); |
| aParameters = NULL; |
| } |
| else |
| { |
| // Unknown key string |
| return PVMFErrArgument; |
| } |
| |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::releaseParameters() Out")); |
| return PVMFSuccess; |
| } |
| |
| void PVMFMP4FFParserNode::createContext(PvmiMIOSession aSession, |
| PvmiCapabilityContext& aContext) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(aContext); |
| // not supported |
| OSCL_LEAVE(PVMFErrNotSupported); |
| } |
| |
| void PVMFMP4FFParserNode::setContextParameters(PvmiMIOSession aSession, |
| PvmiCapabilityContext& aContext, |
| PvmiKvp* aParameters, |
| int num_parameter_elements) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(aContext); |
| OSCL_UNUSED_ARG(aParameters); |
| OSCL_UNUSED_ARG(num_parameter_elements); |
| // not supported |
| OSCL_LEAVE(PVMFErrNotSupported); |
| } |
| |
| void PVMFMP4FFParserNode::DeleteContext(PvmiMIOSession aSession, |
| PvmiCapabilityContext& aContext) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(aContext); |
| // not supported |
| OSCL_LEAVE(PVMFErrNotSupported); |
| } |
| |
| |
| void PVMFMP4FFParserNode::setParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, |
| int num_elements, PvmiKvp* &aRet_kvp) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() In")); |
| |
| |
| // Go through each parameter |
| for (int32 paramind = 0; paramind < num_elements; ++paramind) |
| { |
| // Count the number of components and parameters in the key |
| int compcount = pv_mime_string_compcnt(aParameters[paramind].key); |
| |
| // Retrieve the first component from the key string |
| char* compstr = NULL; |
| pv_mime_string_extract_type(0, aParameters[paramind].key, compstr); |
| |
| // First check if it is key string for the mp4 parser node |
| if (pv_mime_strcmp(compstr, _STRLIT_CHAR("fileio")) >= 0) |
| { |
| iBaseKey = FILE_IO; |
| if (compcount == 2) |
| { |
| pv_mime_string_extract_type(1, aParameters[paramind].key, compstr); |
| uint i; |
| for (i = 0; i < MP4ParserNodeConfig_Num_FileIO_Keys; i++) |
| { |
| if (pv_mime_strcmp(compstr, (char*)(MP4ParserNodeConfig_FileIO_Keys[i].iString)) >= 0) |
| { |
| break; |
| } |
| } |
| |
| if (MP4ParserNodeConfig_Num_FileIO_Keys == i) |
| { |
| // invalid third component |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Unsupported key")); |
| return; |
| } |
| |
| // Verify and set the passed-in setting |
| PVMFStatus retval = VerifyAndSetConfigParameter(i, aParameters[paramind], true); |
| if (retval != PVMFSuccess) |
| { |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Setting " |
| "parameter %d failed", paramind)); |
| return; |
| } |
| } |
| else |
| { |
| // Do not support more than 2 components right now |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Unsupported key")); |
| return; |
| } |
| } |
| else if (pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) >= 0) |
| { |
| if (compcount == 3) |
| { |
| // Retrieve the second component from the key string |
| pv_mime_string_extract_type(1, aParameters[paramind].key, compstr); |
| // First check if it is key string for the streaming manager |
| if (pv_mime_strcmp(compstr, _STRLIT_CHAR("net")) >= 0) |
| { |
| iBaseKey = NET; |
| int32 i; |
| //Extract the third component from the key string |
| pv_mime_string_extract_type(2, aParameters[paramind].key, compstr); |
| for (i = 0; i < (int32)MP4ParserNodeConfig_Num_Net_Keys; i++) |
| { |
| if (pv_mime_strcmp(compstr, (char*)(MP4ParserNodeConfig_Net_Keys[i].iString)) >= 0) |
| { |
| break; |
| } |
| } |
| |
| if ((int32)MP4ParserNodeConfig_Num_Net_Keys == i) |
| { |
| // invalid third component |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Unsupported key")); |
| return; |
| } |
| |
| // Verify and set the passed-in setting |
| PVMFStatus retval = VerifyAndSetConfigParameter(i, aParameters[paramind], true); |
| if (retval != PVMFSuccess) |
| { |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Setting " |
| "parameter %d failed", paramind)); |
| return; |
| } |
| } |
| else if (pv_mime_strcmp(compstr, _STRLIT_CHAR("parser/ff_noaudio")) >= 0) |
| { |
| // Make sure its a bool value |
| PvmiKvpValueType keyvaltype = GetValTypeFromKeyString(aParameters[paramind].key); |
| if (PVMI_KVPVALTYPE_BOOL != keyvaltype) |
| { |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync Setting " |
| "ff_noaudio valtype error")); |
| return; |
| } |
| iParseAudioDuringFF = aParameters[paramind].value.bool_value; |
| } |
| else if (pv_mime_strcmp(compstr, _STRLIT_CHAR("parser/rew_noaudio")) >= 0) |
| { |
| // Make sure its a bool value |
| PvmiKvpValueType keyvaltype = GetValTypeFromKeyString(aParameters[paramind].key); |
| if (PVMI_KVPVALTYPE_BOOL != keyvaltype) |
| { |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync Setting " |
| "ff_noaudio valtype error")); |
| return; |
| } |
| iParseAudioDuringREW = aParameters[paramind].value.bool_value; |
| } |
| else |
| { |
| // Unknown key string |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Unsupported key")); |
| return; |
| } |
| } |
| else |
| { |
| // Do not support more than 3 components right now |
| aRet_kvp = &aParameters[paramind]; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Unsupported key")); |
| return; |
| } |
| } |
| else |
| { |
| // Unknown key string |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Unsupported key")); |
| return; |
| } |
| } |
| |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::setParametersSync() Out")); |
| } |
| |
| PVMFCommandId PVMFMP4FFParserNode::setParametersAsync(PvmiMIOSession aSession, |
| PvmiKvp* aParameters, |
| int num_elements, |
| PvmiKvp*& aRet_kvp, |
| OsclAny* context) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| OSCL_UNUSED_ARG(aParameters); |
| OSCL_UNUSED_ARG(num_elements); |
| OSCL_UNUSED_ARG(aRet_kvp); |
| OSCL_UNUSED_ARG(context); |
| // not supported |
| OSCL_LEAVE(OsclErrNotSupported); |
| // to satisfy compiler, need to return |
| return 0; |
| } |
| |
| uint32 PVMFMP4FFParserNode::getCapabilityMetric(PvmiMIOSession aSession) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| return 0; |
| } |
| |
| PVMFStatus PVMFMP4FFParserNode::verifyParametersSync(PvmiMIOSession aSession, |
| PvmiKvp* aParameters, |
| int num_elements) |
| { |
| OSCL_UNUSED_ARG(aSession); |
| |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::verifyParametersSync() In")); |
| |
| if (aParameters == NULL || num_elements < 1) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::verifyParametersSync() Passed in parameter invalid")); |
| return PVMFErrArgument; |
| } |
| |
| // Go through each parameter and verify |
| for (int32 paramind = 0; paramind < num_elements; ++paramind) |
| { |
| // Retrieve the first component from the key string |
| char* compstr = NULL; |
| |
| // Retrieve the second component from the key string |
| pv_mime_string_extract_type(0, aParameters[paramind].key, compstr); |
| |
| // First check if it is key string for this node |
| if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("fileio")) >= 0)) |
| { |
| iBaseKey = FILE_IO; |
| pv_mime_string_extract_type(1, aParameters[paramind].key, compstr); |
| int32 i; |
| for (i = 0; i < (int32)MP4ParserNodeConfig_Num_FileIO_Keys; i++) |
| { |
| if (pv_mime_strcmp(compstr, (char*)(MP4ParserNodeConfig_FileIO_Keys[i].iString)) >= 0) |
| { |
| break; |
| } |
| } |
| |
| if ((int32)MP4ParserNodeConfig_Num_FileIO_Keys == i) |
| { |
| return PVMFErrArgument; |
| } |
| |
| // Verify the passed-in player setting |
| PVMFStatus retval = VerifyAndSetConfigParameter(i, aParameters[paramind], false); |
| if (retval != PVMFSuccess) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVPlayerEngine::DoCapConfigVerifyParameters() Verifying parameter %d failed", paramind)); |
| return retval; |
| } |
| } |
| // First check if it is key string for this node |
| else if ((pv_mime_strcmp(compstr, _STRLIT_CHAR("x-pvmf")) >= 0)) |
| { |
| pv_mime_string_extract_type(1, aParameters[paramind].key, compstr); |
| if (pv_mime_strcmp(compstr, _STRLIT_CHAR("net")) >= 0) |
| { |
| iBaseKey = NET; |
| int32 i; |
| pv_mime_string_extract_type(2, aParameters[paramind].key, compstr); |
| for (i = 0; i < (int32)MP4ParserNodeConfig_Num_Net_Keys; i++) |
| { |
| if (pv_mime_strcmp(compstr, (char*)(MP4ParserNodeConfig_Net_Keys[i].iString)) >= 0) |
| { |
| break; |
| } |
| } |
| |
| if ((int32)MP4ParserNodeConfig_Num_Net_Keys == i) |
| { |
| return PVMFErrArgument; |
| } |
| |
| // Verify the passed-in player setting |
| PVMFStatus retval = VerifyAndSetConfigParameter(i, aParameters[paramind], false); |
| if (retval != PVMFSuccess) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVPlayerEngine::DoCapConfigVerifyParameters() Verifying parameter %d failed", paramind)); |
| return retval; |
| } |
| } |
| else |
| { |
| // Unknown key string |
| return PVMFErrArgument; |
| } |
| } |
| else |
| { |
| // Unknown key string |
| return PVMFErrArgument; |
| } |
| |
| } |
| |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVPlayerEngine::DoCapConfigVerifyParameters() Out")); |
| return PVMFSuccess; |
| } |
| |
| PVMFStatus PVMFMP4FFParserNode::GetConfigParameter(PvmiKvp*& aParameters, |
| int& aNumParamElements, |
| int32 aIndex, PvmiKvpAttr reqattr) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::GetConfigParameter() In")); |
| |
| aNumParamElements = 0; |
| |
| // Allocate memory for the KVP |
| aParameters = (PvmiKvp*)oscl_malloc(sizeof(PvmiKvp)); |
| if (aParameters == NULL) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::GetConfigParameter() Memory allocation for KVP failed")); |
| return PVMFErrNoMemory; |
| } |
| oscl_memset(aParameters, 0, sizeof(PvmiKvp)); |
| |
| // Allocate memory for the key string in KVP |
| PvmiKeyType memblock = (PvmiKeyType)oscl_malloc(MP4CONFIG_KEYSTRING_SIZE * sizeof(char)); |
| if (memblock == NULL) |
| { |
| oscl_free(aParameters); |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::GetConfigParameter() Memory allocation for key string failed")); |
| return PVMFErrNoMemory; |
| } |
| oscl_strset(memblock, 0, MP4CONFIG_KEYSTRING_SIZE*sizeof(char)); |
| |
| // Assign the key string buffer to KVP |
| aParameters[0].key = memblock; |
| |
| // Copy the key string |
| if (iBaseKey == FILE_IO) |
| { |
| oscl_strncat(aParameters[0].key, _STRLIT_CHAR("fileio/"), 7); |
| oscl_strncat(aParameters[0].key, MP4ParserNodeConfig_FileIO_Keys[aIndex].iString, |
| oscl_strlen(MP4ParserNodeConfig_FileIO_Keys[aIndex].iString)); |
| oscl_strncat(aParameters[0].key, _STRLIT_CHAR(";type=value;valtype="), 20); |
| switch (MP4ParserNodeConfig_FileIO_Keys[aIndex].iValueType) |
| { |
| case PVMI_KVPVALTYPE_RANGE_INT32: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_RANGE_INT32_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_RANGE_INT32_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_KSV: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_KSV_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_KSV_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_CHARPTR: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_CHARPTR_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_CHARPTR_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_WCHARPTR: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_WCHARPTR_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_WCHARPTR_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_BOOL: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_BOOL_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_BOOL_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_UINT32: |
| default: |
| if (reqattr == PVMI_KVPATTR_CAP) |
| { |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_RANGE_UINT32_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_RANGE_UINT32_STRING)); |
| } |
| else |
| { |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_UINT32_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_UINT32_STRING)); |
| } |
| break; |
| } |
| aParameters[0].key[MP4CONFIG_KEYSTRING_SIZE-1] = 0; |
| |
| // Copy the requested info |
| switch (aIndex) |
| { |
| case PV_CACHE_SIZE: |
| if (reqattr == PVMI_KVPATTR_CUR) |
| { |
| aParameters[0].value.uint32_value = iCacheSize; |
| } |
| else if (reqattr == PVMI_KVPATTR_DEF) |
| { |
| // Return default |
| aParameters[0].value.uint32_value = DEFAULT_CAHCE_SIZE; |
| } |
| else |
| { |
| // Return capability |
| range_uint32* rui32 = (range_uint32*)oscl_malloc(sizeof(range_uint32)); |
| if (rui32 == NULL) |
| { |
| oscl_free(aParameters[0].key); |
| oscl_free(aParameters); |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::GetConfigParameter() " |
| "Memory allocation for range uint32 failed")); |
| return PVMFErrNoMemory; |
| } |
| rui32->min = MIN_CACHE_SIZE; |
| rui32->max = MAX_CACHE_SIZE; |
| aParameters[0].value.key_specific_value = (void*)rui32; |
| } |
| break; |
| case ASYNC_READ_BUFFER_SIZE: |
| if (reqattr == PVMI_KVPATTR_CUR) |
| { |
| // Return current value |
| aParameters[0].value.uint32_value = iAsyncReadBuffSize;; |
| } |
| else if (reqattr == PVMI_KVPATTR_DEF) |
| { |
| // Return default |
| aParameters[0].value.uint32_value = DEFAULT_ASYNC_READ_BUFFER_SIZE; |
| } |
| else |
| { |
| // Return capability |
| range_uint32* rui32 = (range_uint32*)oscl_malloc(sizeof(range_uint32)); |
| if (rui32 == NULL) |
| { |
| oscl_free(aParameters[0].key); |
| oscl_free(aParameters); |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::GetConfigParameter() " |
| "Memory allocation for range uint32 failed")); |
| return PVMFErrNoMemory; |
| } |
| rui32->min = MIN_ASYNC_READ_BUFFER_SIZE; |
| rui32->max = MAX_ASYNC_READ_BUFFER_SIZE; |
| aParameters[0].value.key_specific_value = (void*)rui32; |
| } |
| break; |
| case BASEKEY_PVLOGGER_ENABLE: |
| |
| if (reqattr == PVMI_KVPATTR_CUR) |
| { |
| aParameters[0].value.bool_value = iPVLoggerEnableFlag; |
| } |
| else if (reqattr == PVMI_KVPATTR_DEF) |
| { |
| aParameters[0].value.bool_value = false; |
| } |
| else |
| { |
| // Return capability - no concept of capability for keep alive interval |
| // do nothing |
| } |
| break; |
| |
| case PVLOGGER_STATS_ENABLE: |
| if (reqattr == PVMI_KVPATTR_CUR) |
| { |
| aParameters[0].value.bool_value = iPVLoggerStateEnableFlag; |
| } |
| else if (reqattr == PVMI_KVPATTR_DEF) |
| { |
| aParameters[0].value.bool_value = false; |
| } |
| else |
| { |
| // Return capability - no concept of capability for keep alive interval |
| // do nothing |
| } |
| break; |
| case NATIVE_ACCESS_MODE: |
| if (reqattr == PVMI_KVPATTR_CUR) |
| { |
| // Return current value |
| aParameters[0].value.uint32_value = iNativeAccessMode;; |
| } |
| else if (reqattr == PVMI_KVPATTR_DEF) |
| { |
| // Return default |
| aParameters[0].value.uint32_value = DEFAULT_NATIVE_ACCESS_MODE; |
| } |
| else |
| { |
| // Return capability |
| } |
| break; |
| |
| |
| |
| default: |
| // Invalid index |
| oscl_free(aParameters[0].key); |
| oscl_free(aParameters); |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVPlayerEngine::DoGetPlayerParameter() Invalid index to player parameter")); |
| return PVMFErrArgument; |
| } |
| } |
| else if (iBaseKey == NET) |
| { |
| oscl_strncat(aParameters[0].key, _STRLIT_CHAR("x-pvmf/net/"), 11); |
| oscl_strncat(aParameters[0].key, MP4ParserNodeConfig_Net_Keys[aIndex].iString, |
| oscl_strlen(MP4ParserNodeConfig_Net_Keys[aIndex].iString)); |
| oscl_strncat(aParameters[0].key, _STRLIT_CHAR(";type=value;valtype="), 20); |
| switch (MP4ParserNodeConfig_Net_Keys[aIndex].iValueType) |
| { |
| case PVMI_KVPVALTYPE_RANGE_INT32: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_RANGE_INT32_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_RANGE_INT32_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_KSV: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_KSV_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_KSV_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_CHARPTR: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_CHARPTR_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_CHARPTR_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_WCHARPTR: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_WCHARPTR_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_WCHARPTR_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_BOOL: |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_BOOL_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_BOOL_STRING)); |
| break; |
| |
| case PVMI_KVPVALTYPE_UINT32: |
| default: |
| if (reqattr == PVMI_KVPATTR_CAP) |
| { |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_RANGE_UINT32_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_RANGE_UINT32_STRING)); |
| } |
| else |
| { |
| oscl_strncat(aParameters[0].key, |
| _STRLIT_CHAR(PVMI_KVPVALTYPE_UINT32_STRING), |
| oscl_strlen(PVMI_KVPVALTYPE_UINT32_STRING)); |
| } |
| break; |
| } |
| aParameters[0].key[MP4CONFIG_KEYSTRING_SIZE-1] = 0; |
| |
| // Copy the requested info |
| switch (aIndex) |
| { |
| case DELAY: |
| if (reqattr == PVMI_KVPATTR_CUR) |
| { |
| aParameters[0].value.uint32_value = iJitterBufferDurationInMs; |
| } |
| else if (reqattr == PVMI_KVPATTR_DEF) |
| { |
| // Return default |
| aParameters[0].value.uint32_value = PVMF_MP4FFPARSER_NODE_PSEUDO_STREAMING_BUFFER_DURATION_IN_MS; |
| } |
| else |
| { |
| // Return capability |
| range_uint32* rui32 = (range_uint32*)oscl_malloc(sizeof(range_uint32)); |
| if (rui32 == NULL) |
| { |
| oscl_free(aParameters[0].key); |
| oscl_free(aParameters); |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::GetConfigParameter() " |
| "Memory allocation for range uint32 failed")); |
| return PVMFErrNoMemory; |
| } |
| rui32->min = MIN_JITTER_BUFFER_DURATION_IN_MS; |
| rui32->max = MAX_JITTER_BUFFER_DURATION_IN_MS; |
| aParameters[0].value.key_specific_value = (void*)rui32; |
| } |
| break; |
| |
| default: |
| // Invalid index |
| oscl_free(aParameters[0].key); |
| oscl_free(aParameters); |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVPlayerEngine::DoGetPlayerParameter() Invalid index to player parameter")); |
| return PVMFErrArgument; |
| } |
| } |
| |
| aNumParamElements = 1; |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVPlayerEngine::DoGetPlayerParameter() Out")); |
| return PVMFSuccess; |
| |
| } |
| |
| |
| PVMFStatus PVMFMP4FFParserNode::VerifyAndSetConfigParameter(int index, PvmiKvp& aParameter, bool set) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::VerifyAndSetConfigParameter() In")); |
| |
| // Determine the valtype |
| PvmiKvpValueType keyvaltype = GetValTypeFromKeyString(aParameter.key); |
| if (keyvaltype == PVMI_KVPVALTYPE_UNKNOWN) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::VerifyAndSetConfigParameter() " |
| "Valtype in key string unknown")); |
| return PVMFErrArgument; |
| } |
| |
| if (iBaseKey == FILE_IO) |
| { // Verify the valtype |
| if (keyvaltype != MP4ParserNodeConfig_FileIO_Keys[index].iValueType) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::VerifyAndSetConfigParameter() " |
| "Valtype does not match for key")); |
| return PVMFErrArgument; |
| } |
| |
| switch (index) |
| { |
| case PV_CACHE_SIZE: |
| { |
| // Validate |
| if (set) |
| { |
| // save value locally |
| iCacheSize = aParameter.value.uint32_value; |
| } |
| } |
| break; |
| case ASYNC_READ_BUFFER_SIZE: |
| { |
| if (set) |
| { |
| // retrieve and update |
| iAsyncReadBuffSize = aParameter.value.uint32_value; |
| } |
| } |
| break; |
| case BASEKEY_PVLOGGER_ENABLE: |
| { |
| if (set) |
| { |
| // retrieve and update |
| iPVLoggerEnableFlag = aParameter.value.bool_value; |
| } |
| } |
| break; |
| case PVLOGGER_STATS_ENABLE: |
| { |
| if (set) |
| { |
| // user agent update |
| iPVLoggerStateEnableFlag = aParameter.value.bool_value; |
| } |
| } |
| break; |
| |
| case NATIVE_ACCESS_MODE: |
| { |
| if (set) |
| { |
| iNativeAccessMode = aParameter.value.uint32_value; |
| } |
| } |
| break; |
| |
| case FILE_HANDLE: |
| { |
| if (set) |
| { |
| } |
| } |
| break; |
| |
| default: |
| OSCL_ASSERT(0); |
| } |
| } |
| else if (iBaseKey == NET) |
| { |
| // Verify the valtype |
| if (keyvaltype != MP4ParserNodeConfig_FileIO_Keys[index].iValueType) |
| { |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::VerifyAndSetConfigParameter() " |
| "Valtype does not match for key")); |
| return PVMFErrArgument; |
| } |
| |
| switch (index) |
| { |
| case DELAY: |
| { |
| // Validate |
| if (set) |
| { |
| // save value locally |
| iJitterBufferDurationInMs = aParameter.value.uint32_value; |
| } |
| } |
| break; |
| |
| default: |
| return PVMFErrNotSupported; |
| } |
| } |
| |
| PVMF_MP4FFPARSERNODE_LOGINFO((0, "PVMFMP4FFParserNode::VerifyAndSetPlayerParameter() Out")); |
| return PVMFSuccess; |
| } |