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);
}
Exemple #6
0
	void TimeEngine::runLoad()
	{
		TableEngine& tableEngine_ = TableEngine::instance();
		tableEngine_.runReader<TimeEngine>(this, streamUrl(), streamName());
	}
Exemple #7
0
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;
}
Exemple #8
0
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;
}