static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode) { // Close off previously opened device. // It would be nice to determine if the underlying device actually // changes, but we might be recovering from an error or manipulating // mixer settings (see asound.conf). // s_close(handle); LOGD("open called for devices %08x in mode %d...", devices, mode); const char *stream = streamName(handle); const char *devName = deviceName(handle, devices, mode); int err; for (;;) { // The PCM stream is opened in blocking mode, per ALSA defaults. The // AudioFlinger seems to assume blocking mode too, so asynchronous mode // should not be used. err = snd_pcm_open(&handle->handle, devName, direction(handle), SND_PCM_ASYNC); if (err == 0) break; // See if there is a less specific name we can try. // Note: We are changing the contents of a const char * here. char *tail = strrchr(devName, '_'); if (!tail) break; *tail = 0; } if (err < 0) { // None of the Android defined audio devices exist. Open a generic one. devName = "default"; err = snd_pcm_open(&handle->handle, devName, direction(handle), 0); } if (err < 0) { LOGE("Failed to Initialize any ALSA %s device: %s", stream, strerror(err)); return NO_INIT; } err = setHardwareParams(handle); if (err == NO_ERROR) err = setSoftwareParams(handle); LOGI("Initialized ALSA %s device %s", stream, devName); handle->curDev = devices; handle->curMode = mode; return err; }
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams) { char* theUriStr = NULL; QTSS_Error err = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileName, 0, &theUriStr); Assert(err == QTSS_NoErr); if(err != QTSS_NoErr) return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, 0); QTSSCharArrayDeleter theUriStrDeleter(theUriStr); //从接口获取信息结构体 //信息存在rtsp://59.46.115.84:8554/h264/ch1/sub/av_stream RTSPRelaySession* clientSes = NULL; //首先查找Map里面是否已经有了对应的流 StrPtrLen streamName(theUriStr); OSRef* clientSesRef = sRelaySessionMap->Resolve(&streamName); if(clientSesRef != NULL) { clientSes = (RTSPRelaySession*)clientSesRef->GetObject(); } else { clientSes = NEW RTSPRelaySession("rtsp://*****:*****@192.168.66.189/", RTSPRelaySession::kRTSPTCPClientType, theUriStr); QTSS_Error theErr = clientSes->SendDescribe(); if(theErr == QTSS_NoErr) { OS_Error theErr = sRelaySessionMap->Register(clientSes->GetRef()); Assert(theErr == QTSS_NoErr); } else { clientSes->Signal(Task::kKillEvent); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotFound, 0); } //增加一次对RelaySession的无效引用,后面会统一释放 OSRef* debug = sRelaySessionMap->Resolve(&streamName); Assert(debug == clientSes->GetRef()); } return QTSS_NoErr; }
char* GetHLSUrl(char* inSessionName) { OSRefTable* sHLSSessionMap = QTSServerInterface::GetServer()->GetHLSSessionMap(); OSMutexLocker locker (sHLSSessionMap->GetMutex()); char* hlsURL = NULL; //首先查找Map里面是否已经有了对应的流 StrPtrLen streamName(inSessionName); OSRef* clientSesRef = sHLSSessionMap->Resolve(&streamName); if(NULL == clientSesRef) return NULL; EasyHLSSession* session = (EasyHLSSession*)clientSesRef->GetObject(); hlsURL = session->GetHLSURL(); sHLSSessionMap->Release(session->GetRef()); return hlsURL; }
QTSS_Error EasyHLSOpen(Easy_HLSOpen_Params* inParams) { OSRefTable* sHLSSessionMap = QTSServerInterface::GetServer()->GetHLSSessionMap(); OSMutexLocker locker (sHLSSessionMap->GetMutex()); EasyHLSSession* session = NULL; //首先查找MAP里面是否已经有了对应的流 StrPtrLen streamName(inParams->inStreamName); OSRef* clientSesRef = sHLSSessionMap->Resolve(&streamName); if(clientSesRef != NULL) { session = (EasyHLSSession*)clientSesRef->GetObject(); } else { session = NEW EasyHLSSession(&streamName); OS_Error theErr = sHLSSessionMap->Register(session->GetRef()); Assert(theErr == QTSS_NoErr); //增加一次对RelaySession的无效引用,后面会统一释放 OSRef* debug = sHLSSessionMap->Resolve(&streamName); Assert(debug == session->GetRef()); } //到这里,肯定是有一个EasyHLSSession可用的 session->HLSSessionStart(inParams->inRTSPUrl, inParams->inTimeout); if(inParams->outHLSUrl) qtss_sprintf(inParams->outHLSUrl,"%s",session->GetHLSURL()); sHLSSessionMap->Release(session->GetRef()); return QTSS_NoErr; }
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams) { //解析命令 char* theFullPathStr = NULL; QTSS_Error theErr = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileName, 0, &theFullPathStr); Assert(theErr == QTSS_NoErr); QTSSCharArrayDeleter theFullPathStrDeleter(theFullPathStr); if (theErr != QTSS_NoErr) return NULL; StrPtrLen theFullPath(theFullPathStr); if (theFullPath.Len != sRelaySuffix.Len ) return NULL; StrPtrLen endOfPath2(&theFullPath.Ptr[theFullPath.Len - sRelaySuffix.Len], sRelaySuffix.Len); if (!endOfPath2.Equal(sRelaySuffix)) { return NULL; } //解析查询字符串 char* theQueryStr = NULL; theErr = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqQueryString, 0, &theQueryStr); Assert(theErr == QTSS_NoErr); QTSSCharArrayDeleter theQueryStringDeleter(theQueryStr); if (theErr != QTSS_NoErr) return NULL; StrPtrLen theQueryString(theQueryStr); QueryParamList parList(theQueryStr); const char* sName = parList.DoFindCGIValueForParam(QUERY_STREAM_NAME); const char* sChannel = parList.DoFindCGIValueForParam(QUERY_STREAM_CHANNEL); if(sName == NULL && sChannel == NULL) { return NULL; } char szChannelURL[256] = {0,}; char szUsername[32] = {0,}; char szPassword[32] = {0,}; const char* sURL = NULL; char sPushServerAddr[256] = {0,}; if (NULL != sChannel) { //find channel info GetChannelInfoById( (char*)sChannel, szChannelURL, sizeof(szChannelURL), szUsername, sizeof(szUsername), szPassword, sizeof(szPassword), sPushServerAddr, sizeof(sPushServerAddr)); if ( (int)strlen(szChannelURL) < 1 ) return NULL; //not found the channel //sURL = "rtsp://192.168.1.186:8557"; sURL = szChannelURL; } else { sURL = parList.DoFindCGIValueForParam(QUERY_STREAM_URL); } //if(sURL == NULL) return NULL; const char* sCMD = parList.DoFindCGIValueForParam(QUERY_STREAM_CMD); bool bStop = false; if(sCMD) { if(::strcmp(sCMD,QUERY_STREAM_CMD_STOP) == 0) bStop = true; } StrPtrLen streamName(NULL!=sName?(char*)sName:(char*)sChannel); //从接口获取信息结构体 EasyRelaySession* session = NULL; //首先查找Map里面是否已经有了对应的流 OSRef* sessionRef = sRelaySessionMap->Resolve(&streamName); if(sessionRef != NULL) { session = (EasyRelaySession*)sessionRef->GetObject(); } else { if(bStop) return NULL; if(sURL == NULL) return NULL; session = NEW EasyRelaySession((char*)sURL, EasyRelaySession::kRTSPTCPClientType, NULL!=sName?(char*)sName:(char*)sChannel, sPushServerAddr); QTSS_Error theErr = session->RelaySessionStart(); if(theErr == QTSS_NoErr) { OS_Error theErr = sRelaySessionMap->Register(session->GetRef()); Assert(theErr == QTSS_NoErr); } else { session->Signal(Task::kKillEvent); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotFound, 0); } //增加一次对RelaySession的无效引用,后面会统一释放 OSRef* debug = sRelaySessionMap->Resolve(&streamName); Assert(debug == session->GetRef()); } sRelaySessionMap->Release(session->GetRef()); if(bStop) { sRelaySessionMap->UnRegister(session->GetRef()); session->Signal(Task::kKillEvent); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssSuccessOK, 0); } QTSS_RTSPStatusCode statusCode = qtssRedirectPermMoved; QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqStatusCode, 0, &statusCode, sizeof(statusCode)); // Get the ip addr out of the prefs dictionary UInt16 thePort = 554; UInt32 theLen = sizeof(UInt16); theErr = QTSServerInterface::GetServer()->GetPrefs()->GetValue(qtssPrefsRTSPPorts, 0, &thePort, &theLen); Assert(theErr == QTSS_NoErr); //构造本地URL char url[QTSS_MAX_URL_LENGTH] = { 0 }; qtss_sprintf(url,"rtsp://%s:%d/%s.sdp", sLocal_IP_Addr, thePort, NULL!=sName?(char*)sName:(char*)sChannel); StrPtrLen locationRedirect(url); Bool16 sFalse = false; (void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse)); QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssLocationHeader, locationRedirect.Ptr, locationRedirect.Len); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssRedirectPermMoved, 0); }
void TimeEngine::runLoad() { TableEngine& tableEngine_ = TableEngine::instance(); tableEngine_.runReader<TimeEngine>(this, streamUrl(), streamName()); }
static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode) { // Close off previously opened device. // It would be nice to determine if the underlying device actually // changes, but we might be recovering from an error or manipulating // mixer settings (see asound.conf). // ALOGD("open called for devices %08x in mode %d...", devices, mode); if( devices == 0 ){ return BAD_VALUE; } s_close(handle); pthread_mutex_lock(&handle->mLock); const char *stream = streamName(handle); const char *devName = deviceName(handle, devices, mode); int err,card; char prop[20],dev_Name[20],card_name[32]; ALOGD("input handle: %s, devName = %s \n", (char*)handle->modPrivate, devName); #if 1 if ((direction(handle) == SND_PCM_STREAM_CAPTURE)/*||(direction(handle) == SND_PCM_STREAM_PLAYBACK)*/){ card = getDeviceNum(direction(handle), card_name); ALOGD("card : %d\n", card); if(card >= 0){ // sprintf(dev_Name,"plug:SLAVE='hw:%d,0'",card); sprintf(dev_Name, "hw:%d", card); devName = dev_Name; } // if we want usb-audio, but returned builtin-audio, return error. // audiopolicymanager should try next card ALOGD("card name: %s\n", card_name); ALOGD("devName: %s\n", devName); if(strncmp(card_name,"AML", 3) == 0 && strcmp((char*)handle->modPrivate, "usb-audio") == 0){ pthread_mutex_unlock(&handle->mLock); ALOGD("You are request usb-audio with usb's params, but returned builtin-audio card\n"); return NO_INIT; } } if(direction(handle) == SND_PCM_STREAM_PLAYBACK){ card = snd_card_get_aml_card(); ALOGD("SND_PCM_STREAM_PLAYBACK card : %d\n", card); sprintf(dev_Name, "hw:%d", card); devName = dev_Name; } #else if(direction(handle) == SND_PCM_STREAM_CAPTURE){ sprintf(dev_Name, "hw:0"); devName = dev_Name; } #endif for (;;) { // The PCM stream is opened in blocking mode, per ALSA defaults. The // AudioFlinger seems to assume blocking mode too, so asynchronous mode // should not be used. ALOGD("---- devName = %s \n", devName); err = snd_pcm_open(&handle->handle, devName, direction(handle), SND_PCM_ASYNC); if (err == 0) break; // See if there is a less specific name we can try. // Note: We are changing the contents of a const char * here. char *tail = strrchr(devName, '_'); if (!tail) break; *tail = 0; } if (err < 0) { // None of the Android defined audio devices exist. Open a generic one. devName = "default"; ALOGD("-r-- devName = %s \n", devName); err = snd_pcm_open(&handle->handle, devName, direction(handle), 0); } if (err < 0) { ALOGE("Failed to Initialize any ALSA %s device: %s", stream, strerror(err)); pthread_mutex_unlock(&handle->mLock); return NO_INIT; } err = setHardwareParams(handle); if (err == NO_ERROR) err = setSoftwareParams(handle); ALOGI("Initialized ALSA %s device %s", stream, devName); handle->curDev = devices; handle->curMode = mode; pthread_mutex_unlock(&handle->mLock); return err; }
status_t setHardwareParams(alsa_handle_t *handle) { snd_pcm_hw_params_t *hardwareParams; status_t err; snd_pcm_uframes_t bufferSize = handle->bufferSize; unsigned int requestedRate = handle->sampleRate; unsigned int latency = handle->latency; // snd_pcm_format_description() and snd_pcm_format_name() do not perform // proper bounds checking. bool validFormat = (static_cast<int> (handle->format) > SND_PCM_FORMAT_UNKNOWN) && (static_cast<int> (handle->format) <= SND_PCM_FORMAT_LAST); const char *formatDesc = validFormat ? snd_pcm_format_description( handle->format) : "Invalid Format"; const char *formatName = validFormat ? snd_pcm_format_name(handle->format) : "UNKNOWN"; if (snd_pcm_hw_params_malloc(&hardwareParams) < 0) { LOG_ALWAYS_FATAL("Failed to allocate ALSA hardware parameters!"); return NO_INIT; } err = snd_pcm_hw_params_any(handle->handle, hardwareParams); if (err < 0) { ALOGE("Unable to configure hardware: %s", snd_strerror(err)); goto done; } // Set the interleaved read and write format. err = snd_pcm_hw_params_set_access(handle->handle, hardwareParams, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { ALOGE("Unable to configure PCM read/write format: %s", snd_strerror(err)); goto done; } err = snd_pcm_hw_params_set_format(handle->handle, hardwareParams, handle->format); if (err < 0) { ALOGE("Unable to configure PCM format %s (%s): %s", formatName, formatDesc, snd_strerror(err)); goto done; } ALOGW("Set %s PCM format to %s (%s)", streamName(handle), formatName, formatDesc); err = snd_pcm_hw_params_set_channels(handle->handle, hardwareParams, handle->channels); if (err < 0) { ALOGE("Unable to set channel count to %i: %s", handle->channels, snd_strerror(err)); goto done; } ALOGW("Using %i %s for %s.", handle->channels, handle->channels == 1 ? "channel" : "channels", streamName(handle)); ALOGW("requestedRate=%d\n", requestedRate); err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams, &requestedRate, 0); ALOGW("returned Rate=%d, handle->rate=%d\n", requestedRate, handle->sampleRate); if (err < 0) ALOGE("Unable to set %s sample rate to %u: %s", streamName(handle), handle->sampleRate, snd_strerror(err)); else if (requestedRate != handle->sampleRate) // Some devices have a fixed sample rate, and can not be changed. // This may cause resampling problems; i.e. PCM playback will be too // slow or fast. ALOGW("Requested rate (%u HZ) does not match actual rate (%u HZ)", handle->sampleRate, requestedRate); else ALOGI("Set %s sample rate to %u HZ", streamName(handle), requestedRate); #ifdef DISABLE_HARWARE_RESAMPLING // Disable hardware re-sampling. err = snd_pcm_hw_params_set_rate_resample(handle->handle, hardwareParams, static_cast<int>(resample)); if (err < 0) { ALOGE("Unable to %s hardware resampling: %s", resample ? "enable" : "disable", snd_strerror(err)); goto done; } #endif // Make sure we have at least the size we originally wanted err = snd_pcm_hw_params_set_buffer_size_near(handle->handle, hardwareParams, &bufferSize); if (err < 0) { ALOGE("Unable to set buffer size to %d: %s", (int)bufferSize, snd_strerror(err)); goto done; } // Setup buffers for latency err = snd_pcm_hw_params_set_buffer_time_near(handle->handle, hardwareParams, &latency, NULL); if (err < 0) { /* That didn't work, set the period instead */ unsigned int periodTime = latency / 4; err = snd_pcm_hw_params_set_period_time_near(handle->handle, hardwareParams, &periodTime, NULL); if (err < 0) { ALOGE("Unable to set the period time for latency: %s", snd_strerror(err)); goto done; } snd_pcm_uframes_t periodSize; err = snd_pcm_hw_params_get_period_size(hardwareParams, &periodSize, NULL); if (err < 0) { ALOGE("Unable to get the period size for latency: %s", snd_strerror(err)); goto done; } bufferSize = periodSize * 4; if (bufferSize < handle->bufferSize) bufferSize = handle->bufferSize; err = snd_pcm_hw_params_set_buffer_size_near(handle->handle, hardwareParams, &bufferSize); if (err < 0) { ALOGE("Unable to set the buffer size for latency: %s", snd_strerror(err)); goto done; } } else { // OK, we got buffer time near what we expect. See what that did for bufferSize. err = snd_pcm_hw_params_get_buffer_size(hardwareParams, &bufferSize); if (err < 0) { ALOGE("Unable to get the buffer size for latency: %s", snd_strerror(err)); goto done; } // Does set_buffer_time_near change the passed value? It should. err = snd_pcm_hw_params_get_buffer_time(hardwareParams, &latency, NULL); if (err < 0) { ALOGE("Unable to get the buffer time for latency: %s", snd_strerror(err)); goto done; } unsigned int periodTime = latency / 4; err = snd_pcm_hw_params_set_period_time_near(handle->handle, hardwareParams, &periodTime, NULL); if (err < 0) { ALOGE("Unable to set the period time for latency: %s", snd_strerror(err)); goto done; } } ALOGI("Buffer size: %d", (int)bufferSize); ALOGI("Latency: %d", (int)latency); handle->bufferSize = bufferSize; handle->latency = latency; // Commit the hardware parameters back to the device. err = snd_pcm_hw_params(handle->handle, hardwareParams); if (err < 0) ALOGE("Unable to set hardware parameters: %s", snd_strerror(err)); done: snd_pcm_hw_params_free(hardwareParams); return err; }
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams) { char* theUriStr = NULL; QTSS_Error err = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileName, 0, &theUriStr); Assert(err == QTSS_NoErr); if(err != QTSS_NoErr) return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, 0); QTSSCharArrayDeleter theUriStrDeleter(theUriStr); //从接口获取信息结构体 //TODO: DeviceInfo* pDeviceInfo = parseDevice->GetDeviceInfoByIdName(theUriStr); if(pDeviceInfo == NULL) { qtss_printf("ID:%s Not Found\n",theUriStr); return QTSS_RequestFailed; } //信息存在rtsp://59.46.115.84:8554/h264/ch1/sub/av_stream RTSPRelaySession* clientSes = NULL; //首先查找Map里面是否已经有了对应的流 StrPtrLen streamName(theUriStr); OSRef* clientSesRef = sClientSessionMap->Resolve(&streamName); if(clientSesRef != NULL) { clientSes = (RTSPRelaySession*)clientSesRef->GetObject(); } else { clientSes = NEW RTSPRelaySession( SocketUtils::ConvertStringToAddr(pDeviceInfo->m_szIP), pDeviceInfo->m_nPort, pDeviceInfo->m_szSourceUrl, 1, rtcpInterval, 0, theReadInterval, sockRcvBuf, speed, packetPlayHeader, overbufferwindowInK, sendOptions, pDeviceInfo->m_szUser, pDeviceInfo->m_szPassword, theUriStr); OS_Error theErr = clientSes->SendDescribe(); if(theErr == QTSS_NoErr) { OS_Error theErr = sClientSessionMap->Register(clientSes->GetRef()); Assert(theErr == QTSS_NoErr); } else { clientSes->Signal(Task::kKillEvent); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotFound, 0); } //增加一次对RelaySession的无效引用,后面会统一释放 OSRef* debug = sClientSessionMap->Resolve(&streamName); Assert(debug == clientSes->GetRef()); } ReflectorSession* theSession = SetupProxySession(inParams, clientSes); if (theSession == NULL) { sClientSessionMap->Release(clientSes->GetRef()); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerNotImplemented, 0); } QTSS_Error Err = QTSS_SetValue(inParams->inRTSPSession, sRTSPBroadcastSessionAttr, 0, &theSession, sizeof(theSession)); Assert(Err == QTSS_NoErr); clientSes->SetReflectorSession(theSession); theSession->SetRTSPRelaySession((void*)clientSes); sClientSessionMap->Release(clientSes->GetRef()); iovec theDescribeVec[3] = { {0 }}; Assert(theSession->GetLocalSDP()->Ptr != NULL); StrPtrLen theFileData; QTSS_TimeVal outModDate = 0; QTSS_TimeVal inModDate = -1; theFileData.Ptr = theSession->GetLocalSDP()->Ptr; theFileData.Len = theSession->GetLocalSDP()->Len; // -------------- process SDP to remove connection info and add track IDs, port info, and default c= line StrPtrLen theSDPData; SDPSourceInfo tempSDPSourceInfo(theFileData.Ptr, theFileData.Len); // will make a copy and delete in destructor theSDPData.Ptr = tempSDPSourceInfo.GetLocalSDP(&theSDPData.Len); // returns a new buffer with processed sdp OSCharArrayDeleter sdpDeleter(theSDPData.Ptr); // delete the temp sdp source info buffer returned by GetLocalSDP if (theSDPData.Len <= 0) // can't find it on disk or it failed to parse just use the one in the session. { theSDPData.Ptr = theSession->GetLocalSDP()->Ptr; // this sdp isn't ours it must not be deleted theSDPData.Len = theSession->GetLocalSDP()->Len; } // ------------ Clean up missing required SDP lines ResizeableStringFormatter editedSDP(NULL,0); DoDescribeAddRequiredSDPLines2(inParams, theSession, outModDate, &editedSDP, &theSDPData); StrPtrLen editedSDPSPL(editedSDP.GetBufPtr(),editedSDP.GetBytesWritten()); // ------------ Check the headers SDPContainer checkedSDPContainer; checkedSDPContainer.SetSDPBuffer( &editedSDPSPL ); if (!checkedSDPContainer.IsSDPBufferValid()) { return QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssUnsupportedMediaType, &sSDPNotValidMessage); } // ------------ Put SDP header lines in correct order Float32 adjustMediaBandwidthPercent = 1.0; Bool16 adjustMediaBandwidth = false; SDPLineSorter sortedSDP(&checkedSDPContainer,adjustMediaBandwidthPercent); // ------------ Write the SDP UInt32 sessLen = sortedSDP.GetSessionHeaders()->Len; UInt32 mediaLen = sortedSDP.GetMediaHeaders()->Len; theDescribeVec[1].iov_base = sortedSDP.GetSessionHeaders()->Ptr; theDescribeVec[1].iov_len = sortedSDP.GetSessionHeaders()->Len; theDescribeVec[2].iov_base = sortedSDP.GetMediaHeaders()->Ptr; theDescribeVec[2].iov_len = sortedSDP.GetMediaHeaders()->Len; (void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader, kCacheControlHeader.Ptr, kCacheControlHeader.Len); QTSSModuleUtils::SendDescribeResponse(inParams->inRTSPRequest, inParams->inClientSession, &theDescribeVec[0], 3, sessLen + mediaLen ); return QTSS_NoErr; }