Home | History | Annotate | Download | only in src
      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