1 /* ------------------------------------------------------------------ 2 * Copyright (C) 1998-2009 PacketVideo 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 * express or implied. 14 * See the License for the specific language governing permissions 15 * and limitations under the License. 16 * ------------------------------------------------------------------- 17 */ 18 #include "pvmf_protocol_engine_node.h" 19 #include "pvmf_protocol_engine_node_progressive_streaming.h" 20 #include "pvmf_protocol_engine_progressive_download.h" 21 #include "pvmf_protocolengine_node_tunables.h" 22 23 24 #define IS_OVERFLOW_FOR_100x(x) ( (x)>>((sizeof((x))<<2)-PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_DLSIZE_RIGHTSHIFT_FACTOR) ) // (x)>>(32-7)=(x)>>25 25 26 27 //////////////////////////////////////////////////////////////////////////////////// 28 ////// ProgressiveStreamingContainer implementation 29 //////////////////////////////////////////////////////////////////////////////////// 30 OSCL_EXPORT_REF ProgressiveStreamingContainer::ProgressiveStreamingContainer(PVMFProtocolEngineNode *aNode) : 31 ProgressiveDownloadContainer(aNode), iEnableInfoUpdate(true) 32 { 33 ; 34 } 35 36 OSCL_EXPORT_REF bool ProgressiveStreamingContainer::createProtocolObjects() 37 { 38 if (!ProtocolContainer::createProtocolObjects()) return false; 39 40 iProtocol = OSCL_NEW(ProgressiveStreaming, ()); 41 iNodeOutput = OSCL_NEW(pvProgressiveStreamingOutput, (iNode)); 42 iDownloadControl = OSCL_NEW(progressiveStreamingControl, ()); 43 iDownloadProgess = OSCL_NEW(ProgressiveStreamingProgress, ()); 44 iEventReport = OSCL_NEW(progressiveStreamingEventReporter, (iNode)); 45 iCfgFileContainer = OSCL_NEW(PVProgressiveStreamingCfgFileContainer, (iDownloadSource)); 46 iUserAgentField = OSCL_NEW(UserAgentFieldForProgDownload, ()); 47 iDownloadSource = OSCL_NEW(PVMFDownloadDataSourceContainer, ()); 48 49 if (!iProtocol || !iNodeOutput || !iDownloadControl || 50 !iDownloadProgess || !iEventReport || !iCfgFileContainer || 51 !iUserAgentField || !iDownloadSource) return false; 52 53 if (iNodeOutput) 54 { 55 iNodeOutput->setDataStreamSourceRequestObserver((PvmiDataStreamRequestObserver*)iNode); 56 } 57 58 DownloadContainer::setEventReporterSupportObjects(); 59 return true; 60 } 61 62 OSCL_EXPORT_REF PVMFStatus ProgressiveStreamingContainer::doStop() 63 { 64 PVMFStatus status = DownloadContainer::doStop(); 65 if (status != PVMFSuccess) return status; 66 // For progressive streaming, tell the data stream to flush, 67 // so that the socket buffer can be returned to socket node for reset 68 iNodeOutput->flushDataStream(); 69 70 // set resume download mode for stop and play 71 OsclSharedPtr<PVDlCfgFile> aCfgFile = iCfgFileContainer->getCfgFile(); 72 aCfgFile->SetNewSession(true); // don't set resume download session for the next time 73 if (aCfgFile->GetCurrentFileSize() >= aCfgFile->GetOverallFileSize()) aCfgFile->SetCurrentFileSize(0); 74 75 return PVMFSuccess; 76 } 77 78 OSCL_EXPORT_REF PVMFStatus ProgressiveStreamingContainer::doSeek(PVMFProtocolEngineNodeCommand& aCmd) 79 { 80 uint32 newOffset = getSeekOffset(aCmd); 81 82 LOGINFODATAPATH((0, "PVMFProtocolEngineNode::DoReposition()->ProgressiveStreamingContainer::DoSeek : reposition offset=%d, iInterfaceState=%d", 83 newOffset, (uint32)iObserver->GetObserverState())); 84 85 return doSeekBody(newOffset); 86 } 87 88 OSCL_EXPORT_REF uint32 ProgressiveStreamingContainer::getSeekOffset(PVMFProtocolEngineNodeCommand& aCmd) 89 { 90 //extract the parameters. 91 OsclAny* aRequestData; 92 aCmd.PVMFProtocolEngineNodeCommand::Parse(aRequestData); 93 uint32 newOffset = (uint32)aRequestData; 94 return newOffset; 95 } 96 97 OSCL_EXPORT_REF PVMFStatus ProgressiveStreamingContainer::doSeekBody(uint32 aNewOffset) 98 { 99 // reset streaming done and session done flag to restart streaming 100 ProtocolStateCompleteInfo aInfo; 101 iInterfacingObjectContainer->setProtocolStateCompleteInfo(aInfo, true); 102 103 // HTTP GET request looks at the current file size to determine is Range header is needed 104 // TBD, there may be a better way to do this 105 OsclSharedPtr<PVDlCfgFile> aCfgFile = iCfgFileContainer->getCfgFile(); 106 aCfgFile->SetCurrentFileSize(aNewOffset); 107 108 // Reconnect and send new GET request 109 iProtocol->seek(aNewOffset); 110 startDataFlowByCommand(); 111 112 return PVMFPending; 113 } 114 115 OSCL_EXPORT_REF bool ProgressiveStreamingContainer::completeRepositionRequest() 116 { 117 PVMFProtocolEngineNodeCommand *pCmd = iObserver->FindPendingCmd(PVPROTOCOLENGINE_NODE_CMD_DATASTREAM_REQUEST_REPOSITION); 118 if (pCmd == NULL) return false; 119 120 OsclAny* aRequestData; 121 PvmiDataStreamCommandId aDataStreamCmdId; 122 pCmd->PVMFProtocolEngineNodeCommand::Parse(aRequestData, aDataStreamCmdId); 123 124 // set current file offset to the byte range request offset 125 uint32 newOffset = (uint32)(aRequestData); 126 iNodeOutput->seekDataStream(newOffset); 127 iNodeOutput->setCurrentOutputSize(newOffset); 128 iDownloadControl->setPrevDownloadSize(newOffset); 129 130 // find out if download was completed for the previous GET request 131 // reset initial buffering algo variables 132 iDownloadControl->clearPerRequest(); 133 134 // Form a command response 135 PVMFCmdResp resp(aDataStreamCmdId, pCmd->iContext, PVMFSuccess, NULL, NULL); 136 // Make the Command Complete notification 137 iNodeOutput->dataStreamCommandCompleted(resp); 138 iObserver->ErasePendingCmd(pCmd); 139 140 moveToStartedState(); 141 return true; 142 } 143 144 void ProgressiveStreamingContainer::moveToStartedState() 145 { 146 DownloadContainer::setEventReporterSupportObjects(); 147 iObserver->SetObserverState((uint32)EPVMFNodeStarted); 148 iEventReport->startRealDataflow(); // since the state gets changed to started state, enable the buffer status update 149 } 150 151 OSCL_EXPORT_REF void ProgressiveStreamingContainer::updateDownloadControl(const bool isDownloadComplete) 152 { 153 bool downloadComplete = isDownloadComplete; 154 if (downloadComplete && iObserver->IsRepositionCmdPending()) 155 { 156 // if there is a repositioning request pending for progressive streaming, 157 // do not send resume notification due to download complete 158 downloadComplete = false; 159 } 160 161 // check resume notification 162 if (iDownloadControl->checkResumeNotification(downloadComplete) == 1) 163 { 164 LOGINFODATAPATH((0, "ProgressiveStreamingContainer::updateDownloadControl, send data ready event to parser node, downloadComplete=false")); 165 // report data ready event 166 iEventReport->sendDataReadyEvent(); 167 } 168 169 // update download progress 170 iDownloadProgess->update(isDownloadComplete); 171 } 172 173 OSCL_EXPORT_REF bool ProgressiveStreamingContainer::needToCheckResumeNotificationMaually() 174 { 175 iEventReport->enableBufferingCompleteEvent(); 176 177 if (DownloadContainer::needToCheckResumeNotificationMaually()) return true; 178 return (iNodeOutput->getAvailableOutputSize() == 0 && iEnableInfoUpdate); 179 } 180 181 OSCL_EXPORT_REF bool ProgressiveStreamingContainer::doInfoUpdate(const uint32 downloadStatus) 182 { 183 // For pending reposition request, don't do auto-resume checking 184 if (!iEnableInfoUpdate) return true; 185 return DownloadContainer::doInfoUpdate(downloadStatus); 186 } 187 188 //////////////////////////////////////////////////////////////////////////////////// 189 ////// pvProgressiveStreamingOutput implementation 190 /////////////////////////////////////////////////////////////////////////////////// 191 OSCL_EXPORT_REF pvProgressiveStreamingOutput::pvProgressiveStreamingOutput(PVMFProtocolEngineNodeOutputObserver *aObserver) : 192 pvHttpDownloadOutput(aObserver), 193 iSourceRequestObserver(NULL) 194 { 195 ; 196 } 197 198 OSCL_EXPORT_REF int32 pvProgressiveStreamingOutput::openDataStream(OsclAny* aInitInfo) 199 { 200 int32 status = pvHttpDownloadOutput::openDataStream(aInitInfo); 201 if (status == PVMFSuccess && isOpenDataStream) 202 { 203 // protocol engine node is the observer 204 PvmiDataStreamStatus dsStatus = iDataStream->SetSourceRequestObserver(*iSourceRequestObserver); 205 if ((dsStatus != PVDS_NOT_SUPPORTED) && (dsStatus != PVDS_SUCCESS)) return PROCESS_DATA_STREAM_OPEN_FAILURE; 206 } 207 return status; 208 } 209 210 OSCL_EXPORT_REF int32 pvProgressiveStreamingOutput::flushData(const uint32 aOutputType) 211 { 212 int32 status = PVMFProtocolEngineNodeOutput::flushData(aOutputType); 213 if (status != PROCESS_SUCCESS) return status; 214 215 while (!iOutputFramesQueue.empty()) 216 { 217 if (writeToDataStream(iOutputFramesQueue[0], iPendingOutputDataQueue) == 0xffffffff) return PROCESS_OUTPUT_TO_DATA_STREAM_FAILURE; 218 iOutputFramesQueue.erase(iOutputFramesQueue.begin()); 219 } 220 return PROCESS_SUCCESS; 221 } 222 223 uint32 pvProgressiveStreamingOutput::writeToDataStream(OUTPUT_DATA_QUEUE &aOutputQueue, PENDING_OUTPUT_DATA_QUEUE &aPendingOutputQueue) 224 { 225 uint32 totalFragSize = 0; 226 227 // Memory Buffer Data Stream takes memory fragments 228 // Go through the queue, remove the frags, write them to the data stream 229 // If the data stream is holding onto the frags, add the frags to a different queue, to be deleted later 230 // Otherwise the frags are deleted in here 231 while (!aOutputQueue.empty()) 232 { 233 OsclRefCounterMemFrag frag = aOutputQueue.front(); 234 // make a copy otherwise erase will destroy it 235 236 OsclRefCounterMemFrag* copyFrag = OSCL_NEW(OsclRefCounterMemFrag, (frag)); 237 238 uint32 fragSize = 0; 239 PvmiDataStreamStatus status = iDataStream->Write(iSessionID, copyFrag, fragSize); 240 if (PVDS_PENDING == status) 241 { 242 // This buffer is now part of the data stream cache 243 // and cannot be freed until it is returned later on 244 // Move the mem frag to the pending queue 245 aPendingOutputQueue.push_back(copyFrag); 246 } 247 else 248 { 249 // Done with this frag 250 // free the reference 251 OSCL_DELETE(copyFrag); 252 } 253 254 // Remove from output queue 255 aOutputQueue.erase(aOutputQueue.begin()); 256 257 if ((PVDS_SUCCESS != status) && (PVDS_PENDING != status)) 258 { 259 // An error has occurred 260 return ~0; 261 } 262 263 totalFragSize += fragSize; 264 } 265 266 LOGINFODATAPATH((0, "pvProgressiveStreamingOutput::writeToDataStream() SIZE= %d , SEQNUM=%d", totalFragSize, iCounter++)); 267 iCurrTotalOutputSize += totalFragSize; 268 return totalFragSize; 269 } 270 271 OSCL_EXPORT_REF bool pvProgressiveStreamingOutput::releaseMemFrag(OsclRefCounterMemFrag* aFrag) 272 { 273 bool bFound = false; 274 LOGINFODATAPATH((0, "pvProgressiveStreamingOutput::releaseMemFrag(), frag=%x", aFrag->getMemFragPtr())); 275 for (uint32 i = 0; i < iPendingOutputDataQueue.size(); i++) 276 { 277 // Find the frag in the queue and remove it 278 OsclRefCounterMemFrag* frag = iPendingOutputDataQueue[i]; 279 if (aFrag->getMemFragPtr() == frag->getMemFragPtr()) 280 { 281 LOGINFODATAPATH((0, "pvProgressiveStreamingOutput::releaseMemFrag(), found frag %x in pending Q", aFrag->getMemFragPtr())); 282 iPendingOutputDataQueue.erase(&iPendingOutputDataQueue[i]); 283 OSCL_DELETE(frag); 284 bFound = true; 285 break; 286 } 287 // TBD, how do we free the reference 288 } 289 return bFound; 290 } 291 292 OSCL_EXPORT_REF void pvProgressiveStreamingOutput::setContentLength(uint32 aLength) 293 { 294 if (iDataStream) iDataStream->SetContentLength(aLength); 295 } 296 297 OSCL_EXPORT_REF void pvProgressiveStreamingOutput::dataStreamCommandCompleted(const PVMFCmdResp& aResponse) 298 { 299 // propagate the command complete 300 if (iDataStream) iDataStream->SourceRequestCompleted(aResponse); 301 } 302 303 OSCL_EXPORT_REF void pvProgressiveStreamingOutput::flushDataStream() 304 { 305 // tell the data stream to flush all buffered data 306 // for MBDS, empty temp cache and release mem buffers 307 if (iDataStream) iDataStream->Flush(iSessionID); 308 } 309 310 OSCL_EXPORT_REF bool pvProgressiveStreamingOutput::seekDataStream(const uint32 aSeekOffset) 311 { 312 if (!iDataStream) return false; 313 return (iDataStream->Seek(iSessionID, aSeekOffset, PVDS_SEEK_SET) == PVDS_SUCCESS); 314 } 315 316 317 //////////////////////////////////////////////////////////////////////////////////// 318 ////// progressiveStreamingControl implementation 319 //////////////////////////////////////////////////////////////////////////////////// 320 OSCL_EXPORT_REF progressiveStreamingControl::progressiveStreamingControl() : progressiveDownloadControl() 321 { 322 ; 323 } 324 325 OSCL_EXPORT_REF void progressiveStreamingControl::requestResumeNotification(const uint32 currentNPTReadPosition, bool& aDownloadComplete, bool& aNeedSendUnderflowEvent) 326 { 327 LOGINFODATAPATH((0, "progressiveStreamingControl::requestResumeNotification() IN, iPlaybackUnderflow=%d, iRequestResumeNotification=%d, iDownloadComplete=%d, will manually set iDownloadComplete=false", 328 (uint32)iPlaybackUnderflow, (uint32)iRequestResumeNotification, (uint32)iDownloadComplete)); 329 330 iDownloadComplete = aDownloadComplete = false; 331 iSendDownloadCompleteNotification = false; 332 pvDownloadControl::requestResumeNotification(currentNPTReadPosition, aDownloadComplete, aNeedSendUnderflowEvent); 333 } 334 335 OSCL_EXPORT_REF void progressiveStreamingControl::clearPerRequest() 336 { 337 // for progressive playback 338 // after each repositioning (aka new GET request) 339 // the following variables must be reset 340 // to enable auto pause and resume to function properly 341 iDlAlgoPreConditionMet = false; 342 iDownloadComplete = false; 343 iSendDownloadCompleteNotification = false; 344 } 345 346 347 //////////////////////////////////////////////////////////////////////////////////// 348 ////// ProgressiveStreamingProgress implementation 349 //////////////////////////////////////////////////////////////////////////////////// 350 OSCL_EXPORT_REF bool ProgressiveStreamingProgress::calculateDownloadPercent(uint32 &aDownloadProgressPercent) 351 { 352 // in progessive streaming, the getContentLength will change after new GET request 353 // from known to 0 and then to known again 354 uint32 fileSize = iProtocol->getContentLength(); 355 if (!fileSize && iContentLength) 356 { 357 fileSize = iContentLength; 358 } 359 if (fileSize) iContentLength = fileSize; 360 361 return ProgressiveDownloadProgress::calculateDownloadPercentBody(aDownloadProgressPercent, fileSize); 362 } 363 364 //////////////////////////////////////////////////////////////////////////////////// 365 ////// progressiveStreamingEventReporter implementation 366 //////////////////////////////////////////////////////////////////////////////////// 367 OSCL_EXPORT_REF void progressiveStreamingEventReporter::reportBufferStatusEvent(int aDownloadPercent) 368 { 369 // calculate buffer fullness 370 371 uint32 aBufferFullness = getBufferFullness(); 372 if (aBufferFullness == 0xffffffff) return; 373 374 iObserver->ReportEvent(PVMFInfoBufferingStatus, 375 // TODO: Why is that not part of the buffer we 376 // send? Which part uses that? It does not seem to 377 // reach the player driver. 378 (OsclAny*)aBufferFullness, // ugly uses a pointer to carry a value. 379 PVMFPROTOCOLENGINENODEInfo_BufferingStatus, 380 &aDownloadPercent, 381 sizeof(aDownloadPercent)); 382 LOGINFODATAPATH((0, "progressiveStreamingEventReporter::reportBufferStatusEvent() DOWNLOAD PERCENTAGE: %d", aDownloadPercent)); 383 } 384 385 uint32 progressiveStreamingEventReporter::getBufferFullness() 386 { 387 388 uint32 aCacheSize = iNodeOutput->getMaxAvailableOutputSize(); 389 if (aCacheSize == 0) return 0xffffffff; 390 uint32 aCacheFilledSize = iNodeOutput->getAvailableOutputSize(); 391 if (aCacheFilledSize >= aCacheSize) return 100; 392 393 // avoid fix-point multiplication overflow 394 uint32 aBufferEmptiness = 0xffffffff; 395 if (IS_OVERFLOW_FOR_100x(aCacheFilledSize) > 0) 396 { 397 aBufferEmptiness = (aCacheFilledSize >> PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_DLSIZE_RIGHTSHIFT_FACTOR) * 100 / 398 (aCacheSize >> PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_DLSIZE_RIGHTSHIFT_FACTOR); 399 } 400 else 401 { 402 aBufferEmptiness = aCacheFilledSize * 100 / aCacheSize; 403 } 404 405 return 100 - aBufferEmptiness; 406 } 407