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