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_download.h" 20 #include "pvmf_protocolengine_node_tunables.h" 21 #include "pvmf_protocol_engine_progressive_download.h" 22 23 24 #define GET_10_PERCENT(x) ( ((x)>>3)-((x)>>6) ) // 1/8 - 1/64 = 0.109 25 26 27 //////////////////////////////////////////////////////////////////////////////////// 28 ////// ProgessiveDownloadContainer implementation 29 //////////////////////////////////////////////////////////////////////////////////// 30 OSCL_EXPORT_REF ProgressiveDownloadContainer::ProgressiveDownloadContainer(PVMFProtocolEngineNode *aNode) : 31 DownloadContainer(aNode), 32 iNumCheckExtraDataComeIn(0), 33 iNumCheckEOSAfterDisconnectSocket(0) 34 { 35 ; 36 } 37 38 OSCL_EXPORT_REF bool ProgressiveDownloadContainer::createProtocolObjects() 39 { 40 if (!ProtocolContainer::createProtocolObjects()) return false; 41 42 iProtocol = OSCL_NEW(ProgressiveDownload, ()); 43 iNodeOutput = OSCL_NEW(pvHttpDownloadOutput, (iNode)); 44 iDownloadControl = OSCL_NEW(progressiveDownloadControl, ()); 45 iDownloadProgess = OSCL_NEW(ProgressiveDownloadProgress, ()); 46 iEventReport = OSCL_NEW(downloadEventReporter, (iNode)); 47 iCfgFileContainer = OSCL_NEW(PVProgressiveDownloadCfgFileContainer, (iDownloadSource)); 48 iUserAgentField = OSCL_NEW(UserAgentFieldForProgDownload, ()); 49 iDownloadSource = OSCL_NEW(PVMFDownloadDataSourceContainer, ()); 50 51 if (!iProtocol || !iNodeOutput || !iDownloadControl || 52 !iDownloadProgess || !iEventReport || !iCfgFileContainer || 53 !iUserAgentField || !iDownloadSource) return false; 54 55 DownloadContainer::setEventReporterSupportObjects(); 56 return true; 57 } 58 59 OSCL_EXPORT_REF bool ProgressiveDownloadContainer::needSocketReconnect() 60 { 61 // currently, only disallow socket reconnect for head request disabled during prepare->start 62 if (iObserver->GetObserverState() == (uint32)EPVMFNodePrepared && 63 iInterfacingObjectContainer->getHttpHeadRequestDisabled() && 64 !iForceSocketReconnect) return false; 65 return true; 66 } 67 68 OSCL_EXPORT_REF PVMFStatus ProgressiveDownloadContainer::initImpl() 69 { 70 if (!iInterfacingObjectContainer->getHttpHeadRequestDisabled()) return ProtocolContainer::initImpl(); 71 72 if (!isObjectsReady()) 73 { 74 return PVMFErrNotReady; 75 } 76 77 // initialize output object 78 int32 status = initNodeOutput(); 79 if (status != PVMFSuccess) return status; 80 81 // initialize protocol object 82 if (!initProtocol()) return PVMFFailure; 83 84 // initialize download control object 85 initDownloadControl(); 86 87 return PVMFSuccess; 88 } 89 90 OSCL_EXPORT_REF bool ProgressiveDownloadContainer::initProtocol_SetConfigInfo() 91 { 92 OsclSharedPtr<PVDlCfgFile> aCfgFile = iCfgFileContainer->getCfgFile(); 93 if (aCfgFile.GetRep() == NULL) return false; 94 aCfgFile->setHttpHeadRequestDisabled(iInterfacingObjectContainer->getHttpHeadRequestDisabled()); 95 return DownloadContainer::initProtocol_SetConfigInfo(); 96 } 97 98 99 //////////////////////////////////////////////////////////////////////////////////// 100 ////// progressiveDownloadControl implementation 101 //////////////////////////////////////////////////////////////////////////////////// 102 OSCL_EXPORT_REF bool progressiveDownloadControl::isDlAlgoPreConditionMet(const uint32 aDownloadRate, 103 const uint32 aDurationMsec, 104 const uint32 aCurrDownloadSize, 105 const uint32 aFileSize) 106 { 107 // first make sure initial download pre-conditions should be met 108 if (!pvDownloadControl::isDlAlgoPreConditionMet(aDownloadRate, aDurationMsec, aCurrDownloadSize, aFileSize)) return false; 109 110 // then heck the parser consuming rate is close to clip bitrate 111 int32 status = isPlaybackRateCloseToClipBitrate(aDurationMsec, aCurrDownloadSize, aFileSize); 112 if (status == 0) return true; // parser node data consumption rate is close to clip bitrate 113 if (status == -1) return true; // duration is not available, then don't wait and kicks off algo running 114 115 return false; // parser node data consumption rate is not close to clip bitrate 116 } 117 118 // ret_val: 0, success, 119 // 1, playback rate is not close to clip bitrate, but the information is all available 120 // -1, related information, e.g. duration=0, size2time conversion is not available, is not available 121 OSCL_EXPORT_REF int32 progressiveDownloadControl::isPlaybackRateCloseToClipBitrate(const uint32 aDurationMsec, 122 const uint32 aCurrDownloadSize, 123 const uint32 aFileSize) 124 { 125 if (aFileSize == 0 || aDurationMsec == 0 || iProgDownloadSI == NULL) return -1; 126 127 uint32 aNPTInMS = 0; 128 129 if (iProgDownloadSI->convertSizeToTime(aCurrDownloadSize, aNPTInMS) == 0) 130 { 131 132 if (aNPTInMS == 0) return 1; 133 if (iClipByterate == 0) iClipByterate = divisionInMilliSec(aFileSize, aDurationMsec); // aFileSize*1000/aDurationMsec 134 uint32 aInstantByterate = divisionInMilliSec(aCurrDownloadSize, aNPTInMS); // aCurrDownloadSize*1000/aNPTInMS 135 LOGINFODATAPATH((0, "progressiveDownloadControl::isPlaybackRateCloseToClipBitrate, check Instant rate=%d(currDLSize=%d, NPTTimeMs=%d), clip bitrate=%d", 136 (aInstantByterate << 3), aCurrDownloadSize, aNPTInMS, (iClipByterate << 3))); 137 uint32 diffByterate = (aInstantByterate >= iClipByterate ? aInstantByterate - iClipByterate : iClipByterate - aInstantByterate); 138 if (diffByterate < GET_10_PERCENT(iClipByterate) || // OSCL_ABS(aInstantByterate-iClipByterate)/iClipByterate < 1/8-1/64=0.109 139 isBufferingEnoughTime(aCurrDownloadSize, PVPROTOCOLENGINE_JITTER_BUFFER_SIZE_TIME, aNPTInMS)) 140 { 141 if (isBufferingEnoughTime(aCurrDownloadSize, PVPROTOCOLENGINE_JITTER_BUFFER_SIZE_TIME, aNPTInMS)) 142 { 143 return 0; 144 } 145 return 1; 146 } 147 } 148 else 149 { 150 // in case of convertSizeToTime() not supported in parser node, but duration can be estimated and provided 151 if (iClipByterate == 0) iClipByterate = divisionInMilliSec(aFileSize, aDurationMsec); 152 if (isBufferingEnoughTime(aCurrDownloadSize, PVPROTOCOLENGINE_INIT_DOWNLOAD_TIME_THRESHOLD_WITH_CLIPBITRATE)) return 0; 153 } 154 155 return 1; 156 } 157 158 OSCL_EXPORT_REF bool progressiveDownloadControl::isBufferingEnoughTime(const uint32 aCurrDownloadSize, 159 const uint32 aBufferTimeLimitInSec, 160 const uint32 aNPTInMS) 161 { 162 if (aNPTInMS == 0xFFFFFFFF) 163 { 164 // use clip bitrate to calculate buffering time, instead of using convertSizeToTime() 165 uint32 deltaSizeLimit = iClipByterate * aBufferTimeLimitInSec; 166 return (aCurrDownloadSize >= iPrevDownloadSize + deltaSizeLimit); 167 } 168 else if (aNPTInMS > 0) 169 { 170 // convertSizeToTime() should be available 171 if (iPrevDownloadSize == 0) 172 { 173 return (aNPTInMS >= aBufferTimeLimitInSec*1000); 174 } 175 else 176 { 177 uint32 aPrevNPTInMS = 0; 178 if (iProgDownloadSI->convertSizeToTime(iPrevDownloadSize, aPrevNPTInMS) == 0) 179 { 180 return (aNPTInMS > aPrevNPTInMS && (aNPTInMS - aPrevNPTInMS) >= aBufferTimeLimitInSec*1000); 181 } 182 } 183 } 184 return false; 185 } 186 187 OSCL_EXPORT_REF bool progressiveDownloadControl::checkNewDuration(const uint32 aCurrDurationMsec, uint32 &aNewDurationMsec) 188 { 189 aNewDurationMsec = aCurrDurationMsec; 190 if (aCurrDurationMsec > 0 && iClipByterate == 0) 191 { 192 if (iFileSize > 0) iClipByterate = divisionInMilliSec(iFileSize, aCurrDurationMsec); 193 } 194 195 if (iPlaybackByteRate > 0) 196 { 197 if (iPlaybackByteRate > iClipByterate) 198 { 199 uint32 averPlaybackRate = (iClipByterate + iPlaybackByteRate) / 2; 200 aNewDurationMsec = divisionInMilliSec(iFileSize, averPlaybackRate); // aFileSize/averPlaybackRate*1000 201 } 202 } 203 return true; 204 } 205 206 OSCL_EXPORT_REF bool progressiveDownloadControl::approveAutoResumeDecisionShortCut(const uint32 aCurrDownloadSize, 207 const uint32 aDurationMsec, 208 const uint32 aPlaybackTimeMsec, 209 uint32 &aPlaybackRemainingTimeMsec) 210 { 211 if (!iProgDownloadSI || aDurationMsec == 0) return false; 212 213 uint32 aNPTInMS = 0; 214 if (iProgDownloadSI->convertSizeToTime(aCurrDownloadSize, aNPTInMS) == 0) 215 { 216 aPlaybackRemainingTimeMsec = aDurationMsec - aNPTInMS; 217 if (aNPTInMS > PVPROTOCOLENGINE_JITTER_BUFFER_SIZE_TIME*2000 + aPlaybackTimeMsec) 218 return true; 219 } 220 return false; 221 } 222 223 // No constraint: for file size/clip duration/clip bitrate(i.e. playback rate), one of them must be unavailable, except 224 // file size and clip duration are available, but clip bitrate is unavailable 225 OSCL_EXPORT_REF bool progressiveDownloadControl::checkAutoResumeAlgoNoConstraint(const uint32 aCurrDownloadSize, 226 const uint32 aFileSize, 227 uint32 &aDurationMsec) 228 { 229 // first check one exception: file size>0, duration=0, playbackRate>0, then we need to estimate the clip duration 230 if (checkEstDurationAvailable(aFileSize, aDurationMsec)) return false; 231 232 uint32 currDownloadSizeOfInterest = aCurrDownloadSize - iPrevDownloadSize; // use download size as the jitter buffer size 233 if (iPlaybackByteRate > 0) currDownloadSizeOfInterest /= iPlaybackByteRate; // use playback time as the jitter buffer size 234 else if (aFileSize > 0) currDownloadSizeOfInterest /= (aFileSize / PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_CONVERTION_100); // use download percentage as the jitter buffer size 235 236 uint32 aJitterBufferSize = (iPlaybackByteRate > 0 ? PVPROTOCOLENGINE_JITTER_BUFFER_SIZE_TIME : 237 (aFileSize > 0 ? PVPROTOCOLENGINE_JITTER_BUFFER_SIZE_DLPERCENTAGE : 238 PVPROTOCOLENGINE_JITTER_BUFFER_SIZE_BYTES)); 239 bool resumeOK = (currDownloadSizeOfInterest >= aJitterBufferSize); 240 LOGINFODATAPATH((0, "progressiveDownloadControl::isResumePlayback()->checkAutoResumeAlgoNoConstraint(), resumeOK=%d, currDownloadSize=%d, prevDownloadSize=%d, currDownloadSizeOfInterest=%d, aJitterBufferSize=%d, file size=%d", 241 (uint32)resumeOK, aCurrDownloadSize, iPrevDownloadSize, currDownloadSizeOfInterest, aJitterBufferSize, aFileSize)); 242 243 return resumeOK; 244 } 245 246 OSCL_EXPORT_REF bool progressiveDownloadControl::checkEstDurationAvailable(const uint32 aFileSize, uint32 &aDurationMsec) 247 { 248 // check file size>0, duration=0, playbackRate>0, then we need to estimate the clip duration 249 // and use the original algorithm 250 if (iPlaybackByteRate > 0 && aFileSize > 0 && aDurationMsec == 0) 251 { 252 // calculate estimated duration 253 aDurationMsec = divisionInMilliSec(aFileSize, iPlaybackByteRate); 254 LOGINFODATAPATH((0, "progressiveDownloadControl::isResumePlayback()->checkEstDurationAvailable(), aFileSize=%d, aPlaybackByteRate=%d, estDurationMsec=%d", 255 aFileSize, iPlaybackByteRate, aDurationMsec)); 256 return true; 257 } 258 return false; 259 } 260 261 262 OSCL_EXPORT_REF bool progressiveDownloadControl::updateDownloadClock() 263 { 264 if (!iProgDownloadSI || !iProtocol) return false; 265 266 // using size2time conversion to update download clock in PDL only applies to the case where 267 // old algorithm gets running without engine clock input 268 if (!iCurrentPlaybackClock) 269 { 270 uint32 aDownloadNPTTime = 0; 271 // get download size from output object instead of protocol engine. 272 // The download size from output object is the actual size for the data written to the file, and is exposed to user, 273 // while the download size time from protocol engine is internally counted from socket and most likely 274 // larger than that from output object 275 276 if (iProgDownloadSI->convertSizeToTime(iNodeOutput->getCurrentOutputSize(), aDownloadNPTTime) != 0) return false; 277 bool bOverflowFlag = false; 278 iDlProgressClock->SetStartTime32(aDownloadNPTTime, PVMF_MEDIA_CLOCK_MSEC, bOverflowFlag); 279 } 280 return true; 281 } 282 283 284 //////////////////////////////////////////////////////////////////////////////////// 285 ////// ProgressiveDownloadProgress implementation 286 //////////////////////////////////////////////////////////////////////////////////// 287 OSCL_EXPORT_REF void ProgressiveDownloadProgress::setSupportObject(OsclAny *aDLSupportObject, DownloadControlSupportObjectType aType) 288 { 289 switch (aType) 290 { 291 case DownloadControlSupportObjectType_ConfigFileContainer: 292 iCfgFileContainer = (PVDlCfgFileContainer *)aDLSupportObject; 293 break; 294 295 case DownloadControlSupportObjectType_SupportInterface: 296 iProgDownloadSI = (PVMFFormatProgDownloadSupportInterface*)aDLSupportObject; 297 break; 298 299 default: 300 break; 301 } 302 303 DownloadProgress::setSupportObject(aDLSupportObject, aType); 304 } 305 306 OSCL_EXPORT_REF bool ProgressiveDownloadProgress::updateDownloadClock(const bool aDownloadComplete) 307 { 308 OSCL_UNUSED_ARG(aDownloadComplete); 309 if (iProtocol) iDownloadSize = iNodeOutput->getCurrentOutputSize(); 310 if (iDownloadSize == 0) return false; 311 return checkDownloadPercentModeAndUpdateDLClock(); 312 } 313 314 OSCL_EXPORT_REF bool ProgressiveDownloadProgress::checkDownloadPercentModeAndUpdateDLClock() 315 { 316 // (1) if user specifies byte-based download percentage or no content length case, 317 // no need to update download clock and download time 318 if (iDownloadProgressMode > 0 || 319 iProtocol->getContentLength() == 0) 320 { 321 iTimeBasedDownloadPercent = false; 322 return true; 323 } 324 325 // (2) if download and playback mode is not asap mode, i.e. download and play or download only, 326 // then use byte-based download percent 327 if (!iProgDownloadSI && iCfgFileContainer) 328 { 329 if (iCfgFileContainer->getPlaybackMode() != PVMFDownloadDataSourceHTTP::EAsap) iTimeBasedDownloadPercent = false; 330 return true; 331 } 332 333 // (3) if parser node is not ready to do conversion from size to time, 334 // then choose byte-based download percent 335 if (iProgDownloadSI->convertSizeToTime(iDownloadSize, iDownloadNPTTime) != 0) 336 { 337 iTimeBasedDownloadPercent = false; 338 return true; 339 } 340 return true; 341 } 342 343 OSCL_EXPORT_REF bool ProgressiveDownloadProgress::calculateDownloadPercent(uint32 &aDownloadProgressPercent) 344 { 345 return calculateDownloadPercentBody(aDownloadProgressPercent, iProtocol->getContentLength()); 346 } 347 348 OSCL_EXPORT_REF bool ProgressiveDownloadProgress::calculateDownloadPercentBody(uint32 &aDownloadProgressPercent, const uint32 aFileSize) 349 { 350 if (iTimeBasedDownloadPercent) 351 { 352 return DownloadProgress::calculateDownloadPercent(aDownloadProgressPercent); 353 } 354 else 355 { 356 // byte-based download percentage 357 aDownloadProgressPercent = iDownloadSize; 358 if (aFileSize > 0) 359 { 360 aDownloadProgressPercent = getDownloadBytePercent(iDownloadSize, aFileSize); 361 if (aDownloadProgressPercent > 100) aDownloadProgressPercent = 100; 362 if (aDownloadProgressPercent == 100) iDownloadSize = aFileSize; 363 } 364 else 365 { 366 uint32 aMaxFileSize = iCfgFileContainer->getCfgFile()->GetMaxAllowedFileSize(); 367 if (aDownloadProgressPercent > aMaxFileSize) aDownloadProgressPercent = aMaxFileSize; 368 } 369 } 370 return true; 371 } 372 373 // handle overflow issue for integer multiplication: downloadSize*100/fileSize 374 OSCL_EXPORT_REF uint32 ProgressiveDownloadProgress::getDownloadBytePercent(const uint32 aDownloadSize, const uint32 aFileSize) 375 { 376 uint32 aDownloadProgressPercent = aDownloadSize * PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_CONVERTION_100 / aFileSize; // 100 377 if ((aDownloadSize >> PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_DLSIZE_LIMIT_RIGHT_SHIFT_FACTOR) > 0) // right shift 25 bits => larger than 2^25, then *100 may cause overflow 378 { 379 aDownloadProgressPercent = (aDownloadSize >> PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_DLSIZE_RIGHTSHIFT_FACTOR)* // right shift 7 bits, 2^7>100 to avoid overflow 380 PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_CONVERTION_100 / 381 (aFileSize >> PVPROTOCOLENGINE_DOWNLOAD_BYTE_PERCENTAGE_DLSIZE_RIGHTSHIFT_FACTOR); 382 } 383 return aDownloadProgressPercent; 384 } 385 386 OSCL_EXPORT_REF void ProgressiveDownloadProgress::reset() 387 { 388 DownloadProgress::reset(); 389 390 iCfgFileContainer = NULL; 391 iProgDownloadSI = NULL; 392 iTimeBasedDownloadPercent = false; 393 iDownloadProgressMode = (uint32)DownloadProgressMode_ByteBased; 394 } 395 396 //////////////////////////////////////////////////////////////////////////////////// 397 ////// UserAgentField implementation 398 //////////////////////////////////////////////////////////////////////////////////// 399 OSCL_EXPORT_REF UserAgentFieldForProgDownload::UserAgentFieldForProgDownload(OSCL_wString &aUserAgent, const bool isOverwritable) : 400 UserAgentField(aUserAgent, isOverwritable) 401 { 402 ; 403 } 404 405 OSCL_EXPORT_REF UserAgentFieldForProgDownload::UserAgentFieldForProgDownload(OSCL_String &aUserAgent, const bool isOverwritable) : 406 UserAgentField(aUserAgent, isOverwritable) 407 { 408 ; 409 } 410 411 OSCL_EXPORT_REF void UserAgentFieldForProgDownload::getDefaultUserAgent(OSCL_String &aUserAgent) 412 { 413 OSCL_HeapString<OsclMemAllocator> defaultUserAgent(PDL_HTTP_USER_AGENT); // defined in pvmf_protocolengine_node_tunables.h 414 aUserAgent = defaultUserAgent; 415 } 416 417 418 //////////////////////////////////////////////////////////////////////////////////// 419 ////// PVProgressiveDownloadCfgFileContainer implementation 420 //////////////////////////////////////////////////////////////////////////////////// 421 422 OSCL_EXPORT_REF PVMFStatus PVProgressiveDownloadCfgFileContainer::configCfgFile(OSCL_String &aUrl) 423 { 424 // playback mode and proxy 425 iPlaybackMode = (PVMFDownloadDataSourceHTTP::TPVPlaybackControl)iDataSource->iPlaybackControl; 426 iCfgFileObj->SetPlaybackMode(convertToConfigFilePlaybackMode(iPlaybackMode)); 427 iCfgFileObj->SetProxyName(iDataSource->iProxyName); 428 iCfgFileObj->SetProxyPort(iDataSource->iProxyPort); 429 430 // user agent 431 OSCL_FastString user_agent(PDL_HTTP_USER_AGENT); // defined in pvmf_protocolengine_node_tunables.h 432 iCfgFileObj->SetUserAgent(user_agent); 433 434 // user-id and password 435 if (iDataSource->iUserID.get_size() > 0) iCfgFileObj->SetUserId(iDataSource->iUserID); 436 if (iDataSource->iUserPasswd.get_size() > 0) iCfgFileObj->SetUserAuth(iDataSource->iUserPasswd); 437 438 iCfgFileObj->SetDownloadType(false);//not fasttrack 439 return PVDlCfgFileContainer::configCfgFile(aUrl); 440 } 441 442 OSCL_EXPORT_REF PVDlCfgFile::TPVDLPlaybackMode PVProgressiveDownloadCfgFileContainer::convertToConfigFilePlaybackMode(PVMFDownloadDataSourceHTTP::TPVPlaybackControl aPlaybackMode) 443 { 444 OSCL_UNUSED_ARG(aPlaybackMode); 445 446 PVDlCfgFile::TPVDLPlaybackMode mode = PVDlCfgFile::EPVDL_ASAP; 447 switch (iPlaybackMode) 448 { 449 case PVMFDownloadDataSourceHTTP::EAsap: 450 mode = PVDlCfgFile::EPVDL_ASAP; 451 break; 452 case PVMFDownloadDataSourceHTTP::EAfterDownload: 453 mode = PVDlCfgFile::EPVDL_PLAYBACK_AFTER_DOWNLOAD; 454 break; 455 case PVMFDownloadDataSourceHTTP::ENoPlayback: 456 mode = PVDlCfgFile::EPVDL_DOWNLOAD_ONLY; 457 break; 458 default: 459 break; 460 } 461 return mode; 462 } 463 464