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 /** 19 * 20 * @file pvmf_fileoutput_node.cpp 21 * @brief Simple file output node. Writes incoming data to specified 22 * file 23 * 24 */ 25 26 #include "pvmf_fileoutput_inport.h" 27 #include "pvmf_fileoutput_node.h" 28 #include "pvlogger.h" 29 #include "oscl_error_codes.h" 30 #include "pvmf_media_cmd.h" 31 #include "pvmf_media_msg_format_ids.h" 32 #include "pvmf_timedtext.h" 33 34 //////////////////////////////////////////////////////////////////////////// 35 PVMFFileOutputNode::PVMFFileOutputNode(int32 aPriority) 36 : OsclActiveObject(aPriority, "PVMFFileOutputNode") 37 , iCmdIdCounter(0) 38 , iInPort(NULL) 39 , iFileHandle(NULL) 40 , iFileOpened(0) 41 , iFirstMediaData(false) 42 , iLogger(NULL) 43 , iFormat(PVMF_MIME_FORMAT_UNKNOWN) 44 , iExtensionRefCount(0) 45 , iMaxFileSizeEnabled(false) 46 , iMaxDurationEnabled(false) 47 , iMaxFileSize(0) 48 , iMaxDuration(0) 49 , iFileSize(0) 50 , iFileSizeReportEnabled(false) 51 , iDurationReportEnabled(false) 52 , iFileSizeReportFreq(0) 53 , iDurationReportFreq(0) 54 , iNextFileSizeReport(0) 55 , iNextDurationReport(0) 56 , iClock(NULL) 57 , iEarlyMargin(DEFAULT_EARLY_MARGIN) 58 , iLateMargin(DEFAULT_LATE_MARGIN) 59 { 60 ConstructL(); 61 int32 err; 62 OSCL_TRY(err, 63 64 //Create the input command queue. Use a reserve to avoid lots of 65 //dynamic memory allocation. 66 iInputCommands.Construct(PVMF_FILE_OUTPUT_NODE_COMMAND_ID_START, PVMF_FILE_OUTPUT_NODE_COMMAND_VECTOR_RESERVE); 67 68 //Create the "current command" queue. It will only contain one 69 //command at a time, so use a reserve of 1. 70 iCurrentCommand.Construct(0, 1); 71 72 //Create the port vector. 73 iPortVector.Construct(PVMF_FILE_OUTPUT_NODE_PORT_VECTOR_RESERVE); 74 75 //Set the node capability data. 76 //This node can support an unlimited number of ports. 77 iCapability.iCanSupportMultipleInputPorts = false; 78 iCapability.iCanSupportMultipleOutputPorts = false; 79 iCapability.iHasMaxNumberOfPorts = true; 80 iCapability.iMaxNumberOfPorts = 1; 81 ); 82 83 if (err != OsclErrNone) 84 { 85 //if a leave happened, cleanup and re-throw the error 86 iInputCommands.clear(); 87 iCurrentCommand.clear(); 88 iPortVector.clear(); 89 iCapability.iInputFormatCapability.clear(); 90 iCapability.iOutputFormatCapability.clear(); 91 OSCL_CLEANUP_BASE_CLASS(PVMFNodeInterface); 92 OSCL_CLEANUP_BASE_CLASS(OsclActiveObject); 93 OSCL_LEAVE(err); 94 } 95 ChangeNodeState(EPVMFNodeCreated); 96 } 97 98 //////////////////////////////////////////////////////////////////////////// 99 PVMFFileOutputNode::~PVMFFileOutputNode() 100 { 101 //thread logoff 102 if (IsAdded()) 103 RemoveFromScheduler(); 104 105 //Cleanup allocated interfaces 106 107 108 //Cleanup allocated ports 109 if (iInPort) 110 { 111 OSCL_DELETE(((PVMFFileOutputInPort*)iInPort)); 112 iInPort = NULL; 113 } 114 115 //Cleanup commands 116 //The command queues are self-deleting, but we want to 117 //notify the observer of unprocessed commands. 118 while (!iCurrentCommand.empty()) 119 { 120 CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFFailure); 121 } 122 while (!iInputCommands.empty()) 123 { 124 CommandComplete(iInputCommands, iInputCommands.front(), PVMFFailure); 125 } 126 127 //cleanup port activity events 128 iPortActivityQueue.clear(); 129 130 131 if (iAlloc) 132 { 133 OSCL_DELETE(iAlloc); 134 } 135 } 136 137 //////////////////////////////////////////////////////////////////////////// 138 void PVMFFileOutputNode::ConstructL() 139 { 140 iAlloc = (Oscl_DefAlloc*)(new PVMFFileOutputAlloc()); 141 } 142 143 //////////////////////////////////////////////////////////////////////////// 144 PVMFStatus PVMFFileOutputNode::ThreadLogon() 145 { 146 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:ThreadLogon")); 147 148 switch (iInterfaceState) 149 { 150 case EPVMFNodeCreated: 151 if (!IsAdded()) 152 AddToScheduler(); 153 iLogger = PVLogger::GetLoggerObject("PVMFFileOutputNode"); 154 SetState(EPVMFNodeIdle); 155 return PVMFSuccess; 156 default: 157 return PVMFErrInvalidState; 158 } 159 } 160 161 //////////////////////////////////////////////////////////////////////////// 162 PVMFStatus PVMFFileOutputNode::ThreadLogoff() 163 { 164 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:ThreadLogoff")); 165 166 switch (iInterfaceState) 167 { 168 case EPVMFNodeIdle: 169 if (IsAdded()) 170 RemoveFromScheduler(); 171 iLogger = NULL; 172 SetState(EPVMFNodeCreated); 173 return PVMFSuccess; 174 175 default: 176 return PVMFErrInvalidState; 177 } 178 } 179 180 //////////////////////////////////////////////////////////////////////////// 181 void PVMFFileOutputNode::CloseOutputFile() 182 { 183 // Close output file 184 if (iFileOpened) 185 { 186 iOutputFile.Close(); 187 iFs.Close(); 188 iFileOpened = 0; 189 } 190 } 191 192 //////////////////////////////////////////////////////////////////////////// 193 PVMFCommandId PVMFFileOutputNode::Init(PVMFSessionId s, const OsclAny* aContext) 194 { 195 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:Init")); 196 PVMFFileOutputNodeCommand cmd; 197 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_INIT, aContext); 198 return QueueCommandL(cmd); 199 } 200 201 //////////////////////////////////////////////////////////////////////////// 202 PVMFCommandId PVMFFileOutputNode::Prepare(PVMFSessionId s, const OsclAny* aContext) 203 { 204 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:Prepare")); 205 PVMFFileOutputNodeCommand cmd; 206 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_PREPARE, aContext); 207 return QueueCommandL(cmd); 208 } 209 210 //////////////////////////////////////////////////////////////////////////// 211 PVMFStatus PVMFFileOutputNode::GetCapability(PVMFNodeCapability& aNodeCapability) 212 { 213 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:GetCapability")); 214 iCapability.iInputFormatCapability.clear(); 215 216 if (iFormat != PVMF_MIME_FORMAT_UNKNOWN) 217 { 218 // Format is already set, so return only that one 219 iCapability.iInputFormatCapability.push_back(iFormat); 220 } 221 else 222 { 223 iCapability.iInputFormatCapability.push_back(PVMF_MIME_AMR_IETF); 224 iCapability.iInputFormatCapability.push_back(PVMF_MIME_AMRWB_IETF); 225 iCapability.iInputFormatCapability.push_back(PVMF_MIME_M4V); 226 iCapability.iInputFormatCapability.push_back(PVMF_MIME_PCM8); 227 iCapability.iInputFormatCapability.push_back(PVMF_MIME_PCM16); 228 iCapability.iInputFormatCapability.push_back(PVMF_MIME_YUV420); 229 iCapability.iInputFormatCapability.push_back(PVMF_MIME_ADTS); 230 iCapability.iInputFormatCapability.push_back(PVMF_MIME_H2631998); 231 iCapability.iInputFormatCapability.push_back(PVMF_MIME_H2632000); 232 iCapability.iInputFormatCapability.push_back(PVMF_MIME_H264_VIDEO_RAW); 233 iCapability.iInputFormatCapability.push_back(PVMF_MIME_H264_VIDEO_MP4); 234 iCapability.iInputFormatCapability.push_back(PVMF_MIME_H264_VIDEO); 235 iCapability.iInputFormatCapability.push_back(PVMF_MIME_PCM); 236 iCapability.iInputFormatCapability.push_back(PVMF_MIME_3GPP_TIMEDTEXT); 237 } 238 aNodeCapability = iCapability; 239 return PVMFSuccess; 240 } 241 242 243 //////////////////////////////////////////////////////////////////////////// 244 PVMFCommandId PVMFFileOutputNode::QueryUUID(PVMFSessionId s, const PvmfMimeString& aMimeType, 245 Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids, 246 bool aExactUuidsOnly, 247 const OsclAny* aContext) 248 { 249 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:QueryUUID")); 250 251 PVMFFileOutputNodeCommand cmd; 252 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_QUERYUUID, aMimeType, aUuids, aExactUuidsOnly, aContext); 253 return QueueCommandL(cmd); 254 } 255 256 //////////////////////////////////////////////////////////////////////////// 257 PVMFCommandId PVMFFileOutputNode::QueryInterface(PVMFSessionId s, const PVUuid& aUuid, 258 PVInterface*& aInterfacePtr, 259 const OsclAny* aContext) 260 { 261 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:QueryInterface")); 262 263 PVMFFileOutputNodeCommand cmd; 264 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_QUERYINTERFACE, aUuid, aInterfacePtr, aContext); 265 return QueueCommandL(cmd); 266 } 267 268 //////////////////////////////////////////////////////////////////////////// 269 PVMFCommandId PVMFFileOutputNode::RequestPort(PVMFSessionId s, int32 aPortTag, const PvmfMimeString* aPortConfig, const OsclAny* aContext) 270 { 271 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:RequestPort")); 272 PVMFFileOutputNodeCommand cmd; 273 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_REQUESTPORT, aPortTag, aPortConfig, aContext); 274 return QueueCommandL(cmd); 275 } 276 277 278 //////////////////////////////////////////////////////////////////////////// 279 PVMFCommandId PVMFFileOutputNode::ReleasePort(PVMFSessionId s, PVMFPortInterface& aPort, const OsclAny* aContext) 280 { 281 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:ReleasePort")); 282 PVMFFileOutputNodeCommand cmd; 283 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_RELEASEPORT, aPort, aContext); 284 return QueueCommandL(cmd); 285 } 286 287 288 //////////////////////////////////////////////////////////////////////////// 289 PVMFPortIter* PVMFFileOutputNode::GetPorts(const PVMFPortFilter* aFilter) 290 { 291 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:GetPorts")); 292 293 OSCL_UNUSED_ARG(aFilter);//port filter is not implemented. 294 iPortVector.Reset(); 295 return &iPortVector; 296 } 297 298 299 //////////////////////////////////////////////////////////////////////////// 300 PVMFCommandId PVMFFileOutputNode::Start(PVMFSessionId s, const OsclAny* aContext) 301 { 302 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:Start")); 303 PVMFFileOutputNodeCommand cmd; 304 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_START, aContext); 305 return QueueCommandL(cmd); 306 } 307 308 309 310 //////////////////////////////////////////////////////////////////////////// 311 PVMFCommandId PVMFFileOutputNode::Stop(PVMFSessionId s, const OsclAny* aContext) 312 { 313 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:Stop")); 314 PVMFFileOutputNodeCommand cmd; 315 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_STOP, aContext); 316 return QueueCommandL(cmd); 317 } 318 319 //////////////////////////////////////////////////////////////////////////// 320 /** 321 //Queue an asynchronous node command 322 */ 323 PVMFCommandId PVMFFileOutputNode::Flush(PVMFSessionId s, const OsclAny* aContext) 324 { 325 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:Flush")); 326 PVMFFileOutputNodeCommand cmd; 327 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_FLUSH, aContext); 328 return QueueCommandL(cmd); 329 } 330 331 //////////////////////////////////////////////////////////////////////////// 332 PVMFCommandId PVMFFileOutputNode::Pause(PVMFSessionId s, const OsclAny* aContext) 333 { 334 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:Pause")); 335 PVMFFileOutputNodeCommand cmd; 336 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_PAUSE, aContext); 337 return QueueCommandL(cmd); 338 } 339 340 341 //////////////////////////////////////////////////////////////////////////// 342 PVMFCommandId PVMFFileOutputNode::Reset(PVMFSessionId s, const OsclAny* aContext) 343 { 344 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:Reset")); 345 PVMFFileOutputNodeCommand cmd; 346 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_RESET, aContext); 347 return QueueCommandL(cmd); 348 } 349 350 351 //////////////////////////////////////////////////////////////////////////// 352 PVMFCommandId PVMFFileOutputNode::CancelAllCommands(PVMFSessionId s, const OsclAny* aContext) 353 { 354 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:CancelAllCommands")); 355 PVMFFileOutputNodeCommand cmd; 356 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_CANCELALLCOMMANDS, aContext); 357 return QueueCommandL(cmd); 358 } 359 360 361 362 //////////////////////////////////////////////////////////////////////////// 363 PVMFCommandId PVMFFileOutputNode::CancelCommand(PVMFSessionId s, PVMFCommandId aCmdId, const OsclAny* aContext) 364 { 365 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:CancelCommand")); 366 PVMFFileOutputNodeCommand cmd; 367 cmd.PVMFFileOutputNodeCommandBase::Construct(s, PVMF_GENERIC_NODE_CANCELCOMMAND, aCmdId, aContext); 368 return QueueCommandL(cmd); 369 } 370 371 //////////////////////////////////////////////////////////////////////////// 372 void PVMFFileOutputNode::addRef() 373 { 374 ++iExtensionRefCount; 375 } 376 377 //////////////////////////////////////////////////////////////////////////// 378 void PVMFFileOutputNode::removeRef() 379 { 380 if (iExtensionRefCount > 0) 381 --iExtensionRefCount; 382 } 383 384 //////////////////////////////////////////////////////////////////////////// 385 bool PVMFFileOutputNode::queryInterface(const PVUuid& uuid, PVInterface*& iface) 386 { 387 if (uuid == PvmfFileOutputNodeConfigUuid) 388 { 389 PvmfFileOutputNodeConfigInterface* myInterface = OSCL_STATIC_CAST(PvmfFileOutputNodeConfigInterface*, this); 390 iface = OSCL_STATIC_CAST(PVInterface*, myInterface); 391 ++iExtensionRefCount; 392 } 393 else if (uuid == PvmfComposerSizeAndDurationUuid) 394 { 395 PvmfComposerSizeAndDurationInterface* myInterface = OSCL_STATIC_CAST(PvmfComposerSizeAndDurationInterface*, this); 396 iface = OSCL_STATIC_CAST(PVInterface*, myInterface); 397 ++iExtensionRefCount; 398 } 399 else if (uuid == PvmfNodesSyncControlUuid) 400 { 401 PvmfNodesSyncControlInterface* myInterface = OSCL_STATIC_CAST(PvmfNodesSyncControlInterface*, this); 402 iface = OSCL_STATIC_CAST(PVInterface*, myInterface); 403 ++iExtensionRefCount; 404 } 405 406 else if (uuid == PVMI_CAPABILITY_AND_CONFIG_PVUUID) 407 { 408 PvmiCapabilityAndConfig* myInterface = OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, this); 409 iface = OSCL_STATIC_CAST(PVInterface*, myInterface); 410 ++iExtensionRefCount; 411 } 412 413 else 414 { 415 iface = NULL; 416 return false; 417 } 418 419 return true; 420 } 421 422 //////////////////////////////////////////////////////////////////////////// 423 PVMFStatus PVMFFileOutputNode::SetOutputFileName(const OSCL_wString& aFileName) 424 { 425 if (iInterfaceState != EPVMFNodeIdle 426 && iInterfaceState != EPVMFNodeInitialized 427 && iInterfaceState != EPVMFNodeCreated 428 && iInterfaceState != EPVMFNodePrepared) 429 return false; 430 431 iOutputFileName = aFileName; 432 return PVMFSuccess; 433 } 434 435 /////////////////////////////////////////////////////////////////////////// 436 PVMFStatus PVMFFileOutputNode::SetOutputFileDescriptor(const OsclFileHandle* aFileHandle) 437 { 438 if (iInterfaceState != EPVMFNodeIdle 439 && iInterfaceState != EPVMFNodeInitialized 440 && iInterfaceState != EPVMFNodeCreated 441 && iInterfaceState != EPVMFNodePrepared) 442 return false; 443 444 iOutputFile.SetPVCacheSize(0); 445 iOutputFile.SetAsyncReadBufferSize(0); 446 iOutputFile.SetNativeBufferSize(0); 447 iOutputFile.SetLoggingEnable(false); 448 iOutputFile.SetSummaryStatsLoggingEnable(false); 449 iOutputFile.SetFileHandle((OsclFileHandle*)aFileHandle); 450 451 //call open 452 int32 retval = iOutputFile.Open(_STRLIT_CHAR("dummy"), 453 Oscl_File::MODE_READWRITE | Oscl_File::MODE_BINARY, 454 iFs); 455 456 if (retval == 0) 457 { 458 iFileOpened = 1; 459 iFirstMediaData = true; 460 return PVMFSuccess; 461 } 462 return PVMFFailure; 463 } 464 //////////////////////////////////////////////////////////////////////////// 465 PVMFStatus PVMFFileOutputNode::SetMaxFileSize(bool aEnable, uint32 aMaxFileSizeBytes) 466 { 467 iMaxFileSizeEnabled = aEnable; 468 if (iMaxFileSizeEnabled) 469 { 470 iMaxFileSize = aMaxFileSizeBytes; 471 } 472 else 473 { 474 iMaxFileSize = 0; 475 } 476 477 return PVMFSuccess; 478 } 479 480 //////////////////////////////////////////////////////////////////////////// 481 void PVMFFileOutputNode::GetMaxFileSizeConfig(bool& aEnable, uint32& aMaxFileSizeBytes) 482 { 483 aEnable = iMaxFileSizeEnabled; 484 aMaxFileSizeBytes = iMaxFileSize; 485 } 486 487 //////////////////////////////////////////////////////////////////////////// 488 PVMFStatus PVMFFileOutputNode::SetMaxDuration(bool aEnable, uint32 aMaxDurationMilliseconds) 489 { 490 iMaxDurationEnabled = aEnable; 491 if (iMaxDurationEnabled) 492 { 493 iMaxDuration = aMaxDurationMilliseconds; 494 } 495 else 496 { 497 iMaxDuration = 0; 498 } 499 500 return PVMFSuccess; 501 } 502 503 //////////////////////////////////////////////////////////////////////////// 504 void PVMFFileOutputNode::GetMaxDurationConfig(bool& aEnable, uint32& aMaxDurationMilliseconds) 505 { 506 aEnable = iMaxDurationEnabled; 507 aMaxDurationMilliseconds = iMaxDuration; 508 } 509 510 //////////////////////////////////////////////////////////////////////////// 511 PVMFStatus PVMFFileOutputNode::SetFileSizeProgressReport(bool aEnable, uint32 aReportFrequency) 512 { 513 iFileSizeReportEnabled = aEnable; 514 if (iFileSizeReportEnabled) 515 { 516 iFileSizeReportFreq = aReportFrequency; 517 } 518 519 return PVMFSuccess; 520 } 521 522 //////////////////////////////////////////////////////////////////////////// 523 void PVMFFileOutputNode::GetFileSizeProgressReportConfig(bool& aEnable, uint32& aReportFrequency) 524 { 525 aEnable = iFileSizeReportEnabled; 526 aReportFrequency = iFileSizeReportFreq; 527 } 528 529 //////////////////////////////////////////////////////////////////////////// 530 PVMFStatus PVMFFileOutputNode::SetDurationProgressReport(bool aEnable, uint32 aReportFrequency) 531 { 532 iDurationReportEnabled = aEnable; 533 if (iDurationReportEnabled) 534 { 535 iDurationReportFreq = aReportFrequency; 536 } 537 538 return PVMFSuccess; 539 } 540 541 //////////////////////////////////////////////////////////////////////////// 542 void PVMFFileOutputNode::GetDurationProgressReportConfig(bool& aEnable, uint32& aReportFrequency) 543 { 544 aEnable = iDurationReportEnabled; 545 aReportFrequency = iDurationReportFreq; 546 } 547 548 //////////////////////////////////////////////////////////////////////////// 549 PVMFStatus PVMFFileOutputNode::SetClock(PVMFMediaClock* aClock) 550 { 551 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 552 (0, "PVMFFileOutputNode::SetClock: aClock=0x%x", aClock)); 553 554 iClock = aClock; 555 if (iInPort) 556 { 557 return ((PVMFFileOutputInPort*)iInPort)->SetClock(aClock); 558 } 559 560 return PVMFSuccess; 561 } 562 563 /////////////////////////////////////////////////////////////////////////// 564 PVMFStatus PVMFFileOutputNode::ChangeClockRate(int32 aRate) 565 { 566 OSCL_UNUSED_ARG(aRate); 567 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 568 (0, "PVMFFileOutputNode::ChangeClockRate: aRate=%d", aRate)); 569 570 return PVMFSuccess; 571 } 572 573 //////////////////////////////////////////////////////////////////////////// 574 PVMFStatus PVMFFileOutputNode::SetMargins(int32 aEarlyMargin, int32 aLateMargin) 575 { 576 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 577 (0, "PVMFFileOutputNode::SetMargins: aEarlyMargin=%d, aLateMargin=%d", aEarlyMargin, aLateMargin)); 578 579 iEarlyMargin = aEarlyMargin; 580 iLateMargin = aLateMargin; 581 if (iInPort) 582 { 583 return ((PVMFFileOutputInPort*)iInPort)->SetMargins(aEarlyMargin, aLateMargin); 584 } 585 586 return PVMFSuccess; 587 } 588 589 //////////////////////////////////////////////////////////////////////////// 590 void PVMFFileOutputNode::ClockStarted(void) 591 { 592 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 593 (0, "PVMFFileOutputNode::ClockStarted")); 594 595 if (iInPort) 596 { 597 ((PVMFFileOutputInPort*)iInPort)->Start(); 598 } 599 } 600 601 //////////////////////////////////////////////////////////////////////////// 602 void PVMFFileOutputNode::ClockStopped(void) 603 { 604 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 605 (0, "PVMFFileOutputNode::ClockStopped")); 606 607 if (iInPort) 608 { 609 ((PVMFFileOutputInPort*)iInPort)->Pause(); 610 } 611 } 612 613 //////////////////////////////////////////////////////////////////////////// 614 PVMFCommandId PVMFFileOutputNode::SkipMediaData(PVMFSessionId aSessionId, 615 PVMFTimestamp aResumeTimestamp, 616 uint32 aStreamID, 617 bool aPlayBackPositionContinuous, 618 OsclAny* aContext) 619 { 620 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 621 (0, "PVMFFileOutputNode::SkipMediaData: aResumeTimestamp=%d, aContext=0x%x", 622 aResumeTimestamp, aContext)); 623 624 if (!iInPort) 625 { 626 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 627 (0, "PVMFFileOutputNode::SkipMediaData: Error - Input port has not been created")); 628 OSCL_LEAVE(OsclErrNotReady); 629 return 0; 630 } 631 632 switch (iInterfaceState) 633 { 634 case EPVMFNodeStarted: 635 case EPVMFNodeInitialized: 636 case EPVMFNodePaused: 637 return ((PVMFFileOutputInPort*)iInPort)->SkipMediaData(aSessionId, aResumeTimestamp, aStreamID, aPlayBackPositionContinuous, aContext); 638 639 default: 640 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 641 (0, "PVMFFileOutputNode::SkipMediaData: Error - Wrong state")); 642 OSCL_LEAVE(OsclErrInvalidState); 643 return 0; 644 } 645 } 646 647 //////////////////////////////////////////////////////////////////////////// 648 void PVMFFileOutputNode::Run() 649 { 650 if (!iInputCommands.empty()) 651 { 652 if (ProcessCommand(iInputCommands.front())) 653 { 654 //note: need to check the state before re-scheduling 655 //since the node could have been reset in the ProcessCommand 656 //call. 657 if (iInterfaceState != EPVMFNodeCreated) 658 RunIfNotReady(); 659 return; 660 } 661 } 662 663 // Process port activity 664 if (!iPortActivityQueue.empty() 665 && (iInterfaceState == EPVMFNodeStarted || FlushPending())) 666 { 667 // If the port activity cannot be processed because a port is 668 // busy, discard the activity and continue to process the next 669 // activity in queue until getting to one that can be processed. 670 while (!iPortActivityQueue.empty()) 671 { 672 if (ProcessPortActivity()) 673 break; //processed a port 674 } 675 //Re-schedule 676 RunIfNotReady(); 677 return; 678 } 679 680 //If we get here we did not process any ports or commands. 681 //Check for completion of a flush command... 682 if (FlushPending() 683 && iPortActivityQueue.empty()) 684 { 685 SetState(EPVMFNodePrepared); 686 iInPort->ResumeInput(); 687 CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFSuccess); 688 RunIfNotReady(); 689 } 690 } 691 692 void PVMFFileOutputNode::HandlePortActivity(const PVMFPortActivity &aActivity) 693 { 694 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 695 (0, "0x%x PVMFFileOutputNode::PortActivity: port=0x%x, type=%d", 696 this, aActivity.iPort, aActivity.iType)); 697 698 switch (aActivity.iType) 699 { 700 case PVMF_PORT_ACTIVITY_OUTGOING_MSG: 701 //An outgoing message was queued on this port. 702 //We only need to queue a port activity event on the 703 //first message. Additional events will be queued during 704 //the port processing as needed. 705 if (aActivity.iPort->OutgoingMsgQueueSize() == 1) 706 QueuePortActivity(aActivity); 707 break; 708 709 case PVMF_PORT_ACTIVITY_INCOMING_MSG: 710 if (aActivity.iPort->IncomingMsgQueueSize() == 1) 711 QueuePortActivity(aActivity); 712 break; 713 714 case PVMF_PORT_ACTIVITY_DELETED: 715 //Report port deleted info event to the node. 716 ReportInfoEvent(PVMFInfoPortDeleted 717 , (OsclAny*)aActivity.iPort); 718 //Purge any port activity events already queued 719 //for this port. 720 { 721 for (uint32 i = 0; i < iPortActivityQueue.size();) 722 { 723 if (iPortActivityQueue[i].iPort == aActivity.iPort) 724 iPortActivityQueue.erase(&iPortActivityQueue[i]); 725 else 726 i++; 727 } 728 } 729 break; 730 731 case PVMF_PORT_ACTIVITY_CONNECT: 732 //nothing needed. 733 break; 734 735 case PVMF_PORT_ACTIVITY_DISCONNECT: 736 //nothing needed. 737 break; 738 default: 739 break; 740 } 741 } 742 743 ///////////////////////////////////////////////////// 744 // Port Processing routines 745 ///////////////////////////////////////////////////// 746 747 void PVMFFileOutputNode::QueuePortActivity(const PVMFPortActivity &aActivity) 748 { 749 //queue a new port activity event 750 int32 err; 751 OSCL_TRY(err, iPortActivityQueue.push_back(aActivity);); 752 if (err != OsclErrNone) 753 { 754 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 755 (0, "0x%x PVMFFileOutputNode::PortActivity: Error - iPortActivityQueue.push_back() failed", this)); 756 ReportErrorEvent(PVMFErrPortProcessing, (OsclAny*)(aActivity.iPort)); 757 } 758 else 759 { 760 //wake up the AO to process the port activity event. 761 RunIfNotReady(); 762 } 763 } 764 765 //////////////////////////////////////////////////////////////////////////// 766 PVMFStatus PVMFFileOutputNode::ProcessIncomingData(PVMFSharedMediaDataPtr aMediaData) 767 { 768 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 769 (0, "PVMFFileOutputNode::ProcessIncomingData()")); 770 771 PVMFStatus status = PVMFSuccess; 772 773 OsclRefCounterMemFrag frag; 774 uint32 numFrags = aMediaData->getNumFragments(); 775 OsclRefCounterMemFrag formatSpecificInfo; 776 aMediaData->getFormatSpecificInfo(formatSpecificInfo); 777 778 for (uint32 i = 0; (i < numFrags) && status == PVMFSuccess; i++) 779 { 780 aMediaData->getMediaFragment(i, frag); 781 switch (iInterfaceState) 782 { 783 case EPVMFNodeStarted: 784 if (iFirstMediaData) 785 { 786 status = WriteFormatSpecificInfo(formatSpecificInfo.getMemFragPtr(), formatSpecificInfo.getMemFragSize()); 787 if (status != PVMFSuccess) 788 { 789 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 790 (0, "PVMFFileOutputNode::ProcessIncomingData: Error - WriteFormatSpecificInfo failed")); 791 return PVMFFailure; 792 } 793 } 794 795 if (((PVMFFileOutputInPort*)iInPort)->iFormat == PVMF_MIME_3GPP_TIMEDTEXT) 796 { 797 PVMFTimedTextMediaData* textmediadata = (PVMFTimedTextMediaData*)(frag.getMemFragPtr()); 798 // Output the text sample entry 799 if (textmediadata->iTextSampleEntry.GetRep() != NULL) 800 { 801 PVMFTimedTextMediaData* textmediadata = (PVMFTimedTextMediaData*)(frag.getMemFragPtr()); 802 // Output the text sample entry 803 if (textmediadata->iTextSampleEntry.GetRep() != NULL) 804 { 805 // @TODO Write out the text sample entry in a better format 806 status = WriteData((OsclAny*)(textmediadata->iTextSampleEntry.GetRep()), sizeof(PVMFTimedTextSampleEntry)); 807 if (status == PVMFFailure) 808 { 809 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 810 (0, "PVMFFileOutputNode::ProcessIncomingData: Error - WriteData failed for text sample entry")); 811 return PVMFFailure; 812 } 813 } 814 // Write out the raw text sample 815 status = WriteData((OsclAny*)(textmediadata->iTextSample), textmediadata->iTextSampleLength); 816 if (status == PVMFFailure) 817 { 818 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 819 (0, "PVMFFileOutputNode::ProcessIncomingData: Error - WriteData failed for text sample entry")); 820 return PVMFFailure; 821 } 822 } 823 } 824 else 825 { 826 status = WriteData(frag, aMediaData->getTimestamp()); 827 if (status == PVMFFailure) 828 { 829 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 830 (0, "PVMFFileOutputNode::ProcessIncomingData: Error - WriteData failed")); 831 return PVMFFailure; 832 } 833 } 834 835 break; 836 837 case EPVMFNodeInitialized: 838 // Already stopped. Ignore incoming data. 839 break; 840 841 default: 842 // Wrong state 843 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 844 (0, "PVMFFileOutputNode::ProcessIncomingData: Error - Wrong state")); 845 status = PVMFFailure; 846 break; 847 } 848 } 849 850 return status; 851 } 852 ////////////////////////////////////////////////////////////////////////////////// 853 PVMFStatus PVMFFileOutputNode::WriteFormatSpecificInfo(OsclAny* aPtr, uint32 aSize) 854 { 855 PVMFStatus status = PVMFSuccess; 856 857 if (!iFileOpened) 858 { 859 if (iFs.Connect() != 0) 860 { 861 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 862 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: iFs.Connect Error.")); 863 status = PVMFErrNoResources; 864 return status; 865 } 866 867 if (0 != iOutputFile.Open(iOutputFileName.get_cstr(), Oscl_File::MODE_READWRITE | Oscl_File::MODE_BINARY, iFs)) 868 { 869 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 870 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: iOutputFile.Open Error.")); 871 status = PVMFErrNoResources; 872 return status; 873 } 874 875 iFileOpened = 1; 876 877 iFirstMediaData = true; 878 } 879 880 if (iFirstMediaData) 881 { 882 // Add the amr header if required 883 if (((PVMFFileOutputInPort*)iInPort)->iFormat == PVMF_MIME_AMR_IETF) 884 { 885 // Check if the incoming data has "#!AMR\n" string 886 if (aSize < AMR_HEADER_SIZE || 887 oscl_strncmp((const char*)aPtr, AMR_HEADER, AMR_HEADER_SIZE) != 0) 888 { 889 // AMR header not found, add AMR header to file first 890 status = WriteData((OsclAny*)AMR_HEADER, AMR_HEADER_SIZE); 891 if (status != PVMFSuccess) 892 { 893 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 894 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: Error - WriteData failed")); 895 return status; 896 } 897 } 898 iFirstMediaData = false; 899 } 900 // Add the amr-wb header if required 901 else if (((PVMFFileOutputInPort*)iInPort)->iFormat == PVMF_MIME_AMRWB_IETF) 902 { 903 // Check if the incoming data has "#!AMR-WB\n" string 904 if (aSize < AMRWB_HEADER_SIZE || 905 oscl_strncmp((const char*)aPtr, AMRWB_HEADER, AMRWB_HEADER_SIZE) != 0) 906 { 907 // AMR header not found, add AMR header to file first 908 status = WriteData((OsclAny*)AMRWB_HEADER, AMRWB_HEADER_SIZE); 909 if (status != PVMFSuccess) 910 { 911 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 912 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: Error - WriteData failed")); 913 return status; 914 } 915 } 916 iFirstMediaData = false; 917 } 918 else if (((PVMFFileOutputInPort*)iInPort)->iFormat == PVMF_MIME_M4V) 919 { 920 if (aSize > 0) 921 { 922 status = WriteData(aPtr, aSize); 923 if (status != PVMFSuccess) 924 { 925 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 926 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: Error - WriteData failed")); 927 return status; 928 } 929 } 930 iFirstMediaData = false; 931 } 932 else if (((PVMFFileOutputInPort*)iInPort)->iFormat == PVMF_MIME_PCM8) 933 { 934 if (aSize > 0) 935 { 936 status = WriteData(aPtr, aSize); 937 if (status != PVMFSuccess) 938 { 939 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 940 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: Error - WriteData failed")); 941 return status; 942 } 943 } 944 iFirstMediaData = false; 945 } 946 else if (((PVMFFileOutputInPort*)iInPort)->iFormat == PVMF_MIME_PCM16) 947 { 948 if (aSize > 0) 949 { 950 status = WriteData(aPtr, aSize); 951 if (status != PVMFSuccess) 952 { 953 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 954 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: Error - WriteData failed")); 955 return status; 956 } 957 } 958 iFirstMediaData = false; 959 } 960 else if (((PVMFFileOutputInPort*)iInPort)->iFormat == PVMF_MIME_3GPP_TIMEDTEXT) 961 { 962 if (aSize > 0) 963 { 964 // TODO Write out the text track level info in some formatted way 965 status = WriteData(aPtr, aSize); 966 if (status != PVMFSuccess) 967 { 968 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 969 (0, "PVMFFileOutputNode::WriteFormatSpecificInfo: Error - WriteData failed")); 970 return status; 971 } 972 } 973 iFirstMediaData = false; 974 } 975 else 976 { 977 iFirstMediaData = false; 978 } 979 } 980 return status; 981 } 982 983 984 ////////////////////////////////////////////////////////////////////////////////// 985 PVMFStatus PVMFFileOutputNode::SendDurationProgress(uint32 aTimestamp) 986 { 987 988 if (iDurationReportEnabled && 989 aTimestamp >= iNextDurationReport) 990 { 991 iNextDurationReport = aTimestamp - (aTimestamp % iDurationReportFreq) + iDurationReportFreq; 992 ReportInfoEvent(PVMF_COMPOSER_DURATION_PROGRESS, (OsclAny*)aTimestamp); 993 } 994 return PVMFSuccess; 995 } 996 997 ////////////////////////////////////////////////////////////////////////////////// 998 PVMFStatus PVMFFileOutputNode::SendFileSizeProgress() 999 { 1000 if (iFileSizeReportEnabled && 1001 iFileSize >= iNextFileSizeReport) 1002 { 1003 iNextFileSizeReport = iFileSize - (iFileSize % iFileSizeReportFreq) + iFileSizeReportFreq; 1004 ReportInfoEvent(PVMF_COMPOSER_FILESIZE_PROGRESS, (OsclAny*)iFileSize); 1005 } 1006 return PVMFSuccess; 1007 } 1008 1009 ////////////////////////////////////////////////////////////////////////////////// 1010 PVMFStatus PVMFFileOutputNode::CheckMaxFileSize(uint32 aFrameSize) 1011 { 1012 if (iMaxFileSizeEnabled) 1013 { 1014 if ((iFileSize + aFrameSize) >= iMaxFileSize) 1015 { 1016 // Change state to initialized 1017 ChangeNodeState(EPVMFNodeInitialized); 1018 1019 // Clear all pending port activity 1020 ClearPendingPortActivity(); 1021 1022 // Report max file size event 1023 ReportInfoEvent(PVMF_COMPOSER_MAXFILESIZE_REACHED, NULL); 1024 return PVMFSuccess; 1025 } 1026 1027 return PVMFPending; 1028 } 1029 return PVMFErrNotSupported; 1030 } 1031 1032 ////////////////////////////////////////////////////////////////////////////////// 1033 PVMFStatus PVMFFileOutputNode::CheckMaxDuration(uint32 aTimestamp) 1034 { 1035 if (iMaxDurationEnabled) 1036 { 1037 if (aTimestamp >= iMaxDuration) 1038 { 1039 // Change state to initialized 1040 ChangeNodeState(EPVMFNodeInitialized); 1041 1042 // Clear all pending port activity 1043 ClearPendingPortActivity(); 1044 1045 // Report max duration event 1046 ReportInfoEvent(PVMF_COMPOSER_MAXDURATION_REACHED, NULL); 1047 return PVMFSuccess; 1048 } 1049 1050 return PVMFPending; 1051 } 1052 return PVMFErrNotSupported; 1053 } 1054 1055 ////////////////////////////////////////////////////////////////////////////////// 1056 PVMFStatus PVMFFileOutputNode::WriteData(OsclAny* aData, uint32 aSize) 1057 { 1058 if (!aData || aSize == 0) 1059 { 1060 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 1061 (0, "PVMFFileOutputNode::WriteData: Error - Invalid data or data size")); 1062 return PVMFFailure; 1063 } 1064 1065 switch (CheckMaxFileSize(aSize)) 1066 { 1067 case PVMFFailure: 1068 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 1069 (0, "PVMFFileOutputNode::WriteData: Error - CheckMaxFileSize failed")); 1070 return PVMFFailure; 1071 1072 case PVMFSuccess: 1073 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, 1074 (0, "PVMFFileOutputNode::WriteData: Maxmimum file size reached")); 1075 return PVMFSuccess; 1076 1077 default: 1078 break; 1079 } 1080 1081 int32 wlength = 0; 1082 if ((wlength = iOutputFile.Write(aData, sizeof(uint8), aSize)) != (int32)aSize) 1083 { 1084 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 1085 (0, "PVMFFileOutputNode::WriteData: Error - File write failed")); 1086 ReportInfoEvent(PVMFInfoProcessingFailure); 1087 return PVMFFailure; 1088 } 1089 else 1090 { 1091 iOutputFile.Flush(); 1092 } 1093 1094 iFileSize += wlength; 1095 return SendFileSizeProgress(); 1096 } 1097 1098 ////////////////////////////////////////////////////////////////////////////////// 1099 PVMFStatus PVMFFileOutputNode::WriteData(OsclRefCounterMemFrag aMemFrag, uint32 aTimestamp) 1100 { 1101 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_STACK_TRACE, 1102 (0, "PVMFFileOutputNode::WriteData: aTimestamp=%d", aTimestamp)); 1103 1104 switch (CheckMaxDuration(aTimestamp)) 1105 { 1106 case PVMFFailure: 1107 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 1108 (0, "PVMFFileOutputNode::WriteData: Error - CheckMaxDuration failed")); 1109 return PVMFFailure; 1110 1111 case PVMFSuccess: 1112 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_DEBUG, 1113 (0, "PVMFFileOutputNode::WriteData: Maxmimum duration reached")); 1114 return PVMFSuccess; 1115 1116 default: 1117 break; 1118 } 1119 1120 if (WriteData(aMemFrag.getMemFragPtr(), aMemFrag.getMemFragSize()) == PVMFSuccess) 1121 return SendDurationProgress(aTimestamp); 1122 else 1123 return PVMFFailure; 1124 } 1125 1126 ////////////////////////////////////////////////////////////////////////////////// 1127 void PVMFFileOutputNode::ClearPendingPortActivity() 1128 { 1129 // index starts at 1 because the current command (i.e. iCmdQueue[0]) will be erased inside Run 1130 while (!iInputCommands.empty()) 1131 { 1132 CommandComplete(iInputCommands, iInputCommands.front(), PVMFFailure); 1133 } 1134 } 1135 1136 void PVMFFileOutputNode::ChangeNodeState(TPVMFNodeInterfaceState aNewState) 1137 { 1138 iInterfaceState = aNewState; 1139 } 1140 1141 /***********************************/ 1142 void PVMFFileOutputNode::CommandComplete(PVMFFileOutputNodeCmdQ& aCmdQ, PVMFFileOutputNodeCommand& aCmd, PVMFStatus aStatus, OsclAny* aEventData) 1143 { 1144 PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO, (0, "PVMFFileOutputNode:CommandComplete Id %d Cmd %d Status %d Context %d Data %d" 1145 , aCmd.iId, aCmd.iCmd, aStatus, aCmd.iContext, aEventData)); 1146 1147 //create response 1148 PVMFCmdResp resp(aCmd.iId, aCmd.iContext, aStatus, aEventData); 1149 PVMFSessionId session = aCmd.iSession; 1150 1151 //Erase the command from the queue. 1152 aCmdQ.Erase(&aCmd); 1153 1154 //Report completion to the session observer. 1155 ReportCmdCompleteEvent(session, resp); 1156 } 1157 1158 PVMFCommandId PVMFFileOutputNode::QueueCommandL(PVMFFileOutputNodeCommand& aCmd) 1159 { 1160 PVMFCommandId id; 1161 1162 id = iInputCommands.AddL(aCmd); 1163 1164 //wakeup the AO 1165 RunIfNotReady(); 1166 1167 return id; 1168 } 1169 1170 1171 ///////////////////////////////////////////////////// 1172 bool PVMFFileOutputNode::ProcessPortActivity() 1173 {//called by the AO to process a port activity message 1174 //Pop the queue... 1175 PVMFPortActivity activity(iPortActivityQueue.front()); 1176 iPortActivityQueue.erase(&iPortActivityQueue.front()); 1177 1178 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 1179 (0, "0x%x PVMFFileOutputNode::ProcessPortActivity: port=0x%x, type=%d", 1180 this, activity.iPort, activity.iType)); 1181 1182 int32 err = OsclErrNone; 1183 1184 PVMFStatus status = PVMFSuccess; 1185 switch (activity.iType) 1186 { 1187 case PVMF_PORT_ACTIVITY_INCOMING_MSG: 1188 status = ProcessIncomingMsg(activity.iPort); 1189 //if there is still data, queue another port activity event. 1190 if (activity.iPort->IncomingMsgQueueSize() > 0) 1191 { 1192 OSCL_TRY(err, iPortActivityQueue.push_back(activity);); 1193 } 1194 break; 1195 case PVMF_PORT_ACTIVITY_OUTGOING_MSG: 1196 default: 1197 return false; 1198 } 1199 1200 return true; 1201 } 1202 1203 ///////////////////////////////////////////////////// 1204 PVMFStatus PVMFFileOutputNode::ProcessIncomingMsg(PVMFPortInterface* aPort) 1205 { 1206 //Called by the AO to process one buffer off the port's 1207 //incoming data queue. This routine will dequeue and 1208 //dispatch the data. 1209 1210 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 1211 (0, "0x%x PVMFFileOutputNode::ProcessIncomingMsg: aPort=0x%x", this, aPort)); 1212 1213 if (aPort->GetPortTag() != PVMF_FILE_OUTPUT_NODE_PORT_TYPE_SINK) 1214 { 1215 return PVMFFailure; 1216 } 1217 PVMFSharedMediaMsgPtr msg; 1218 PVMFStatus status = aPort->DequeueIncomingMsg(msg); 1219 if (status != PVMFSuccess) 1220 { 1221 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 1222 (0, "0x%x PVMFFileOutputNode::ProcessIncomingMsg: Error - DequeueIncomingMsg failed", this)); 1223 return status; 1224 } 1225 /* 1226 INFORMATION!!! 1227 The FileOutputNode is generally used by the engine unit tests as SinkNode 1228 For now, most of the unit tests have OBSOLETED the use of FileOutputNode, 1229 But still some of the tests are using the FileOutputNode in place of, 1230 MIO (RefFileOutput). 1231 1232 Since the usage FileOutputNode is not defined yet, we are adding support for 1233 BOS Message as a NO-OP so that the node should be able to handle Any and all 1234 the BOS Messages gracefully. 1235 1236 IMPORTANT!!!, 1237 For Complete support of BOS in the FileOutputNode, we need to make more changes. 1238 Those changes will be done only once the life scope of FileOutputNode is defined. 1239 */ 1240 if (msg->getFormatID() == PVMF_MEDIA_CMD_BOS_FORMAT_ID) 1241 { 1242 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 1243 (0, "PVFileOutputNode::ProcessIncomingMsg BOS Received")); 1244 return PVMFSuccess; 1245 } 1246 1247 // Transfer to the port's sync queue to do synchronization 1248 // This is temporary until data is directly saved to the sync queue 1249 uint32 dropped; 1250 uint32 skipped; 1251 status = ((PVMFFileOutputInPort*)aPort)->iDataQueue.QueueMediaData(msg, &dropped, &skipped); 1252 if (dropped > 0) 1253 { 1254 PVMFNodeInterface::ReportInfoEvent(PVMFInfoDataDiscarded); 1255 } 1256 1257 if (status == PVMFErrNoMemory) 1258 { 1259 return PVMFFailure; 1260 } 1261 else 1262 { 1263 return PVMFSuccess; 1264 } 1265 } 1266 1267 bool PVMFFileOutputNode::ProcessCommand(PVMFFileOutputNodeCommand& aCmd) 1268 { 1269 //normally this node will not start processing one command 1270 //until the prior one is finished. However, a hi priority 1271 //command such as Cancel must be able to interrupt a command 1272 //in progress. 1273 if (!iCurrentCommand.empty() && !aCmd.hipri()) 1274 return false; 1275 1276 switch (aCmd.iCmd) 1277 { 1278 case PVMF_GENERIC_NODE_QUERYUUID: 1279 DoQueryUuid(aCmd); 1280 break; 1281 1282 case PVMF_GENERIC_NODE_QUERYINTERFACE: 1283 DoQueryInterface(aCmd); 1284 break; 1285 1286 case PVMF_GENERIC_NODE_REQUESTPORT: 1287 DoRequestPort(aCmd); 1288 break; 1289 1290 case PVMF_GENERIC_NODE_RELEASEPORT: 1291 DoReleasePort(aCmd); 1292 break; 1293 1294 case PVMF_GENERIC_NODE_INIT: 1295 DoInit(aCmd); 1296 break; 1297 1298 case PVMF_GENERIC_NODE_PREPARE: 1299 DoPrepare(aCmd); 1300 break; 1301 1302 case PVMF_GENERIC_NODE_START: 1303 DoStart(aCmd); 1304 break; 1305 1306 case PVMF_GENERIC_NODE_STOP: 1307 DoStop(aCmd); 1308 break; 1309 1310 case PVMF_GENERIC_NODE_FLUSH: 1311 DoFlush(aCmd); 1312 break; 1313 1314 case PVMF_GENERIC_NODE_PAUSE: 1315 DoPause(aCmd); 1316 break; 1317 1318 case PVMF_GENERIC_NODE_RESET: 1319 DoReset(aCmd); 1320 break; 1321 1322 case PVMF_GENERIC_NODE_CANCELALLCOMMANDS: 1323 DoCancelAllCommands(aCmd); 1324 break; 1325 1326 case PVMF_GENERIC_NODE_CANCELCOMMAND: 1327 DoCancelCommand(aCmd); 1328 break; 1329 1330 default://unknown command type 1331 CommandComplete(iInputCommands, aCmd, PVMFFailure); 1332 break; 1333 } 1334 1335 return true; 1336 } 1337 1338 /** 1339 //A routine to tell if a flush operation is in progress. 1340 */ 1341 bool PVMFFileOutputNode::FlushPending() 1342 { 1343 return (iCurrentCommand.size() > 0 1344 && iCurrentCommand.front().iCmd == PVMF_GENERIC_NODE_FLUSH); 1345 } 1346 1347 void PVMFFileOutputNode::DoQueryUuid(PVMFFileOutputNodeCommand& aCmd) 1348 { 1349 //This node supports Query UUID from any state 1350 1351 OSCL_String* mimetype; 1352 Oscl_Vector<PVUuid, OsclMemAllocator> *uuidvec; 1353 bool exactmatch; 1354 aCmd.PVMFFileOutputNodeCommandBase::Parse(mimetype, uuidvec, exactmatch); 1355 1356 //Try to match the input mimetype against any of 1357 //the custom interfaces for this node 1358 //Match against custom interface1... 1359 if (*mimetype == PVMF_FILE_OUTPUT_NODE_CUSTOM1_MIMETYPE 1360 //also match against base mimetypes for custom interface1, 1361 //unless exactmatch is set. 1362 || (!exactmatch && *mimetype == PVMF_FILE_OUTPUT_NODE_MIMETYPE) 1363 || (!exactmatch && *mimetype == PVMF_BASEMIMETYPE)) 1364 { 1365 uuidvec->push_back(PvmfFileOutputNodeConfigUuid); 1366 uuidvec->push_back(PvmfComposerSizeAndDurationUuid); 1367 uuidvec->push_back(PvmfNodesSyncControlUuid); 1368 } 1369 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1370 } 1371 1372 void PVMFFileOutputNode::DoQueryInterface(PVMFFileOutputNodeCommand& aCmd) 1373 { 1374 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 1375 (0, "PVMFFileOutputNode::DoQueryInterface")); 1376 1377 PVUuid* uuid; 1378 PVInterface** ptr; 1379 aCmd.PVMFFileOutputNodeCommandBase::Parse(uuid, ptr); 1380 1381 if (queryInterface(*uuid, *ptr)) 1382 { 1383 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1384 } 1385 else 1386 { 1387 CommandComplete(iInputCommands, aCmd, PVMFFailure); 1388 } 1389 } 1390 1391 void PVMFFileOutputNode::DoInit(PVMFFileOutputNodeCommand& aCmd) 1392 { 1393 PVMFStatus iRet = PVMFSuccess; 1394 switch (iInterfaceState) 1395 { 1396 case EPVMFNodeIdle: 1397 //this node doesn't need to do anything to get ready 1398 //to start. 1399 SetState(EPVMFNodeInitialized); 1400 break; 1401 case EPVMFNodeInitialized: 1402 break; 1403 default: 1404 iRet = PVMFErrInvalidState; 1405 break; 1406 } 1407 CommandComplete(iInputCommands, aCmd, iRet); 1408 } 1409 1410 /** 1411 //Called by the command handler AO to do the node Prepare 1412 */ 1413 void PVMFFileOutputNode::DoPrepare(PVMFFileOutputNodeCommand& aCmd) 1414 { 1415 PVMFStatus iRet = PVMFSuccess; 1416 switch (iInterfaceState) 1417 { 1418 case EPVMFNodeInitialized: 1419 //this node doesn't need to do anything to get ready 1420 //to start. 1421 SetState(EPVMFNodePrepared); 1422 break; 1423 case EPVMFNodePrepared: 1424 break; 1425 default: 1426 iRet = PVMFErrInvalidState; 1427 break; 1428 } 1429 CommandComplete(iInputCommands, aCmd, iRet); 1430 } 1431 1432 void PVMFFileOutputNode::DoStart(PVMFFileOutputNodeCommand& aCmd) 1433 { 1434 PVMFStatus status = PVMFSuccess; 1435 switch (iInterfaceState) 1436 { 1437 case EPVMFNodePrepared: 1438 case EPVMFNodePaused: 1439 { 1440 if (!iClock) 1441 { 1442 // If not using sync clock, start processing incoming data 1443 ((PVMFFileOutputInPort*)iInPort)->Start(); 1444 } 1445 if (!iFileOpened) 1446 { 1447 if (iFs.Connect() != 0) 1448 { 1449 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 1450 (0, "PVMFFileOutputNode::DoStart: iFs.Connect Error.")); 1451 status = PVMFErrNoResources; 1452 break; 1453 } 1454 1455 if (0 != iOutputFile.Open(iOutputFileName.get_cstr(), Oscl_File::MODE_READWRITE | Oscl_File::MODE_BINARY, iFs)) 1456 { 1457 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 1458 (0, "PVMFFileOutputNode::DoStart: iOutputFile.Open Error.")); 1459 status = PVMFErrNoResources; 1460 break; 1461 } 1462 1463 iFileOpened = 1; 1464 1465 iFirstMediaData = true; 1466 } 1467 SetState(EPVMFNodeStarted); 1468 1469 break; 1470 } 1471 case EPVMFNodeStarted: 1472 status = PVMFSuccess; 1473 break; 1474 default: 1475 status = PVMFErrInvalidState; 1476 break; 1477 } 1478 1479 CommandComplete(iInputCommands, aCmd, status); 1480 } 1481 1482 void PVMFFileOutputNode::DoStop(PVMFFileOutputNodeCommand& aCmd) 1483 { 1484 switch (iInterfaceState) 1485 { 1486 case EPVMFNodeStarted: 1487 case EPVMFNodePaused: 1488 // Stop data source 1489 if (iInPort) 1490 { 1491 1492 ((PVMFFileOutputInPort*)iInPort)->Stop(); 1493 CloseOutputFile(); 1494 } 1495 1496 // Clear queued messages in ports 1497 uint32 i; 1498 for (i = 0; i < iPortVector.size(); i++) 1499 iPortVector[i]->ClearMsgQueues(); 1500 1501 // Clear scheduled port activities 1502 iPortActivityQueue.clear(); 1503 1504 //transition to Initialized state 1505 SetState(EPVMFNodePrepared); 1506 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1507 break; 1508 case EPVMFNodePrepared: 1509 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1510 break; 1511 default: 1512 CommandComplete(iInputCommands, aCmd, PVMFErrInvalidState); 1513 break; 1514 } 1515 } 1516 1517 void PVMFFileOutputNode::DoFlush(PVMFFileOutputNodeCommand& aCmd) 1518 { 1519 switch (iInterfaceState) 1520 { 1521 case EPVMFNodeStarted: 1522 case EPVMFNodePaused: 1523 //the flush is asynchronous. move the command from 1524 //the input command queue to the current command, where 1525 //it will remain until the flush completes. 1526 int32 err; 1527 OSCL_TRY(err, iCurrentCommand.StoreL(aCmd);); 1528 if (err != OsclErrNone) 1529 { 1530 CommandComplete(iInputCommands, aCmd, PVMFErrNoMemory); 1531 return; 1532 } 1533 iInputCommands.Erase(&aCmd); 1534 1535 //Notify all ports to suspend their input 1536 { 1537 for (uint32 i = 0; i < iPortVector.size(); i++) 1538 iPortVector[i]->SuspendInput(); 1539 } 1540 1541 // Stop data source 1542 break; 1543 1544 default: 1545 CommandComplete(iInputCommands, aCmd, PVMFErrInvalidState); 1546 break; 1547 } 1548 } 1549 1550 void PVMFFileOutputNode::DoPause(PVMFFileOutputNodeCommand& aCmd) 1551 { 1552 switch (iInterfaceState) 1553 { 1554 case EPVMFNodeStarted: 1555 { 1556 // Pause data source 1557 if (!iClock) 1558 { 1559 // If not using sync clock, pause processing of incoming data 1560 ((PVMFFileOutputInPort*)iInPort)->Pause(); 1561 } 1562 1563 SetState(EPVMFNodePaused); 1564 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1565 break; 1566 } 1567 case EPVMFNodePaused: 1568 { 1569 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1570 } 1571 break; 1572 default: 1573 CommandComplete(iInputCommands, aCmd, PVMFErrInvalidState); 1574 break; 1575 } 1576 } 1577 1578 void PVMFFileOutputNode::DoReset(PVMFFileOutputNodeCommand& aCmd) 1579 { 1580 if (IsAdded()) 1581 { 1582 if (iInPort) 1583 { 1584 OSCL_DELETE(((PVMFFileOutputInPort*)iInPort)); 1585 iInPort = NULL; 1586 } 1587 1588 //logoff & go back to Created state. 1589 SetState(EPVMFNodeIdle); 1590 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1591 } 1592 else 1593 { 1594 OSCL_LEAVE(OsclErrInvalidState); 1595 } 1596 } 1597 1598 1599 void PVMFFileOutputNode::DoRequestPort(PVMFFileOutputNodeCommand& aCmd) 1600 { 1601 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 1602 (0, "PVMFFileOutputNode::DoRequestPort")); 1603 //This node supports port request from any state 1604 1605 //retrieve port tag. 1606 int32 tag; 1607 OSCL_String* portconfig; 1608 1609 aCmd.PVMFFileOutputNodeCommandBase::Parse(tag, portconfig); 1610 1611 //validate the tag... 1612 switch (tag) 1613 { 1614 case PVMF_FILE_OUTPUT_NODE_PORT_TYPE_SINK: 1615 break; 1616 default: 1617 //bad port tag 1618 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_ERR, 1619 (0, "PVMFFileOutputNode::DoRequestPort: Error - Invalid port tag")); 1620 CommandComplete(iInputCommands, aCmd, PVMFFailure); 1621 return; 1622 } 1623 1624 if (iInPort) 1625 { 1626 // it's been taken for now, so reject this request 1627 CommandComplete(iInputCommands, aCmd, PVMFFailure); 1628 return; 1629 } 1630 1631 // Create and configure output port 1632 int32 err; 1633 PVMFFormatType fmt = PVMF_MIME_FORMAT_UNKNOWN; 1634 if (portconfig) 1635 { 1636 fmt = portconfig->get_str(); 1637 } 1638 1639 if (iFormat != PVMF_MIME_FORMAT_UNKNOWN && 1640 iFormat != fmt) 1641 { 1642 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 1643 (0, "PVMFFileOutputNode::DoRequestPort: Error - Format not supported (format was preset)")); 1644 CommandComplete(iInputCommands, aCmd, PVMFErrArgument); 1645 return; 1646 } 1647 1648 OSCL_TRY(err, iInPort = OSCL_NEW(PVMFFileOutputInPort, (tag, this));); 1649 if (err != OsclErrNone || !iInPort) 1650 { 1651 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, iLogger, PVLOGMSG_ERR, 1652 (0, "PVMFFileOutputNode::DoRequestPort: Error - PVMFFileOutputInPort::Create() failed")); 1653 CommandComplete(iInputCommands, aCmd, PVMFErrNoMemory); 1654 return; 1655 } 1656 1657 ((PVMFFileOutputInPort*)iInPort)->SetClock(iClock); 1658 ((PVMFFileOutputInPort*)iInPort)->SetMargins(iEarlyMargin, iLateMargin); 1659 1660 //if format was provided in mimestring, set it now. 1661 if (portconfig) 1662 { 1663 PVMFFormatType fmt = portconfig->get_str(); 1664 if (fmt != PVMF_MIME_FORMAT_UNKNOWN 1665 && ((PVMFFileOutputInPort*)iInPort)->IsFormatSupported(fmt)) 1666 { 1667 ((PVMFFileOutputInPort*)iInPort)->iFormat = fmt; 1668 ((PVMFFileOutputInPort*)iInPort)->FormatUpdated(); 1669 } 1670 } 1671 1672 //Return the port pointer to the caller. 1673 CommandComplete(iInputCommands, aCmd, PVMFSuccess, (OsclAny*)iInPort); 1674 } 1675 1676 void PVMFFileOutputNode::DoReleasePort(PVMFFileOutputNodeCommand& aCmd) 1677 { 1678 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 1679 (0, "PVMFFileOutputNode::DoReleasePort")); 1680 1681 if (iInPort) 1682 { 1683 OSCL_DELETE(((PVMFFileOutputInPort*)iInPort)); 1684 iInPort = NULL; 1685 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1686 return; 1687 } 1688 CommandComplete(iInputCommands, aCmd, PVMFFailure); 1689 } 1690 1691 void PVMFFileOutputNode::DoCancelAllCommands(PVMFFileOutputNodeCommand& aCmd) 1692 { 1693 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, 1694 (0, "PVMFFileOutputNode::DoCancelAllCommands")); 1695 //first cancel the current command if any 1696 { 1697 while (!iCurrentCommand.empty()) 1698 CommandComplete(iCurrentCommand, iCurrentCommand[0], PVMFErrCancelled); 1699 } 1700 1701 //next cancel all queued commands 1702 { 1703 //start at element 1 since this cancel command is element 0. 1704 while (iInputCommands.size() > 1) 1705 CommandComplete(iInputCommands, iInputCommands[1], PVMFErrCancelled); 1706 } 1707 1708 //finally, report cancel complete. 1709 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1710 } 1711 1712 void PVMFFileOutputNode::DoCancelCommand(PVMFFileOutputNodeCommand& aCmd) 1713 { 1714 //extract the command ID from the parameters. 1715 PVMFCommandId id; 1716 aCmd.PVMFFileOutputNodeCommandBase::Parse(id); 1717 1718 //first check "current" command if any 1719 { 1720 PVMFFileOutputNodeCommand* cmd = iCurrentCommand.FindById(id); 1721 if (cmd) 1722 { 1723 //cancel the queued command 1724 CommandComplete(iCurrentCommand, *cmd, PVMFErrCancelled); 1725 //report cancel success 1726 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1727 return; 1728 } 1729 } 1730 1731 //next check input queue. 1732 { 1733 //start at element 1 since this cancel command is element 0. 1734 PVMFFileOutputNodeCommand* cmd = iInputCommands.FindById(id, 1); 1735 if (cmd) 1736 { 1737 //cancel the queued command 1738 CommandComplete(iInputCommands, *cmd, PVMFErrCancelled); 1739 //report cancel success 1740 CommandComplete(iInputCommands, aCmd, PVMFSuccess); 1741 return; 1742 } 1743 } 1744 //if we get here the command isn't queued so the cancel fails. 1745 CommandComplete(iInputCommands, aCmd, PVMFFailure); 1746 } 1747