• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ////////////////////////////////////////////////////////////////////////////////////
ProgressiveStreamingContainer(PVMFProtocolEngineNode * aNode)30 OSCL_EXPORT_REF ProgressiveStreamingContainer::ProgressiveStreamingContainer(PVMFProtocolEngineNode *aNode) :
31         ProgressiveDownloadContainer(aNode), iEnableInfoUpdate(true)
32 {
33     ;
34 }
35 
createProtocolObjects()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 
doStop()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 
doSeek(PVMFProtocolEngineNodeCommand & aCmd)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 
getSeekOffset(PVMFProtocolEngineNodeCommand & aCmd)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 
doSeekBody(uint32 aNewOffset)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 
completeRepositionRequest()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 
moveToStartedState()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 
updateDownloadControl(const bool isDownloadComplete)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 
needToCheckResumeNotificationMaually()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 
doInfoUpdate(const uint32 downloadStatus)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 ///////////////////////////////////////////////////////////////////////////////////
pvProgressiveStreamingOutput(PVMFProtocolEngineNodeOutputObserver * aObserver)191 OSCL_EXPORT_REF pvProgressiveStreamingOutput::pvProgressiveStreamingOutput(PVMFProtocolEngineNodeOutputObserver *aObserver) :
192         pvHttpDownloadOutput(aObserver),
193         iSourceRequestObserver(NULL)
194 {
195     ;
196 }
197 
openDataStream(OsclAny * aInitInfo)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 
flushData(const uint32 aOutputType)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 
writeToDataStream(OUTPUT_DATA_QUEUE & aOutputQueue,PENDING_OUTPUT_DATA_QUEUE & aPendingOutputQueue)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 
releaseMemFrag(OsclRefCounterMemFrag * aFrag)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 
setContentLength(uint32 aLength)292 OSCL_EXPORT_REF void pvProgressiveStreamingOutput::setContentLength(uint32 aLength)
293 {
294     if (iDataStream) iDataStream->SetContentLength(aLength);
295 }
296 
dataStreamCommandCompleted(const PVMFCmdResp & aResponse)297 OSCL_EXPORT_REF void pvProgressiveStreamingOutput::dataStreamCommandCompleted(const PVMFCmdResp& aResponse)
298 {
299     // propagate the command complete
300     if (iDataStream) iDataStream->SourceRequestCompleted(aResponse);
301 }
302 
flushDataStream()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 
seekDataStream(const uint32 aSeekOffset)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 ////////////////////////////////////////////////////////////////////////////////////
progressiveStreamingControl()320 OSCL_EXPORT_REF progressiveStreamingControl::progressiveStreamingControl() : progressiveDownloadControl()
321 {
322     ;
323 }
324 
requestResumeNotification(const uint32 currentNPTReadPosition,bool & aDownloadComplete,bool & aNeedSendUnderflowEvent)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 
clearPerRequest()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 ////////////////////////////////////////////////////////////////////////////////////
calculateDownloadPercent(uint32 & aDownloadProgressPercent)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 ////////////////////////////////////////////////////////////////////////////////////
reportBufferStatusEvent(int aDownloadPercent)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 
getBufferFullness()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