diff --git a/AampConfig.cpp b/AampConfig.cpp index ca0a2d06e..fc206e840 100644 --- a/AampConfig.cpp +++ b/AampConfig.cpp @@ -447,7 +447,6 @@ static const ConfigLookupEntryInt mConfigLookupTableInt[AAMPCONFIG_INT_COUNT+CON {0,"drmStallTimeout",eAAMPConfig_DrmStallTimeout,true,eCONFIG_RANGE_TIMEOUT}, {0,"drmStartTimeout",eAAMPConfig_DrmStartTimeout,true,eCONFIG_RANGE_TIMEOUT}, {0,"timeBasedBufferSeconds",eAAMPConfig_TimeBasedBufferSeconds,true,eCONFIG_RANGE_PLAYBACK_OFFSET}, - {DEFAULT_MAX_DOWNLOAD_BUFFER,"maxDownloadBuffer",eAAMPConfig_MaxDownloadBuffer,true,eCONFIG_RANGE_PLAYBACK_OFFSET}, {DEFAULT_TELEMETRY_REPORT_INTERVAL,"telemetryInterval",eAAMPConfig_TelemetryInterval,true}, {0,"rateCorrectionDelay", eAAMPConfig_RateCorrectionDelay,true}, {-1,"harvestDuration",eAAMPConfig_HarvestDuration,false,eCONFIG_RANGE_HARVEST_DURATION}, diff --git a/AampConfig.h b/AampConfig.h index 714ba9371..635947fc7 100644 --- a/AampConfig.h +++ b/AampConfig.h @@ -290,7 +290,6 @@ typedef enum eAAMPConfig_DrmStallTimeout, /**< Stall Timeout for DRM license request*/ eAAMPConfig_DrmStartTimeout, /**< Start Timeout for DRM license request*/ eAAMPConfig_TimeBasedBufferSeconds, - eAAMPConfig_MaxDownloadBuffer, /**< Max download buffer in seconds, this can be used to limit player download job scheduling for DASH*/ eAAMPConfig_TelemetryInterval, /**< time interval for the telemetry reporting*/ eAAMPConfig_RateCorrectionDelay, /**< Delay Rate Correction upon discontinuity in seconds */ eAAMPConfig_HarvestDuration, /**< Harvest duration time */ diff --git a/AampDefine.h b/AampDefine.h index 225ca103c..7f1537ef3 100644 --- a/AampDefine.h +++ b/AampDefine.h @@ -141,7 +141,6 @@ #define MIN_MONITOR_AV_JUMP_THRESHOLD_MS 1 /**< minimum jump threshold to trigger MonitorAV reporting */ #define MAX_MONITOR_AV_JUMP_THRESHOLD_MS 10000 /**< maximum jump threshold to trigger MonitorAV reporting */ #define DEFAULT_MONITOR_AV_JUMP_THRESHOLD_MS 100 /**< default jump threshold to MonitorAV reporting */ -#define DEFAULT_MAX_DOWNLOAD_BUFFER 10 /**< Default maximum download buffer in seconds, this can be used to limit player download job scheduling for DASH */ #define DEFAULT_MONITOR_AV_REPORTING_INTERVAL 1000 /**< time interval in ms for MonitorAV reporting */ // We can enable the following once we have a thread monitoring video PTS progress and triggering subtec clock fast update when we detect video freeze. Disabled it for now for brute force fast refresh.. diff --git a/AampDownloadInfo.hpp b/AampDownloadInfo.hpp deleted file mode 100644 index e08d13c3f..000000000 --- a/AampDownloadInfo.hpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file AampDownloadInfo.hpp - * @brief Download information for AAMP fragment downloads - */ - -#ifndef AAMP_DOWNLOAD_INFO_HPP -#define AAMP_DOWNLOAD_INFO_HPP - -#include -#include -#include -#include -#include -#include "AampConstants.h" -#include "AampCurlDefine.h" -#include "AampMediaType.h" -#include "AampUtils.h" -#include "AampConfig.h" -#include "AampTime.h" -#include "main_aamp.h" - -struct URIInfo -{ - std::string url; /**< URL of the fragment */ - std::string range; /**< Byte range of the fragment in the format "-" i.e. "0-511" for first 512 bytes from url. Empty string if no explicit range (downloads whole media segment) */ - - /** - * @brief Default constructor - */ - URIInfo() - : url(""), - range("") - { - } - - /** - * @brief Parameterized constructor - * @param url URL of the fragment - * @param range Byte range of the fragment - */ - URIInfo(const std::string &url, const std::string &range) - : url(url), - range(range) - { - } - - /** - * @brief Parameterized constructor - * @param url URL of the fragment - */ - URIInfo(const std::string &url) - : url(url), - range("") - { - } -}; - -typedef std::map URLBitrateMap; - -/** - * @struct DownloadInfo - * @brief Stores information for downloading a fragment - */ -struct DownloadInfo -{ - AampMediaType mediaType; /**< Media type of the fragment */ - AampCurlInstance curlInstance; /**< Curl instance to be used for download */ - double fragmentDurationSec; /**< Duration of the fragment in seconds */ - double absolutePosition; /**< Absolute position of the fragment in seconds as per manifest file. For live it will be in epoch time and for VOD, it will be resolved based on the position in period */ - std::string range; /**< Byte range of the fragment in the format "-" i.e. "0-511" for first 512 bytes from url. Empty string if no explicit range (downloads whole media segment) */ - int fragmentIndex; /**< Index of the byte range in the fragment */ - uint64_t fragmentOffset; /**< Offset of the fragment in byte range based stream */ - bool isInitSegment; /**< Flag indicating if the fragment is an initialization segment */ - bool isDiscontinuity; /**< Flag indicating if the fragment is discontinuous */ - bool isPlayingAd; /**< Flag indicating if an ad is playing */ - bool failoverContentSegment; /**< Flag indicating if the FCS content matched */ - double pts; /**< Scaled PTS value from the fragment */ - uint64_t fragmentNumber; /**< Fragment number, incremented with each new segment in track, corresponds to $Number& in segment template */ - uint32_t timeScale; /**< Fragment Time scale, divide fragment time or duration by timeScale to convert to seconds */ - std::string url; /**< URL of the fragment */ - uint32_t bandwidth; /**< Bandwidth of the fragment at the time of job submission */ - AampTime ptsOffset; /**< Period specific PTS offset used for restamping */ - URLBitrateMap uriList; /**< List of all possible URLs with their respective bitrates */ - - /** - * @brief Default constructor - */ - DownloadInfo() - : mediaType(eMEDIATYPE_DEFAULT), - curlInstance(eCURLINSTANCE_MAX), - fragmentDurationSec(0), - absolutePosition(0), - range(""), - fragmentIndex(-1), - fragmentOffset(0), - isInitSegment(false), - isDiscontinuity(false), - isPlayingAd(false), - failoverContentSegment(false), - url(""), - pts(0), - fragmentNumber(0), - timeScale(1), - bandwidth(0), - ptsOffset(0), - uriList() - { - } - - /** - * @brief Parameterized constructor - * @param mediaType Media type of the fragment - * @param curlInstance Curl instance to be used for download - * @param absolutePosition Absolute position of the fragment in seconds - * @param fragmentDurationSec Duration of the fragment in seconds - * @param range Range of the fragment - * @param fragmentIndex Index of the byte range in the fragment - * @param fragmentOffset Offset of the fragment in byte range based stream - * @param isInitSegment Flag indicating if the fragment is an initialization segment - * @param isDiscontinuity Flag indicating if the fragment is discontinuous - * @param isPlayingAd Flag indicating if an ad is playing - * @param failoverContentSegment Flag indicating if the FCS content - * @param pts Scale PTS - * @param fragmentNumber Fragment number - * @param timeScale Time scale - * @param bandwidth Bandwidth of the fragment - * @param ptsOffset PTS offset - * @param uriList List of all possible URLs with their respective bitrates - */ - DownloadInfo(AampMediaType mediaType, AampCurlInstance curlInstance, double absolutePosition, double fragmentDurationSec, std::string range, int fragmentIndex, uint64_t fragmentOffset, bool isInitSegment, bool isDiscontinuity, bool isPlayingAd, bool failoverContentSegment, double pts, uint64_t fragmentNumber, uint32_t timeScale, uint32_t bandwidth, AampTime ptsOffset, URLBitrateMap uriList) - : mediaType(mediaType), - curlInstance(curlInstance), - absolutePosition(absolutePosition), - fragmentDurationSec(fragmentDurationSec), - range(std::move(range)), - fragmentIndex(fragmentIndex), - fragmentOffset(fragmentOffset), - isInitSegment(isInitSegment), - isDiscontinuity(isDiscontinuity), - isPlayingAd(isPlayingAd), - failoverContentSegment(failoverContentSegment), - pts(pts), - fragmentNumber(fragmentNumber), - timeScale(timeScale), - bandwidth(bandwidth), - ptsOffset(ptsOffset), - uriList(std::move(uriList)), - url("") - { - } -}; - -typedef std::shared_ptr DownloadInfoPtr; - -#endif /* AAMP_DOWNLOAD_INFO_HPP */ diff --git a/AampFragmentDescriptor.cpp b/AampFragmentDescriptor.cpp deleted file mode 100644 index 9fdee790f..000000000 --- a/AampFragmentDescriptor.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file AampFragmentDescriptor.cpp - * @brief FragmentDescriptor implementation - */ - - -#include "AampUtils.h" -#include "AampFragmentDescriptor.hpp" -#include "AampLogManager.h" - -FragmentDescriptor::FragmentDescriptor() : manifestUrl(""), Bandwidth(0), Number(0), Time(0), RepresentationID(""), matchingBaseURL(""), bUseMatchingBaseUrl(false), nextfragmentNum(-1), nextfragmentTime(0), TimeScale(1) -{ -} - -FragmentDescriptor::FragmentDescriptor(const FragmentDescriptor &p) : manifestUrl(p.manifestUrl), Bandwidth(p.Bandwidth), RepresentationID(p.RepresentationID), Number(p.Number), Time(p.Time), matchingBaseURL(p.matchingBaseURL), bUseMatchingBaseUrl(p.bUseMatchingBaseUrl), nextfragmentNum(p.nextfragmentNum), nextfragmentTime(p.nextfragmentTime), TimeScale(p.TimeScale) -{ -} - -FragmentDescriptor &FragmentDescriptor::operator=(const FragmentDescriptor &p) -{ - manifestUrl = p.manifestUrl; - RepresentationID.assign(p.RepresentationID); - Bandwidth = p.Bandwidth; - Number = p.Number; - Time = p.Time; - matchingBaseURL = p.matchingBaseURL; - nextfragmentNum = p.nextfragmentNum; - nextfragmentTime = p.nextfragmentTime; - TimeScale = p.TimeScale; - return *this; -} - -std::string FragmentDescriptor::GetMatchingBaseUrl() const -{ - return matchingBaseURL; -} - -void FragmentDescriptor::ClearMatchingBaseUrl() -{ - matchingBaseURL.clear(); -} - -void FragmentDescriptor::AppendMatchingBaseUrl(const std::vector *baseUrls) -{ - if (baseUrls && baseUrls->size() > 0) - { - const std::string &url = baseUrls->at(0)->GetUrl(); - if (url.empty()) - { - } - else if (aamp_IsAbsoluteURL(url)) - { - if (bUseMatchingBaseUrl) - { - std::string prefHost = aamp_getHostFromURL(manifestUrl); - for (auto &item : *baseUrls) - { - const std::string itemUrl = item->GetUrl(); - std::string host = aamp_getHostFromURL(itemUrl); - if (0 == prefHost.compare(host)) - { - matchingBaseURL = item->GetUrl(); - return; - } - } - } - matchingBaseURL = url; - } - else if (url.rfind("/", 0) == 0) - { - matchingBaseURL = aamp_getHostFromURL(matchingBaseURL); - matchingBaseURL += url; - AAMPLOG_WARN("baseURL with leading /"); - } - else - { - if (!matchingBaseURL.empty() && matchingBaseURL.back() != '/') - { // add '/' delimiter only if parent baseUrl doesn't already end with one - matchingBaseURL += "/"; - } - matchingBaseURL += url; - } - } -} - -void FragmentDescriptor::AppendMatchingBaseUrl(const std::vector &baseUrls) -{ - if (!baseUrls.empty()) - { - const std::string &url = baseUrls.at(0); - if (url.empty()) - { - // Do nothing if the URL is empty - } - else if (aamp_IsAbsoluteURL(url)) - { - if (bUseMatchingBaseUrl) - { - std::string prefHost = aamp_getHostFromURL(manifestUrl); - for (const auto &itemUrl : baseUrls) - { - std::string host = aamp_getHostFromURL(itemUrl); - if (0 == prefHost.compare(host)) - { - matchingBaseURL = itemUrl; - return; - } - } - } - matchingBaseURL = url; - } - else if (url.rfind("/", 0) == 0) - { - matchingBaseURL = aamp_getHostFromURL(matchingBaseURL); - matchingBaseURL += url; - AAMPLOG_WARN("baseURL with leading /"); - } - else - { - if (!matchingBaseURL.empty() && matchingBaseURL.back() != '/') - { - // Add '/' delimiter only if parent baseUrl doesn't already end with one - matchingBaseURL += "/"; - } - matchingBaseURL += url; - } - } -} diff --git a/AampFragmentDescriptor.hpp b/AampFragmentDescriptor.hpp deleted file mode 100644 index aa2b99e5c..000000000 --- a/AampFragmentDescriptor.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file AampFragmentDescriptor.hpp - * @brief FragmentDescriptor class definition. - */ - -#ifndef AAMP_FRAGMENT_DESCRIPTOR_HPP -#define AAMP_FRAGMENT_DESCRIPTOR_HPP - -#include -#include -#include -#include "libdash/IBaseUrl.h" -#include "libdash/IMPD.h" - -using namespace dash; -using namespace dash::mpd; - -/** - * @class FragmentDescriptor - * @brief Stores information of dash fragment - */ -class FragmentDescriptor -{ -private: - std::string matchingBaseURL; - -public: - std::string manifestUrl; - uint32_t Bandwidth; - std::string RepresentationID; - uint64_t Number; - double Time; // In units of timescale - bool bUseMatchingBaseUrl; - int64_t nextfragmentNum; - double nextfragmentTime; - uint32_t TimeScale; - FragmentDescriptor(); - FragmentDescriptor(const FragmentDescriptor &p); - FragmentDescriptor &operator=(const FragmentDescriptor &p); - std::string GetMatchingBaseUrl() const; - void ClearMatchingBaseUrl(); - void AppendMatchingBaseUrl(const std::vector *baseUrls); - void AppendMatchingBaseUrl(const std::vector &baseUrls); -}; - -#endif // AAMP_FRAGMENT_DESCRIPTOR_HPP diff --git a/AampMPDParseHelper.cpp b/AampMPDParseHelper.cpp index a83a185e0..9f86d8dd2 100644 --- a/AampMPDParseHelper.cpp +++ b/AampMPDParseHelper.cpp @@ -544,7 +544,7 @@ double AampMPDParseHelper::GetPeriodStartTime(int periodIndex,uint64_t mLastPlay if(mNumberOfPeriods == 1 && periodIndex == 0 && mIsLiveManifest && !mIsFogMPD && (periodStart == mAvailabilityStartTime) && deltaInStartTime == 0) { // Temp hack to avoid running below if condition code for segment timeline , Due to this periodStart is getting changed for Cloud TSB or Hot Cloud DVR with segment timeline, which is not required. - bool bHasSegmentTimeline = aamp_HasSegmentTime(mMPDInstance->GetPeriods().at(periodIndex)); + bool bHasSegmentTimeline = aamp_HasSegmentTimeline(mMPDInstance->GetPeriods().at(periodIndex)); if( false == bHasSegmentTimeline ) // only for segment template { // segmentTemplate without timeline having period start "PT0S". @@ -759,64 +759,52 @@ double AampMPDParseHelper::aamp_GetPeriodStartTimeDeltaRelativeToPTSOffset(IPeri } /** - * @brief A helper function to check if period has segment timeline for video track + * @brief A helper function to check if period has segment timeline for video track * @param period period of segment * @return True if period has segment timeline for video otherwise false */ -bool AampMPDParseHelper::aamp_HasSegmentTime(IPeriod * period) +bool AampMPDParseHelper::aamp_HasSegmentTimeline(IPeriod * period) { - auto segmentTemplates = GetSegmentTemplateForVideo(period); - if (segmentTemplates && segmentTemplates->HasSegmentTemplate()) - { - const ISegmentTimeline *segmentTimeline = segmentTemplates->GetSegmentTimeline(); - return (segmentTimeline != nullptr); - } - return false; -} + bool bRetValue = false; + + const std::vector adaptationSets = period->GetAdaptationSets(); + const ISegmentTemplate *representation = NULL; + const ISegmentTemplate *adaptationSet = NULL; + if( adaptationSets.size() > 0 ) + { + IAdaptationSet * firstAdaptation = NULL; + for (auto &adaptationSet : period->GetAdaptationSets()) + { + //Check for video adaptation + if (!IsContentType(adaptationSet, eMEDIATYPE_VIDEO)) + { + continue; + } + firstAdaptation = adaptationSet; + } -/** - * @brief A helper function to check if period has segment template for video track - * @param period period of segment - * @return True if period has segment template for video otherwise false - */ -bool AampMPDParseHelper::aamp_HasSegmentTemplate(IPeriod * period) -{ - auto segmentTemplates = GetSegmentTemplateForVideo(period); - return (segmentTemplates && segmentTemplates->HasSegmentTemplate()); -} + if(firstAdaptation != NULL) + { + adaptationSet = firstAdaptation->GetSegmentTemplate(); + const std::vector representations = firstAdaptation->GetRepresentation(); + if (representations.size() > 0) + { + representation = representations.at(0)->GetSegmentTemplate(); + } + } -/** - * @brief A helper function to get segment template for video track - * @param period period of segment - * @return SegmentTemplates structure for video track if present, otherwise empty SegmentTemplates - */ -std::shared_ptr AampMPDParseHelper::GetSegmentTemplateForVideo(IPeriod * period) -{ - std::shared_ptr segmentTemplates = nullptr; - const std::vector adaptationSets = period->GetAdaptationSets(); - if (adaptationSets.empty()) - { - return segmentTemplates; - } + SegmentTemplates segmentTemplates(representation,adaptationSet); - for (auto &adaptationSet : adaptationSets) - { - if (IsContentType(adaptationSet, eMEDIATYPE_VIDEO)) - { - const ISegmentTemplate *adaptationSetTemplate = adaptationSet->GetSegmentTemplate(); - const std::vector representations = adaptationSet->GetRepresentation(); - if (!representations.empty()) - { - const ISegmentTemplate *representationTemplate = representations.at(0)->GetSegmentTemplate(); - if (adaptationSetTemplate || representationTemplate) - { - segmentTemplates = std::make_shared(representationTemplate, adaptationSetTemplate); - break; - } - } - } - } - return segmentTemplates; + if (segmentTemplates.HasSegmentTemplate()) + { + const ISegmentTimeline *segmentTimeline = segmentTemplates.GetSegmentTimeline(); + if (segmentTimeline) + { + bRetValue = true; + } + } + } + return bRetValue; } /** diff --git a/AampMPDParseHelper.h b/AampMPDParseHelper.h index a5f87ef9c..4e3edfa73 100644 --- a/AampMPDParseHelper.h +++ b/AampMPDParseHelper.h @@ -466,28 +466,12 @@ public : */ void GetStartAndDurationFromTimeline(IPeriod * period, int representationIdx, int adaptationSetIdx, AampTime &scaledStartTime, AampTime &duration); - /** - * @brief A helper function to check if period has segment timeline for video track - * @param period period of segment - * @return True if period has segment timeline for video otherwise false - */ - bool aamp_HasSegmentTime(IPeriod *period); - - /** - * @brief A helper function to check if period has segment template for video track - * @param period period of segment - * @return True if period has segment template for video otherwise false - */ - bool aamp_HasSegmentTemplate(IPeriod *period); - - /** - * @brief A helper function to get segment template for video - * @param[in] period for current period - * - * @return segment template for video track - */ - std::shared_ptr GetSegmentTemplateForVideo(IPeriod *period); - + /** + * @brief A helper function to check if period has segment timeline for video track + * @param period period of segment + * @return True if period has segment timeline for video otherwise false + */ + bool aamp_HasSegmentTimeline(IPeriod * period); /** * @brief Get the MPD instance. diff --git a/AampMPDUtils.cpp b/AampMPDUtils.cpp index d01a74936..13365bc70 100644 --- a/AampMPDUtils.cpp +++ b/AampMPDUtils.cpp @@ -18,7 +18,6 @@ */ #include "AampMPDUtils.h" -#include /** * @brief Get xml node form reader @@ -212,308 +211,3 @@ double ComputeFragmentDuration( uint32_t duration, uint32_t timeScale ) return newduration; } -/** - * @brief Parse segment index box - * @note The SegmentBase indexRange attribute points to Segment Index Box location with segments and random access points. - * @param start start of box - * @param size size of box - * @param segmentIndex segment index - * @param[out] referenced_size referenced size - * @param[out] referenced_duration referenced duration - * @retval true on success - */ -bool ParseSegmentIndexBox( const char *start, size_t size, int segmentIndex, unsigned int *referenced_size, float *referenced_duration, unsigned int *firstOffset) -{ - if (!start) - { - // If the fragment pointer is NULL then return from here, no need to process it further. - return false; - } - - const char **f = &start; - - unsigned int len = Read32(f); - if (len != size) - { - AAMPLOG_WARN("Wrong size in ParseSegmentIndexBox %d found, %zu expected", len, size); - if (firstOffset) *firstOffset = 0; - return false; - } - - unsigned int type = Read32(f); - if (type != 'sidx') - { - AAMPLOG_WARN("Wrong type in ParseSegmentIndexBox %c%c%c%c found, %zu expected", - (type >> 24) % 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, type & 0xff, size); - if (firstOffset) *firstOffset = 0; - return false; - } - - unsigned int version = Read32(f); (void) version; - unsigned int reference_ID = Read32(f); (void)reference_ID; - unsigned int timescale = Read32(f); - uint64_t earliest_presentation_time; - uint64_t first_offset; - if( version==0 ) - { - earliest_presentation_time = Read32(f); - (void)earliest_presentation_time; // unused - first_offset = Read32(f); - } - else - { - earliest_presentation_time = Read64(f); - (void)earliest_presentation_time; // unused - first_offset = Read64(f); - } - unsigned int reserved = Read16(f); (void)reserved; - unsigned int reference_count = Read16(f); - if (firstOffset) - { - *firstOffset = (unsigned int)first_offset; - return true; - } - if( segmentIndex 0) - { - std::string format = str.substr(pos + tokenLength + 1, formatLen - 1); - char type = str[pos + tokenLength + formatLen]; - switch (type) - { // don't use the number-formatting string from dash manifest as-is; map to uint64_t equivalent - case 'd': - format += PRIu64; - break; - case 'x': - format += PRIx64; - break; - case 'X': - format += PRIX64; - break; - default: - AAMPLOG_WARN("unsupported template format: %s%c", format.c_str(), type); - format += type; - break; - } - - snprintf(buf, sizeof(buf), format.c_str(), toNumber); - tokenLength += formatLen; - } - else - { - snprintf(buf, sizeof(buf), "%" PRIu64 "", toNumber); - } - str.replace(pos, tokenLength + 2, buf); - done = false; - rc++; - break; - } - pos = next + 1; - } - else - { - AAMPLOG_WARN("next is not found "); // CID:81252 - checked return - break; - } - } - if (done) - break; - } - - return rc; -} - -/** - * @brief Replace matching token with given string - * @param str String in which operation to be performed - * @param from token - * @param toString string to replace token - * @retval position - */ -int replace(std::string &str, const std::string &from, const std::string &toString) -{ - int rc = 0; - size_t tokenLength = from.length(); - - for (;;) - { - bool done = true; - size_t pos = 0; - for (;;) - { - pos = str.find('$', pos); - if (pos == std::string::npos) - { - break; - } - size_t next = str.find('$', pos + 1); - if (next != 0) - { - if (str.substr(pos + 1, tokenLength) == from) - { - str.replace(pos, tokenLength + 2, toString); - done = false; - rc++; - break; - } - pos = next + 1; - } - else - { - AAMPLOG_ERR("Error at next"); // CID:81346 - checked return - break; - } - } - - if (done) - break; - } - - return rc; -} - -/** - * @fn ConstructFragmentURL - * @param[out] fragmentUrl fragment url - * @param[in] fragmentDescriptor descriptor - * @param[in] media media information string - * @param[in] config Aamp configuration - */ -void ConstructFragmentURL(std::string &fragmentUrl, const FragmentDescriptor *fragmentDescriptor, std::string media, AampConfig *config) -{ - std::string constructedUri = fragmentDescriptor->GetMatchingBaseUrl(); - if (media.empty()) - { - } - else if (aamp_IsAbsoluteURL(media)) - { // don't pre-pend baseurl if media starts with http:// or https:// - constructedUri.clear(); - } - else if (!constructedUri.empty()) - { - if (config->IsConfigSet(eAAMPConfig_DASHIgnoreBaseURLIfSlash)) - { - if (constructedUri == "/") - { - AAMPLOG_WARN("ignoring baseurl /"); - constructedUri.clear(); - } - } - // append '/' suffix to BaseURL if not already present - if (aamp_IsAbsoluteURL(constructedUri)) - { - if (constructedUri.back() != '/') - { - constructedUri += '/'; - } - } - } - else - { - AAMPLOG_TRACE("BaseURL not available"); - } - constructedUri += media; - replace(constructedUri, "Bandwidth", fragmentDescriptor->Bandwidth); - replace(constructedUri, "RepresentationID", fragmentDescriptor->RepresentationID); - replace(constructedUri, "Number", fragmentDescriptor->Number); - replace(constructedUri, "Time", (uint64_t)fragmentDescriptor->Time ); - aamp_ResolveURL(fragmentUrl, fragmentDescriptor->manifestUrl, constructedUri.c_str(), config->IsConfigSet(eAAMPConfig_PropagateURIParam)); -} - - diff --git a/AampMPDUtils.h b/AampMPDUtils.h index 545fe4673..34548fe00 100644 --- a/AampMPDUtils.h +++ b/AampMPDUtils.h @@ -32,8 +32,6 @@ #include "AampLogManager.h" #include "AampUtils.h" #include "AampMPDPeriodInfo.h" -#include "AampFragmentDescriptor.hpp" -#include "AampConfig.h" using namespace dash; using namespace std; @@ -74,72 +72,4 @@ bool IsCompatibleMimeType(const std::string& mimeType, AampMediaType mediaType); */ double ComputeFragmentDuration( uint32_t duration, uint32_t timeScale ); -/** - * @fn ConstructFragmentURL - * @param[out] fragmentUrl fragment url - * @param[in] fragmentDescriptor descriptor - * @param[in] media media information string - * @param[in] config AAMP configuration - */ -void ConstructFragmentURL( std::string& fragmentUrl, const FragmentDescriptor *fragmentDescriptor, std::string media, AampConfig *config); - -/** - * @brief Parse segment index box - * @note The SegmentBase indexRange attribute points to Segment Index Box location with segments and random access points. - * @param start start of box - * @param size size of box - * @param segmentIndex segment index - * @param[out] referenced_size referenced size - * @param[out] referenced_duration referenced duration - * @retval true on success - */ -bool ParseSegmentIndexBox( const char *start, size_t size, int segmentIndex, unsigned int *referenced_size, float *referenced_duration, unsigned int *firstOffset); - -/** - * @brief Read 16 word helper function - * @param pptr pointer to read from - * @retval word value - */ -unsigned int Read16( const char **pptr); - -/** - * @brief Read 32 word helper function - * @param pptr pointer to read from - * @retval word value - */ -unsigned int Read32( const char **pptr); - -/** - * @brief Read 64 word helper function - * @param pptr pointer to read from - * @retval word value - */ -uint64_t Read64( const char **pptr); - -/** - * @brief read unsigned multi-byte value and update buffer pointer - * @param[in] pptr buffer - * @param[in] n word size in bytes - * @retval 32 bit value - */ -uint64_t ReadWordHelper( const char **pptr, int n ); - -/** - * @brief Replace matching token with given number - * @param str String in which operation to be performed - * @param from token - * @param toNumber number to replace token - * @retval position - */ -int replace(std::string& str, const std::string& from, uint64_t toNumber ); - -/** - * @brief Replace matching token with given string - * @param str String in which operation to be performed - * @param from token - * @param toString string to replace token - * @retval position - */ -int replace(std::string& str, const std::string& from, const std::string& toString ); - #endif /* __AAMP_MPD_UTILS_H__ */ diff --git a/AampTimeBasedBufferManager.cpp b/AampTimeBasedBufferManager.cpp deleted file mode 100644 index 75b344a9a..000000000 --- a/AampTimeBasedBufferManager.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AampTimeBasedBufferManager.hpp" - -namespace aamp -{ - /** - * @brief Constructor for AampTimeBasedBufferManager. - * - * @param[in] maxBufferTime Maximum buffer time in seconds. - * @param[in] trickPlayMultiplier Multiplier for trick play mode (default is 1.0). - * @param[in] mediaType Media type for which this buffer is used. - */ - AampTimeBasedBufferManager::AampTimeBasedBufferManager(int maxBufferTime, double trickPlayMultiplier, AampMediaType mediaType) - : maxBufferTime(maxBufferTime), - currentBufferTime(0), - trickPlayMultiplier(trickPlayMultiplier), - mediaType(mediaType) - { - AAMPLOG_DEBUG("[%s] maxBufferTime: %d, trickPlayMultiplier: %f", GetMediaTypeName(mediaType), maxBufferTime, trickPlayMultiplier); - if (maxBufferTime <= 0 || trickPlayMultiplier <= 0) - { - AAMPLOG_ERR("maxBufferTime or trickPlayMultiplier is initialized as negative."); - } - } - - /** - * @brief Populate a specified amount of time to the buffer. - * - * @param[in] fragmentDuration Duration of the fragment in seconds. - */ - void AampTimeBasedBufferManager::PopulateBuffer(double fragmentDuration) - { - if (fragmentDuration < 0) - { - AAMPLOG_ERR("Fragment duration must be non-negative."); - } - - std::lock_guard lock(mutex); - currentBufferTime += fragmentDuration; - AAMPLOG_DEBUG("[%s] Buffer time: %f", GetMediaTypeName(mediaType), currentBufferTime); - } - - /** - * @brief Consume a specified amount of time from the buffer. - * - * @param[in] timeToConsume Amount of time to consume from the buffer in seconds. - */ - void AampTimeBasedBufferManager::ConsumeBuffer(double timeToConsume) - { - if (timeToConsume < 0) - { - AAMPLOG_ERR("Time to consume must be non-negative."); - } - - std::lock_guard lock(mutex); - currentBufferTime -= timeToConsume; - AAMPLOG_DEBUG("[%s] Buffer time: %f\n", GetMediaTypeName(mediaType), currentBufferTime); - if (currentBufferTime < 0) - { - currentBufferTime = 0; - } - } - - /** - * @brief Check if the buffer is full. - * - * @return True if buffer is full, false otherwise. - */ - bool AampTimeBasedBufferManager::IsFull() const - { - std::lock_guard lock(mutex); - bool ret = (currentBufferTime >= (maxBufferTime * trickPlayMultiplier)); - if (ret) - { - AAMPLOG_DEBUG("[%s] Buffer is full. Current buffer time: %f, Max buffer time: %d, Trick play multiplier: %f", GetMediaTypeName(mediaType), currentBufferTime, maxBufferTime, trickPlayMultiplier); - } - else - { - AAMPLOG_DEBUG("[%s] Buffer is not full. Current buffer time: %f, Max buffer time: %d, Trick play multiplier: %f", GetMediaTypeName(mediaType), currentBufferTime, maxBufferTime, trickPlayMultiplier); - } - return ret; - } - - /** - * @brief Clear the buffer to its initial state. - */ - void AampTimeBasedBufferManager::ClearBuffer() - { - std::lock_guard lock(mutex); - currentBufferTime = 0; - AAMPLOG_DEBUG("[%s] Buffer cleared. Current buffer time: %f", GetMediaTypeName(mediaType), currentBufferTime); - } -} // namespace aamp diff --git a/AampTimeBasedBufferManager.hpp b/AampTimeBasedBufferManager.hpp deleted file mode 100644 index 00994d44f..000000000 --- a/AampTimeBasedBufferManager.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "priv_aamp.h" - -#ifndef AAMP_TIME_BASED_BUFFER_MANAGER_HPP -#define AAMP_TIME_BASED_BUFFER_MANAGER_HPP -namespace aamp -{ - /** - * @class AampTimeBasedBufferManager - * @brief A class for managing a time-based media buffer. - */ - class AampTimeBasedBufferManager - { - public: - /** - * @brief Constructor for AampTimeBasedBufferManager. - * - * @param[in] maxBufferTime Maximum buffer time in seconds. - * @param[in] trickPlayMultiplier Multiplier for trick play mode (default is 1.0). - */ - AampTimeBasedBufferManager(int maxBufferTime, double trickPlayMultiplier = 1.0, AampMediaType mediaType = eMEDIATYPE_DEFAULT); - - /** - * @brief Default Destructor for AampTimeBasedBufferManager. - */ - ~AampTimeBasedBufferManager() = default; - - /** - * @brief Populate a specified amount of time to the buffer. - * - * @param[in] fragmentDuration Duration of the fragment in seconds. - */ - void PopulateBuffer(double fragmentDuration); - - /** - * @brief Consume a specified amount of time from the buffer. - * - * @param[in] timeToConsume Amount of time to consume from the buffer in seconds. - */ - void ConsumeBuffer(double timeToConsume); - - /** - * @brief Check if the buffer is full. - * - * @return True if buffer is full, false otherwise. - */ - bool IsFull() const; - - /** - * @brief Clear the buffer to its initial state. - */ - void ClearBuffer(); - - private: - int maxBufferTime; - double currentBufferTime; - double trickPlayMultiplier; - mutable std::mutex mutex; - AampMediaType mediaType; // Media type for which this buffer is used - }; -} // namespace aamp - -#endif // AAMP_TIME_BASED_BUFFER_MANAGER_HPP \ No newline at end of file diff --git a/AampTrackWorker.cpp b/AampTrackWorker.cpp index f739c44ca..d9c596201 100644 --- a/AampTrackWorker.cpp +++ b/AampTrackWorker.cpp @@ -17,141 +17,11 @@ * limitations under the License. */ -#include "AampTrackWorker.hpp" -#include "priv_aamp.h" +#include "AampTrackWorker.h" #include namespace aamp { - /** - * @brief Default destructor for AampTrackWorkerJob. - * - * Cleans up resources used by the job. - */ - AampTrackWorkerJob::~AampTrackWorkerJob() = default; - - /** - * @brief Default constructor for AampTrackWorkerJob. - * - * Initializes the promise and sets the shared future. - */ - AampTrackWorkerJob::AampTrackWorkerJob() - : mCancelled(false), - mPromise() - { - mSharedFuture = mPromise.get_future().share(); - AAMPLOG_DEBUG("AampTrackWorkerJob constructor"); - } - - /** - * @brief Runs the job in the worker thread. - * - * This method is called by the worker thread to execute the job. - * It catches any exceptions thrown during execution and sets them on the promise. - */ - void AampTrackWorkerJob::Run() - { - try - { - bool cancelledBeforeExecute = mCancelled.load(); - if (!cancelledBeforeExecute) - { - Execute(); // calls derived class's Execute method - } - // Only set value if not cancelled during Execute and promise not already satisfied - if (!mCancelled.load()) - { - try - { - mPromise.set_value(); - } - catch (const std::future_error& e) - { - AAMPLOG_WARN("Promise already satisfied in AampTrackWorkerJob::Run: %s", e.what()); - } - } - } - catch (...) - { - try - { - mPromise.set_exception(std::current_exception()); - } - catch (const std::future_error& e) - { - AAMPLOG_ERR("Exception in AampTrackWorkerJob::Run: Failed to set exception on promise: %s", e.what()); - } - catch (...) - { - AAMPLOG_ERR("Exception in AampTrackWorkerJob::Run: Failed to set exception on promise"); - } - } - } - - /** - * @brief Default implementation of Execute method. - * - * This method does nothing by default and should be overridden in derived classes. - */ - void AampTrackWorkerJob::Execute() - { - // Default implementation does nothing - } - - /** - * @brief Clones the job for worker pool. - * - * This method creates a new instance of AampTrackWorkerJob. - * - * @return std::unique_ptr A unique pointer to the cloned job. - */ - std::unique_ptr AampTrackWorkerJob::Clone() const - { - return aamp_utils::make_unique(); - } - - /** - * @brief Cancels the job by setting the cancelled flag. - * - * If the job is already cancelled, it does nothing. - * If not, it sets the exception on the promise to indicate cancellation. - */ - void AampTrackWorkerJob::SetCancelled() - { - if (!mCancelled.exchange(true)) - { - try - { - mPromise.set_exception(std::make_exception_ptr(std::runtime_error("Job cancelled"))); - } - catch (...) - { - AAMPLOG_ERR("Exception in AampTrackWorkerJob::SetCancelled: Failed to set exception on promise"); - } - } - } - - /** - * @brief Checks if the job has been cancelled. - * - * @return true if the job is cancelled, false otherwise. - */ - bool AampTrackWorkerJob::IsCancelled() const - { - return mCancelled.load(); - } - - /** - * @brief Gets a future to wait for job completion. - * - * This method returns a shared_future that can be used to wait for the job to complete. - * - * @return std::shared_future A future that will be set when the job is completed. - */ - std::shared_future AampTrackWorkerJob::GetFuture() const - { - return mSharedFuture; - } /** * @brief Constructs an AampTrackWorker object. @@ -163,87 +33,51 @@ namespace aamp * */ AampTrackWorker::AampTrackWorker(PrivateInstanceAAMP *_aamp, AampMediaType _mediaType) - : aamp(_aamp), mMediaType(_mediaType), mStop(false), mPaused(false), mActiveJob(nullptr), mWorkerThread(), mJobQueue(), mQueueMutex(), mCondVar(), mInitialized(false) + : aamp(_aamp), mMediaType(_mediaType), mJobAvailable(false), mStop(false), mWorkerThread(), mJob(), mMutex(), mCondVar(), mCompletionVar() { if (_aamp == nullptr) { - throw std::invalid_argument("AampTrackWorker: _aamp cannot be null"); - } - AAMPLOG_DEBUG("AampTrackWorker constructor for media type %s", GetMediaTypeName(mMediaType)); - } - - /** - * @brief Destructs the AampTrackWorker object. - * - * Signals the worker thread to stop, waits for it to finish, and cleans up resources. - * - * @return void - */ - AampTrackWorker::~AampTrackWorker() - { - StopWorker(); - AAMPLOG_DEBUG("AampTrackWorker destructor for media type %s", GetMediaTypeName(mMediaType)); - } - - /** - * @brief Starts the worker thread. - * - * Creates the worker thread and starts it. - * - * @return void - */ - void AampTrackWorker::StartWorker() - { - if (mWorkerThread.joinable() || mInitialized) - { - AAMPLOG_WARN("Worker thread for media type %s is already running", GetMediaTypeName(mMediaType)); - throw std::runtime_error("Worker thread is already running"); + AAMPLOG_ERR("AampTrackWorker constructor received null aamp"); + mStop = true; + return; } try { - if(!mInitialized) - { - mStop.store(false); - mWorkerThread = std::thread(&AampTrackWorker::ProcessJob, shared_from_this(), std::weak_ptr(shared_from_this())); - mInitialized = true; - } + mWorkerThread = std::thread(&AampTrackWorker::ProcessJob, this); } catch (const std::exception &e) { - AAMPLOG_ERR("Exception caught in AampTrackWorker %s", e.what()); - mStop.store(true); + AAMPLOG_ERR("Exception caught in AampTrackWorker constructor: %s", e.what()); + mStop = true; } catch (...) { - AAMPLOG_ERR("Unknown exception caught in AampTrackWorker for media type %s", GetMediaTypeName(mMediaType)); - mStop.store(true); + AAMPLOG_ERR("Unknown exception caught in AampTrackWorker constructor"); + mStop = true; } } /** - * @brief Stops the worker thread. + * @brief Destructs the AampTrackWorker object. * - * Signals the worker thread to stop and waits for it to finish. + * Signals the worker thread to stop, waits for it to finish, and cleans up resources. * * @return void */ - void AampTrackWorker::StopWorker() + AampTrackWorker::~AampTrackWorker() { - mStop.store(true); - AAMPLOG_DEBUG("Stopping worker thread for media type %s", GetMediaTypeName(mMediaType)); - if(mInitialized) { - mCondVar.notify_all(); - if (mWorkerThread.joinable()) - { - mWorkerThread.join(); - } - ClearJobs(); - std::lock_guard queueLock(mQueueMutex); - mActiveJob = nullptr; // Clear active job - mInitialized = false; + std::lock_guard lock(mMutex); + mStop = true; + mJobAvailable = true; // Wake up thread to exit + } + mCondVar.notify_one(); + if (mWorkerThread.joinable()) + { + mWorkerThread.join(); } + mCompletionVar.notify_one(); } /** @@ -252,198 +86,86 @@ namespace aamp * The job is a function that will be executed by the worker thread. * * @param[in] job The job to be executed by the worker thread. - * @param[in] highPriority Flag to indicate if the job should be executed - * - * @return std::shared_future A future that will be set when the job is completed. - */ - std::shared_future AampTrackWorker::SubmitJob(AampTrackWorkerJobSharedPtr job, bool highPriority) - { - if(nullptr == job) - { - AAMPLOG_ERR("Attempted to submit a null job to worker for media type %s", GetMediaTypeName(mMediaType)); - return std::shared_future(); // Return an empty future - } - auto future = job->GetFuture(); - { - std::lock_guard lock(mQueueMutex); - if (!mStop.load()) - { - if (highPriority) - { - mJobQueue.push_front(job); - } - else - { - mJobQueue.push_back(job); - } - } - else - { - AAMPLOG_WARN("Attempted to submit job to stopped worker for media type %s", GetMediaTypeName(mMediaType)); - return std::shared_future(); // Return an empty future - } - } - AAMPLOG_DEBUG("Async job submitted for media type %s", GetMediaTypeName(mMediaType)); - mCondVar.notify_one(); - return future; - } - - /** - * @brief Pauses the worker thread. - * - * Signals the worker thread to pause and waits for it to acknowledge the pause signal. * * @return void */ - void AampTrackWorker::Pause() + void AampTrackWorker::SubmitJob(std::function job) { - mPaused.store(true); - AAMPLOG_DEBUG("Pausing worker thread for media type %s", GetMediaTypeName(mMediaType)); - mCondVar.notify_one(); // Wake up thread to pause - } - - /** - * @brief Resumes the worker thread. - * - * Signals the worker thread to resume and waits for it to acknowledge the resume signal. - * - * @return void - */ - void AampTrackWorker::Resume() - { - mPaused.store(false); - AAMPLOG_DEBUG("Resuming worker thread for media type %s", GetMediaTypeName(mMediaType)); - mCondVar.notify_one(); - } - - /** - * @brief Clears all jobs from the worker thread. - * - * Removes all jobs from the worker thread's queue. - * - * @return void - */ - void AampTrackWorker::ClearJobs() - { - std::lock_guard lock(mQueueMutex); - // Signal all pending jobs that they've been cancelled - for (auto& job : mJobQueue) { - try - { - job->SetCancelled(); // Signal cancellation to the job - } - catch (const std::exception& e) - { - AAMPLOG_WARN("Exception while cancelling job: %s", e.what()); - } + std::lock_guard lock(mMutex); + this->mJob = std::move(job); + mJobAvailable = true; } - mJobQueue.clear(); - AAMPLOG_DEBUG("All jobs cleared for media type %s", GetMediaTypeName(mMediaType)); + AAMPLOG_DEBUG("Job submitted for media type %s", GetMediaTypeName(mMediaType)); + mCondVar.notify_one(); } /** - * @brief Reschedules the active job to the job queue. + * @brief Waits for the current job to complete. * - * If there is an active job being processed, it is rescheduled to the front of the job queue. + * Blocks the calling thread until the current job has been processed by the worker thread. * * @return void */ - void AampTrackWorker::RescheduleActiveJob() + void AampTrackWorker::WaitForCompletion() { - std::lock_guard lock(mQueueMutex); - if (mActiveJob) - { - // Reschedule the active job to the queue - AAMPLOG_DEBUG("Rescheduling active job for media type %s", GetMediaTypeName(mMediaType)); - auto newJob = mActiveJob->Clone(); // Ensure the job can be cloned if needed - mJobQueue.push_front(std::move(newJob)); - mActiveJob = nullptr; // Clear active job after rescheduling - mCondVar.notify_one(); - } + std::unique_lock lock(mMutex); + mCompletionVar.wait(lock, [this]() { return !mJobAvailable; }); + AAMPLOG_DEBUG("Job wait completed for media type %s", GetMediaTypeName(mMediaType)); } /** * @brief The main function executed by the worker thread. * - * @param[in] weakSelf Weak pointer to the AampTrackWorker instance. * Waits for jobs to be submitted, processes them, and signals their completion. * The function runs in a loop until the worker is signaled to stop. * * @return void */ - void AampTrackWorker::ProcessJob(AampTrackWorkerWeakPtr weakSelf) + void AampTrackWorker::ProcessJob() { - if (auto self = weakSelf.lock()) - { - UsingPlayerId playerId(self->aamp->mPlayerId); - AAMPLOG_INFO("Starting worker for media type %s", GetMediaTypeName(self->mMediaType)); + UsingPlayerId playerId(aamp->mPlayerId); + AAMPLOG_INFO("Process Job for media type %s", GetMediaTypeName(mMediaType)); - while (true) + // Main loop + while (true) + { + std::function currentJob; { - std::unique_lock lock(self->mQueueMutex); - - // Wait while (queue is empty or paused) and not stopped - self->mCondVar.wait(lock, [&] { - return self->mStop.load() || (!self->mPaused.load() && !self->mJobQueue.empty()); - }); - - if (self->mStop.load()) + std::unique_lock lock(mMutex); + mCondVar.wait(lock, [this]() { return mJobAvailable || mStop; }); + if (mStop) { - AAMPLOG_DEBUG("Worker thread stopped for media type %s", GetMediaTypeName(self->mMediaType)); break; } + currentJob = mJob; - if (self->mPaused.load()) - { - AAMPLOG_DEBUG("Worker thread paused for media type %s", GetMediaTypeName(self->mMediaType)); - continue; - } - - // Extract the job safely - AampTrackWorkerJobSharedPtr currentJob; - if (!self->mJobQueue.empty()) - { - self->mActiveJob = std::move(self->mJobQueue.front()); - self->mJobQueue.pop_front(); - currentJob = self->mActiveJob; - } - - lock.unlock(); // Release lock before executing the job - - if (currentJob) + // Execute the job + if (!mStop && currentJob) { + AAMPLOG_DEBUG("Executing Job for media type %s Job: %p", GetMediaTypeName(mMediaType), ¤tJob); + lock.unlock(); try { - AAMPLOG_DEBUG("Running job for media type %s", GetMediaTypeName(self->mMediaType)); - // Run the job and catch any exceptions - // The promise in the job will be set when the job completes - // This allows the job to signal completion without blocking the worker thread - currentJob->Run(); - AAMPLOG_DEBUG("Finished job for media type %s", GetMediaTypeName(self->mMediaType)); + currentJob(); } catch (const std::exception &e) { - AAMPLOG_ERR("Exception caught while executing job: %s", e.what()); + AAMPLOG_ERR("Exception caught while executing job for media type %s: %s", GetMediaTypeName(mMediaType), e.what()); } catch (...) { - AAMPLOG_ERR("Unknown exception caught in ProcessJob."); + AAMPLOG_ERR("Unknown exception caught while executing job for media type %s", GetMediaTypeName(mMediaType)); } + lock.lock(); } - lock.lock(); - self->mActiveJob = nullptr; - if (self->mStop.load()) - { - break; - } + AAMPLOG_DEBUG("Job completed for media type %s", GetMediaTypeName(mMediaType)); + mJobAvailable = false; + mCompletionVar.notify_one(); } - AAMPLOG_INFO("Exiting for media type %s", GetMediaTypeName(self->mMediaType)); - } - else - { - AAMPLOG_WARN("AampTrackWorker instance is destroyed, exiting ProcessJob"); } + + AAMPLOG_INFO("Exiting Process Job for media type %s", GetMediaTypeName(mMediaType)); } } // namespace aamp diff --git a/AampTrackWorker.h b/AampTrackWorker.h index e69de29bb..71a549bae 100644 --- a/AampTrackWorker.h +++ b/AampTrackWorker.h @@ -0,0 +1,80 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAMP_TRACK_WORKER_H +#define AAMP_TRACK_WORKER_H + +/** + * @file AampTrackWorker.h + * @brief Implementation of the AampTrackWorker class. + * + * This file contains the implementation of the AampTrackWorker class, which is responsible for + * managing a worker thread that processes jobs submitted to it. The worker thread waits for jobs + * to be submitted, processes them, and signals their completion. + */ + +#include +#include +#include +#include +#include +#include "AampUtils.h" +#include "AampLogManager.h" +#include "AampConfig.h" +#include "priv_aamp.h" + +namespace aamp +{ + + /** + * @class AampTrackWorker + * @brief A class that manages a worker thread for processing jobs. + * + * The AampTrackWorker class creates a worker thread that waits for jobs to be submitted, + * processes them, and signals their completion. The class provides methods to submit jobs, + * wait for job completion, and clean up the worker thread. + */ + + class AampTrackWorker + { + public: + AampTrackWorker(PrivateInstanceAAMP *_aamp, AampMediaType _mediaType); + ~AampTrackWorker(); + + void SubmitJob(std::function job); + void WaitForCompletion(); + + protected: + AampMediaType mMediaType; + std::thread mWorkerThread; + std::mutex mMutex; + std::condition_variable mCondVar; + std::condition_variable mCompletionVar; + std::function mJob; + PrivateInstanceAAMP *aamp; + bool mJobAvailable; + bool mStop; + + private: + void ProcessJob(); + }; + +} // namespace aamp + +#endif // AAMP_TRACK_WORKER_H diff --git a/AampTrackWorker.hpp b/AampTrackWorker.hpp deleted file mode 100644 index cb315cf67..000000000 --- a/AampTrackWorker.hpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2024 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAMP_TRACK_WORKER_HPP -#define AAMP_TRACK_WORKER_HPP - -/** - * @file AampTrackWorker.hpp - * @brief Implementation of the AampTrackWorker class. - * - * This file contains the implementation of the AampTrackWorker class, which is responsible for - * managing a worker thread that processes jobs submitted to it. The worker thread waits for jobs - * to be submitted, processes them, and signals their completion. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "AampUtils.h" -#include "AampLogManager.h" -#include "AampConfig.h" -#include "AampMediaType.h" - -// Forward declaration to avoid recursive include -class PrivateInstanceAAMP; - -namespace aamp -{ - class AampTrackWorkerJob - { - public: - /** - * @brief Default constructor for AampTrackWorkerJob. - */ - AampTrackWorkerJob(); - - /** - * @brief Destructor for AampTrackWorkerJob. - * - * Cleans up resources used by the job. - */ - virtual ~AampTrackWorkerJob(); - - // Delete copy constructor and copy assignment operator - AampTrackWorkerJob(const AampTrackWorkerJob&) = delete; - AampTrackWorkerJob& operator=(const AampTrackWorkerJob&) = delete; - // Default move constructor and move assignment operator - AampTrackWorkerJob(AampTrackWorkerJob&&) = delete; - AampTrackWorkerJob& operator=(AampTrackWorkerJob&&) = delete; - - /** - * @brief Called by the worker thread to run the job. - */ - void Run(); - - /** - * @brief Virtual Execute method to override in subclasses. - */ - virtual void Execute(); - - /** - * @brief Clones the job for worker pool. - */ - virtual std::unique_ptr Clone() const; - - /** - * @brief Cancels the job by setting the cancelled flag. - */ - void SetCancelled(); - - /** - * @brief Check if job has been cancelled. - * - * @return true if cancelled - */ - bool IsCancelled() const; - - /** - * @brief Get a future to wait for job completion. - */ - std::shared_future GetFuture() const; - - private: - std::atomic mCancelled{false}; - std::shared_future mSharedFuture; - std::promise mPromise; - }; - - /** - * @typedef AampTrackWorkerJobSharedPtr - * @typedef AampTrackWorkerJobUniquePtr - * @brief Represents a job to download a media fragment. - * - * The DownloadJob typedef encapsulates the job to download a media fragment. - **/ - typedef std::shared_ptr AampTrackWorkerJobSharedPtr; - typedef std::unique_ptr AampTrackWorkerJobUniquePtr; - - /** - * Forward declaration of AampTrackWorker to resolve unknown type error. - */ - class AampTrackWorker; - - /** - * @typedef AampTrackWorkerWeakPtr - * @brief Represents a weak pointer to an AampTrackWorker instance. - * - * This typedef is used to avoid circular references between the worker and the job. - */ - typedef std::weak_ptr AampTrackWorkerWeakPtr; - - /** - * @class AampTrackWorker - * @brief A class that manages a worker thread for processing jobs. - * - * The AampTrackWorker class creates a worker thread that waits for jobs to be submitted, - * processes them, and signals their completion. The class provides methods to submit jobs, - * wait for job completion, and clean up the worker thread. - */ - - class AampTrackWorker : public std::enable_shared_from_this - { - public: - AampTrackWorker(PrivateInstanceAAMP *_aamp, AampMediaType _mediaType); - ~AampTrackWorker(); - - std::shared_future SubmitJob(AampTrackWorkerJobSharedPtr job, bool highPriority = false); - void Pause(); - void Resume(); - void ClearJobs(); - void RescheduleActiveJob(); - void StartWorker(); - void StopWorker(); - bool IsStopped() const { return mStop.load(); } - AampMediaType GetMediaType() const { return mMediaType; } - - protected: - AampMediaType mMediaType; - std::thread mWorkerThread; - std::mutex mQueueMutex; // Mutex to protect job queue - std::condition_variable mCondVar; // Condition variable to notify worker thread - std::deque mJobQueue; // Job queue - PrivateInstanceAAMP *aamp; - std::atomic mInitialized; // Flag to indicate if the worker is initialized - std::atomic mStop; - std::atomic mPaused; // Flag to pause the worker threads - - private: - void ProcessJob(AampTrackWorkerWeakPtr weakSelf); - AampTrackWorkerJobSharedPtr mActiveJob; // Active job being processed - }; -} // namespace aamp - -#endif // AAMP_TRACK_WORKER_HPP diff --git a/AampTrackWorkerManager.cpp b/AampTrackWorkerManager.cpp deleted file mode 100644 index 7a4cfea2d..000000000 --- a/AampTrackWorkerManager.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AampTrackWorkerManager.hpp" - -namespace aamp -{ - /** - * @brief Default constructor. - */ - AampTrackWorkerManager::AampTrackWorkerManager() : mWorkers(), mMutex(), mStopInProgress(false) - { - } - - /** - * @brief Default destructor. - */ - AampTrackWorkerManager::~AampTrackWorkerManager() - { - StopWorkers(); - RemoveWorkers(); - } - - /** - * @brief Creates an AampTrackWorker instance. - * - * If an instance with the same media type already exists, it returns the existing instance. - * @param[in] aamp Pointer to the PrivateInstanceAAMP. - * @param[in] mediaType The media type for the worker. - * - * @return Shared pointer to the created or existing AampTrackWorker instance. - */ - std::shared_ptr AampTrackWorkerManager::CreateWorker(PrivateInstanceAAMP *aamp, AampMediaType mediaType) - { - std::lock_guard lock(mMutex); - - auto it = mWorkers.find(mediaType); - if (it != mWorkers.end()) - { - return it->second; - } - - std::shared_ptr worker; - try - { - worker = std::make_shared(aamp, mediaType); - } - catch (const std::exception &e) - { - AAMPLOG_ERR("Exception caught in AampTrackWorkerManager::CreateWorker: %s", e.what()); - return nullptr; - } - - mWorkers[mediaType] = worker; - return worker; - } - - - - /** - * @brief Gets an existing AampTrackWorker instance. - * - * @param[in] mediaType The media type of the worker. - * - * @return Shared pointer to the AampTrackWorker instance, or nullptr if not found. - */ - std::shared_ptr AampTrackWorkerManager::GetWorker(AampMediaType mediaType) - { - std::lock_guard lock(mMutex); - - auto it = mWorkers.find(mediaType); - if (it != mWorkers.end()) - { - return it->second; - } - return nullptr; - } - - /** - * @brief Submits a job to the specified worker. - * - * @param[in] mediaType The media type of the worker. - * @param[in] job The job to submit. - * @param[in] highPriority Whether the job should be treated as high priority. - * - * @note If the worker is not found, a default-constructed future is returned. - * @return A future representing the submitted job, or a default-constructed future if worker not found. - */ - std::shared_future AampTrackWorkerManager::SubmitJob(AampMediaType mediaType, aamp::AampTrackWorkerJobSharedPtr job, bool highPriority) - { - // If stopping, reject new submissions immediately (fast path, no lock) - if (mStopInProgress.load()) - { - AAMPLOG_WARN("SubmitJob rejected: stop in progress for media type %s", GetMediaTypeName(mediaType)); - return std::shared_future(); // default future indicates failure - } - - std::lock_guard lock(mMutex); - // check again under lock to avoid race where stop started after first check - if (mStopInProgress.load()) - { - AAMPLOG_WARN("SubmitJob rejected (post-lock): stop in progress for media type %s", GetMediaTypeName(mediaType)); - return std::shared_future(); - } - - auto it = mWorkers.find(mediaType); - if (it != mWorkers.end()) - { - std::shared_ptr worker = it->second; - if (worker) - { - return worker->SubmitJob(std::move(job), highPriority); - } - } - AAMPLOG_ERR("Worker for media type %s not found", GetMediaTypeName(mediaType)); - return std::shared_future(); - } - - /** - * @brief Removes all AampTrackWorker instances. - * - * Removes the worker instances - */ - void AampTrackWorkerManager::RemoveWorkers() - { - std::lock_guard lock(mMutex); - mWorkers.clear(); - mStopInProgress.store(false); - } - - /** - * @brief Starts all AampTrackWorker instances. - * - * Starts all saved workers - */ - void AampTrackWorkerManager::StartWorkers() - { - std::lock_guard lock(mMutex); - for (const auto &worker : mWorkers) - { - if(worker.second) - { - try - { - worker.second->StartWorker(); - } - catch (const std::exception &e) - { - AAMPLOG_ERR("Exception caught for while starting %s", e.what()); - } - } - } - } - - /** - * @brief Stops all AampTrackWorker instances. - * - * Stops all saved workers - */ - void AampTrackWorkerManager::StopWorkers() - { - mStopInProgress.store(true); - // Copy workers under lock then release before calling into worker shutdown - std::vector> workers; - { - std::lock_guard lock(mMutex); - workers.reserve(mWorkers.size()); - for (const auto &p : mWorkers) - { - if (p.second) - { - workers.push_back(p.second); - } - } - } - - // Now safely call Pause()/StopWorker() outside mMutex to avoid deadlock with workers that call back - for (const auto &worker : workers) - { - if (worker) - { - try - { - worker->Pause(); - worker->StopWorker(); // Join the worker thread internally - } - catch (const std::exception &e) - { - AAMPLOG_ERR("Exception while stopping worker: %s", e.what()); - } - } - } - } - - /** - * @brief Waits for all workers to complete their jobs. - * - * @param[in] timeInterval The time interval to wait for each onTimeout in milliseconds. - * @param[in] onTimeout callback function - */ - void AampTrackWorkerManager::WaitForCompletionWithTimeout(int timeInterval, std::function onTimeout) - { - std::vector> workers; - { - std::lock_guard lock(mMutex); - for (const auto &worker : mWorkers) - { - workers.push_back(worker.second); - } - } - - for (auto &worker : workers) - { - // Submit a dummy job to ensure the worker is active and can process jobs - auto job = std::make_shared(); - auto future = worker->SubmitJob(std::move(job)); - AampMediaType mediaType = worker->GetMediaType(); - if(future.valid()) - { - // Wait for the reset job to complete - AAMPLOG_DEBUG("Waiting for worker job completion for media type %s", GetMediaTypeName(mediaType)); - } - else - { - AAMPLOG_ERR("Failed to submit job to worker for media type %s", GetMediaTypeName(mediaType)); - continue; // Skip this worker if job submission failed - } - - if(mediaType > eMEDIATYPE_AUDIO) - { - // This is added for backward compatibility to avoid waiting on non critical workers - // TODO : Make sure text track is controlled by proper buffer control logic and remove this check - AAMPLOG_DEBUG("Skipping wait for media type %s", GetMediaTypeName(mediaType)); - continue; // Skip default media type worker - } - try - { - while (true) - { - auto status = future.wait_for(std::chrono::milliseconds(timeInterval)); - if (status == std::future_status::ready) - { - // Job completed: check for cancellation or error - future.get(); // Will throw if exception/cancelled - break; - } - else - { - onTimeout(); - } - } - } - catch (const std::exception &e) - { - AAMPLOG_WARN("Exception in %s worker: %s", GetMediaTypeName(mediaType), e.what()); - } - } - } - - /** - * @brief Reset the worker by clearing all jobs - * - * @param[in] mediaType The media type of the worker to reset. - */ - void AampTrackWorkerManager::ResetWorker(AampMediaType mediaType) - { - std::lock_guard lock(mMutex); - auto it = mWorkers.find(mediaType); - if (it != mWorkers.end()) - { - std::shared_ptr worker = it->second; - if(worker) - { - worker->ClearJobs(); - // Submit a dummy job to ensure the worker is active and can process jobs - auto job = std::make_shared(); - auto future = worker->SubmitJob(std::move(job)); - // If the future is valid, wait for the reset job to complete - if(future.valid()) - { - // Wait for the reset job to complete - future.get(); - } - } - } - else - { - AAMPLOG_ERR("Worker for media type %s not found", GetMediaTypeName(mediaType)); - } - } - - /** - * @brief Checks if there are any workers. - * - * @return True if there are no workers, false otherwise. - */ - bool AampTrackWorkerManager::IsEmpty() - { - std::lock_guard lock(mMutex); - return mWorkers.empty(); - } - - /** - * @brief Gets the number of workers. - * - * @return The number of workers. - */ - size_t AampTrackWorkerManager::GetWorkerCount() - { - std::lock_guard lock(mMutex); - return mWorkers.size(); - } -} diff --git a/AampTrackWorkerManager.hpp b/AampTrackWorkerManager.hpp deleted file mode 100644 index 6901683d1..000000000 --- a/AampTrackWorkerManager.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAMP_TRACK_WORKER_MANAGER_HPP -#define AAMP_TRACK_WORKER_MANAGER_HPP - -#include "AampTrackWorker.hpp" -#include -#include -#include -#include - -namespace aamp -{ - class AampTrackWorker; - class AampTrackWorkerJob; - using AampTrackWorkerJobSharedPtr = std::shared_ptr; - - /** - * @class AampTrackWorkerManager - * @brief Factory class for managing AampTrackWorker instances. - * - * Provides methods to create, retrieve, and remove AampTrackWorker instances - * in a thread-safe manner. - */ - class AampTrackWorkerManager - { - public: - /** - * @brief Default constructor. - */ - AampTrackWorkerManager(); - - /** - * @brief Default destructor. - */ - ~AampTrackWorkerManager(); - - /** - * @brief Creates an AampTrackWorker instance. - * - * If an instance with the same media type already exists, it returns the existing instance. - * @param aamp Pointer to the PrivateInstanceAAMP. - * @param mediaType The media type for the worker. - * @return Shared pointer to the created or existing AampTrackWorker instance. - */ - std::shared_ptr CreateWorker(PrivateInstanceAAMP *aamp, AampMediaType mediaType); - - /** - * @brief Gets an existing AampTrackWorker instance. - * - * @param mediaType The media type of the worker. - * @return Shared pointer to the AampTrackWorker instance, or nullptr if not found. - */ - std::shared_ptr GetWorker(AampMediaType mediaType); - - /** - * @brief Submits a job to the specified worker. - * - * @param[in] mediaType The media type of the worker. - * @param[in] job The job to submit. - * @param[in] highPriority Whether the job should be treated as high priority. - * - * @note If the worker is not found, a default-constructed future is returned. - * @return A future representing the submitted job, or a default-constructed future if worker not found. - */ - std::shared_future SubmitJob(AampMediaType mediaType, AampTrackWorkerJobSharedPtr job, bool highPriority = false); - - /** - * @brief Reset the worker by clearing all jobs - * - * @param[in] mediaType The media type of the worker to reset. - */ - void ResetWorker(AampMediaType mediaType); - - /** - * @brief Removes an AampTrackWorker instance. - * - * Removes the worker instances - */ - void RemoveWorkers(); - - /** - * @brief Starts all AampTrackWorker instances. - * - * Starts all saved workers - */ - void StartWorkers(); - - /** - * @brief Stops all AampTrackWorker instances. - * - * Stops all saved workers - */ - void StopWorkers(); - - /** - * @brief Wait for completion of all workers with a timeout. - * - * @param timeout The timeout value in milliseconds. - * @param onTimeout The lambda function to execute if a timeout occurs. - */ - void WaitForCompletionWithTimeout(int timeout, std::function onTimeout); - - /** - * @brief Checks if there are any workers. - * - * @return True if there are no workers, false otherwise. - */ - bool IsEmpty(); - - /** - * @brief Gets the number of workers. - * - * @return The number of workers. - */ - size_t GetWorkerCount(); - - private: - std::unordered_map> mWorkers; - std::mutex mMutex; // Protect access to the workers map - std::atomic mStopInProgress; - }; - -} // namespace aamp - -#endif // AAMP_TRACK_WORKER_MANAGER_HPP diff --git a/CMakeLists.txt b/CMakeLists.txt index d3ff73cb5..8c14398d2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -352,9 +352,6 @@ set(LIBAAMP_SOURCES AampTrackWorker.cpp AampTrackWorker.h LangCodePreference.h test/gstTestHarness/mp4demux.hpp - AampTrackWorkerManager.cpp - AampFragmentDescriptor.cpp - AampTimeBasedBufferManager.cpp ) if(CMAKE_SOC_PLATFORM_RPI) @@ -506,12 +503,6 @@ install(FILES AampLLDASHData.h AampTSBSessionManager.h AampTsbReader.h - AampTrackWorker.hpp - AampTrackWorkerManager.hpp - AampDownloadInfo.hpp - AampFragmentDescriptor.hpp - AampTimeBasedBufferManager.hpp - MediaSegmentDownloadJob.hpp tsb/api/TsbApi.h LangCodePreference.h StreamOutputFormat.h VideoZoomMode.h StreamSink.h TimedMetadata.h DESTINATION include diff --git a/MediaSegmentDownloadJob.hpp b/MediaSegmentDownloadJob.hpp deleted file mode 100644 index d2dd872a2..000000000 --- a/MediaSegmentDownloadJob.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file MediaSegmentDownloadJob.hpp - * @brief Dash Segment Download Job Wrapper structure for AAMP - */ - -#ifndef MEDIA_SEGMENT_DOWNLOAD_JOB_HPP -#define MEDIA_SEGMENT_DOWNLOAD_JOB_HPP - -#include -#include -#include -#include -#include -#include "AampDownloadInfo.hpp" - -namespace aamp -{ - class MediaSegmentDownloadJob : public AampTrackWorkerJob - { - private: - DownloadInfoPtr mDownloadInfo; - std::function mJobFunction; - - public: - MediaSegmentDownloadJob(DownloadInfoPtr downloadInfo, std::function jobFunction) - : mDownloadInfo(std::move(downloadInfo)), mJobFunction(std::move(jobFunction)) {} - - // This is where the job gets executed - void Execute() override - { - if (mJobFunction) - { - mJobFunction(); // Call the provided job function - } - } - - // Create a clone of this job for the worker pool - AampTrackWorkerJobUniquePtr Clone() const override - { - return aamp_utils::make_unique(mDownloadInfo, mJobFunction); - } - - // Accessor for DownloadInfo - DownloadInfoPtr GetDownloadInfo() const - { - return mDownloadInfo; - } - }; -} - -#endif /* MEDIA_SEGMENT_DOWNLOAD_JOB_HPP */ diff --git a/MediaStreamContext.cpp b/MediaStreamContext.cpp index 88511c048..4036975b6 100644 --- a/MediaStreamContext.cpp +++ b/MediaStreamContext.cpp @@ -26,7 +26,6 @@ #include "isobmff/isobmffbuffer.h" #include "AampCacheHandler.h" #include "AampTSBSessionManager.h" -#include "AampMPDUtils.h" /** * @brief Receives cached fragment and injects to sink. @@ -54,52 +53,56 @@ void MediaStreamContext::InjectFragmentInternal(CachedFragment* cachedFragment, fragmentDiscarded = false; } // InjectFragmentInternal + /** * @brief Fetch and cache a fragment */ -bool MediaStreamContext::CacheFragment(std::string fragmentUrl, unsigned int curlInstance, double position, double fragmentDurationS, const char *range, bool initSegment, bool discontinuity, bool playingAd, uint32_t scale) +bool MediaStreamContext::CacheFragment(std::string fragmentUrl, unsigned int curlInstance, double position, double fragmentDurationS, const char *range, bool initSegment, bool discontinuity, bool playingAd, double pto, uint32_t scale, bool overWriteTrackId) { bool ret = false; - AAMPLOG_INFO("Type[%d] position(before restamp) %f discontinuity %d scale %u duration %f mPTSOffsetSec %f absTime %lf fragmentUrl %s", type, position, discontinuity, scale, fragmentDurationS, GetContext()->mPTSOffset.inSeconds(), mActiveDownloadInfo->absolutePosition, fragmentUrl.c_str()); + double posInAbsTimeline = ((double)fragmentTime); + AAMPLOG_INFO("Type[%d] position(before restamp) %f discontinuity %d pto %f scale %u duration %f mPTSOffsetSec %f absTime %lf fragmentUrl %s", type, position, discontinuity, pto, scale, fragmentDurationS, GetContext()->mPTSOffset.inSeconds(), posInAbsTimeline, fragmentUrl.c_str()); fragmentDurationSeconds = fragmentDurationS; ProfilerBucketType bucketType = aamp->GetProfilerBucketForMedia(mediaType, initSegment); - CachedFragment *cachedFragment = GetFetchBuffer(true); + CachedFragment* cachedFragment = GetFetchBuffer(true); BitsPerSecond bitrate = 0; double downloadTimeS = 0; - AampMediaType actualType = (AampMediaType)(initSegment ? (eMEDIATYPE_INIT_VIDEO + mediaType) : mediaType); // Need to revisit the logic + AampMediaType actualType = (AampMediaType)(initSegment?(eMEDIATYPE_INIT_VIDEO+mediaType):mediaType); //Need to revisit the logic cachedFragment->type = actualType; cachedFragment->initFragment = initSegment; cachedFragment->timeScale = fragmentDescriptor.TimeScale; cachedFragment->uri = fragmentUrl; // For debug output - cachedFragment->absPosition = 0; - if (mActiveDownloadInfo) - { - cachedFragment->absPosition = mActiveDownloadInfo->absolutePosition; - cachedFragment->timeScale = mActiveDownloadInfo->timeScale; - cachedFragment->PTSOffsetSec = mActiveDownloadInfo->ptsOffset.inSeconds(); - /* The value of PTSOffsetSec in the context can get updated at the start of a period before - * the last segment from the previous period has been injected, hence we copy it - */ - if (ISCONFIGSET(eAAMPConfig_EnablePTSReStamp)) - { - // apply pts offset to position which ends up getting put into gst_buffer in sendHelper - position += mActiveDownloadInfo->ptsOffset.inSeconds(); - AAMPLOG_INFO("Type[%d] position after restamp = %fs", type, position); - } - } - else + cachedFragment->absPosition = posInAbsTimeline; + /* The value of PTSOffsetSec in the context can get updated at the start of a period before + * the last segment from the previous period has been injected, hence we copy it + */ + cachedFragment->PTSOffsetSec = GetContext()->mPTSOffset.inSeconds(); + if(ISCONFIGSET(eAAMPConfig_EnablePTSReStamp)) { - AAMPLOG_WARN("mActiveDownloadInfo is NULL"); + // apply pts offset to position which ends up getting put into gst_buffer in sendHelper + position += GetContext()->mPTSOffset.inSeconds(); + AAMPLOG_INFO("Type[%d] position after restamp = %fs", type, position); } + AampTSBSessionManager *tsbSessionManager = aamp->GetTSBSessionManager(); - if (initSegment && discontinuity) + auto CheckEos = [this, &tsbSessionManager, &actualType]() { + return IsLocalTSBInjection() && + AAMP_NORMAL_PLAY_RATE == aamp->rate && + !aamp->pipeline_paused && + eTUNETYPE_SEEKTOLIVE == context->mTuneType && + tsbSessionManager && + tsbSessionManager->GetTsbReader((AampMediaType)type) && + tsbSessionManager->GetTsbReader((AampMediaType)type)->IsEos(); + }; + + if(initSegment && discontinuity ) { setDiscontinuityState(true); } - if (!initSegment && mDownloadedFragment.GetPtr()) + if(!initSegment && mDownloadedFragment.GetPtr() ) { ret = true; cachedFragment->fragment.Replace(&mDownloadedFragment); @@ -110,11 +113,11 @@ bool MediaStreamContext::CacheFragment(std::string fragmentUrl, unsigned int cur int iFogError = -1; int iCurrentRate = aamp->rate; // Store it as back up, As sometimes by the time File is downloaded, rate might have changed due to user initiated Trick-Play bool bReadfromcache = false; - if (initSegment) + if(initSegment) { ret = bReadfromcache = aamp->getAampCacheHandler()->RetrieveFromInitFragmentCache(fragmentUrl,&cachedFragment->fragment,effectiveUrl); } - if (!bReadfromcache) + if(!bReadfromcache) { AampMPDDownloader *dnldInstance = aamp->GetMPDDownloader(); int maxInitDownloadTimeMS = 0; @@ -145,68 +148,315 @@ bool MediaStreamContext::CacheFragment(std::string fragmentUrl, unsigned int cur if ((actualType == eMEDIATYPE_INIT_VIDEO || actualType == eMEDIATYPE_INIT_AUDIO || actualType == eMEDIATYPE_INIT_SUBTITLE) && ret) // Only if init fragment successful or available from cache { - // To read track_id from the init fragments to check if there any mismatch. - // A mismatch in track_id is not handled in the gstreamer version 1.10.4 - // But is handled in the latest version (1.18.5), - // so upon upgrade to it or introduced a patch in qtdemux, - // this portion can be reverted + //To read track_id from the init fragments to check if there any mismatch. + //A mismatch in track_id is not handled in the gstreamer version 1.10.4 + //But is handled in the latest version (1.18.5), + //so upon upgrade to it or introduced a patch in qtdemux, + //this portion can be reverted IsoBmffBuffer buffer; - buffer.setBuffer((uint8_t *)cachedFragment->fragment.GetPtr(), cachedFragment->fragment.GetLen()); + buffer.setBuffer((uint8_t *)cachedFragment->fragment.GetPtr(), cachedFragment->fragment.GetLen() ); buffer.parseBuffer(); uint32_t track_id = 0; buffer.getTrack_id(track_id); - if (buffer.isInitSegment()) + if(buffer.isInitSegment()) { uint32_t timeScale = 0; - if (buffer.getTimeScale(timeScale)) + buffer.getTimeScale(timeScale); + if(actualType == eMEDIATYPE_INIT_VIDEO) + { + AAMPLOG_INFO("Video TimeScale [%d]", timeScale); + aamp->SetVidTimeScale(timeScale); + } + else if (actualType == eMEDIATYPE_INIT_AUDIO) + { + AAMPLOG_INFO("Audio TimeScale [%d]", timeScale); + aamp->SetAudTimeScale(timeScale); + } + else if (actualType == eMEDIATYPE_INIT_SUBTITLE) + { + AAMPLOG_INFO("Subtitle TimeScale [%d]", timeScale); + aamp->SetSubTimeScale(timeScale); + } + } + if(actualType == eMEDIATYPE_INIT_VIDEO) + { + AAMPLOG_INFO("Video track_id read from init fragment: %d ", track_id); + bool trackIdUpdated = false; + if(aamp->mCurrentVideoTrackId != -1 && track_id != aamp->mCurrentVideoTrackId) { - if (actualType == eMEDIATYPE_INIT_VIDEO) + if(overWriteTrackId) + { + //Overwrite the track id of the init fragment with the existing track id since overWriteTrackId is true only while pushing the encrypted init fragment while clear content is being played + buffer.parseBuffer(false, aamp->mCurrentVideoTrackId); + AAMPLOG_WARN("Video track_id of the current track is overwritten as init fragment is pushing only for DRM purpose, track id: %d ", track_id); + trackIdUpdated = true; + } + else { - AAMPLOG_INFO("Video TimeScale [%d]", timeScale); - aamp->SetVidTimeScale(timeScale); + aamp->mIsTrackIdMismatch = true; + AAMPLOG_WARN("TrackId mismatch detected for video, current track_id: %d, next period track_id: %d", aamp->mCurrentVideoTrackId, track_id); } - else if (actualType == eMEDIATYPE_INIT_AUDIO) + } + if(!trackIdUpdated) + { + aamp->mCurrentVideoTrackId = track_id; + } + } + else if(actualType == eMEDIATYPE_INIT_AUDIO) + { + bool trackIdUpdated = false; + AAMPLOG_INFO("Audio track_id read from init fragment: %d ", track_id); + if(aamp->mCurrentAudioTrackId != -1 && track_id != aamp->mCurrentAudioTrackId) + { + if(overWriteTrackId) { - AAMPLOG_INFO("Audio TimeScale [%d]", timeScale); - aamp->SetAudTimeScale(timeScale); + buffer.parseBuffer(false, aamp->mCurrentAudioTrackId); + AAMPLOG_WARN("Audio track_id of the current track is overwritten as init fragment is pushing only for DRM purpose, track id: %d ", track_id); + trackIdUpdated = true; } - else if (actualType == eMEDIATYPE_INIT_SUBTITLE) + else { - AAMPLOG_INFO("Subtitle TimeScale [%d]", timeScale); - aamp->SetSubTimeScale(timeScale); + aamp->mIsTrackIdMismatch = true; + AAMPLOG_WARN("TrackId mismatch detected for audio, current track_id: %d, next period track_id: %d", aamp->mCurrentAudioTrackId, track_id); } } + if(!trackIdUpdated) + { + aamp->mCurrentAudioTrackId = track_id; + } } + // Not overwriting for subtitles, as subtecmp4transform never read trackId from init fragments } + if (iCurrentRate != AAMP_NORMAL_PLAY_RATE) { - if (actualType == eMEDIATYPE_VIDEO) + if(actualType == eMEDIATYPE_VIDEO) { actualType = eMEDIATYPE_IFRAME; } - else if (actualType == eMEDIATYPE_INIT_VIDEO) + else if(actualType == eMEDIATYPE_INIT_VIDEO) { actualType = eMEDIATYPE_INIT_IFRAME; } } - if (!bReadfromcache) + if(!bReadfromcache) { - // update videoend info - aamp->UpdateVideoEndMetrics(actualType, bitrate ? bitrate : fragmentDescriptor.Bandwidth, (iFogError > 0 ? iFogError : httpErrorCode), effectiveUrl, fragmentDurationS, downloadTimeS); + //update videoend info + aamp->UpdateVideoEndMetrics( actualType, bitrate? bitrate : fragmentDescriptor.Bandwidth, (iFogError > 0 ? iFogError : httpErrorCode),effectiveUrl,fragmentDurationS, downloadTimeS); } } mCheckForRampdown = false; - if (ret && (bitrate > 0 && bitrate != fragmentDescriptor.Bandwidth)) + // Check for overWriteTrackId to avoid this logic for PushEncrypted init fragment use-case + if(ret && (bitrate > 0 && bitrate != fragmentDescriptor.Bandwidth && !overWriteTrackId)) { - AAMPLOG_INFO("Bitrate changed from %u to %ld", fragmentDescriptor.Bandwidth, bitrate); + AAMPLOG_INFO("Bitrate changed from %u to %ld",fragmentDescriptor.Bandwidth, bitrate); fragmentDescriptor.Bandwidth = (uint32_t)bitrate; context->SetTsbBandwidth(bitrate); context->mUpdateReason = true; mDownloadedFragment.Replace(&cachedFragment->fragment); ret = false; } + else if (!ret) + { + AAMPLOG_INFO("fragment fetch failed - Free cachedFragment for %d",actualType); + cachedFragment->fragment.Free(); + if( aamp->DownloadsAreEnabled()) + { + AAMPLOG_WARN("%sfragment fetch failed -- fragmentUrl %s", (initSegment)?"Init ":" ", fragmentUrl.c_str()); + if (mSkipSegmentOnError) + { + // Skip segment on error, and increase fail count + if(httpErrorCode != 502) + { + segDLFailCount += 1; + } + } + else + { + // Rampdown already attempted on same segment + // Reset flag for next fetch + mSkipSegmentOnError = true; + } + int FragmentDownloadFailThreshold = GETCONFIGVALUE(eAAMPConfig_FragmentDownloadFailThreshold); + if (FragmentDownloadFailThreshold <= segDLFailCount) + { + if(!playingAd) //If playingAd, we are invalidating the current Ad in onAdEvent(). + { + if (!initSegment) + { + if(type != eTRACK_SUBTITLE) // Avoid sending error for failure to download subtitle fragments + { + AAMPLOG_ERR("%s Not able to download fragments; reached failure threshold sending tune failed event",name); + abortWaitForVideoPTS(); + aamp->SetFlushFdsNeededInCurlStore(true); + aamp->SendDownloadErrorEvent(AAMP_TUNE_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); + } + } + else + { + // When rampdown limit is not specified, init segment will be ramped down, this will + AAMPLOG_ERR("%s Not able to download init fragments; reached failure threshold sending tune failed event",name); + abortWaitForVideoPTS(); + aamp->SetFlushFdsNeededInCurlStore(true); + aamp->SendDownloadErrorEvent(AAMP_TUNE_INIT_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); + } + } + } + // Profile RampDown check and rampdown is needed only for Video . If audio fragment download fails + // should continue with next fragment,no retry needed . + else if ((eTRACK_VIDEO == type) && !(context->CheckForRampDownLimitReached())) + { + // Attempt rampdown + if (context->CheckForRampDownProfile(httpErrorCode)) + { + mCheckForRampdown = true; + if (!initSegment) + { + // Rampdown attempt success, download same segment from lower profile. + mSkipSegmentOnError = false; + } + AAMPLOG_WARN( "StreamAbstractionAAMP_MPD::Error while fetching fragment:%s, failedCount:%d. decrementing profile", + fragmentUrl.c_str(), segDLFailCount); + } + else + { + if(!playingAd && initSegment && httpErrorCode !=502 ) + { + // Already at lowest profile, send error event for init fragment. + AAMPLOG_ERR("Not able to download init fragments; reached failure threshold sending tune failed event"); + abortWaitForVideoPTS(); + aamp->SetFlushFdsNeededInCurlStore(true); + aamp->SendDownloadErrorEvent(AAMP_TUNE_INIT_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); + } + else + { + AAMPLOG_WARN("%s StreamAbstractionAAMP_MPD::Already at the lowest profile, skipping segment at pos:%lf dur:%lf disc:%d",name,position,fragmentDurationS,discontinuity); + if(!initSegment) + updateSkipPoint(position+fragmentDurationS,fragmentDurationS ); + context->mRampDownCount = 0; + } + } + } + else if (AampLogManager::isLogworthyErrorCode(httpErrorCode)) + { + AAMPLOG_ERR("StreamAbstractionAAMP_MPD::Error on fetching %s fragment. failedCount:%d",name, segDLFailCount); + + if (initSegment) + { + // For init fragment, rampdown limit is reached. Send error event. + if (!playingAd && httpErrorCode != 502) + { + abortWaitForVideoPTS(); + aamp->SetFlushFdsNeededInCurlStore(true); + aamp->SendDownloadErrorEvent(AAMP_TUNE_INIT_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); + } + } + else + { + updateSkipPoint(position + fragmentDurationS, fragmentDurationS); + } + } + } + } + else + { + cachedFragment->position = position; + cachedFragment->duration = fragmentDurationS; + cachedFragment->discontinuity = discontinuity; + segDLFailCount = 0; + if ((eTRACK_VIDEO == type) && (!initSegment)) + { + // reset count on video fragment success + context->mRampDownCount = 0; + } + + if(tsbSessionManager && cachedFragment->fragment.GetLen()) + { + std::shared_ptr fragmentToTsbSessionMgr = std::make_shared(); + fragmentToTsbSessionMgr->Copy(cachedFragment, cachedFragment->fragment.GetLen()); + if(fragmentToTsbSessionMgr->initFragment) + { + fragmentToTsbSessionMgr->profileIndex = GetContext()->profileIdxForBandwidthNotification; + GetContext()->UpdateStreamInfoBitrateData(fragmentToTsbSessionMgr->profileIndex, fragmentToTsbSessionMgr->cacheFragStreamInfo); + } + fragmentToTsbSessionMgr->cacheFragStreamInfo.bandwidthBitsPerSecond = fragmentDescriptor.Bandwidth; + + if (CheckEos()) + { + // A reader EOS check is performed after downloading live edge segment + // If reader is at EOS, inject the missing live segment directly + AAMPLOG_INFO("Reader at EOS, Pushing last downloaded data"); + tsbSessionManager->GetTsbReader((AampMediaType)type)->CheckForWaitIfReaderDone(); + // If reader is at EOS, inject the last data in AAMP TSB + if (aamp->GetLLDashChunkMode()) + { + CacheTsbFragment(fragmentToTsbSessionMgr); + } + SetLocalTSBInjection(false); + // If all of the active media contexts are no longer injecting from TSB, update the AAMP flag + aamp->UpdateLocalAAMPTsbInjection(); + } + else if (fragmentToTsbSessionMgr->initFragment && !IsLocalTSBInjection() && !aamp->pipeline_paused) + { + // In chunk mode, media segments are added to the chunk cache in the SSL callback, but init segments are added here + if (aamp->GetLLDashChunkMode()) + { + CacheTsbFragment(fragmentToTsbSessionMgr); + } + } + tsbSessionManager->EnqueueWrite(std::move(fragmentUrl), std::move(fragmentToTsbSessionMgr), context->GetPeriod()->GetId()); + } + // Added the duplicate conditional statements, to log only for localAAMPTSB cases. + else if(tsbSessionManager && cachedFragment->fragment.GetLen() == 0) + { + AAMPLOG_WARN("Type[%d] Empty cachedFragment ignored!! fragmentUrl %s fragmentTime %f discontinuity %d pto %f scale %u duration %f", type, fragmentUrl.c_str(), position, discontinuity, pto, scale, fragmentDurationS); + } + else if(aamp->GetLLDashChunkMode() && initSegment) + { + std::shared_ptr fragmentToTsbSessionMgr = std::make_shared(); + fragmentToTsbSessionMgr->Copy(cachedFragment, cachedFragment->fragment.GetLen()); + if(fragmentToTsbSessionMgr->initFragment) + { + fragmentToTsbSessionMgr->profileIndex = GetContext()->profileIdxForBandwidthNotification; + GetContext()->UpdateStreamInfoBitrateData(fragmentToTsbSessionMgr->profileIndex, fragmentToTsbSessionMgr->cacheFragStreamInfo); + } + fragmentToTsbSessionMgr->cacheFragStreamInfo.bandwidthBitsPerSecond = fragmentDescriptor.Bandwidth; + CacheTsbFragment(std::move(fragmentToTsbSessionMgr)); + } + + // If playing back from local TSB, or pending playing back from local TSB as paused, but not paused due to underflow + if (tsbSessionManager && + (IsLocalTSBInjection() || (aamp->pipeline_paused && !aamp->GetBufUnderFlowStatus()))) + { + AAMPLOG_TRACE("[%s] cachedFragment %p ptr %p not injecting IsLocalTSBInjection %d, aamp->pipeline_paused %d, aamp->GetBufUnderFlowStatus() %d", + name, cachedFragment, cachedFragment->fragment.GetPtr(), IsLocalTSBInjection(), aamp->pipeline_paused, aamp->GetBufUnderFlowStatus()); + cachedFragment->fragment.Free(); + } + else + { + // Update buffer index after fetch for injection + UpdateTSAfterFetch(initSegment); + + // With AAMP TSB enabled, the chunk cache is used for any content type (SLD or LLD) + // When playing live SLD content, the fragment is written to the regular cache and to the chunk cache + if(tsbSessionManager && !IsLocalTSBInjection() && !aamp->GetLLDashChunkMode()) + { + std::shared_ptr fragmentToCache = std::make_shared(); + fragmentToCache->Copy(cachedFragment, cachedFragment->fragment.GetLen()); + CacheTsbFragment(std::move(fragmentToCache)); + } + + // If injection is from chunk buffer, remove the fragment for injection + if(IsInjectionFromCachedFragmentChunks()) + { + UpdateTSAfterInject(); + } + } + + ret = true; + } return ret; } @@ -227,17 +477,13 @@ bool MediaStreamContext::CacheFragmentChunk(AampMediaType actualType, const char AAMPLOG_WARN("[%s] Something Went wrong - Can't get FetchChunkBuffer", name); return false; } - cachedFragment->absPosition = 0; + double posInAbsTimeline = ((double)fragmentTime); + cachedFragment->absPosition = posInAbsTimeline; cachedFragment->type = actualType; cachedFragment->downloadStartTime = dnldStartTime; cachedFragment->fragment.AppendBytes(ptr, size); cachedFragment->timeScale = fragmentDescriptor.TimeScale; cachedFragment->uri = std::move(remoteUrl); - if (mActiveDownloadInfo) - { - cachedFragment->absPosition = mActiveDownloadInfo->absolutePosition; - cachedFragment->timeScale = mActiveDownloadInfo->timeScale; - } /* The value of PTSOffsetSec in the context can get updated at the start of a period before * the last segment from the previous period has been injected, hence we copy it */ @@ -302,8 +548,6 @@ void MediaStreamContext::updateSkipPoint(double position, double duration ) */ void MediaStreamContext::ABRProfileChanged(void) { - // TODO: Use this lock across all the functions which uses shared variables - AcquireMediaStreamContextLock(); struct ProfileInfo profileMap = context->GetAdaptationSetAndRepresentationIndicesForProfile(context->currentProfileIndex); // Get AdaptationSet Index and Representation Index from the corresponding profile int adaptIdxFromProfile = profileMap.adaptationSetIndex; @@ -351,7 +595,7 @@ void MediaStreamContext::ABRProfileChanged(void) AAMPLOG_DEBUG("StreamAbstractionAAMP_MPD:: Not switching ABR %dx%d[%d] ", representation->GetWidth(), representation->GetHeight(), representation->GetBandwidth()); } - ReleaseMediaStreamContextLock(); + } /** @@ -514,464 +758,3 @@ bool MediaStreamContext::CacheTsbFragment(std::shared_ptr fragme } return ret; } - -/** - * @fn OnFragmentDownloadSuccess - * @brief Function called on fragment download success - * @param[in] downloadInfo - download information - */ -void MediaStreamContext::OnFragmentDownloadSuccess(DownloadInfoPtr dlInfo) -{ - if (nullptr == mActiveDownloadInfo || nullptr == dlInfo || !aamp->DownloadsAreEnabled() || abort) - { - AAMPLOG_WARN("mActiveDownloadInfo or dlInfo is NULL or downloads are disabled"); - return; - } - - // Get active buffer - CachedFragment *cachedFragment = GetFetchBuffer(false); - mActiveDownloadInfo = nullptr; - AampTSBSessionManager *tsbSessionManager = aamp->GetTSBSessionManager(); - - auto CheckEos = [this, &tsbSessionManager]() - { - return IsLocalTSBInjection() && - AAMP_NORMAL_PLAY_RATE == aamp->rate && - !aamp->pipeline_paused && - eTUNETYPE_SEEKTOLIVE == context->mTuneType && - tsbSessionManager && - tsbSessionManager->GetTsbReader((AampMediaType)type) && - tsbSessionManager->GetTsbReader((AampMediaType)type)->IsEos(); - }; - - cachedFragment->position = dlInfo->pts; - cachedFragment->duration = dlInfo->fragmentDurationSec; - cachedFragment->discontinuity = dlInfo->isDiscontinuity; - segDLFailCount = 0; - // Update the last downloaded position for buffered duration calculation - lastDownloadedPosition.store(dlInfo->absolutePosition + dlInfo->fragmentDurationSec); - AAMPLOG_DEBUG("[%s] lastDownloadedPosition %lfs fragmentTime %lfs", - GetMediaTypeName(dlInfo->mediaType), - lastDownloadedPosition.load(), - dlInfo->absolutePosition); - if ((eTRACK_VIDEO == type) && (!dlInfo->isInitSegment)) - { - // reset count on video fragment success - context->mRampDownCount = 0; - } - - if(tsbSessionManager && cachedFragment->fragment.GetLen()) - { - std::shared_ptr fragmentToTsbSessionMgr = std::make_shared(); - fragmentToTsbSessionMgr->Copy(cachedFragment, cachedFragment->fragment.GetLen()); - if(fragmentToTsbSessionMgr->initFragment) - { - fragmentToTsbSessionMgr->profileIndex = GetContext()->profileIdxForBandwidthNotification; - GetContext()->UpdateStreamInfoBitrateData(fragmentToTsbSessionMgr->profileIndex, fragmentToTsbSessionMgr->cacheFragStreamInfo); - } - fragmentToTsbSessionMgr->cacheFragStreamInfo.bandwidthBitsPerSecond = fragmentDescriptor.Bandwidth; - - if (CheckEos()) - { - // A reader EOS check is performed after downloading live edge segment - // If reader is at EOS, inject the missing live segment directly - AAMPLOG_INFO("Reader at EOS, Pushing last downloaded data"); - tsbSessionManager->GetTsbReader((AampMediaType)type)->CheckForWaitIfReaderDone(); - // If reader is at EOS, inject the last data in AAMP TSB - if (aamp->GetLLDashChunkMode()) - { - CacheTsbFragment(fragmentToTsbSessionMgr); - } - SetLocalTSBInjection(false); - // If all of the active media contexts are no longer injecting from TSB, update the AAMP flag - aamp->UpdateLocalAAMPTsbInjection(); - } - else if (fragmentToTsbSessionMgr->initFragment && !IsLocalTSBInjection() && !aamp->pipeline_paused) - { - // In chunk mode, media segments are added to the chunk cache in the SSL callback, but init segments are added here - if (aamp->GetLLDashChunkMode()) - { - CacheTsbFragment(fragmentToTsbSessionMgr); - } - } - tsbSessionManager->EnqueueWrite(std::move(dlInfo->url), std::move(fragmentToTsbSessionMgr), context->GetPeriod()->GetId()); - } - // Added the duplicate conditional statements, to log only for localAAMPTSB cases. - else if (tsbSessionManager && cachedFragment->fragment.GetLen() == 0) - { - AAMPLOG_WARN("Type[%d] Empty cachedFragment ignored!! fragmentUrl %s fragmentTime %f discontinuity %d scale %u duration %f", type, dlInfo->url.c_str(), dlInfo->pts, dlInfo->isDiscontinuity, dlInfo->timeScale, dlInfo->fragmentDurationSec); - } - else if (aamp->GetLLDashChunkMode() && dlInfo->isInitSegment) - { - std::shared_ptr fragmentToTsbSessionMgr = std::make_shared(); - fragmentToTsbSessionMgr->Copy(cachedFragment, cachedFragment->fragment.GetLen()); - if (fragmentToTsbSessionMgr->initFragment) - { - fragmentToTsbSessionMgr->profileIndex = GetContext()->profileIdxForBandwidthNotification; - GetContext()->UpdateStreamInfoBitrateData(fragmentToTsbSessionMgr->profileIndex, fragmentToTsbSessionMgr->cacheFragStreamInfo); - } - fragmentToTsbSessionMgr->cacheFragStreamInfo.bandwidthBitsPerSecond = fragmentDescriptor.Bandwidth; - CacheTsbFragment(std::move(fragmentToTsbSessionMgr)); - } - - // If playing back from local TSB, or pending playing back from local TSB as paused, but not paused due to underflow - if (tsbSessionManager && - (IsLocalTSBInjection() || (aamp->pipeline_paused && !aamp->GetBufUnderFlowStatus()))) - { - AAMPLOG_TRACE("[%s] cachedFragment %p ptr %p not injecting IsLocalTSBInjection %d, aamp->pipeline_paused %d, aamp->GetBufUnderFlowStatus() %d", - name, cachedFragment, cachedFragment->fragment.GetPtr(), IsLocalTSBInjection(), aamp->pipeline_paused, aamp->GetBufUnderFlowStatus()); - cachedFragment->fragment.Free(); - auto timeBasedBufferManager = GetTimeBasedBufferManager(); - if(timeBasedBufferManager) - { - timeBasedBufferManager->ConsumeBuffer(cachedFragment->duration); - } - } - else - { - // Update buffer index after fetch for injection - UpdateTSAfterFetch(dlInfo->isInitSegment); - - // With AAMP TSB enabled, the chunk cache is used for any content type (SLD or LLD) - // When playing live SLD content, the fragment is written to the regular cache and to the chunk cache - if(tsbSessionManager && !IsLocalTSBInjection() && !aamp->GetLLDashChunkMode()) - { - std::shared_ptr fragmentToCache = std::make_shared(); - fragmentToCache->Copy(cachedFragment, cachedFragment->fragment.GetLen()); - CacheTsbFragment(std::move(fragmentToCache)); - } - - // If injection is from chunk buffer, remove the fragment for injection - if(IsInjectionFromCachedFragmentChunks()) - { - UpdateTSAfterInject(); - auto timeBasedBufferManager = GetTimeBasedBufferManager(); - if(timeBasedBufferManager) - { - timeBasedBufferManager->ConsumeBuffer(cachedFragment->duration); - } - } - } - - if (aamp->IsLive()) - { - GetContext()->CheckForPlaybackStall(true); - } - if ((!GetContext()->trickplayMode) && (eMEDIATYPE_VIDEO == dlInfo->mediaType) && !failAdjacentSegment && !dlInfo->isInitSegment) - { - // Check for ABR profile change - // ABR is performed from TrackWorker thread to ensure the profile change is done in the same thread - if (aamp->CheckABREnabled()) - { - GetContext()->CheckForProfileChange(); - } - } -} - -/** - * @fn OnFragmentDownloadFailed - * @brief Callback on fragment download failure - * @param[in] downloadInfo - download information - */ -void MediaStreamContext::OnFragmentDownloadFailed(DownloadInfoPtr dlInfo) -{ - - if (nullptr == mActiveDownloadInfo || nullptr == dlInfo || !aamp->DownloadsAreEnabled() || abort) - { - AAMPLOG_WARN("OnFragmentDownloadFailed: mActiveDownloadInfo or dlInfo is NULL"); - return; - } - - // Get active buffer - CachedFragment *cachedFragment = GetFetchBuffer(false); - mActiveDownloadInfo = nullptr; - AAMPLOG_INFO("fragment fetch failed - Free cachedFragment for %d", cachedFragment->type); - cachedFragment->fragment.Free(); - if (aamp->DownloadsAreEnabled()) - { - AAMPLOG_WARN("%sfragment fetch failed -- fragmentUrl %s", (dlInfo->isInitSegment) ? "Init " : " ", dlInfo->url.c_str()); - if (mSkipSegmentOnError) - { - // Skip segment on error, and increase fail count - if (httpErrorCode != 502) - { - segDLFailCount += 1; - } - } - else - { - // Rampdown already attempted on same segment - // Reset flag for next fetch - mSkipSegmentOnError = true; - } - int FragmentDownloadFailThreshold = GETCONFIGVALUE(eAAMPConfig_FragmentDownloadFailThreshold); - if (FragmentDownloadFailThreshold <= segDLFailCount) - { - if (!dlInfo->isPlayingAd) // If playingAd, we are invalidating the current Ad in onAdEvent(). - { - if (!dlInfo->isInitSegment) - { - if (type != eTRACK_SUBTITLE) // Avoid sending error for failure to download subtitle fragments - { - AAMPLOG_ERR("%s Not able to download fragments; reached failure threshold sending tune failed event", name); - abortWaitForVideoPTS(); - aamp->SetFlushFdsNeededInCurlStore(true); - aamp->SendDownloadErrorEvent(AAMP_TUNE_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); - } - } - else - { - // When rampdown limit is not specified, init segment will be ramped down, this will - AAMPLOG_ERR("%s Not able to download init fragments; reached failure threshold sending tune failed event", name); - abortWaitForVideoPTS(); - aamp->SetFlushFdsNeededInCurlStore(true); - - aamp->SendDownloadErrorEvent(AAMP_TUNE_INIT_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); - } - } - } - // Profile RampDown check and rampdown is needed only for Video . If audio fragment download fails - // should continue with next fragment,no retry needed . - else if ((eTRACK_VIDEO == type) && !ISCONFIGSET(eAAMPConfig_AudioOnlyPlayback) && !(context->CheckForRampDownLimitReached())) - { - // Attempt rampdown - // ABR is performed from TrackWorker thread to ensure the profile change is done in the same thread - if (context->CheckForRampDownProfile(httpErrorCode)) - { - mCheckForRampdown = true; - if (!dlInfo->isInitSegment) - { - // Rampdown attempt success, download same segment from lower profile. - mSkipSegmentOnError = false; - } - AAMPLOG_WARN("StreamAbstractionAAMP_MPD::Error while fetching fragment:%s, failedCount:%d. decrementing profile", - dlInfo->url.c_str(), segDLFailCount); - - // Submit job to download same fragment from lower profile and push it to the front of the fetch queue. - // To ensure the init fragment is downloaded from the lower profile, we need to push it to the front of the fetch queue - // This is done from onFragmentDownloadFailed() from context. - aamp->GetAampTrackWorkerManager()->GetWorker(dlInfo->mediaType)->RescheduleActiveJob(); - } - else - { - if (!dlInfo->isPlayingAd && dlInfo->isInitSegment && httpErrorCode != 502) - { - // Already at lowest profile, send error event for init fragment. - AAMPLOG_ERR("Not able to download init fragments; reached failure threshold sending tune failed event"); - abortWaitForVideoPTS(); - aamp->SetFlushFdsNeededInCurlStore(true); - aamp->SendDownloadErrorEvent(AAMP_TUNE_INIT_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); - } - else - { - AAMPLOG_WARN("%s StreamAbstractionAAMP_MPD::Already at the lowest profile, skipping segment at pos:%lf dur:%lf disc:%d", name, dlInfo->pts, dlInfo->fragmentDurationSec, dlInfo->isDiscontinuity); - if (!dlInfo->isInitSegment) - updateSkipPoint((dlInfo->pts + dlInfo->fragmentDurationSec), dlInfo->fragmentDurationSec); - auto timeBasedBufferManager = GetTimeBasedBufferManager(); - if(timeBasedBufferManager) - { - // Consume the buffer for the segment duration as segment is skipped - timeBasedBufferManager->ConsumeBuffer(dlInfo->fragmentDurationSec); - } - context->mRampDownCount = 0; - } - } - } - else if (AampLogManager::isLogworthyErrorCode(httpErrorCode)) - { - AAMPLOG_ERR("StreamAbstractionAAMP_MPD::Error on fetching %s fragment. failedCount:%d", name, segDLFailCount); - if (dlInfo->isInitSegment) - { - // For init fragment, rampdown limit is reached. Send error event. - if (!dlInfo->isPlayingAd && httpErrorCode != 502) - { - abortWaitForVideoPTS(); - aamp->SetFlushFdsNeededInCurlStore(true); - aamp->SendDownloadErrorEvent(AAMP_TUNE_INIT_FRAGMENT_DOWNLOAD_FAILURE, httpErrorCode); - } - } - else - { - updateSkipPoint((dlInfo->pts + dlInfo->fragmentDurationSec), dlInfo->fragmentDurationSec); - } - } - } -} - -/** - * @fn DownloadFragment - * @brief Download submitted fragment - * @param[in] downloadInfo - download information - * - * @return true on success - */ -bool MediaStreamContext::DownloadFragment(DownloadInfoPtr dlInfo) -{ - bool retval = true; - std::string fragmentUrl; - - // Now construct the fragment URL - if (!dlInfo) - { - AAMPLOG_WARN("DownloadFragment called with NULL downloadInfo"); - return false; - } - - URIInfo uriInfo; - if (dlInfo->uriList.size() > 0) - { - // Asses the current bandwidth and get the appropriate URIInfo from the map with resolved URLs - if (dlInfo->uriList.find(fragmentDescriptor.Bandwidth) != dlInfo->uriList.end()) - { - uriInfo = dlInfo->uriList[fragmentDescriptor.Bandwidth]; - } - if (uriInfo.url.empty() && dlInfo->uriList.size() > 0) - { - // If the fragment URL is not found in the map, then use the first URL in the map - AAMPLOG_WARN("Fragment URL not found in the map, using the first URL in the map"); - uriInfo = dlInfo->uriList.begin()->second; - } - } - - // Handle change in bandwidth for segmentBase streams, so need to load new range - if((dlInfo->bandwidth != fragmentDescriptor.Bandwidth) && IDX.GetPtr() && uriInfo.range.empty()) - { - // If the bandwidth is different, then set the range - if (dlInfo->bandwidth > 0) - { - dlInfo->fragmentOffset = 0; - dlInfo->fragmentOffset++; // first byte following packed index - if (IDX.GetPtr() ) - { - unsigned int firstOffset; - ParseSegmentIndexBox( - IDX.GetPtr(), - IDX.GetLen(), - 0, - NULL, - NULL, - &firstOffset); - dlInfo->fragmentOffset += firstOffset; - } - if (dlInfo->fragmentOffset != 0 && IDX.GetPtr() ) - { - unsigned int referenced_size; - float fragmentDuration; - AAMPLOG_DEBUG("current fragmentIndex = %d", dlInfo->fragmentIndex); - //Find the offset of previous fragment in new representation - for (int i = 0; i < dlInfo->fragmentIndex; i++) - { - if (ParseSegmentIndexBox( - IDX.GetPtr(), - IDX.GetLen(), - i, - &referenced_size, - &fragmentDuration, - NULL)) - { - dlInfo->fragmentOffset += referenced_size; - } - } - } - unsigned int referenced_size; - float fragmentDuration; - if (ParseSegmentIndexBox( - IDX.GetPtr(), - IDX.GetLen(), - dlInfo->fragmentIndex, - &referenced_size, - &fragmentDuration, - NULL) ) - { - char range[MAX_RANGE_STRING_CHARS]; - snprintf(range, sizeof(range), "%" PRIu64 "-%" PRIu64 "", dlInfo->fragmentOffset, dlInfo->fragmentOffset + referenced_size - 1); - AAMPLOG_INFO("%s [%s]",GetMediaTypeName(dlInfo->mediaType), range); - uriInfo.range = range; - dlInfo->fragmentDurationSec = fragmentDuration; - } - } - if(!uriInfo.range.empty()) - { - // If the range is not empty, then set the range - dlInfo->range = uriInfo.range; - } - } - - if (uriInfo.url.empty()) - { - AAMPLOG_WARN("Fragment URL is empty"); - retval = false; - } - else - { - dlInfo->url = uriInfo.url; - } - - if (dlInfo->isInitSegment) - { - if (!(initialization.empty()) && (0 == initialization.compare(dlInfo->url)) && !dlInfo->isDiscontinuity) - { - AAMPLOG_TRACE("We have pushed the same initialization segment for %s skipping", GetMediaTypeName(dlInfo->mediaType)); - return retval; - } - else - { - initialization = std::string(dlInfo->url); - } - - if(lastDownloadedPosition > 0) - { - // Reset the absolute position to the last injected position for profile change - AAMPLOG_TRACE("Setting absolute position to last injected position: %lf", lastDownloadedPosition.load()); - dlInfo->absolutePosition = lastDownloadedPosition.load(); - } - } - - AAMPLOG_DEBUG("[%s] DownloadFragment from position:%lf url:%s;%s", name, dlInfo->absolutePosition, dlInfo->url.c_str(), dlInfo->range.c_str()); - - if (retval && aamp->DownloadsAreEnabled()) - { - if (dlInfo->failoverContentSegment) - { - if (mediaType == eMEDIATYPE_VIDEO) - { - // Attempt rampdown - int http_code = 404; - retval = false; - if (GetContext()->CheckForRampDownProfile(http_code)) - { - AAMPLOG_WARN("RampDownProfile Due to failover Content %" PRIu64 " Number %lf FDT", dlInfo->fragmentNumber, dlInfo->pts); - this->mCheckForRampdown = true; - // Rampdown attempt success, download same segment from lower profile. - this->mSkipSegmentOnError = false; - } - else - { - AAMPLOG_WARN("Already at the lowest profile, skipping segment due to failover"); - GetContext()->mRampDownCount = 0; - } - return retval; - } - } - if (!mActiveDownloadInfo) - { - // Assign the new download info to mActiveDownloadInfo - mActiveDownloadInfo = dlInfo; - } - int maxCachedFragmentsPerTrack = GETCONFIGVALUE(eAAMPConfig_MaxFragmentCached); // Max cached fragments per track - // Wait for free fragment only if the number of fragments cached is equal to the max cached fragments per track - if (numberOfFragmentsCached == maxCachedFragmentsPerTrack) - { - while (!WaitForFreeFragmentAvailable(MAX_WAIT_TIMEOUT_MS) && aamp->DownloadsAreEnabled() && !abort) - { - AAMPLOG_TRACE("Waiting for free fragment"); - } - } - if (aamp->DownloadsAreEnabled() && !abort) - { - retval = CacheFragment(dlInfo->url, dlInfo->curlInstance, dlInfo->pts, dlInfo->fragmentDurationSec, dlInfo->range.c_str(), dlInfo->isInitSegment, dlInfo->isDiscontinuity, dlInfo->isPlayingAd, dlInfo->timeScale); - } - } - - return retval; -} diff --git a/MediaStreamContext.h b/MediaStreamContext.h index 146b87b4c..73df8d948 100644 --- a/MediaStreamContext.h +++ b/MediaStreamContext.h @@ -54,15 +54,13 @@ class MediaStreamContext : public MediaTrack , scaledPTO(0) , failAdjacentSegment(false),httpErrorCode(0) , mPlaylistUrl(""), mEffectiveUrl(""),freshManifest(false),nextfragmentIndex(-1) - , mReachedFirstFragOnRewind(false),fetchChunkBufferMutex(), mActiveDownloadInfo(nullptr) - , mMediaStreamContextMutex() + , mReachedFirstFragOnRewind(false),fetchChunkBufferMutex() { AAMPLOG_INFO("[%s] Create new MediaStreamContext", GetMediaTypeName(mediaType)); mPlaylistUrl = aamp->GetManifestUrl(); fragmentDescriptor.bUseMatchingBaseUrl = ISCONFIGSET(eAAMPConfig_MatchBaseUrl); mTempFragment = std::make_shared("temp"); - mTimeBasedBufferManager = std::make_shared(GETCONFIGVALUE(eAAMPConfig_MaxDownloadBuffer), std::abs(aamp->rate), mediaType); } /** @@ -72,7 +70,6 @@ class MediaStreamContext : public MediaTrack { mDownloadedFragment.Free(); mTempFragment.reset(); - mTimeBasedBufferManager.reset(); } /** @@ -114,9 +111,10 @@ class MediaStreamContext : public MediaTrack * @param playingAd flag if playing Ad * @param pto unscaled pto value from mpd * @param scale timeScale value from mpd + * @param overWriteTrackId flag to overwrite the trackID of the init fragment with the current one if those are different * @retval true on success */ - bool CacheFragment(std::string fragmentUrl, unsigned int curlInstance, double position, double duration, const char *range = NULL, bool initSegment = false, bool discontinuity = false, bool playingAd = false, uint32_t scale = 0); + bool CacheFragment(std::string fragmentUrl, unsigned int curlInstance, double position, double duration, const char *range = NULL, bool initSegment = false, bool discontinuity = false, bool playingAd = false, double pto = 0, uint32_t scale = 0, bool overWriteTrackId = false); /** * @fn CacheTsbFragment @@ -246,41 +244,6 @@ class MediaStreamContext : public MediaTrack return GetLastInjectedFragmentPosition( ); } - /** - * @fn OnFragmentDownloadSuccess - * @brief Function called on fragment download success - * @param[in] downloadInfo - download information - */ - void OnFragmentDownloadSuccess(DownloadInfoPtr downloadInfo); - - /** - * @fn OnFragmentDownloadFailed - * @brief Callback on fragment download failure - * @param[in] downloadInfo - download information - */ - void OnFragmentDownloadFailed(DownloadInfoPtr downloadInfo); - - /** - * @fn DownloadFragment - * @brief Download submitted fragment - * @param[in] downloadInfo - download information - * - * @return true on success - */ - bool DownloadFragment(DownloadInfoPtr downloadInfo); - - /** - * @fn AcquireMediaStreamContextLock - * @brief Acquire lock for MediaStreamContext - */ - inline void AcquireMediaStreamContextLock() { mMediaStreamContextMutex.lock(); } - - /** - * @fn ReleaseMediaStreamContextLock - * @brief Release lock for MediaStreamContext - */ - inline void ReleaseMediaStreamContextLock() { mMediaStreamContextMutex.unlock(); } - AampMediaType mediaType; struct FragmentDescriptor fragmentDescriptor; const IAdaptationSet *adaptationSet; @@ -318,8 +281,6 @@ class MediaStreamContext : public MediaTrack int nextfragmentIndex; //CMCD get next index to fetch url from Segment List bool mReachedFirstFragOnRewind; /**< flag denotes if we reached the first fragment in a period on rewind */ std::mutex fetchChunkBufferMutex; - DownloadInfoPtr mActiveDownloadInfo; - std::mutex mMediaStreamContextMutex; }; // MediaStreamContext #endif /* MEDIASTREAMCONTEXT_H */ diff --git a/StreamAbstractionAAMP.h b/StreamAbstractionAAMP.h index 573a67527..de19df12d 100644 --- a/StreamAbstractionAAMP.h +++ b/StreamAbstractionAAMP.h @@ -44,7 +44,6 @@ #include "AampDRMLicPreFetcherInterface.h" #include "AampTime.h" -#include "AampTimeBasedBufferManager.hpp" #include "CachedFragment.h" /** @@ -659,15 +658,6 @@ class MediaTrack */ bool IsInjectionFromCachedFragmentChunks(); - /** - * @fn GetTimeBasedBufferManager - * - * @brief Get the time based buffer manager for this track - * - * @return AampTimeBasedBufferManager object - */ - std::shared_ptr GetTimeBasedBufferManager() { return mTimeBasedBufferManager; } - protected: /** * @fn UpdateTSAfterInject @@ -836,13 +826,8 @@ class MediaTrack bool loadNewAudio; /**< Flag to indicate new audio loading started on seamless audio switch */ std::mutex subtitleMutex; bool loadNewSubtitle; - int fragmentIdxToInject; /**< Write position */ - int fragmentChunkIdxToInject; /**< Write position */ - int fragmentIdxToFetch; /**< Read position */ - int fragmentChunkIdxToFetch; /**< Read position */ StreamOutputFormat mSourceFormat {StreamOutputFormat::FORMAT_INVALID}; - std::shared_ptr mTimeBasedBufferManager; /**< Time based buffer for managing fragment download and playback */ private: enum class TrickmodeState @@ -871,6 +856,10 @@ class MediaTrack int currentInitialCacheDurationSeconds; /**< Current cached fragments duration before playing*/ bool sinkBufferIsFull; /**< True if sink buffer is full and do not want new fragments*/ bool cachingCompleted; /**< Fragment caching completed or not*/ + int fragmentIdxToInject; /**< Write position */ + int fragmentChunkIdxToInject; /**< Write position */ + int fragmentIdxToFetch; /**< Read position */ + int fragmentChunkIdxToFetch; /**< Read position */ int bandwidthBitsPerSecond; /**< Bandwidth of last selected profile*/ double totalFetchedDuration; /**< Total fragment fetched duration*/ bool discontinuityProcessed; diff --git a/admanager_mpd.cpp b/admanager_mpd.cpp index 935f9bcdf..cdd637fcb 100644 --- a/admanager_mpd.cpp +++ b/admanager_mpd.cpp @@ -25,7 +25,6 @@ #include "admanager_mpd.h" #include "AampUtils.h" #include "fragmentcollector_mpd.h" -#include "AampCacheHandler.h" #include #include @@ -908,8 +907,7 @@ MPD* PrivateCDAIObjectMPD::GetAdMPD(std::string &manifestUrl, bool &finalManifes { finalManifest = true; } - std::string manifestStr(manifest.GetPtr(), manifest.GetLen()); - xmlTextReaderPtr reader = xmlReaderForMemory(manifestStr.c_str(), (int) manifestStr.size(), NULL, NULL, 0); + xmlTextReaderPtr reader = xmlReaderForMemory( manifest.GetPtr(), (int) manifest.GetLen(), NULL, NULL, 0); if(tryFog && !mAamp->mConfig->IsConfigSet(eAAMPConfig_PlayAdFromCDN) && reader && mIsFogTSB) //Main content from FOG. Ad is expected from FOG. { std::string channelUrl = mAamp->GetManifestUrl(); //TODO: Get FOG URL from channel URL @@ -943,7 +941,6 @@ MPD* PrivateCDAIObjectMPD::GetAdMPD(std::string &manifestUrl, bool &finalManifes //FOG already has the manifest. Releasing the one from CDN and using FOG's xmlFreeTextReader(reader); reader = xmlReaderForMemory(fogManifest.GetPtr(), (int) fogManifest.GetLen(), NULL, NULL, 0); - manifestStr.assign(fogManifest.GetPtr(), fogManifest.GetLen()); manifest.Free(); manifest.Replace(&fogManifest); } @@ -965,76 +962,72 @@ MPD* PrivateCDAIObjectMPD::GetAdMPD(std::string &manifestUrl, bool &finalManifes } if (reader != NULL) { - // Cache the init headers before processing the manifest nodes - if (FetchAndCacheInitHeaders(manifestStr, manifestUrl, errorCode)) + if (xmlTextReaderRead(reader)) { - if (xmlTextReaderRead(reader)) + Node* root = MPDProcessNode(&reader, manifestUrl, true); + if (NULL != root) { - Node *root = MPDProcessNode(&reader, manifestUrl, true); - if (NULL != root) + std::vector children = root->GetSubNodes(); + for (size_t i = 0; i < children.size(); i++) { - std::vector children = root->GetSubNodes(); - for (size_t i = 0; i < children.size(); i++) + Node* child = children.at(i); + const std::string& name = child->GetName(); + AAMPLOG_INFO("PrivateCDAIObjectMPD:: child->name %s", name.c_str()); + if (name == "Period") { - Node *child = children.at(i); - const std::string &name = child->GetName(); - AAMPLOG_INFO("PrivateCDAIObjectMPD:: child->name %s", name.c_str()); - if (name == "Period") + AAMPLOG_INFO("PrivateCDAIObjectMPD:: found period"); + std::vector children = child->GetSubNodes(); + bool hasBaseUrl = false; + for (size_t i = 0; i < children.size(); i++) { - AAMPLOG_INFO("PrivateCDAIObjectMPD:: found period"); - std::vector children = child->GetSubNodes(); - bool hasBaseUrl = false; + if (children.at(i)->GetName() == "BaseURL") + { + hasBaseUrl = true; + } + } + if (!hasBaseUrl) + { + // BaseUrl not found in the period. Get it from the root and put it in the period + children = root->GetSubNodes(); for (size_t i = 0; i < children.size(); i++) { if (children.at(i)->GetName() == "BaseURL") { + Node* baseUrl = new Node(*children.at(i)); + child->AddSubNode(baseUrl); hasBaseUrl = true; + break; } } - if (!hasBaseUrl) - { - // BaseUrl not found in the period. Get it from the root and put it in the period - children = root->GetSubNodes(); - for (size_t i = 0; i < children.size(); i++) - { - if (children.at(i)->GetName() == "BaseURL") - { - Node *baseUrl = new Node(*children.at(i)); - child->AddSubNode(baseUrl); - hasBaseUrl = true; - break; - } - } - } - if (!hasBaseUrl) - { - std::string baseUrlStr = Path::GetDirectoryPath(manifestUrl); - Node *baseUrl = new Node(); - baseUrl->SetName("BaseURL"); - baseUrl->SetType(Text); - baseUrl->SetText(baseUrlStr); - AAMPLOG_INFO("PrivateCDAIObjectMPD:: manual adding BaseURL Node [%p] text %s", - baseUrl, baseUrl->GetText().c_str()); - child->AddSubNode(baseUrl); - } - break; } + if (!hasBaseUrl) + { + std::string baseUrlStr = Path::GetDirectoryPath(manifestUrl); + Node* baseUrl = new Node(); + baseUrl->SetName("BaseURL"); + baseUrl->SetType(Text); + baseUrl->SetText(baseUrlStr); + AAMPLOG_INFO("PrivateCDAIObjectMPD:: manual adding BaseURL Node [%p] text %s", + baseUrl, baseUrl->GetText().c_str()); + child->AddSubNode(baseUrl); + } + break; } - adMpd = root->ToMPD(); - SAFE_DELETE(root); } - else - { - AAMPLOG_ERR("Could not create root node"); - errorCode = eCDAI_ERROR_INVALID_MANIFEST; - } + adMpd = root->ToMPD(); + SAFE_DELETE(root); } else { + AAMPLOG_ERR("Could not create root node"); errorCode = eCDAI_ERROR_INVALID_MANIFEST; - AAMPLOG_ERR("xmlTextReaderRead failed"); } } + else + { + errorCode = eCDAI_ERROR_INVALID_MANIFEST; + AAMPLOG_ERR("xmlTextReaderRead failed"); + } xmlFreeTextReader(reader); } else @@ -1052,7 +1045,7 @@ MPD* PrivateCDAIObjectMPD::GetAdMPD(std::string &manifestUrl, bool &finalManifes else { AAMPLOG_ERR("[CDAI]: Error on manifest fetch"); - if (http_error != CURLE_ABORTED_BY_CALLBACK) + if(http_error != CURLE_ABORTED_BY_CALLBACK) { errorCode = (http_error<100)? eCDAI_ERROR_DELIVERY_ERROR : eCDAI_ERROR_DELIVERY_HTTP_ERROR; } @@ -1546,10 +1539,7 @@ void PrivateCDAIObjectMPD::StopFulfillAdLoop() { mExitFulfillAdLoop = true; NotifyAdLoopWait(); - if (mAdObjThreadID.joinable()) - { - mAdObjThreadID.join(); - } + mAdObjThreadID.join(); mAdObjThreadStarted = false; } } @@ -1728,151 +1718,3 @@ bool PrivateCDAIObjectMPD::GetNextAdInBreakToPlace() mPlacementObj.adNextOffset = 0; return ret; } - -/** - * @brief Getting all init headers for the Ad - * @param[in] manifestStr - Manifest string - * @param[in] manifestUrl - Manifest URL - * @param[out] errorCode - AAMPCDAIError Error code if any. - * @return true if all init headers are fetched and cached successfully, false otherwise - */ -bool PrivateCDAIObjectMPD::FetchAndCacheInitHeaders(std::string& manifestStr, std::string& manifestUrl, AAMPCDAIError &errorCode) -{ - bool ret = true; - std::shared_ptr mpdDoc = std::make_shared(manifestStr); - if (!mpdDoc || !mpdDoc->getRoot()) - { - ret = false; - errorCode = eCDAI_ERROR_INVALID_MANIFEST; - AAMPLOG_ERR("Failed to parse MPD document or root is null"); - } - else - { - auto periods = mpdDoc->getRoot()->getPeriods(); - if (periods.empty()) - { - ret = false; - errorCode = eCDAI_ERROR_INVALID_MANIFEST; - AAMPLOG_ERR("No periods found in the MPD document"); - } - else - { - const auto& period = periods.at(0); - if (!period) - { - ret = false; - errorCode = eCDAI_ERROR_INVALID_MEDIA; - AAMPLOG_ERR("Period is null in the MPD document"); - } - else - { - const auto& adaptationSets = period->getAdaptationSets(); - for (AampMediaType track = eMEDIATYPE_VIDEO; track < eMEDIATYPE_SUBTITLE; track = static_cast(static_cast(track) + 1)) - { - bool initFragmentFetched = false; - for (const auto& adaptationSet : adaptationSets) - { - std::string mediaType = adaptationSet->getMediaType(); - AAMPLOG_INFO("AdaptationSet mediaType[%s] track[%s]", mediaType.c_str(), GetMediaTypeName(track)); - if (!(mediaType.empty() || strcasecmp(mediaType.c_str(), GetMediaTypeName(track)) == 0 || IsCompatibleMimeType(mediaType, track))) - { - continue; - } - if (track == eMEDIATYPE_VIDEO && adaptationSet->isIframeTrack()) - { - continue; - } - const auto& representations = adaptationSet->getRepresentations(); - if (representations.empty()) - { - continue; - } - const auto& representation = representations.at(0); - if (!representation) - { - continue; - } - auto segmentTemplate = representation->getSegmentTemplate(); - if (!segmentTemplate) - { - AAMPLOG_ERR("No segment template available for ad representation"); - continue; - } - std::string fragmentUrl; - std::unique_ptr fragmentDescriptor = aamp_utils::make_unique(); - fragmentDescriptor->manifestUrl = manifestUrl; - fragmentDescriptor->Bandwidth = static_cast(representation->getBandwidth()); - fragmentDescriptor->RepresentationID = representation->getId(); - fragmentDescriptor->ClearMatchingBaseUrl(); - if (!representation->getBaseUrls().empty()) - { - fragmentDescriptor->AppendMatchingBaseUrl(representation->getBaseUrls()); - } - else - { - fragmentDescriptor->AppendMatchingBaseUrl({Path::GetDirectoryPath(manifestUrl)}); - } - AampMediaType actualMediaType = static_cast(eMEDIATYPE_INIT_VIDEO + track); - ConstructFragmentURL(fragmentUrl, fragmentDescriptor.get(), segmentTemplate->getInitializationAttr(), mAamp->mConfig); - if (fragmentUrl.empty()) - { - continue; - } - std::shared_ptr adInit = std::make_shared("adInit"); - int segment_http_error = 0; - double segment_downloadTime = 0; - AAMPLOG_INFO("Fetching init header %s for %s adId:%s periodId:%s", fragmentUrl.c_str(), GetMediaTypeName(actualMediaType), mAdFulfillObj.adId.c_str(), mAdFulfillObj.periodId.c_str()); - bool gotInit = mAamp->getAampCacheHandler()->RetrieveFromInitFragmentCache(fragmentUrl, adInit.get(), fragmentUrl); - if(!gotInit) - { - gotInit = mAamp->GetFile(fragmentUrl, actualMediaType, adInit.get(), fragmentUrl, &segment_http_error, &segment_downloadTime, nullptr, eCURLINSTANCE_DAI); - mAamp->UpdateVideoEndMetrics(actualMediaType, fragmentDescriptor->Bandwidth, segment_http_error, fragmentUrl, 0, segment_downloadTime); - } - if (gotInit) - { - AAMPLOG_INFO("Init header fetched successfully for %s adId:%s periodId:%s", GetMediaTypeName(actualMediaType), mAdFulfillObj.adId.c_str(), mAdFulfillObj.periodId.c_str()); - mAamp->getAampCacheHandler()->InsertToInitFragCache(fragmentUrl, adInit.get(), fragmentUrl, actualMediaType); - adInit->Free(); - initFragmentFetched = true; - break; - } - else - { - AAMPLOG_ERR("Error on %s fragment fetch, error code: %d", GetMediaTypeName(actualMediaType), segment_http_error); - if (track == eMEDIATYPE_VIDEO) - { - break; - } - } - } - if (!initFragmentFetched) - { - if (isAdBreakObjectExist(mAdFulfillObj.periodId)) - { - auto& adbreakObj = mAdBreaks[mAdFulfillObj.periodId]; - if (adbreakObj.ads) - { - for (auto& node : *adbreakObj.ads) - { - if (node.adId == mAdFulfillObj.adId) - { - AAMPLOG_ERR("Failed to fetch init fragment for %s adId:%s periodId:%s", GetMediaTypeName(track), mAdFulfillObj.adId.c_str(), mAdFulfillObj.periodId.c_str()); - node.resolved = true; - node.invalid = true; - errorCode = eCDAI_ERROR_INVALID_MEDIA; - ret = false; - } - } - } - } - if (track == eMEDIATYPE_VIDEO) - { - break; - } - } - } - } - } - } - return ret; -} diff --git a/admanager_mpd.h b/admanager_mpd.h index 760dbc5b6..6545ec136 100644 --- a/admanager_mpd.h +++ b/admanager_mpd.h @@ -32,7 +32,6 @@ #include "libdash/IDASHManager.h" #include "libdash/xml/Node.h" #include "libdash/IMPD.h" -#include "dash/mpd/MPDModel.h" #include "AampMPDParseHelper.h" #include "AampEvent.h" @@ -601,15 +600,6 @@ class PrivateCDAIObjectMPD */ bool GetNextAdInBreakToPlace(); - /** - * @brief Getting all init headers for the Ad - * @param[in] manifestStr - Manifest string - * @param[in] manifestUrl - Manifest URL - * @param[out] errorCode - AAMPCDAIError Error code if any. - * @return true if all init headers are fetched and cached successfully, false otherwise - */ - bool FetchAndCacheInitHeaders(std::string& manifestStr, std::string& manifestUrl, AAMPCDAIError &errorCode); - /** * @fn ValidateAdManifest * @brief Validate the ad manifest for basic requirements diff --git a/dash/mpd/MPDModel.cpp b/dash/mpd/MPDModel.cpp index cb8b4ebe5..b7d137547 100644 --- a/dash/mpd/MPDModel.cpp +++ b/dash/mpd/MPDModel.cpp @@ -179,96 +179,6 @@ string findBaseUrl(DomElement &element, const string ¤t, bool isFile) { } } - -/** - * Extracts all Base URLs from the element. - * @param element Element to extract Base URLs from - * @return Vector of Base URL texts - */ -std::vector extractBaseUrlTexts(DomElement &element) -{ - vector baseUrlTexts; - for (DomElement eUrl = element.firstChildElement("BaseURL"); !eUrl.isNull(); eUrl = eUrl.nextSiblingElement("BaseURL")) - { - string text = eUrl.text(); - if (!text.empty()) - { - baseUrlTexts.push_back(std::move(text)); - } - } - return baseUrlTexts; -} - -/** - * @brief Finds all Base URLs for an element - * @param element Element - * @param current Parent Base URLs - * @param isFile Flag to indicate File - * @retval Vector of Base URLs - */ -std::vector findAllBaseUrls(DomElement &element, const std::vector ¤t, bool isFile) -{ - vector baseUrls; - vector baseUrlTexts = extractBaseUrlTexts(element); - string slash = isFile ? "" : "/"; - - if (!baseUrlTexts.empty()) - { - for (const auto &urlText : baseUrlTexts) - { - Url newbase(urlText); - if (newbase.isRelative()) - { - if (current.empty()) - { - baseUrls.push_back(newbase.format(Url::StripTrailingSlash).append(slash)); - } - else - { - for (const auto &base : current) - { - baseUrls.push_back(Url(base).resolve(newbase).format(Url::StripTrailingSlash).append(slash)); - } - } - } - else - { - baseUrls.push_back(newbase.format(Url::StripTrailingSlash).append(slash)); - } - } - } - else if (!current.empty()) - { - for (const auto &base : current) - { - if (!base.empty()) - { - if (isFile) - { - baseUrls.push_back(base); - } - else - { - if (base.back() == '/') - { - baseUrls.push_back(base); - } - else - { - auto i = base.find_last_of('/'); - if (i != string::npos) - baseUrls.push_back(base.substr(0, i + 1)); - else - baseUrls.push_back(base + "/"); - } - } - } - } - } - - return baseUrls; -} - /** * @brief Get Dash MPD Segment Template * @param fromChildren Flag to get from children @@ -301,19 +211,6 @@ std::string DashMPDRepresentation::getBaseUrl() { return baseurl; } -/** - * @brief Get Base URL from Parent - * @retval Base URL - */ -std::vector DashMPDRepresentation::getBaseUrls() { - vector baseUrls; - auto parent = this->parent.lock(); - if(parent) { - baseUrls = findAllBaseUrls(elem, parent->getBaseUrls()); - } - return baseUrls; -} - /** * @brief Get Bandwidth from "bandwidth" element attribute * @retval bandwidth @@ -447,29 +344,6 @@ std::string DashMPDAdaptationSet::getInitUrl() return initUrl; } -/** - * @brief Get mediaType by checking contentType, then mimeType, then representations - * @retval mediaType string - */ -std::string DashMPDAdaptationSet::getMediaType() { - std::string mediaType = getContentType(); - if (!mediaType.empty() && mediaType != MPD_UNSET_STRING) { - return mediaType; - } - mediaType = getMimeType(); - if (!mediaType.empty() && mediaType != MPD_UNSET_STRING) { - return mediaType; - } - auto reps = getRepresentations(); - for (const auto& rep : reps) { - mediaType = rep->getMimeType(); - if (!mediaType.empty() && mediaType != MPD_UNSET_STRING) { - return mediaType; - } - } - return ""; -} - /** * @brief Validates current Segment Template with Dash MPD Adaptation Set * @param adaptationSet Dash MPD Adaptation Set @@ -669,18 +543,6 @@ std::string DashMPDRoot::getBaseUrlValue() { return findBaseUrl(elem, location.str()); } - -/** - * @brief Gets Base URL - * @retval Base URLs - */ -std::vector DashMPDRoot::getAllBaseUrls() { - Url location = getLocation(); - location = location.parent(); - vector baseUrls = {location.str()}; - return findAllBaseUrls(elem, baseUrls); -} - /** * @brief Gets Availability Start Time * @retval AvailabilityStartTime in seconds @@ -994,19 +856,6 @@ std::string DashMPDPeriod::getBaseUrl() { return baseUrl; } -/** - * @brief Get Base URL from Parent - * @retval Base URL - */ -std::vector DashMPDPeriod::getBaseUrls() { - vector baseUrls; - auto parent = this->parent.lock(); - if(parent) { - baseUrls = findAllBaseUrls(elem, parent->getAllBaseUrls()); - } - return baseUrls; -} - /** * @brief Get Dash MPD SegmentTemplate * @retval Dash MPD SegmentTemplate @@ -1829,19 +1678,6 @@ std::string DashMPDAdaptationSet::getBaseUrl() { return baseUrl; } -/** - * @brief Get Base URL from Parent - * @retval Base URL - */ -std::vector DashMPDAdaptationSet::getBaseUrls() { - vector baseUrls; - auto parent = this->parent.lock(); - if(parent) { - baseUrls = findAllBaseUrls(elem, parent->getBaseUrls()); - } - return baseUrls; -} - /** * @brief Get SegmentTemplate diff --git a/dash/mpd/MPDModel.h b/dash/mpd/MPDModel.h index f81619e77..4b6bf7661 100644 --- a/dash/mpd/MPDModel.h +++ b/dash/mpd/MPDModel.h @@ -33,8 +33,8 @@ * @brief */ -#ifndef AAMP_DASHMODEL_H -#define AAMP_DASHMODEL_H +#ifndef FOG_CLI_DASHMODEL_H +#define FOG_CLI_DASHMODEL_H #include #include @@ -360,8 +360,6 @@ class DashMPDRoot : public DashMPDElement { std::string getBaseUrlValue(); - std::vector getAllBaseUrls(); - shared_ptr setBaseURLValue(const string &value); /** @@ -766,8 +764,6 @@ class DashMPDPeriod : public DashMPDElement { std::string getBaseUrl(); - std::vector getBaseUrls(); - std::shared_ptr getSegmentTemplate(); std::vector> getAdaptationSets(); @@ -914,12 +910,8 @@ class DashMPDAdaptationSet : public DashMPDElement { std::string getBaseUrl(); - std::vector getBaseUrls(); - string getMimeType(); - string getMediaType(); - string getLanguage(); std::shared_ptr newSegmentTemplate(); @@ -1042,8 +1034,6 @@ class DashMPDRepresentation : public DashMPDElement { std::string getBaseUrl(); - std::vector getBaseUrls(); - /** * @brief set BaseURL * @param Base URL @@ -1227,10 +1217,6 @@ struct TimelineItem { std::string findBaseUrl(DomElement &element, const std::string ¤t, bool isFile = false); -std::vector findAllBaseUrls(DomElement &element, const std::vector ¤t, bool isFile = false); - -std::vector extractBaseUrlTexts(DomElement &element); - void extractTimeline(DashMPDSegmentTimeline &timeline, std::vector &timelineItems); -#endif //AAMP_DASHMODEL_H +#endif //FOG_CLI_DASHMODEL_H diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index d4897a6c5..fcb3cef97 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -52,7 +52,6 @@ #include "AampMPDUtils.h" #include #include "AampTSBSessionManager.h" -#include "MediaSegmentDownloadJob.hpp" //#define DEBUG_TIMELINE #include "PlayerCCManager.h" @@ -151,10 +150,12 @@ StreamAbstractionAAMP_MPD::StreamAbstractionAAMP_MPD(class PrivateInstanceAAMP * ,mIsFcsRepresentation(false) ,mFcsRepresentationId(-1) ,mFcsSegments() + ,isVidDiscInitFragFail(false) ,abortTsbReader(false) ,mShortAdOffsetCalc(false) ,mNextPts(0.0) ,mPrevFirstPeriodStart(0.0f) + ,mTrackWorkers() ,mAudioSurplus(0) ,mVideoSurplus(0) ,mIsSegmentTimelineEnabled(false) @@ -543,6 +544,290 @@ static AampMediaType MediaTypeToPlaylist( AampMediaType mediaType ) } } + +/** + * @brief read unsigned multi-byte value and update buffer pointer + * @param[in] pptr buffer + * @param[in] n word size in bytes + * @retval 32 bit value + */ +static uint64_t ReadWordHelper( const char **pptr, int n ) +{ + const char *ptr = *pptr; + uint64_t rc = 0; + while( n-- ) + { + rc <<= 8; + rc |= (unsigned char)*ptr++; + } + *pptr = ptr; + return rc; +} + +static unsigned int Read16( const char **pptr) +{ + return (unsigned int)ReadWordHelper(pptr,2); +} +static unsigned int Read32( const char **pptr) +{ + return (unsigned int)ReadWordHelper(pptr,4); +} +static uint64_t Read64( const char **pptr) +{ + return ReadWordHelper(pptr,8); +} + +/** + * @brief Parse segment index box + * @note The SegmentBase indexRange attribute points to Segment Index Box location with segments and random access points. + * @param start start of box + * @param size size of box + * @param segmentIndex segment index + * @param[out] referenced_size referenced size + * @param[out] referenced_duration referenced duration + * @retval true on success + */ +static bool ParseSegmentIndexBox( const char *start, size_t size, int segmentIndex, unsigned int *referenced_size, float *referenced_duration, unsigned int *firstOffset) +{ + if (!start) + { + // If the fragment pointer is NULL then return from here, no need to process it further. + return false; + } + + const char **f = &start; + + unsigned int len = Read32(f); + if (len != size) + { + AAMPLOG_WARN("Wrong size in ParseSegmentIndexBox %d found, %zu expected", len, size); + if (firstOffset) *firstOffset = 0; + return false; + } + + unsigned int type = Read32(f); + if (type != 'sidx') + { + AAMPLOG_WARN("Wrong type in ParseSegmentIndexBox %c%c%c%c found, %zu expected", + (type >> 24) % 0xff, (type >> 16) & 0xff, (type >> 8) & 0xff, type & 0xff, size); + if (firstOffset) *firstOffset = 0; + return false; + } + + unsigned int version = Read32(f); (void) version; + unsigned int reference_ID = Read32(f); (void)reference_ID; + unsigned int timescale = Read32(f); + uint64_t earliest_presentation_time; + uint64_t first_offset; + if( version==0 ) + { + earliest_presentation_time = Read32(f); + (void)earliest_presentation_time; // unused + first_offset = Read32(f); + } + else + { + earliest_presentation_time = Read64(f); + (void)earliest_presentation_time; // unused + first_offset = Read64(f); + } + unsigned int reserved = Read16(f); (void)reserved; + unsigned int reference_count = Read16(f); + if (firstOffset) + { + *firstOffset = (unsigned int)first_offset; + return true; + } + if( segmentIndex 0) + { + std::string format = str.substr(pos + tokenLength + 1, formatLen-1); + char type = str[pos+tokenLength+formatLen]; + switch( type ) + { // don't use the number-formatting string from dash manifest as-is; map to uint64_t equivalent + case 'd': + format += PRIu64; + break; + case 'x': + format += PRIx64; + break; + case 'X': + format += PRIX64; + break; + default: + AAMPLOG_WARN( "unsupported template format: %s%c", format.c_str(), type ); + format += type; + break; + } + + snprintf(buf, sizeof(buf), format.c_str(), toNumber); + tokenLength += formatLen; + } + else + { + snprintf(buf, sizeof(buf), "%" PRIu64 "", toNumber); + } + str.replace(pos, tokenLength + 2, buf); + done = false; + rc++; + break; + } + pos = next + 1; + } + else + { + AAMPLOG_WARN("next is not found "); //CID:81252 - checked return + break; + } + } + if (done) break; + } + + return rc; +} + + +/** + * @brief Replace matching token with given string + * @param str String in which operation to be performed + * @param from token + * @param toString string to replace token + * @retval position + */ +static int replace(std::string& str, const std::string& from, const std::string& toString ) +{ + int rc = 0; + size_t tokenLength = from.length(); + + for (;;) + { + bool done = true; + size_t pos = 0; + for (;;) + { + pos = str.find('$', pos); + if (pos == std::string::npos) + { + break; + } + size_t next = str.find('$', pos + 1); + if(next != 0) + { + if (str.substr(pos + 1, tokenLength) == from) + { + str.replace(pos, tokenLength + 2, toString); + done = false; + rc++; + break; + } + pos = next + 1; + } + else + { + AAMPLOG_ERR("Error at next"); //CID:81346 - checked return + break; + } + } + + if (done) break; + } + + return rc; +} + + +/** + * @brief Generates fragment url from media information + */ +void StreamAbstractionAAMP_MPD::ConstructFragmentURL( std::string& fragmentUrl, const FragmentDescriptor *fragmentDescriptor, std::string media) +{ + std::string constructedUri = fragmentDescriptor->GetMatchingBaseUrl(); + if( media.empty() ) + { + } + else if( aamp_IsAbsoluteURL(media) ) + { // don't pre-pend baseurl if media starts with http:// or https:// + constructedUri.clear(); + } + else if (!constructedUri.empty()) + { + if(ISCONFIGSET(eAAMPConfig_DASHIgnoreBaseURLIfSlash)) + { + if (constructedUri == "/") + { + AAMPLOG_WARN("ignoring baseurl /"); + constructedUri.clear(); + } + } + // append '/' suffix to BaseURL if not already present + if( aamp_IsAbsoluteURL(constructedUri) ) + { + if( constructedUri.back() != '/' ) + { + constructedUri += '/'; + } + } + } + else + { + AAMPLOG_TRACE("BaseURL not available"); + } + constructedUri += media; + replace(constructedUri, "Bandwidth", fragmentDescriptor->Bandwidth); + replace(constructedUri, "RepresentationID", fragmentDescriptor->RepresentationID); + replace(constructedUri, "Number", fragmentDescriptor->Number); + replace(constructedUri, "Time", (uint64_t)fragmentDescriptor->Time ); + aamp_ResolveURL(fragmentUrl, fragmentDescriptor->manifestUrl, constructedUri.c_str(),ISCONFIGSET(eAAMPConfig_PropagateURIParam)); +} + /** * @brief Gets a curlInstance index for a given AampMediaType * @param type the stream AampMediaType @@ -584,123 +869,133 @@ static void deIndexTileInfo(std::vector &indexedTileInfo) /** * @brief Fetch and cache a fragment * - * @param[in] pMediaStreamContext Media stream context - * @param[in] mediaType Type of media - * @param[in] fragmentDuration Duration of fragment - * @param[in] isInitializationSegment True if fragment is initialization segment - * @param[in] curlInstance Instance of curl to be used to fetch - * @param[in] fcsContent True if fragment is FailOver Content Segment - * @param[in] discontinuity True if fragment is discontinuous - * @param[in] pto Presentation time offset - * @param[in] timeScale Time scale - * @param[in] range Range of fragment - * * @retval true on fetch success */ -bool StreamAbstractionAAMP_MPD::FetchFragment(MediaStreamContext *pMediaStreamContext, std::string media, double fragmentDuration, bool isInitializationSegment, unsigned int curlInstance, bool fcsContent, bool discontinuity, double pto, uint32_t timeScale, std::string range) +bool StreamAbstractionAAMP_MPD::FetchFragment(MediaStreamContext *pMediaStreamContext, std::string media, double fragmentDuration, bool isInitializationSegment, unsigned int curlInstance, bool discontinuity, double pto , uint32_t timeScale ) { // given url, synchronously download and transmit associated fragment bool retval = true; - - URLBitrateMap uriList; - if(pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO) - { - GenerateFragmentURLList(uriList, pMediaStreamContext, isInitializationSegment); - } - - // If uriList is empty, it means we are not able to generate the URL list, - // so we need to construct the URL for the fragment descriptor - // This is applicable for audio and subtitle tracks - // And for audio only playback, video track slot is used, thus uriList will be empty - if(uriList.empty()) - { - std::string fragmentUrl; - ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, std::move(media), aamp->mConfig); - uriList[pMediaStreamContext->fragmentDescriptor.Bandwidth] = fragmentUrl; - } - AampMediaType actualType = (AampMediaType)(isInitializationSegment?(eMEDIATYPE_INIT_VIDEO+pMediaStreamContext->type):pMediaStreamContext->type); - // Log the fragment URLs submission for debugging purpose - AAMPLOG_TRACE("JobRequestEnd: %d,%d,", isInitializationSegment? 2:0, actualType); - for (const auto& url : uriList) - { - // All the possible URLs for the fragment are logged with trace level - AAMPLOG_TRACE("[%" PRIu32 "] : %s,",url.first, url.second.url.c_str()); - } - - AampTicks ticks(pMediaStreamContext->fragmentDescriptor.Time, pMediaStreamContext->fragmentDescriptor.TimeScale); - double scaledPts = AampTime(ticks).inSeconds(); - DownloadInfoPtr downloadInfo = std::make_shared( - static_cast(pMediaStreamContext->type), - static_cast(curlInstance), - static_cast(pMediaStreamContext->fragmentTime), - fragmentDuration, - range, - pMediaStreamContext->fragmentOffset, - pMediaStreamContext->fragmentIndex, - isInitializationSegment, - discontinuity, - (mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING), - fcsContent, - scaledPts, - pMediaStreamContext->fragmentDescriptor.Number, - pMediaStreamContext->fragmentDescriptor.TimeScale, - pMediaStreamContext->fragmentDescriptor.Bandwidth, - mPTSOffset, - uriList); - - // Wrap the lambda in a JobWrapper - auto downloadJob = std::make_shared(downloadInfo, [this, pMediaStreamContext, downloadInfo]() { - bool status = pMediaStreamContext->DownloadFragment(downloadInfo); - this->OnFragmentDownloadComplete(status, downloadInfo); - }); - - if (ISCONFIGSET(eAAMPConfig_DashParallelFragDownload)) - { - auto future = aamp->GetAampTrackWorkerManager()->SubmitJob(downloadInfo->mediaType, downloadJob, (isInitializationSegment && pMediaStreamContext->profileChanged)); - if (future.valid()) - { - AAMPLOG_DEBUG("Submitted download job for fragment: %s", downloadInfo->uriList.begin()->second.url.c_str()); + std::string fragmentUrl; + ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, std::move(media)); + double position = ((double) pMediaStreamContext->fragmentDescriptor.Time) / ((double) pMediaStreamContext->fragmentDescriptor.TimeScale); + if(isInitializationSegment) + { + if(!(pMediaStreamContext->initialization.empty()) && (0 == pMediaStreamContext->initialization.compare(fragmentUrl))&& !discontinuity) + { + AAMPLOG_TRACE("We have pushed the same initialization segment for %s skipping", GetMediaTypeName(AampMediaType(pMediaStreamContext->type))); + return retval; } else { - AAMPLOG_ERR("Failed to submit download job for fragment: %s", downloadInfo->uriList.begin()->second.url.c_str()); - retval = false; + pMediaStreamContext->initialization = std::string(fragmentUrl); } } - else - { - downloadJob->Execute(); - AAMPLOG_DEBUG("Executed download job for fragment: %s", downloadInfo->uriList.begin()->second.url.c_str()); - retval = true; - } - auto timeBasedBufferManager = pMediaStreamContext->GetTimeBasedBufferManager(); - if (timeBasedBufferManager) - { - timeBasedBufferManager->PopulateBuffer(fragmentDuration); - } + bool fragmentCached = pMediaStreamContext->CacheFragment(fragmentUrl, curlInstance, position, fragmentDuration, NULL, isInitializationSegment, discontinuity + ,(mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING), pto, timeScale); + // Check if we have downloaded the fragment and waiting for init fragment download on + // bitrate switching before caching it. + bool fragmentSaved = (NULL != pMediaStreamContext->mDownloadedFragment.GetPtr() ); - if (mPlayRate > AAMP_RATE_PAUSE) + if (!fragmentCached) { - pMediaStreamContext->fragmentTime += fragmentDuration; - if (pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO) + if(!fragmentSaved) + { + AAMPLOG_WARN("StreamAbstractionAAMP_MPD: failed. fragmentUrl %s fragmentTime %f %d %d", fragmentUrl.c_str(), pMediaStreamContext->fragmentTime,isInitializationSegment, pMediaStreamContext->type); + //Added new check to avoid marking ad as failed if the http code is not worthy. + if (isInitializationSegment && mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING && + (pMediaStreamContext->httpErrorCode!=CURLE_WRITE_ERROR && pMediaStreamContext->httpErrorCode!= CURLE_ABORTED_BY_CALLBACK)) + { + AAMPLOG_WARN("StreamAbstractionAAMP_MPD: [CDAI] Ad init fragment not available. Playback failed."); + mCdaiObject->mAdBreaks[mBasePeriodId].mAdFailed = true; + } + } + if (discontinuity && isInitializationSegment) { - mBasePeriodOffset += fragmentDuration; + if(eTRACK_VIDEO == pMediaStreamContext->type) + { + isVidDiscInitFragFail = true; + AAMPLOG_WARN("StreamAbstractionAAMP_MPD: failed. isInit: %d IsTrackVideo: %s isDisc: %d vidInitFail: %d", + isInitializationSegment, GetMediaTypeName(AampMediaType(pMediaStreamContext->type) ), isInitializationSegment, isVidDiscInitFragFail); + } + if(mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING) + { + // Insert a dummy fragment with discontinuity, since we didn't get an init fragments so it wouldn't get flagged + CachedFragment* cachedFragment = nullptr; + if(pMediaStreamContext->IsInjectionFromCachedFragmentChunks()) + { + if(pMediaStreamContext->WaitForCachedFragmentChunkInjected()) + { + cachedFragment = pMediaStreamContext->GetFetchChunkBuffer(true); + } + } + else + { + if(pMediaStreamContext->WaitForFreeFragmentAvailable()) + { + cachedFragment = pMediaStreamContext->GetFetchBuffer(true); + } + } + if(cachedFragment && !(aamp->GetTSBSessionManager() && pMediaStreamContext->IsLocalTSBInjection())) + { + // The pointer is loaded to bypass null check in InjectFragment thread + cachedFragment->fragment.AppendBytes("0x0a", 2); + cachedFragment->position=0; + cachedFragment->duration=0; + cachedFragment->initFragment=true; + cachedFragment->discontinuity=true; + cachedFragment->profileIndex=0; + cachedFragment->isDummy=true; + cachedFragment->type=pMediaStreamContext->mediaType; + if(pMediaStreamContext->IsInjectionFromCachedFragmentChunks()) + { + pMediaStreamContext->UpdateTSAfterChunkFetch(); + } + else + { + pMediaStreamContext->UpdateTSAfterFetch(true); + } + } + } } + retval = false; } - else + + if (discontinuity && (isInitializationSegment && eTRACK_VIDEO == pMediaStreamContext->type ) && (retval && isVidDiscInitFragFail)) +{ + isVidDiscInitFragFail = false; + AAMPLOG_WARN("StreamAbstractionAAMP_MPD: rampdown init download success. isInit: %d IsTrackVideo: %s isDisc: %d vidInitFail: %d", + isInitializationSegment, GetMediaTypeName(AampMediaType(pMediaStreamContext->type) ), isInitializationSegment, isVidDiscInitFragFail); + } + + /**In the case of ramp down same fragment will be retried + *Avoid fragmentTime update in such scenarios. + *In other cases if it's success or failure, AAMP will be going + *For next fragment so update fragmentTime with fragment duration + */ + if (!pMediaStreamContext->mCheckForRampdown && !fragmentSaved) { - pMediaStreamContext->fragmentTime -= fragmentDuration; - if (pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO) + if(mPlayRate > AAMP_RATE_PAUSE) { - mBasePeriodOffset -= fragmentDuration; + pMediaStreamContext->fragmentTime += fragmentDuration; + if(pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO) + { + mBasePeriodOffset += fragmentDuration; + } } - if (pMediaStreamContext->fragmentTime < 0) + else { - pMediaStreamContext->fragmentTime = 0; + pMediaStreamContext->fragmentTime -= fragmentDuration; + if(pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO) + { + mBasePeriodOffset -= fragmentDuration; + } + if(pMediaStreamContext->fragmentTime < 0) + { + pMediaStreamContext->fragmentTime = 0; + } } } return retval; } - /* * @brief Use lastSegmentTime to find position in segment timeline after manifest update * @@ -811,7 +1106,6 @@ uint64_t StreamAbstractionAAMP_MPD::FindPositionInTimeline(class MediaStreamCont #endif return startTime; } - /** * @brief Fetch and push next fragment * @param pMediaStreamContext Track object @@ -1167,7 +1461,8 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed firstStartTime, tScale, presentationTimeOffset, positionInPeriod, firstSegStartTime, endTime, mPeriodStartTime, mPeriodDuration); } - if((mIsFogTSB || + if(!fcsContent && + (mIsFogTSB || ((0 != mPeriodDuration) && (((firstSegStartTime + positionInPeriod) < endTime) || liveEdgePeriodPlayback || mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING)))) //For split period ads, the position in the period doesn't need to be between the period's start and end { @@ -1181,13 +1476,35 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed { setNextobjectrequestUrl(media,&pMediaStreamContext->fragmentDescriptor,AampMediaType(pMediaStreamContext->type)); } - retval = FetchFragment( pMediaStreamContext, std::move(media), fragmentDuration, false, curlInstance, fcsContent); + retval = FetchFragment( pMediaStreamContext, std::move(media), fragmentDuration, false, curlInstance); } else { AAMPLOG_WARN("Type[%d] Skipping Fetchfragment, Number(%" PRIu64 ") fragment beyond duration. fragmentPosition: %lf starttime:%lf periodEndTime : %lf ", pMediaStreamContext->type , pMediaStreamContext->fragmentDescriptor.Number, positionInPeriod , firstSegStartTime, endTime); } + if(fcsContent) + { + int http_code = 404; + retval = false; + if (pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO) + { + // Attempt rampdown + if (CheckForRampDownProfile(http_code)) + { + AAMPLOG_WARN("RampDownProfile Due to failover Content %" PRIu64 " Number %lf FDT",pMediaStreamContext->fragmentDescriptor.Number,pMediaStreamContext->fragmentDescriptor.Time); + pMediaStreamContext->mCheckForRampdown = true; + // Rampdown attempt success, download same segment from lower profile. + pMediaStreamContext->mSkipSegmentOnError = false; + return retval; + } + else + { + AAMPLOG_WARN("Already at the lowest profile, skipping segment due to failover"); + mRampDownCount = 0; + } + } + } if (retval) { @@ -1204,6 +1521,24 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed pMediaStreamContext->lastSegmentDuration = pMediaStreamContext->fragmentDescriptor.Time + duration; pMediaStreamContext->lastSegmentNumber = pMediaStreamContext->fragmentDescriptor.Number; } + + // pMediaStreamContext->lastDownloadedPosition is introduced to calculate the buffered duration value. + // Update position in period after fragment download + pMediaStreamContext->lastDownloadedPosition = pMediaStreamContext->fragmentTime; + AAMPLOG_INFO("[%s] lastDownloadedPosition %lfs fragmentTime %lfs fragmentDuration %fs", + GetMediaTypeName(pMediaStreamContext->mediaType), + pMediaStreamContext->lastDownloadedPosition.load(), + pMediaStreamContext->fragmentTime, + fragmentDuration); + } + else if((mIsFogTSB && !mAdPlayingFromCDN) && pMediaStreamContext->mDownloadedFragment.GetPtr() ) + { + pMediaStreamContext->profileChanged = true; + profileIdxForBandwidthNotification = GetProfileIdxForBandwidthNotification(pMediaStreamContext->fragmentDescriptor.Bandwidth); + FetchAndInjectInitialization(eMEDIATYPE_VIDEO); + UpdateRampUpOrDownProfileReason(); + pMediaStreamContext->SetCurrentBandWidth(pMediaStreamContext->fragmentDescriptor.Bandwidth); + return false; } else if( pMediaStreamContext->mCheckForRampdown && pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO) { @@ -1246,6 +1581,15 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed setNextobjectrequestUrl(media,&pMediaStreamContext->fragmentDescriptor,AampMediaType(pMediaStreamContext->type)); } retval = FetchFragment( pMediaStreamContext, std::move(media), fragmentDuration, false, curlInstance); + if (!retval && ((mIsFogTSB && !mAdPlayingFromCDN) && pMediaStreamContext->mDownloadedFragment.GetPtr() )) + { + pMediaStreamContext->profileChanged = true; + profileIdxForBandwidthNotification = GetProfileIdxForBandwidthNotification(pMediaStreamContext->fragmentDescriptor.Bandwidth); + FetchAndInjectInitialization(eMEDIATYPE_VIDEO); + UpdateRampUpOrDownProfileReason(); + pMediaStreamContext->SetCurrentBandWidth(pMediaStreamContext->fragmentDescriptor.Bandwidth); + return false; + } } else if(pMediaStreamContext->mediaType == eMEDIATYPE_VIDEO && ((pMediaStreamContext->lastSegmentTime - pMediaStreamContext->fragmentDescriptor.Time) > TIMELINE_START_RESET_DIFF)) @@ -1526,8 +1870,19 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed { setNextobjectrequestUrl(media,&pMediaStreamContext->fragmentDescriptor,AampMediaType(pMediaStreamContext->type)); } - retval = FetchFragment(pMediaStreamContext, std::move(media), fragmentDuration, false, curlInstance, false, false, pto, scale); + retval = FetchFragment(pMediaStreamContext, std::move(media), fragmentDuration, false, curlInstance, false, pto, scale); string startTimeStringValue = mpd->GetPeriods().at(mCurrentPeriodIdx)->GetStart(); + pMediaStreamContext->lastDownloadedPosition = pMediaStreamContext->fragmentTime; + AAMPLOG_INFO("[%s] lastDownloadedPosition %lfs fragmentTime %lfs", + GetMediaTypeName(pMediaStreamContext->mediaType), + pMediaStreamContext->lastDownloadedPosition.load(), + pMediaStreamContext->fragmentTime); + if( pMediaStreamContext->mCheckForRampdown ) + { + /* NOTE : This case needs to be validated with the segmentTimeline not available stream */ + return retval; + + } if(!pMediaStreamContext->freshManifest) { @@ -1578,7 +1933,7 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed if (segmentBase) { // single-segment std::string fragmentUrl; - ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, "", aamp->mConfig); + ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, ""); if (!pMediaStreamContext->IDX.GetPtr() ) { // lazily load index std::string range = segmentBase->GetIndexRange(); @@ -1592,10 +1947,7 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed double downloadTime; int iFogError = -1; int iCurrentRate = aamp->rate; // Store it as back up, As sometimes by the time File is downloaded, rate might have changed due to user initiated Trick-Play - AampCurlInstance curlInst = aamp->GetPlaylistCurlInstance(actualType, false); - aamp->CurlInit(curlInst, 1, aamp->GetNetworkProxy()); - aamp->LoadIDX(bucketType, fragmentUrl, effectiveUrl,&pMediaStreamContext->IDX, curlInst, range.c_str(),&http_code, &downloadTime, actualType,&iFogError); - aamp->CurlTerm(curlInst); + aamp->LoadIDX(bucketType, fragmentUrl, effectiveUrl,&pMediaStreamContext->IDX, curlInstance, range.c_str(),&http_code, &downloadTime, actualType,&iFogError); if (iCurrentRate != AAMP_NORMAL_PLAY_RATE) @@ -1681,13 +2033,19 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed snprintf(nextrange, sizeof(nextrange), "%" PRIu64 "-%" PRIu64 "",nextfragmentOffset, nextfragmentOffset+nextReferencedSize - 1); setNextRangeRequest(fragmentUrl,nextrange,(&pMediaStreamContext->fragmentDescriptor)->Bandwidth,AampMediaType(pMediaStreamContext->type)); } - - if(FetchFragment(pMediaStreamContext, std::move(fragmentUrl), fragmentDuration, false, curlInstance, false, false, 0.0, pMediaStreamContext->fragmentDescriptor.TimeScale, range)) + if(pMediaStreamContext->CacheFragment(std::move(fragmentUrl), curlInstance, pMediaStreamContext->fragmentTime, fragmentDuration, range )) { pMediaStreamContext->fragmentTime += fragmentDuration; pMediaStreamContext->fragmentOffset += referenced_size; retval = true; } + // pMediaStreamContext->lastDownloadedPosition is introduced to calculate the buffered duration value for SegmentBase contents. + //Absolute position reporting + pMediaStreamContext->lastDownloadedPosition = pMediaStreamContext->fragmentTime; + AAMPLOG_INFO("[%s] lastDownloadedPosition %lfs fragmentTime %lfs", + GetMediaTypeName(pMediaStreamContext->mediaType), + pMediaStreamContext->lastDownloadedPosition.load(), + pMediaStreamContext->fragmentTime); } else { // done with index @@ -1730,14 +2088,14 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed if(rawAttributes.find("customlist") == rawAttributes.end()) //"CheckForFogSegmentList") { std::string fragmentUrl; - ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, segmentURL->GetMediaURI(), aamp->mConfig); + ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, segmentURL->GetMediaURI()); AAMPLOG_INFO("%s [%s]", GetMediaTypeName(pMediaStreamContext->mediaType), segmentURL->GetMediaRange().c_str()); if(nextsegmentURL != NULL && (mIsFogTSB != true)) { setNextobjectrequestUrl(nextsegmentURL->GetMediaURI(),&pMediaStreamContext->fragmentDescriptor,AampMediaType(pMediaStreamContext->type)); } double fragmentDurationS = ComputeFragmentDuration(segmentList->GetDuration(), segmentList->GetTimescale()); - if(FetchFragment(pMediaStreamContext, fragmentUrl, fragmentDurationS, false, curlInstance, false, false, 0.0, segmentList->GetTimescale(), segmentURL->GetMediaRange().c_str())) + if( pMediaStreamContext->CacheFragment(fragmentUrl, curlInstance, pMediaStreamContext->fragmentTime, fragmentDurationS, segmentURL->GetMediaRange().c_str() ) ) { pMediaStreamContext->fragmentTime += fragmentDurationS; } @@ -1862,6 +2220,7 @@ bool StreamAbstractionAAMP_MPD::PushNextFragment( class MediaStreamContext *pMed pMediaStreamContext->fragmentIndex--; pMediaStreamContext->nextfragmentIndex = pMediaStreamContext->fragmentIndex-1; } + } else { @@ -2408,13 +2767,11 @@ double StreamAbstractionAAMP_MPD::SkipFragments( MediaStreamContext *pMediaStrea ISegmentBase *segmentBase = pMediaStreamContext->representation->GetSegmentBase(); if (segmentBase) { // single-segment - // Disable parallel fragment download for segment base streams as there is a sidx box dependency for live contents - SETCONFIGVALUE(AAMP_STREAM_SETTING, eAAMPConfig_DashParallelFragDownload, false); std::string range = segmentBase->GetIndexRange(); if (!pMediaStreamContext->IDX.GetPtr() ) { // lazily load index std::string fragmentUrl; - ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, "", aamp->mConfig); + ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, ""); //update the next segment for download @@ -2424,10 +2781,7 @@ double StreamAbstractionAAMP_MPD::SkipFragments( MediaStreamContext *pMediaStrea int http_code; double downloadTime; int iFogError = -1; - AampCurlInstance curlInstance = aamp->GetPlaylistCurlInstance(actualType, false); - aamp->CurlInit(curlInstance, 1, aamp->GetNetworkProxy()); - aamp->LoadIDX(bucketType, std::move(fragmentUrl), effectiveUrl, &pMediaStreamContext->IDX, curlInstance, range.c_str(),&http_code, &downloadTime, actualType,&iFogError); - aamp->CurlTerm(curlInstance); + aamp->LoadIDX(bucketType, std::move(fragmentUrl), effectiveUrl, &pMediaStreamContext->IDX, pMediaStreamContext->mediaType, range.c_str(),&http_code, &downloadTime, actualType,&iFogError); } if (pMediaStreamContext->IDX.GetPtr() ) { @@ -3252,10 +3606,8 @@ AAMPStatusType StreamAbstractionAAMP_MPD::Init(TuneType tuneType) } durationMs = mMPDParseHelper->GetMediaPresentationDuration(); - if(durationMs != 0) - { - mpdDurationAvailable = true; - } + mpdDurationAvailable = true; + AAMPLOG_MIL("StreamAbstractionAAMP_MPD: MPD duration val %" PRIu64 " seconds", durationMs/1000); mIsLiveStream = mMPDParseHelper->IsLiveManifest(); aamp->SetIsLive(mIsLiveStream); @@ -3683,7 +4035,7 @@ AAMPStatusType StreamAbstractionAAMP_MPD::Init(TuneType tuneType) if(mCurrentPeriod != NULL) { mBasePeriodId = mCurrentPeriod->GetId(); - mIsSegmentTimelineEnabled = mMPDParseHelper->aamp_HasSegmentTime(mCurrentPeriod); + mIsSegmentTimelineEnabled = mMPDParseHelper->aamp_HasSegmentTimeline(mCurrentPeriod); } else { @@ -3894,7 +4246,6 @@ AAMPStatusType StreamAbstractionAAMP_MPD::Init(TuneType tuneType) AAMPLOG_ERR("StreamAbstractionAAMP_MPD: corrupt/invalid manifest"); retval = eAAMPSTATUS_MANIFEST_PARSE_ERROR; } - if (ret == eAAMPSTATUS_OK) { //CheckForInitalClearPeriod() check if the current period is clear or encrypted @@ -5176,6 +5527,28 @@ void StreamAbstractionAAMP_MPD::ProcessTrickModeRestriction(Node* node, const st } } + +/** + * @brief Fragment downloader thread + * @param arg HeaderFetchParams pointer + */ +void StreamAbstractionAAMP_MPD::TrackDownloader(int trackIdx, std::string initialization) +{ + UsingPlayerId playerId(aamp->mPlayerId); + double fragmentDuration = 0.0; + class MediaStreamContext *pMediaStreamContext = mMediaStreamContext[trackIdx]; + + //Calling WaitForFreeFragmentAvailable timeout as 0 since waiting for one tracks + //init header fetch can slow down fragment downloads for other track + if(pMediaStreamContext->WaitForFreeFragmentAvailable(0)) + { + pMediaStreamContext->profileChanged = false; + FetchFragment(pMediaStreamContext, std::move(initialization), fragmentDuration, true, getCurlInstanceByMediaType(pMediaStreamContext->mediaType), //CurlContext 0=Video, 1=Audio) + pMediaStreamContext->discontinuity); + pMediaStreamContext->discontinuity = false; + } +} + /** * @brief Check if adaptation set is iframe track * @param adaptationSet Pointer to adaptationSet @@ -5989,16 +6362,9 @@ void StreamAbstractionAAMP_MPD::SwitchSubtitleTrack(bool newTune) } AbortWaitForAudioTrackCatchup(true); - /* Flush Subtitle Fragments from worker and cache*/ - aamp->GetAampTrackWorkerManager()->ResetWorker(eMEDIATYPE_SUBTITLE); pMediaStreamContext->LoadNewSubtitle(true); /* Flush Subtitle Fragments */ pMediaStreamContext->FlushFragments(); - auto timeBasedBuffer = pMediaStreamContext->GetTimeBasedBufferManager(); - if(timeBasedBuffer) - { - timeBasedBuffer->ClearBuffer(); - } if( pMediaStreamContext->freshManifest ) { /*In Live scenario, the manifest refresh got happened frequently,so in the UpdateTrackinfo(), all the params @@ -6637,15 +7003,9 @@ void StreamAbstractionAAMP_MPD::SwitchAudioTrack() pMediaStreamContext->NotifyCachedAudioFragmentAvailable(); return; } - /* Flush Audio Fragments from worker and cache*/ - aamp->GetAampTrackWorkerManager()->ResetWorker(eMEDIATYPE_AUDIO); pMediaStreamContext->LoadNewAudio(true); + /* Flush Audio Fragments */ pMediaStreamContext->FlushFragments(); - auto timeBasedBuffer = pMediaStreamContext->GetTimeBasedBufferManager(); - if(timeBasedBuffer) - { - timeBasedBuffer->ClearBuffer(); - } if( pMediaStreamContext->freshManifest ) { /*In Live scenario, the manifest refresh got happened frequently,so in the UpdateTrackinfo(), all the params @@ -7861,11 +8221,15 @@ double StreamAbstractionAAMP_MPD::GetCulledSeconds(std::vector &curr double newStartTimeSeconds = 0; double culled = 0; MediaStreamContext *pMediaStreamContext = mMediaStreamContext[eMEDIATYPE_VIDEO]; - if (pMediaStreamContext->adaptationSet || pMediaStreamContext->representation) + if (pMediaStreamContext->adaptationSet) { - if(mCurrentPeriod && mMPDParseHelper->aamp_HasSegmentTemplate(mCurrentPeriod)) + SegmentTemplates segmentTemplates(pMediaStreamContext->representation->GetSegmentTemplate(), + pMediaStreamContext->adaptationSet->GetSegmentTemplate()); + const ISegmentTimeline *segmentTimeline = NULL; + if(segmentTemplates.HasSegmentTemplate()) { - if (mCurrentPeriod && mMPDParseHelper->aamp_HasSegmentTime(mCurrentPeriod)) + segmentTimeline = segmentTemplates.GetSegmentTimeline(); + if (segmentTimeline) { int iter1 = 0; PeriodInfo currFirstPeriodInfo = GetFirstValidCurrMPDPeriod(currMPDPeriodDetails); @@ -7909,7 +8273,7 @@ double StreamAbstractionAAMP_MPD::GetCulledSeconds(std::vector &curr { AAMPLOG_INFO("StreamAbstractionAAMP_MPD: NULL segmentTimeline. Hence modifying culling logic based on MPD availabilityStartTime, periodStartTime, fragment number and current time"); double newStartSegment = 0; - std::shared_ptr firstSegTemplate = nullptr; + ISegmentTemplate *firstSegTemplate = NULL; int iter1 = 0; PeriodInfo currFirstPeriodInfo = currMPDPeriodDetails.at(0); @@ -7918,8 +8282,22 @@ double StreamAbstractionAAMP_MPD::GetCulledSeconds(std::vector &curr for (auto period : periods) { currMPDPeriodDetails.at(iter1).periodStartTime = mMPDParseHelper->GetPeriodStartTime(iter1,mLastPlaylistDownloadTimeMs); - firstSegTemplate = mMPDParseHelper->GetSegmentTemplateForVideo(period); - if(firstSegTemplate && firstSegTemplate->HasSegmentTemplate()) + auto adaptationSets = period->GetAdaptationSets(); + for(auto adaptation : adaptationSets) + { + auto segTemplate = adaptation->GetSegmentTemplate(); + if(!segTemplate && adaptation->GetRepresentation().size() > 0) + { + segTemplate = adaptation->GetRepresentation().at(0)->GetSegmentTemplate(); + } + + if(segTemplate) + { + firstSegTemplate = segTemplate; + break; + } + } + if(firstSegTemplate) { break; } @@ -7929,19 +8307,10 @@ double StreamAbstractionAAMP_MPD::GetCulledSeconds(std::vector &curr if(firstSegTemplate) { newStartSegment = (double)firstSegTemplate->GetStartNumber(); - if(firstSegTemplate->GetDuration() != 0) + if(segmentTemplates.GetTimescale() != 0) { - double fragmentDuration = 0.0; - uint32_t timescale = firstSegTemplate->GetTimescale(); - if (timescale != 0) - { - fragmentDuration = ((double)firstSegTemplate->GetDuration()) / timescale; - } - else - { - AAMPLOG_WARN("firstSegTemplate->GetTimescale() is zero, cannot compute fragmentDuration"); - } - if (mMPDParseHelper->GetLiveTimeFragmentSync() && fragmentDuration != 0) + double fragmentDuration = ((double)segmentTemplates.GetDuration()) / segmentTemplates.GetTimescale(); + if (mMPDParseHelper->GetLiveTimeFragmentSync()) { newStartSegment += (long)((mMPDParseHelper->GetPeriodStartTime(0,mLastPlaylistDownloadTimeMs) - mAvailabilityStartTime) / fragmentDuration); } @@ -8202,7 +8571,25 @@ void StreamAbstractionAAMP_MPD::FetchAndInjectInitFragments(bool discontinuity) { for( int i = 0; i < mNumberOfTracks; i++) { - FetchAndInjectInitialization(i,discontinuity); + if (i < mTrackWorkers.size() && ISCONFIGSET(eAAMPConfig_DashParallelFragDownload) && mTrackWorkers[i]) + { + // Download the video, audio & subtitle init fragments in a separate parallel thread. + AAMPLOG_DEBUG("Submitting init job for track %d", i); + mTrackWorkers[i]->SubmitJob([this, i, discontinuity]() { FetchAndInjectInitialization(i,discontinuity); }); + } + else + { + AAMPLOG_INFO("Track %d worker not available, downloading init fragment sequentially", i); + FetchAndInjectInitialization(i,discontinuity); + } + } + + for (int trackIdx = (mNumberOfTracks - 1); (ISCONFIGSET(eAAMPConfig_DashParallelFragDownload) && trackIdx >= 0); trackIdx--) + { + if(trackIdx < mTrackWorkers.size() && mTrackWorkers[trackIdx]) + { + mTrackWorkers[trackIdx]->WaitForCompletion(); + } } } @@ -8236,9 +8623,7 @@ void StreamAbstractionAAMP_MPD::FetchAndInjectInitialization(int trackIdx, bool setNextobjectrequestUrl(std::move(media), &pMediaStreamContext->fragmentDescriptor, AampMediaType(pMediaStreamContext->type)); } pMediaStreamContext->fragmentDescriptor.nextfragmentNum = pMediaStreamContext->fragmentDescriptor.Number+1; - FetchFragment(pMediaStreamContext, std::move(initialization), 0.0, true, getCurlInstanceByMediaType(pMediaStreamContext->mediaType), false, pMediaStreamContext->discontinuity); - pMediaStreamContext->discontinuity = false; - pMediaStreamContext->profileChanged = false; + TrackDownloader(trackIdx, std::move(initialization)); } else { @@ -8288,19 +8673,23 @@ void StreamAbstractionAAMP_MPD::FetchAndInjectInitialization(int trackIdx, bool } } std::string fragmentUrl; - ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, "", aamp->mConfig); + ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, ""); if(pMediaStreamContext->WaitForFreeFragmentAvailable(0)) { + pMediaStreamContext->profileChanged = false; if(!nextrange.empty()) { setNextRangeRequest(fragmentUrl, std::move(nextrange), (&pMediaStreamContext->fragmentDescriptor)->Bandwidth, AampMediaType(pMediaStreamContext->type)); } - if (!FetchFragment(pMediaStreamContext, fragmentUrl, 0.0, true, getCurlInstanceByMediaType(pMediaStreamContext->mediaType), false, pMediaStreamContext->discontinuity, 0, pMediaStreamContext->fragmentDescriptor.TimeScale, std::move(range))) + if(!pMediaStreamContext->CacheFragment(fragmentUrl, + getCurlInstanceByMediaType(pMediaStreamContext->mediaType), + pMediaStreamContext->fragmentTime, + 0, // duration - zero for init fragment + range.c_str(), true )) { AAMPLOG_TRACE("StreamAbstractionAAMP_MPD: did not cache fragmentUrl %s fragmentTime %f", fragmentUrl.c_str(), pMediaStreamContext->fragmentTime); } - pMediaStreamContext->profileChanged = false; } } else @@ -8322,26 +8711,19 @@ void StreamAbstractionAAMP_MPD::FetchAndInjectInitialization(int trackIdx, bool std::string initialization = urlType->GetSourceURL(); if (!initialization.empty()) { - /* - * This block is added to download the initialization tracks in parallel - * to reduce the tune time, especially when using DRM. - * Moving the fragment download of first AAMPTRACK to separate thread - */ - const std::vector segmentURLs = segmentList->GetSegmentURLs(); - ISegmentURL* nextsegmentURL = segmentURLs.at(pMediaStreamContext->fragmentIndex); - pMediaStreamContext->fragmentDescriptor.nextfragmentTime = pMediaStreamContext->fragmentDescriptor.Time; - if(nextsegmentURL != NULL && (mIsFogTSB != true)) - { - setNextobjectrequestUrl(nextsegmentURL->GetMediaURI(),&pMediaStreamContext->fragmentDescriptor,AampMediaType(pMediaStreamContext->type)); - } - FetchFragment(pMediaStreamContext, std::move(initialization), 0.0, true, getCurlInstanceByMediaType(pMediaStreamContext->mediaType), false, pMediaStreamContext->discontinuity); - pMediaStreamContext->discontinuity = false; - pMediaStreamContext->profileChanged = false; + const std::vector segmentURLs = segmentList->GetSegmentURLs(); + ISegmentURL* nextsegmentURL = segmentURLs.at(pMediaStreamContext->fragmentIndex); + pMediaStreamContext->fragmentDescriptor.nextfragmentTime = pMediaStreamContext->fragmentDescriptor.Time; + if(nextsegmentURL != NULL && (mIsFogTSB != true)) + { + setNextobjectrequestUrl(nextsegmentURL->GetMediaURI(),&pMediaStreamContext->fragmentDescriptor,AampMediaType(pMediaStreamContext->type)); + } + TrackDownloader(trackIdx, std::move(initialization)); } else { string range; - string nextrange; + string nextrange; #ifdef LIBDASH_SEGMENTLIST_GET_INIT_SUPPORT const ISegmentURL *segmentURL = NULL; segmentURL = segmentList->Getinitialization(); @@ -8386,22 +8768,27 @@ void StreamAbstractionAAMP_MPD::FetchAndInjectInitialization(int trackIdx, bool if (!range.empty()) { std::string fragmentUrl; - ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, "", aamp->mConfig); + ConstructFragmentURL(fragmentUrl, &pMediaStreamContext->fragmentDescriptor, ""); AAMPLOG_INFO("%s [%s]", GetMediaTypeName(pMediaStreamContext->mediaType), range.c_str()); if(pMediaStreamContext->WaitForFreeFragmentAvailable(0)) { + pMediaStreamContext->profileChanged = false; if(nextsegurl != NULL && (mIsFogTSB != true)) { setNextobjectrequestUrl(nextsegurl->GetMediaURI(),&pMediaStreamContext->fragmentDescriptor,AampMediaType(pMediaStreamContext->type)); } - if(!FetchFragment(pMediaStreamContext, fragmentUrl, 0.0, true, getCurlInstanceByMediaType(pMediaStreamContext->mediaType), false, pMediaStreamContext->discontinuity, 0, pMediaStreamContext->fragmentDescriptor.TimeScale, std::move(range))) + if(!pMediaStreamContext->CacheFragment(fragmentUrl, + getCurlInstanceByMediaType(pMediaStreamContext->mediaType), + pMediaStreamContext->fragmentTime, + 0.0, // duration - zero for init fragment + range.c_str(), + true )) { AAMPLOG_TRACE("StreamAbstractionAAMP_MPD: did not cache fragmentUrl %s fragmentTime %f", fragmentUrl.c_str(), pMediaStreamContext->fragmentTime); } - pMediaStreamContext->profileChanged = false; } } else @@ -8414,9 +8801,7 @@ void StreamAbstractionAAMP_MPD::FetchAndInjectInitialization(int trackIdx, bool { if( pMediaStreamContext->mediaType == eMEDIATYPE_SUBTITLE ) { - FetchFragment(pMediaStreamContext, "", 0.0, true, getCurlInstanceByMediaType(pMediaStreamContext->mediaType), false, pMediaStreamContext->discontinuity); - pMediaStreamContext->discontinuity = false; - pMediaStreamContext->profileChanged = false; + TrackDownloader(trackIdx,"");// BaseUrl used for WebVTT download } else { // note: this risks flooding logs, as will get called repeatedly @@ -8463,18 +8848,15 @@ bool StreamAbstractionAAMP_MPD::CheckForInitalClearPeriod() */ void StreamAbstractionAAMP_MPD::PushEncryptedHeaders(std::map& mappedHeaders) { - std::vector> futures; - for (std::map::iterator it = mappedHeaders.begin(); it != mappedHeaders.end(); ++it) + for (std::map::iterator it=mappedHeaders.begin(); it!=mappedHeaders.end(); ++it) { - if (ISCONFIGSET(eAAMPConfig_DashParallelFragDownload)) + if (it->first < mTrackWorkers.size() && ISCONFIGSET(eAAMPConfig_DashParallelFragDownload) && mTrackWorkers[it->first]) { // Download the video, audio & subtitle fragments in a separate parallel thread. AAMPLOG_DEBUG("Submitting job for init encrypted header track %d", it->first); auto track = it->first; auto header = it->second; - auto dashWorkerJob = std::make_shared([this, track, header]() { CacheEncryptedHeader(track, header); }); - auto future = aamp->GetAampTrackWorkerManager()->SubmitJob(static_cast(it->first), dashWorkerJob); - futures.push_back(std::move(future)); + mTrackWorkers[it->first]->SubmitJob([this, track, header]() { CacheEncryptedHeader(track, header); }); } else { @@ -8482,16 +8864,12 @@ void StreamAbstractionAAMP_MPD::PushEncryptedHeaders(std::map& CacheEncryptedHeader(it->first, it->second); } } - // Wait for all submitted jobs to complete - for (auto& f : futures) + + for (int trackIdx = (mNumberOfTracks - 1); (ISCONFIGSET(eAAMPConfig_DashParallelFragDownload) && trackIdx >= 0); trackIdx--) { - try + if(trackIdx < mTrackWorkers.size() && mTrackWorkers[trackIdx]) { - f.get(); - } - catch (const std::exception& e) - { - AAMPLOG_ERR("Exception while waiting for encrypted header job: %s", e.what()); + mTrackWorkers[trackIdx]->WaitForCompletion(); } } } @@ -8504,26 +8882,16 @@ void StreamAbstractionAAMP_MPD::CacheEncryptedHeader(int trackIdx, std::string h if (mMediaStreamContext[trackIdx]->WaitForFreeFragmentAvailable()) { AAMPLOG_WARN("Pushing encrypted header for %s fragmentUrl %s", GetMediaTypeName(AampMediaType(trackIdx)), headerUrl.c_str()); + //Set the last parameter (overWriteTrackId) true to overwrite the track id if ad and content has different track ids bool temp = false; try { - DownloadInfoPtr info = std::make_shared(); - info->absolutePosition = 0; - info->ptsOffset = 0; - info->isInitSegment = true; - info->mediaType = (AampMediaType)trackIdx; - mMediaStreamContext[trackIdx]->mActiveDownloadInfo = std::move(info); - temp = mMediaStreamContext[trackIdx]->CacheFragment(headerUrl, (eCURLINSTANCE_VIDEO + mMediaStreamContext[trackIdx]->mediaType), mMediaStreamContext[trackIdx]->fragmentTime, 0.0, NULL, true, false, false, 0); + temp = mMediaStreamContext[trackIdx]->CacheFragment(headerUrl, (eCURLINSTANCE_VIDEO + mMediaStreamContext[trackIdx]->mediaType), mMediaStreamContext[trackIdx]->fragmentTime, 0.0, NULL, true, false, false, 0, 0, true); } catch(const std::regex_error& e) { AAMPLOG_ERR("regex exception in Calling CacheFragment: %s", e.what()); } - catch (...) - { - AAMPLOG_ERR("unknown exception calling CacheFragment"); - } - mMediaStreamContext[trackIdx]->mActiveDownloadInfo = nullptr; if(!temp) { AAMPLOG_TRACE("StreamAbstractionAAMP_MPD: did not cache fragmentUrl %s fragmentTime %f", headerUrl.c_str(), mMediaStreamContext[trackIdx]->fragmentTime); //CID:84438 - checked return @@ -8629,7 +8997,7 @@ bool StreamAbstractionAAMP_MPD::GetEncryptedHeaders(std::map& fragmentDescriptor->RepresentationID.assign(representation->GetId()); FragmentDescriptor *fragmentDescriptorCMCD(fragmentDescriptor); - ConstructFragmentURL(fragmentUrl,fragmentDescriptorCMCD , std::move(initialization), aamp->mConfig); + ConstructFragmentURL(fragmentUrl,fragmentDescriptorCMCD , std::move(initialization)); mappedHeaders[i] = std::move(fragmentUrl); @@ -8714,7 +9082,7 @@ bool StreamAbstractionAAMP_MPD::ExtractAndAddSubtitleMediaHeader() fragmentDescriptor->RepresentationID.assign(representation->GetId()); FragmentDescriptor *fragmentDescriptorCMCD(fragmentDescriptor); - ConstructFragmentURL(fragmentUrl,fragmentDescriptorCMCD , std::move(initialization), aamp->mConfig); + ConstructFragmentURL(fragmentUrl,fragmentDescriptorCMCD , std::move(initialization) ); AAMPLOG_MIL("[SUBTITLE]: mimeType:%s, init url %s", subtitleMimeType.c_str(), fragmentUrl.c_str()); subtitleHeader->url = std::move(fragmentUrl); subtitleHeader->mimeType = std::move(subtitleMimeType); @@ -8748,81 +9116,173 @@ bool StreamAbstractionAAMP_MPD::ExtractAndAddSubtitleMediaHeader() /** - * @fn AdvanceTrack * @brief Fetches and caches audio fragment parallelly for video fragment. - * @param[in] trackIdx - track index - * @param[in] trickPlay - flag indicates if its trickplay - * @param[in, out] delta - delta for skipping fragments - * @return void */ -void StreamAbstractionAAMP_MPD::AdvanceTrack(int trackIdx, bool trickPlay, double &delta) +void StreamAbstractionAAMP_MPD::AdvanceTrack(int trackIdx, bool trickPlay, double *delta, bool &waitForFreeFrag, bool &bCacheFullState,bool throttleAudioDownload,bool isDiscontinuity) { UsingPlayerId playerId(aamp->mPlayerId); class MediaStreamContext *pMediaStreamContext = mMediaStreamContext[trackIdx]; - int vodTrickplayFPS = GETCONFIGVALUE(eAAMPConfig_VODTrickPlayFPS); + bool lowLatency = aamp->GetLLDashServiceData()->lowLatencyMode; + bool isAllowNextFrag = true; + int vodTrickplayFPS = GETCONFIGVALUE(eAAMPConfig_VODTrickPlayFPS); - if (pMediaStreamContext->adaptationSet) + AAMPLOG_TRACE("trackIdx %d, trickPlay %d, delta %p, waitForFreeFrag %d, bCacheFullState %d, throttleAudioDownload %d, isDiscontinuity %d", + trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState, throttleAudioDownload, isDiscontinuity); + + if (waitForFreeFrag && !trickPlay) { - if (!pMediaStreamContext->eos) + AAMPPlayerState state = aamp->GetState(); + if(ISCONFIGSET(eAAMPConfig_SuppressDecode)) + { + state = eSTATE_PLAYING; + } + if(state == eSTATE_PLAYING) + { + waitForFreeFrag = false; + } + else { - if (trickPlay && pMediaStreamContext->mDownloadedFragment.GetPtr() == NULL && !pMediaStreamContext->freshManifest) + int timeoutMs = -1; + if (bCacheFullState && pMediaStreamContext->IsFragmentCacheFull()) { - double skipTime = 0; - skipTime = delta; - // When player started in trickplay rate during player switching, make sure that we are showing at least one frame (mainly to avoid cases where trickplay rate is so high that an ad could get skipped completely) - // TODO: Check for this condition?? delta is always zero from FetcherLoop - if (aamp->playerStartedWithTrickPlay) - { - AAMPLOG_WARN("Played switched in trickplay, delta set to zero"); - skipTime = 0; - aamp->playerStartedWithTrickPlay = false; - } - else if ((mPlayRate > AAMP_RATE_PAUSE && skipTime <= 0) || (mPlayRate < AAMP_RATE_PAUSE && skipTime >= 0)) - { - skipTime = mPlayRate / vodTrickplayFPS; - } - double currFragTime = pMediaStreamContext->fragmentTime; - skipTime = SkipFragments(pMediaStreamContext, skipTime); - if (pMediaStreamContext->eos) + timeoutMs = MAX_WAIT_TIMEOUT_MS; + } + isAllowNextFrag = pMediaStreamContext->WaitForFreeFragmentAvailable(timeoutMs); + } + } + + if (isAllowNextFrag) + { + if (pMediaStreamContext->adaptationSet ) + { + bool profileNotChanged = !pMediaStreamContext->profileChanged; + bool isTsbInjection = aamp->IsLocalAAMPTsbInjection(); + bool cacheNotFull = !pMediaStreamContext->IsFragmentCacheFull(); + bool isTrackDownloadEnabled = aamp->TrackDownloadsAreEnabled(static_cast(trackIdx)); + + /* + * When injecting from TSBReader we do not want to stop the fetcher loop because of injector cache full. TSB injection + * uses numberOfFragmentChunksCached so assuming (pMediaStreamContext->numberOfFragmentsCached != maxCachedFragmentsPerTrack) == true + * + * Also aamp->IsLocalAAMPTsbInjection() || aamp->TrackDownloadsAreEnabled(static_cast(trackIdx) because a pause in playback + * should not stop the fetcher loop during TSB injection. + */ + + if(profileNotChanged && (isTsbInjection || (cacheNotFull && (!lowLatency || isTrackDownloadEnabled)))) + { + // profile not changed and Cache not full scenario + if (!pMediaStreamContext->eos) { - // If we reached end of period, only the remaining delta should be skipped in new period - // Otherwise we should skip based on formula rate/fps. This will also avoid any issues due to floating precision - delta = skipTime; + if(trickPlay && pMediaStreamContext->mDownloadedFragment.GetPtr() == NULL && !pMediaStreamContext->freshManifest) + { + double skipTime = 0; + if (delta) + { + skipTime = *delta; + } + //When player started in trickplay rate during player switching, make sure that we are showing at least one frame (mainly to avoid cases where trickplay rate is so high that an ad could get skipped completely) + //TODO: Check for this condition?? delta is always zero from FetcherLoop + if(aamp->playerStartedWithTrickPlay) + { + AAMPLOG_WARN("Played switched in trickplay, delta set to zero"); + skipTime = 0; + aamp->playerStartedWithTrickPlay = false; + } + else if((mPlayRate > AAMP_RATE_PAUSE && skipTime <= 0) || (mPlayRate < AAMP_RATE_PAUSE && skipTime >= 0)) + { + skipTime = mPlayRate / vodTrickplayFPS; + } + double currFragTime = pMediaStreamContext->fragmentTime; + skipTime = SkipFragments(pMediaStreamContext, skipTime); + if( delta ) + { + if (pMediaStreamContext->eos) + { + // If we reached end of period, only the remaining delta should be skipped in new period + // Otherwise we should skip based on formula rate/fps. This will also avoid any issues due to floating precision + *delta = skipTime; + } + else + { + *delta = 0; + } + } + mBasePeriodOffset += (pMediaStreamContext->fragmentTime - currFragTime); + } + + if (PushNextFragment(pMediaStreamContext, getCurlInstanceByMediaType(static_cast(trackIdx)))) + { + if (mIsLiveManifest) + { + pMediaStreamContext->GetContext()->CheckForPlaybackStall(true); + } + if((!pMediaStreamContext->GetContext()->trickplayMode) && (eMEDIATYPE_VIDEO == trackIdx)&& !pMediaStreamContext->failAdjacentSegment) + { + if (aamp->CheckABREnabled()) + { + pMediaStreamContext->GetContext()->CheckForProfileChange(); + } + } + } + else if (pMediaStreamContext->eos == true && mIsLiveManifest && trackIdx == eMEDIATYPE_VIDEO && !(ISCONFIGSET(eAAMPConfig_InterruptHandling) && mIsFogTSB)) + { + pMediaStreamContext->GetContext()->CheckForPlaybackStall(false); + } + + //Determining the current position within the period by calculating the difference between + //the fragmentTime and the periodStartOffset (both in absolute terms). + //If this difference exceeds the total duration of the ad, the period is considered to have ended. + if (AdState::IN_ADBREAK_AD_PLAYING == mCdaiObject->mAdState && mPlayRate > AAMP_RATE_PAUSE && !(pMediaStreamContext->eos)&& mCdaiObject->CheckForAdTerminate(pMediaStreamContext->fragmentTime - pMediaStreamContext->periodStartOffset)) + { + //Ensuring that Ad playback doesn't go beyond Adbreak + AAMPLOG_WARN("[CDAI] Track[%d] Adbreak ended early. Terminating Ad playback. fragmentTime[%lf] periodStartOffset[%lf]", + trackIdx, pMediaStreamContext->fragmentTime, pMediaStreamContext->periodStartOffset); + pMediaStreamContext->eos = true; + } } else { - delta = 0; + AAMPLOG_TRACE("Track %s is EOS, not pushing next fragment", GetMediaTypeName((AampMediaType) trackIdx)); } - mBasePeriodOffset += (pMediaStreamContext->fragmentTime - currFragTime); - } - - if (PushNextFragment(pMediaStreamContext, getCurlInstanceByMediaType(static_cast(trackIdx)))) - { - AAMPLOG_TRACE("StreamAbstractionAAMP_MPD::%s[%d] Advancing track downloads", __FUNCTION__, __LINE__); } - else if (pMediaStreamContext->eos == true && mIsLiveManifest && trackIdx == eMEDIATYPE_VIDEO && !(ISCONFIGSET(eAAMPConfig_InterruptHandling) && mIsFogTSB)) - { - pMediaStreamContext->GetContext()->CheckForPlaybackStall(false); + // Fetch init header for both audio and video ,after mpd refresh(stream selection) , profileChanged = true for both tracks . + // Need to reset profileChanged flag which is done inside FetchAndInjectInitialization + // Without resetting profileChanged flag , fetch of audio was stopped causing audio drop + else if(pMediaStreamContext->profileChanged) + { // Profile changed case + FetchAndInjectInitialization(trackIdx,isDiscontinuity); } - // Determining the current position within the period by calculating the difference between - // the fragmentTime and the periodStartOffset (both in absolute terms). - // If this difference exceeds the total duration of the ad, the period is considered to have ended. - if (AdState::IN_ADBREAK_AD_PLAYING == mCdaiObject->mAdState && mPlayRate > AAMP_RATE_PAUSE && !(pMediaStreamContext->eos) && mCdaiObject->CheckForAdTerminate(pMediaStreamContext->fragmentTime - pMediaStreamContext->periodStartOffset)) + + if ((isTsbInjection || (!pMediaStreamContext->IsFragmentCacheFull())) && + bCacheFullState && (!lowLatency || aamp->TrackDownloadsAreEnabled(static_cast(trackIdx)))) { - // Ensuring that Ad playback doesn't go beyond Adbreak - AAMPLOG_WARN("[CDAI] Track[%d] Adbreak ended early. Terminating Ad playback. fragmentTime[%lf] periodStartOffset[%lf]", - trackIdx, pMediaStreamContext->fragmentTime, pMediaStreamContext->periodStartOffset); - pMediaStreamContext->eos = true; + bCacheFullState = false; } } else { - AAMPLOG_TRACE("Track %s is EOS, not pushing next fragment", GetMediaTypeName((AampMediaType) trackIdx)); + AAMPLOG_ERR("AdaptationSet is NULL for %s", GetMediaTypeName((AampMediaType) trackIdx)); } } else { - AAMPLOG_ERR("AdaptationSet is NULL for %s", GetMediaTypeName((AampMediaType) trackIdx)); + std::lock_guard lock(mutex); + // Important DEBUG area, live downloader is delayed due to some external factors (Injector or Gstreamer) + if (pMediaStreamContext->IsInjectionFromCachedFragmentChunks()) + { + AAMPLOG_ERR("%s Live downloader is not advancing at the moment cache (%d / %d)", GetMediaTypeName((AampMediaType)trackIdx), pMediaStreamContext->numberOfFragmentChunksCached, pMediaStreamContext->maxCachedFragmentChunksPerTrack); + } + else + { + AAMPLOG_ERR("%s Live downloader is not advancing at the moment cache (%d / %d)", GetMediaTypeName((AampMediaType)trackIdx), pMediaStreamContext->numberOfFragmentsCached, pMediaStreamContext->maxCachedFragmentsPerTrack); + } + } + // If throttle audio download is set and prev fragment download happened and cache is not full, attempt to download an additional fragment + if (throttleAudioDownload && (trackIdx == eMEDIATYPE_AUDIO) && isAllowNextFrag && !bCacheFullState) + { + AAMPLOG_INFO("throttleAudioDownload enabled, invoking AdvanceTrack again"); + // Disable throttleAudioDownload this time to prevent continuous looping here + AdvanceTrack(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState, false); } } @@ -9403,6 +9863,10 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() aamp_setThreadName("aampFragDL"); bool exitFetchLoop = false; bool trickPlay = (AAMP_NORMAL_PLAY_RATE != aamp->rate); + + //If we are injecting from TSB then we are not injecting from the Fetcher hence + //the fetcher does not need to wait for a free slot in the fragment cache FIFO + bool waitForFreeFrag = !aamp->IsLocalAAMPTsbInjection(); double delta = 0; bool adStateChanged = false; bool resetIterator = true; @@ -9566,6 +10030,9 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() * Segment downloader loop */ double lastPrdOffset = mBasePeriodOffset; + bool parallelDnld = ISCONFIGSET(eAAMPConfig_DashParallelFragDownload); + bool *cacheFullStatus = new bool[AAMP_TRACK_COUNT]{false}; + bool throttleAudio = false; while (!exitFetchLoop) { if (mIsLiveStream && !mIsLiveManifest && playlistDownloaderThreadStarted) @@ -9579,6 +10046,7 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() { SwitchAudioTrack(); mMediaStreamContext[eTRACK_AUDIO]->refreshAudio = false; + throttleAudio = true; } if(mPlayRate == AAMP_NORMAL_PLAY_RATE && mMediaStreamContext[eTRACK_SUBTITLE] && mMediaStreamContext[eTRACK_SUBTITLE]->refreshSubtitles ) { @@ -9587,10 +10055,30 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() } for (int trackIdx = (mNumberOfTracks - 1); trackIdx >= 0; trackIdx--) { - auto timeBasedBuffer = mMediaStreamContext[trackIdx]->GetTimeBasedBufferManager(); - if (!mMediaStreamContext[trackIdx]->eos && timeBasedBuffer && !timeBasedBuffer->IsFull()) + // When injecting from TSB reader then fetcher should ignore the cache full status + cacheFullStatus[trackIdx] = !aamp->IsLocalAAMPTsbInjection(); + if (!mMediaStreamContext[trackIdx]->eos) + { + if (parallelDnld && trackIdx < mTrackWorkers.size() && mTrackWorkers[trackIdx]) + { + // Download the video, audio & subtitle fragments in a separate parallel thread. + AAMPLOG_DEBUG("Submitting job for track %d", trackIdx); + mTrackWorkers[trackIdx]->SubmitJob([this, trackIdx, &delta, &waitForFreeFrag, &cacheFullStatus, trickPlay, throttleAudio]() + { AdvanceTrack(trackIdx, trickPlay, &delta, waitForFreeFrag, cacheFullStatus[trackIdx], + (trackIdx == eMEDIATYPE_AUDIO) ? throttleAudio : false, false); }); + } + else + { + AdvanceTrack(trackIdx, trickPlay, &delta, waitForFreeFrag, cacheFullStatus[trackIdx], false, isVidDiscInitFragFail); + } + } + } + + for (int trackIdx = (mNumberOfTracks - 1); (parallelDnld && trackIdx >= 0); trackIdx--) + { + if(trackIdx < mTrackWorkers.size() && mTrackWorkers[trackIdx]) { - AdvanceTrack(trackIdx, trickPlay, delta); + mTrackWorkers[trackIdx]->WaitForCompletion(); } } @@ -9599,7 +10087,7 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() { AAMPLOG_INFO("Downloads are disabled, so exit FetcherLoop"); exitFetchLoop = true; - break; + cacheFullStatus[eMEDIATYPE_VIDEO] = cacheFullStatus[eMEDIATYPE_AUDIO] = false; } // -- Exit from fetch loop for period to be done only after audio and video fetch @@ -9627,24 +10115,16 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() { if (vEos) { - auto dashWorkerJob = std::make_shared([this]() { - mMediaStreamContext[eMEDIATYPE_VIDEO]->eosReached = true; - mMediaStreamContext[eMEDIATYPE_VIDEO]->AbortWaitForCachedAndFreeFragment(false); - AAMPLOG_INFO("Video EOS Marked"); - }); - aamp->GetAampTrackWorkerManager()->SubmitJob(eMEDIATYPE_VIDEO , dashWorkerJob); AAMPLOG_INFO("EOS Reached.eosOutSideAd:%d eosAdPlayback:%d", eosOutSideAd, eosAdPlayback); + mMediaStreamContext[eMEDIATYPE_VIDEO]->eosReached = true; + mMediaStreamContext[eMEDIATYPE_VIDEO]->AbortWaitForCachedAndFreeFragment(false); } if (audioEnabled) { if (mMediaStreamContext[eMEDIATYPE_AUDIO]->eos) { - auto dashWorkerJob = std::make_shared([this]() { - mMediaStreamContext[eMEDIATYPE_AUDIO]->eosReached = true; - mMediaStreamContext[eMEDIATYPE_AUDIO]->AbortWaitForCachedAndFreeFragment(false); - AAMPLOG_INFO("Audio EOS Marked"); - }); - aamp->GetAampTrackWorkerManager()->SubmitJob(eMEDIATYPE_AUDIO, dashWorkerJob); + mMediaStreamContext[eMEDIATYPE_AUDIO]->eosReached = true; + mMediaStreamContext[eMEDIATYPE_AUDIO]->AbortWaitForCachedAndFreeFragment(false); } } else @@ -9659,50 +10139,21 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() aEos = true; } } - - // Helper lambdas for clarity - auto IsAtLiveEdge = [this]() { - return mIsLiveManifest && - (mPlayRate > AAMP_RATE_PAUSE) && - (mIterPeriodIndex == mMPDParseHelper->mUpperBoundaryPeriod); - }; - - auto ShouldWaitForFragments = [this, &IsAtLiveEdge]() { - return (!IsAtLiveEdge() || mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING) && - (mIsLiveManifest || - (mPlayRate < AAMP_RATE_PAUSE && mIterPeriodIndex > mMPDParseHelper->mLowerBoundaryPeriod) || - (mPlayRate > AAMP_RATE_PAUSE && mIterPeriodIndex < mMPDParseHelper->mUpperBoundaryPeriod)); - }; - - // Check for End Of Stream + // If audio and video reached EOS then only break the fetch loop . if (vEos && aEos) { - AAMPLOG_DEBUG("EOS - Exit fetch loop"); - - // TODO: This is required now as we profile ABR from current period, after decoupling the ABR - // dependency by saving period based profile data, this wait can be removed. - // Wait for pending fragment downloads if necessary - if (ShouldWaitForFragments()) - { - aamp->GetAampTrackWorkerManager()->WaitForCompletionWithTimeout(MAX_WAIT_TIMEOUT_MS, [this]() { - if (mIsLiveManifest) - { - if (eAAMPSTATUS_OK != UpdateMPD()) - { - AAMPLOG_DEBUG("Failed to refresh MPD"); - } - } - }); - } - - // Handle ad end logic - if (mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING) + AAMPLOG_DEBUG("EOS - Exit fetch loop "); + // Disabling this log to avoid flooding, as Fetcher loop maintains track EOS until + // playlist refreshes in parallel thread. + // Enable 'info' level to track EOS from PushNextFragment. + if (AdState::IN_ADBREAK_AD_PLAYING == mCdaiObject->mAdState) { adStateChanged = onAdEvent(AdEvent::AD_FINISHED); } - - // Decide whether to wait or exit - if (IsAtLiveEdge() && mCdaiObject->mAdState != AdState::IN_ADBREAK_WAIT2CATCHUP) + // EOS from both tracks for dynamic (live) manifests for all periods. + // If ad state is not IN_ADBREAK_WAIT2CATCHUP, go for the manifest update, otherwise break the loop. + if (mIsLiveManifest && (mPlayRate > AAMP_RATE_PAUSE) && (mIterPeriodIndex == mMPDParseHelper->mUpperBoundaryPeriod) && + (AdState::IN_ADBREAK_WAIT2CATCHUP != mCdaiObject->mAdState)) { aamp->interruptibleMsSleep(500); } @@ -9732,10 +10183,36 @@ void StreamAbstractionAAMP_MPD::FetcherLoop() lastPrdOffset = mBasePeriodOffset; } - // This sleep will hit when there is no content to download and cache is not full - // and refresh interval timeout not reached . To Avoid tight loop adding a min delay - aamp->interruptibleMsSleep(50); + if (cacheFullStatus[eMEDIATYPE_VIDEO] || (vEos && !aEos)) + { + // play cache is full , wait until cache is available to inject next, max wait of 1sec + int timeoutMs = MAX_WAIT_TIMEOUT_MS; + int trackIdx = (vEos && !aEos) ? eMEDIATYPE_AUDIO : eMEDIATYPE_VIDEO; + AAMPLOG_DEBUG("Cache full state trackIdx %d vEos %d aEos %d timeoutMs %d Time %lld", + trackIdx, vEos, aEos, timeoutMs, aamp_GetCurrentTimeMS()); + if(aamp->GetLLDashChunkMode() && !aamp->TrackDownloadsAreEnabled(static_cast(trackIdx))) + { + // Track is already at enough-data state, no need to wait for cache full + aamp->interruptibleMsSleep(timeoutMs); + AAMPLOG_DEBUG("Waited for track(%d) need-data", trackIdx); + } + else + { + bool temp = mMediaStreamContext[trackIdx]->WaitForFreeFragmentAvailable(timeoutMs); + if (temp == false) + { + AAMPLOG_DEBUG("Waited for FreeFragmentAvailable"); // CID:82355 - checked return + } + } + } + else + { + // This sleep will hit when there is no content to download and cache is not full + // and refresh interval timeout not reached . To Avoid tight loop adding a min delay + aamp->interruptibleMsSleep(50); + } } // Loop 2: end of while loop (!exitFetchLoop) + SAFE_DELETE_ARRAY(cacheFullStatus); if(exitFetchLoop) { break; @@ -10210,6 +10687,7 @@ StreamAbstractionAAMP_MPD::~StreamAbstractionAAMP_MPD() void StreamAbstractionAAMP_MPD::StartFromOtherThanAampLocalTsb(void) { aamp->mDRMLicenseManager->setSessionMgrState(SessionMgrState::eSESSIONMGR_ACTIVE); + // Start the worker threads for each track try { // Attempting to assign to a running thread will cause std::terminate(), not an exception @@ -10375,14 +10853,9 @@ void StreamAbstractionAAMP_MPD::Stop(bool clearChannelData) AAMPLOG_INFO("Joined StartLatencyMonitorThread"); latencyMonitorThreadStarted = false; } - if (!aamp->DownloadsAreEnabled()) + if (!aamp->DownloadsAreEnabled() && fragmentCollectorThreadID.joinable()) { - aamp->GetAampTrackWorkerManager()->StopWorkers(); - if (fragmentCollectorThreadID.joinable()) - { - fragmentCollectorThreadID.join(); - } - ClearWorkers(); + fragmentCollectorThreadID.join(); } if(tsbReaderThreadID.joinable()) @@ -10393,7 +10866,6 @@ void StreamAbstractionAAMP_MPD::Stop(bool clearChannelData) AAMPLOG_INFO("Joined tsbReaderThreadID"); } - for (int iTrack = 0; iTrack < mMaxTracks; iTrack++) { MediaStreamContext *track = mMediaStreamContext[iTrack]; @@ -10447,14 +10919,8 @@ void StreamAbstractionAAMP_MPD::Stop(bool clearChannelData) aamp->EnableDownloads(); } - if (clearChannelData) - { - // Stop the ad loop if it is running on player stop - mCdaiObject->StopFulfillAdLoop(); - } - else if (!aamp->IsLocalAAMPTsb()) + if (!aamp->IsLocalAAMPTsb() && !clearChannelData) { - // If we are at local TSB, we don't need to stop ad loop mCdaiObject->NotifyAdLoopWait(); } } @@ -13844,271 +14310,11 @@ void StreamAbstractionAAMP_MPD::SetSubtitleTrackOffset() */ void StreamAbstractionAAMP_MPD::InitializeWorkers() { - if (aamp->GetAampTrackWorkerManager()->IsEmpty()) - { - try - { - for (int i = 0; i < mMaxTracks; i++) - { - if (aamp->GetAampTrackWorkerManager()->CreateWorker(aamp, static_cast(i))) - { - AAMPLOG_DEBUG("Created worker for %s", GetMediaTypeName(static_cast(i))); - } - } - // Start the worker threads for each track - aamp->GetAampTrackWorkerManager()->StartWorkers(); - } - catch (const std::exception &e) - { - AAMPLOG_ERR("Exception caught in InitializeWorkers: %s", e.what()); - } - } -} - -/** - * @fn ClearWorkers - * @brief Remove each worker threads - * - * @return void - */ -void StreamAbstractionAAMP_MPD::ClearWorkers() -{ - if (!aamp->GetAampTrackWorkerManager()->IsEmpty()) - { - try - { - aamp->GetAampTrackWorkerManager()->RemoveWorkers(); - } - catch (const std::exception &e) - { - AAMPLOG_ERR("Exception caught in ClearWorkers: %s", e.what()); - } - } -} - -/** - * @fn OnFragmentDownloadComplete - * @brief Callback function to be called after fragment download is complete - * @param[in] status - download status, true if success - * @param[in] downloadInfo - download information - */ -void StreamAbstractionAAMP_MPD::OnFragmentDownloadComplete(bool status, DownloadInfoPtr downloadInfo) -{ - auto pMediaStreamContext = mMediaStreamContext[downloadInfo->mediaType]; - if (pMediaStreamContext) - { - if ((downloadInfo->mediaType == eMEDIATYPE_VIDEO) && !downloadInfo->isInitSegment && (mIsFogTSB && !mAdPlayingFromCDN) && pMediaStreamContext->mDownloadedFragment.GetPtr()) - { - pMediaStreamContext->profileChanged = true; - profileIdxForBandwidthNotification = GetProfileIdxForBandwidthNotification(pMediaStreamContext->fragmentDescriptor.Bandwidth); - UpdateRampUpOrDownProfileReason(); - pMediaStreamContext->SetCurrentBandWidth(pMediaStreamContext->fragmentDescriptor.Bandwidth); - // Push the active job back to the queue - aamp->GetAampTrackWorkerManager()->GetWorker(downloadInfo->mediaType)->RescheduleActiveJob(); - } - else - { - if (status) - { - pMediaStreamContext->OnFragmentDownloadSuccess(downloadInfo); - } - else - { - pMediaStreamContext->OnFragmentDownloadFailed(downloadInfo); - } - } - // Fetch init header for both audio and video ,after mpd refresh(stream selection) , profileChanged = true for both tracks . - // Need to reset profileChanged flag which is done inside FetchAndInjectInitialization - // Without resetting profileChanged flag , fetch of audio was stopped causing audio drop - if (status && downloadInfo->isInitSegment) - { - pMediaStreamContext->profileChanged = false; - pMediaStreamContext->discontinuity = false; - } - else if (pMediaStreamContext->profileChanged) - { // Profile changed case, reuse the same downloadInfo for init header fetch - AAMPLOG_DEBUG("%s Profile changed, reuse downloadInfo for init header fetch", GetMediaTypeName(downloadInfo->mediaType)); - // The absolute position for init fragment will be taken from latest media stream context last injected duration - // This is taken in DownloadFragment after scheduling new init fragment from here. - FetchAndInjectInitialization(downloadInfo->mediaType, downloadInfo->isDiscontinuity); - } - } - else - { - AAMPLOG_WARN("MediaStreamContext not found for mediaType %s", GetMediaTypeName(downloadInfo->mediaType)); - } -} - -/** - * @fn GenerateFragmentURLList - * @param[out] uriList fragment url list, bitrate as key and url as value - * @param[in] pMediaStreamContext MediaStreamContext object - * @param[in] isInit true if init fragment - * @return fragment url list, bitrate as key and url as value - */ -void StreamAbstractionAAMP_MPD::GenerateFragmentURLList(URLBitrateMap &uriList, MediaStreamContext *pMediaStreamContext, bool isInit) -{ - if (!pMediaStreamContext) - { - AAMPLOG_ERR("pMediaStreamContext is NULL"); - return; - } - - auto adaptationSet = pMediaStreamContext->adaptationSet; - if (!adaptationSet || !mMPDParseHelper->IsContentType(adaptationSet, eMEDIATYPE_VIDEO)) + if(mTrackWorkers.empty()) { - return; - } - - auto appendBaseUrls = [&](FragmentDescriptor *desc, IRepresentation *repr) - { - desc->ClearMatchingBaseUrl(); - desc->AppendMatchingBaseUrl(&mpd->GetBaseUrls()); - desc->AppendMatchingBaseUrl(&mCurrentPeriod->GetBaseURLs()); - desc->AppendMatchingBaseUrl(&adaptationSet->GetBaseURLs()); - desc->AppendMatchingBaseUrl(&repr->GetBaseURLs()); - }; - - for (auto &representation : adaptationSet->GetRepresentation()) - { - URIInfo uriInfo; - auto fragmentDescriptor = aamp_utils::make_unique(); - fragmentDescriptor->Bandwidth = representation->GetBandwidth(); - fragmentDescriptor->RepresentationID = representation->GetId(); - fragmentDescriptor->bUseMatchingBaseUrl = ISCONFIGSET(eAAMPConfig_MatchBaseUrl); - fragmentDescriptor->manifestUrl = (mCdaiObject->mAdState == AdState::IN_ADBREAK_AD_PLAYING) - ? aamp->GetManifestUrl() - : pMediaStreamContext->fragmentDescriptor.manifestUrl; - appendBaseUrls(fragmentDescriptor.get(), representation); - - SegmentTemplates segmentTemplates(representation->GetSegmentTemplate(), adaptationSet->GetSegmentTemplate()); - if (segmentTemplates.HasSegmentTemplate()) - { - std::string urlTemplate = isInit ? segmentTemplates.Getinitialization() : segmentTemplates.Getmedia(); - if (urlTemplate.empty()) - { - AAMPLOG_ERR("media is empty for representation %s", representation->GetId().c_str()); - continue; - } - - fragmentDescriptor->Number = pMediaStreamContext->fragmentDescriptor.Number; - fragmentDescriptor->Time = pMediaStreamContext->fragmentDescriptor.Time; - fragmentDescriptor->TimeScale = pMediaStreamContext->fragmentDescriptor.TimeScale; - - - if(mIsFogTSB && isInit) - { - // Seperate handling for fog TSB init fragments - // For fog TSB, we need to fetch init fragments from available bitrates - auto reprFromAvailableBitrates = mMPDParseHelper->GetBitrateInfoFromCustomMpd(adaptationSet); - for (auto rep : reprFromAvailableBitrates) - { - URIInfo fogUriInfo; - fragmentDescriptor->Bandwidth = rep->GetBandwidth(); - // Note : Don't use std::move on urlTemplate as its used multiple times in the loop - ConstructFragmentURL(fogUriInfo.url, fragmentDescriptor.get(), urlTemplate, aamp->mConfig); - uriList[fragmentDescriptor->Bandwidth] = std::move(fogUriInfo); - } - break; // No need to process further representations for fog TSB init fragments - } - else - { - // For non-fog TSB, we can use the representation's bandwidth - // to construct the URL for init fragments - ConstructFragmentURL(uriInfo.url, fragmentDescriptor.get(), std::move(urlTemplate), aamp->mConfig); - uriList[fragmentDescriptor->Bandwidth] = std::move(uriInfo); - continue; - } - } - - if (auto segmentBase = representation->GetSegmentBase()) - { - if (isInit) - { - const IURLType *urlType = segmentBase->GetInitialization(); - uriInfo.range = urlType ? urlType->GetRange() : segmentBase->GetIndexRange(); - - if (!urlType) - { - uint64_t s1, s2; - sscanf(uriInfo.range.c_str(), "%" PRIu64 "-%" PRIu64 "", &s1, &s2); - char temp[MAX_RANGE_STRING_CHARS]; - snprintf(temp, sizeof(temp), "0-%" PRIu64, s1 - 1); - uriInfo.range = temp; - } - } - // For segment base, we need to use the index range for the URL - // The range is identified by parsing sidx box and it is done at downloader level - ConstructFragmentURL(uriInfo.url, fragmentDescriptor.get(), "", aamp->mConfig); - uriList[fragmentDescriptor->Bandwidth] = std::move(uriInfo); - continue; - } - - if (auto segmentList = representation->GetSegmentList()) + for (int i = 0; i < mMaxTracks; i++) { - if (isInit) - { - const IURLType *urlType = segmentList->GetInitialization(); - if (!urlType) - { - // Check if the segment list has an initialization URL at adaptation set level - segmentList = adaptationSet->GetSegmentList(); - urlType = segmentList ? segmentList->GetInitialization() : nullptr; - if (!urlType) - { - AAMPLOG_ERR("initialization is null in segmentList"); - return; - } - } - - std::string initUrl = urlType->GetSourceURL(); - if (!initUrl.empty()) - { - ConstructFragmentURL(uriInfo.url, fragmentDescriptor.get(), std::move(initUrl), aamp->mConfig); - uriList[fragmentDescriptor->Bandwidth] = std::move(uriInfo); - } - else - { - // Segment list uses range, so parse init from first segment URL - const auto &segmentURLs = segmentList->GetSegmentURLs(); - if (!segmentURLs.empty() && segmentURLs[0]) - { - const char *rangeStr = segmentURLs[0]->GetMediaRange().c_str(); - int start, end; - if (sscanf(rangeStr, "%d-%d", &start, &end) == 2 && start > 1) - { - char temp[MAX_RANGE_STRING_CHARS]; - snprintf(temp, sizeof(temp), "0-%d", start - 1); - uriInfo.range = temp; - ConstructFragmentURL(uriInfo.url, fragmentDescriptor.get(), "", aamp->mConfig); - uriList[fragmentDescriptor->Bandwidth] = std::move(uriInfo); - } - else - { - AAMPLOG_ERR("Cannot determine init range from segmentList"); - } - } - } - } - else - { - const auto &segmentURLs = segmentList->GetSegmentURLs(); - if (pMediaStreamContext->fragmentIndex < segmentURLs.size() && segmentURLs[pMediaStreamContext->fragmentIndex]) - { - auto *segmentURL = segmentURLs[pMediaStreamContext->fragmentIndex]; - auto rawAttrs = segmentList->GetRawAttributes(); - if (rawAttrs.find("customlist") == rawAttrs.end()) - { - ConstructFragmentURL(uriInfo.url, fragmentDescriptor.get(), segmentURL->GetMediaURI(), aamp->mConfig); - uriInfo.range = segmentURL->GetMediaRange(); - uriList[fragmentDescriptor->Bandwidth] = std::move(uriInfo); - } - else - { - AAMPLOG_ERR("segmentList is unsupported (customlist)"); - } - } - } + mTrackWorkers.push_back(aamp_utils::make_unique(aamp, static_cast(i))); } } } diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index ff1b0683b..3842be33e 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -42,9 +42,7 @@ #include "AampMPDDownloader.h" #include "AampDRMLicPreFetcher.h" #include "AampMPDParseHelper.h" -#include "AampTrackWorker.hpp" -#include "AampTrackWorkerManager.hpp" -#include "AampDownloadInfo.hpp" +#include "AampTrackWorker.h" using namespace dash; using namespace std; @@ -67,36 +65,94 @@ struct ProfileInfo int representationIndex; }; -class AampDashWorkerJob : public aamp::AampTrackWorkerJob +/** + * @struct FragmentDescriptor + * @brief Stores information of dash fragment + */ +struct FragmentDescriptor { -private: - std::function mJobFunction; +private : + std::string matchingBaseURL; +public : + std::string manifestUrl; + uint32_t Bandwidth; + std::string RepresentationID; + uint64_t Number; + double Time; //In units of timescale + bool bUseMatchingBaseUrl; + int64_t nextfragmentNum; + double nextfragmentTime; + uint32_t TimeScale; + FragmentDescriptor() : manifestUrl(""), Bandwidth(0), Number(0), Time(0), RepresentationID(""),matchingBaseURL(""),bUseMatchingBaseUrl(false),nextfragmentNum(-1),nextfragmentTime(0), TimeScale(1) + { + } -public: - AampDashWorkerJob(std::function jobFunction) : mJobFunction(std::move(jobFunction)) {} - /** - * @fn Execute - * @brief Execute the job function - */ - void Execute() override + FragmentDescriptor(const FragmentDescriptor& p) : manifestUrl(p.manifestUrl), Bandwidth(p.Bandwidth), RepresentationID(p.RepresentationID), Number(p.Number), Time(p.Time),matchingBaseURL(p.matchingBaseURL),bUseMatchingBaseUrl(p.bUseMatchingBaseUrl),nextfragmentNum(p.nextfragmentNum),nextfragmentTime(p.nextfragmentTime), TimeScale(p.TimeScale) { - if (mJobFunction) - { - mJobFunction(); // Call the provided job function - } - else - { - AAMPLOG_WARN("AampDashWorkerJob::Execute called with empty job function"); - } } - /** - * @fn Clone - * @brief Clone the job for worker pool - * @return shared_ptr to cloned job - */ - aamp::AampTrackWorkerJobUniquePtr Clone() const override + + FragmentDescriptor& operator=(const FragmentDescriptor &p) + { + manifestUrl = p.manifestUrl; + RepresentationID.assign(p.RepresentationID); + Bandwidth = p.Bandwidth; + Number = p.Number; + Time = p.Time; + matchingBaseURL = p.matchingBaseURL; + nextfragmentNum = p.nextfragmentNum; + nextfragmentTime = p.nextfragmentTime; + TimeScale = p.TimeScale; + return *this; + } + std::string GetMatchingBaseUrl() const + { + return matchingBaseURL; + } + + void ClearMatchingBaseUrl() + { + matchingBaseURL.clear(); + } + void AppendMatchingBaseUrl( const std::vector*baseUrls ) { - return aamp_utils::make_unique(mJobFunction); + if( baseUrls && baseUrls->size()>0 ) + { + const std::string &url = baseUrls->at(0)->GetUrl(); + if( url.empty() ) + { + } + else if( aamp_IsAbsoluteURL(url) ) + { + if(bUseMatchingBaseUrl) + { + std::string prefHost = aamp_getHostFromURL(manifestUrl); + for (auto & item : *baseUrls) { + std::string itemUrl = item->GetUrl(); + std::string host = aamp_getHostFromURL(std::move(itemUrl)); + if(0 == prefHost.compare(host)) + { + matchingBaseURL = item->GetUrl(); + return; + } + } + } + matchingBaseURL = url; + } + else if( url.rfind("/",0)==0 ) + { + matchingBaseURL = aamp_getHostFromURL(matchingBaseURL); + matchingBaseURL += url; + AAMPLOG_WARN( "baseURL with leading /" ); + } + else + { + if( !matchingBaseURL.empty() && matchingBaseURL.back() != '/' ) + { // add '/' delimiter only if parent baseUrl doesn't already end with one + matchingBaseURL += "/"; + } + matchingBaseURL += url; + } + } } }; @@ -293,12 +349,11 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP * @param fragmentDuration duration of fragment in seconds * @param isInitializationSegment true if fragment is init fragment * @param curlInstance curl instance to be used to fetch - * @param fcsContent true if content is inside FailOver tag * @param discontinuity true if fragment is discontinuous * @param pto presentation time offset in seconds * @param timeScale denominator for fixed point math */ - bool FetchFragment( class MediaStreamContext *pMediaStreamContext, std::string media, double fragmentDuration, bool isInitializationSegment, unsigned int curlInstance, bool fscContent = false, bool discontinuity = false, double pto = 0 , uint32_t timeScale = 0, std::string range = ""); + bool FetchFragment( class MediaStreamContext *pMediaStreamContext, std::string media, double fragmentDuration, bool isInitializationSegment, unsigned int curlInstance, bool discontinuity = false, double pto = 0 , uint32_t timeScale = 0); /** * @fn PushNextFragment * @param pMediaStreamContext Track object @@ -439,15 +494,6 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP */ double GetAvailabilityStartTime() override; - /** - * @fn GenerateFragmentURLList - * @param[out] urlList fragment url list, bitrate as key and url as value - * @param[in] pMediaStreamContext MediaStreamContext object - * @param[in] isInit true if init fragment - * @return fragment url list - */ - void GenerateFragmentURLList(URLBitrateMap& urlList, MediaStreamContext *pMediaStreamContext, bool isInit); - /** * @brief Selects the audio track based on the available audio tracks and updates the desired representation index. * @@ -582,10 +628,13 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP * @fn AdvanceTrack * @param[in] trackIdx - track index * @param[in] trickPlay - flag indicates if its trickplay - * @param[in, out] delta - delta for skipping fragments + * @param[in/out] waitForFreeFrag - flag is updated if we are waiting for free fragment + * @param[in/out] bCacheFullState - flag is updated if the cache is full for this track + * @param[in] throttleAudio - flag indicates if we should throttle audio download + * @param[in] isDiscontinuity - flag indicates if its a discontinuity * @return void */ - void AdvanceTrack(int trackIdx, bool trickPlay, double &delta); + void AdvanceTrack(int trackIdx, bool trickPlay, double *delta, bool &waitForFreeFrag, bool &bCacheFullState,bool throttleAudio,bool isDiscontinuity = false); /** * @fn AdvanceTsbFetch * @param[in] trackIdx - trackIndex @@ -772,6 +821,12 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP * @param reportBulkMeta true if bulk metadata is enabled */ void ProcessTrickModeRestriction(Node *node, const std::string &AdID, uint64_t startMS, bool isInit, bool reportBulkMeta); + /** + * @fn Fragment downloader thread + * @param trackIdx track index + * @param initialization Initialization string + */ + void TrackDownloader(int trackIdx, std::string initialization); /** * @fn FetchAndInjectInitFragments * @param discontinuity number of tracks and discontinuity true if discontinuous fragment @@ -935,7 +990,13 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP * @param[out] representationIndex - representation within adaptation with matching params */ bool IsMatchingLanguageAndMimeType(AampMediaType type, std::string lang, IAdaptationSet *adaptationSet, int &representationIndex); - + /** + * @fn ConstructFragmentURL + * @param[out] fragmentUrl fragment url + * @param fragmentDescriptor descriptor + * @param media media information string + */ + void ConstructFragmentURL( std::string& fragmentUrl, const FragmentDescriptor *fragmentDescriptor, std::string media); double GetEncoderDisplayLatency(); /** * @fn StartLatencyMonitorThread @@ -1032,29 +1093,6 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP */ void InitializeWorkers(); - /** - * @fn ClearWorkers - * @brief Remove each worker threads - * - * @return void - */ - void ClearWorkers(); - - /** - * @fn OnFragmentDownloadComplete - * @brief Callback function to be called after fragment download is complete - * @param[in] status - download status, true if success - * @param[in] downloadInfo - download information - */ - void OnFragmentDownloadComplete(bool status, DownloadInfoPtr downloadInfo); - - /** - * @fn OnFragmentDownloadFailed - * @brief Callback function to be called after fragment download is failed - * @param[in] downloadInfo - download information - */ - void OnFragmentDownloadFailed(DownloadInfoPtr downloadInfo); - uint64_t FindPositionInTimeline(class MediaStreamContext *pMediaStreamContext, const std::vector&timelines); /** @@ -1130,6 +1168,7 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP int mPrevAdaptationSetCount; std::vector mBitrateIndexVector; bool playlistDownloaderThreadStarted; // Playlist downloader thread start status + bool isVidDiscInitFragFail; double mLivePeriodCulledSeconds; bool mIsSegmentTimelineEnabled; /**< Flag to indicate if segment timeline is enabled, to determine if PTS is available from manifest */ // In case of streams with multiple video Adaptation Sets, A profile @@ -1246,6 +1285,7 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP double mFragmentTimeOffset; /**< denotes the offset added to fragment time when absolute timeline is disabled, holds currentPeriodOffset*/ bool mShortAdOffsetCalc; AampTime mNextPts; /*For PTS restamping*/ + std::vector> mTrackWorkers; /**< Track workers for fetching fragments*/ bool mIsFinalFirstPTS; /**< Flag to indicate if the first PTS is final or not */ }; diff --git a/priv_aamp.cpp b/priv_aamp.cpp index eeff24f04..b515a9c22 100644 --- a/priv_aamp.cpp +++ b/priv_aamp.cpp @@ -1298,7 +1298,6 @@ PrivateInstanceAAMP::PrivateInstanceAAMP(AampConfig *config) : mReportProgressPo , mIsChunkMode(false) , prevFirstPeriodStartTime(0) , mIsFlushOperationInProgress(false) - , mAampTrackWorkerManager() , mThumbnailLastProgramDateTime(0) , mLastSleThumbnailInfo() { @@ -1380,7 +1379,6 @@ PrivateInstanceAAMP::PrivateInstanceAAMP(AampConfig *config) : mReportProgressPo mAsyncTuneEnabled = ISCONFIGSET_PRIV(eAAMPConfig_AsyncTune); AampGrowableBuffer::EnableLogging(ISCONFIGSET_PRIV(eAAMPConfig_TrackMemory)); mLastTelemetryTimeMS = aamp_GetCurrentTimeMS(); - mAampTrackWorkerManager = std::make_shared(); } /** @@ -1388,7 +1386,6 @@ PrivateInstanceAAMP::PrivateInstanceAAMP(AampConfig *config) : mReportProgressPo */ PrivateInstanceAAMP::~PrivateInstanceAAMP() { - mAampTrackWorkerManager.reset(); StopPausePositionMonitoring("AAMP destroyed"); PlayerCCManager::GetInstance()->Release(mCCId); mCCId = 0; @@ -6749,10 +6746,6 @@ void PrivateInstanceAAMP::detach() seek_pos_seconds = GetPositionSeconds(); AAMPLOG_WARN("Player %s=>%s and soft release.Detach at position %f", STRFGPLAYER, STRBGPLAYER,seek_pos_seconds ); DisableDownloads(); //disable download - if (mAampTrackWorkerManager) - { - mAampTrackWorkerManager->StopWorkers(); - } mpStreamAbstractionAAMP->SeekPosUpdate(seek_pos_seconds ); mpStreamAbstractionAAMP->StopInjection(); if(mMPDDownloaderInstance != nullptr) @@ -7701,13 +7694,6 @@ void PrivateInstanceAAMP::Stop( bool isDestructing ) if (mpStreamAbstractionAAMP) { AcquireStreamLock(); - if(DownloadsAreEnabled()) - { - // Parallel TuneHelper after EOS or retune re-enables downloads - // but we need to disable them again before stopping the player - AAMPLOG_WARN("Re-Enabled downloads after Stop, Disabling again!!"); - DisableDownloads(); // disable download - } if (mDRMLicenseManager) { ReleaseDynamicDRMToUpdateWait(); @@ -8995,13 +8981,9 @@ void PrivateInstanceAAMP::SendStalledErrorEvent() */ void PrivateInstanceAAMP::UpdateSubtitleTimestamp() { - if(TryStreamLock()) + if (mpStreamAbstractionAAMP) { - if (mpStreamAbstractionAAMP) - { - mpStreamAbstractionAAMP->StartSubtitleParser(); - } - ReleaseStreamLock(); + mpStreamAbstractionAAMP->StartSubtitleParser(); } } @@ -9011,13 +8993,9 @@ void PrivateInstanceAAMP::UpdateSubtitleTimestamp() */ void PrivateInstanceAAMP::PauseSubtitleParser(bool pause) { - if(TryStreamLock()) + if (mpStreamAbstractionAAMP) { - if (mpStreamAbstractionAAMP) - { - mpStreamAbstractionAAMP->PauseSubtitleParser(pause); - } - ReleaseStreamLock(); + mpStreamAbstractionAAMP->PauseSubtitleParser(pause); } } diff --git a/priv_aamp.h b/priv_aamp.h index 39ef1c054..8a906c54c 100644 --- a/priv_aamp.h +++ b/priv_aamp.h @@ -54,7 +54,6 @@ #include #include #include -#include #include "AampConfig.h" #include #include @@ -69,7 +68,6 @@ #include "AampLLDASHData.h" #include "AampMPDPeriodInfo.h" #include "TsbApi.h" -#include "AampTrackWorkerManager.hpp" #include "AudioTrackInfo.h" #include "TextTrackInfo.h" #include "AAMPAnomalyMessageType.h" @@ -93,12 +91,6 @@ typedef struct PreCacheUrlData typedef std::vector < PreCacheUrlStruct> PreCacheUrlList; class AampTSBSessionManager; - -namespace aamp -{ - // Other declarations - class AampTrackWorkerManager; // Forward declaration -} #include "ID3Metadata.hpp" #define AAMP_SEEK_TO_LIVE_POSITION (-1) @@ -3986,15 +3978,6 @@ class PrivateInstanceAAMP : public DrmCallbacks, public std::enable_shared_from_ */ void CalculateTrickModePositionEOS(void); - /** - * @fn GetAampTrackWorkerManager - * @brief Get the AampTrackWorkerManager instance - * - * @return AampTrackWorkerManager instance - */ - std::shared_ptr GetAampTrackWorkerManager() { return mAampTrackWorkerManager; } - - /** * @fn GetLivePlayPosition * @@ -4249,7 +4232,6 @@ class PrivateInstanceAAMP : public DrmCallbacks, public std::enable_shared_from_ std::mutex mPreProcessLock; bool mIsChunkMode; /** LLD ChunkMode */ - std::shared_ptr mAampTrackWorkerManager; bool mLocalAAMPTsbFromConfig; /**< AAMP TSB enabled in the configuration, regardless of the current channel */ private: diff --git a/streamabstraction.cpp b/streamabstraction.cpp index da29a96e6..0343f83d4 100644 --- a/streamabstraction.cpp +++ b/streamabstraction.cpp @@ -1352,8 +1352,6 @@ void MediaTrack::ClearMediaHeaderDuration(CachedFragment *fragment) void MediaTrack::ProcessAndInjectFragment(CachedFragment *cachedFragment, bool fragmentDiscarded, bool isDiscontinuity, bool &ret ) { class StreamAbstractionAAMP* pContext = GetContext(); - // This will change for trickplay if restamping is enabled (cachedFragment->duration is changed according to abs rate) - double inFragmentDuration = cachedFragment->duration; if (aamp->GetLLDashChunkMode()) { bool bIgnore = true; @@ -1484,11 +1482,6 @@ void MediaTrack::ProcessAndInjectFragment(CachedFragment *cachedFragment, bool f else { UpdateTSAfterInject(); - auto timeBasedBufferManager = GetTimeBasedBufferManager(); - if (timeBasedBufferManager) - { - timeBasedBufferManager->ConsumeBuffer(inFragmentDuration); - } } } } @@ -2039,7 +2032,7 @@ MediaTrack::MediaTrack(TrackType type, PrivateInstanceAAMP* aamp, const char* na ,mIsLocalTSBInjection(false), mCachedFragmentChunksSize(0) ,mIsoBmffHelper(std::make_shared()) ,mLastFragmentPts(0), mRestampedPts(0), mRestampedDuration(0), mTrickmodeState(TrickmodeState::UNDEF) - ,mTrackParamsMutex(), mCheckForRampdown(false), mTimeBasedBufferManager(nullptr) + ,mTrackParamsMutex(), mCheckForRampdown(false) ,gotLocalTime(false),ptsRollover(false),currentLocalTimeMs(0) { maxCachedFragmentsPerTrack = GETCONFIGVALUE(eAAMPConfig_MaxFragmentCached); diff --git a/test/utests/fakes/FakeAampFragmentDescriptor.cpp b/test/utests/fakes/FakeAampFragmentDescriptor.cpp deleted file mode 100644 index 41686ca62..000000000 --- a/test/utests/fakes/FakeAampFragmentDescriptor.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AampFragmentDescriptor.hpp" - -FragmentDescriptor::FragmentDescriptor() : manifestUrl(""), Bandwidth(0), Number(0), Time(0), RepresentationID(""), matchingBaseURL(""), bUseMatchingBaseUrl(false), nextfragmentNum(-1), nextfragmentTime(0), TimeScale(1) -{ -} - -FragmentDescriptor::FragmentDescriptor(const FragmentDescriptor &p) : manifestUrl(p.manifestUrl), Bandwidth(p.Bandwidth), RepresentationID(p.RepresentationID), Number(p.Number), Time(p.Time), matchingBaseURL(p.matchingBaseURL), bUseMatchingBaseUrl(p.bUseMatchingBaseUrl), nextfragmentNum(p.nextfragmentNum), nextfragmentTime(p.nextfragmentTime), TimeScale(p.TimeScale) -{ -} - -FragmentDescriptor &FragmentDescriptor::operator=(const FragmentDescriptor &p) -{ - manifestUrl = p.manifestUrl; - RepresentationID.assign(p.RepresentationID); - Bandwidth = p.Bandwidth; - Number = p.Number; - Time = p.Time; - matchingBaseURL = p.matchingBaseURL; - nextfragmentNum = p.nextfragmentNum; - nextfragmentTime = p.nextfragmentTime; - TimeScale = p.TimeScale; - return *this; -} - -std::string FragmentDescriptor::GetMatchingBaseUrl() const -{ - return matchingBaseURL; -} - -void FragmentDescriptor::ClearMatchingBaseUrl() -{ - matchingBaseURL.clear(); -} - -void FragmentDescriptor::AppendMatchingBaseUrl(const std::vector *baseUrls) -{ -} - -void FragmentDescriptor::AppendMatchingBaseUrl(const std::vector &baseUrls) -{ -} diff --git a/test/utests/fakes/FakeAampMPDParseHelper.cpp b/test/utests/fakes/FakeAampMPDParseHelper.cpp index 626399aae..226927e93 100644 --- a/test/utests/fakes/FakeAampMPDParseHelper.cpp +++ b/test/utests/fakes/FakeAampMPDParseHelper.cpp @@ -239,18 +239,7 @@ double AampMPDParseHelper::GetPeriodNewContentDurationMs(IPeriod * period, uint6 { return 0; } - -bool AampMPDParseHelper::aamp_HasSegmentTime(IPeriod *period) -{ - return false; -} - -bool AampMPDParseHelper::aamp_HasSegmentTemplate(IPeriod *period) +bool AampMPDParseHelper::aamp_HasSegmentTimeline(IPeriod * period) { return false; -} - -std::shared_ptr AampMPDParseHelper::GetSegmentTemplateForVideo(IPeriod *period) -{ - return nullptr; -} +} \ No newline at end of file diff --git a/test/utests/fakes/FakeAampMPDUtils.cpp b/test/utests/fakes/FakeAampMPDUtils.cpp index 67159acc2..f856ada59 100644 --- a/test/utests/fakes/FakeAampMPDUtils.cpp +++ b/test/utests/fakes/FakeAampMPDUtils.cpp @@ -19,115 +19,11 @@ #include "AampMPDUtils.h" -/** - * @brief Computes the fragment duration - * @param duration of the fragment. - * @param timeScale value. - * @return - computed fragment duration in double. - */ double ComputeFragmentDuration( uint32_t duration, uint32_t timeScale ) { return 0; } - -/** - * @brief Check if mime type is compatible with media type - * @param mimeType mime type - * @param mediaType media type - * @retval true if compatible - */ bool IsCompatibleMimeType(const std::string& mimeType, AampMediaType mediaType) { return false; -} - -/** - * @fn ConstructFragmentURL - * @param[out] fragmentUrl fragment url - * @param[in] fragmentDescriptor descriptor - * @param[in] media media information string - * @param[in] config AAMP configuration - */ -void ConstructFragmentURL( std::string& fragmentUrl, const FragmentDescriptor *fragmentDescriptor, std::string media, AampConfig *config) -{ -} - -/** - * @brief Parse segment index box - * @note The SegmentBase indexRange attribute points to Segment Index Box location with segments and random access points. - * @param start start of box - * @param size size of box - * @param segmentIndex segment index - * @param[out] referenced_size referenced size - * @param[out] referenced_duration referenced duration - * @retval true on success - */ -bool ParseSegmentIndexBox( const char *start, size_t size, int segmentIndex, unsigned int *referenced_size, float *referenced_duration, unsigned int *firstOffset) -{ - return true; -} - -/** - * @brief Read 16 word helper function - * @param pptr pointer to read from - * @retval word value - */ -unsigned int Read16( const char **pptr) -{ - return 0; -} - -/** - * @brief Read 32 word helper function - * @param pptr pointer to read from - * @retval word value - */ -unsigned int Read32( const char **pptr) -{ - return 0; -} - -/** - * @brief Read 64 word helper function - * @param pptr pointer to read from - * @retval word value - */ -uint64_t Read64( const char **pptr) -{ - return 0; -} - -/** - * @brief read unsigned multi-byte value and update buffer pointer - * @param[in] pptr buffer - * @param[in] n word size in bytes - * @retval 32 bit value - */ -uint64_t ReadWordHelper( const char **pptr, int n ) -{ - return 0; -} - -/** - * @brief Replace matching token with given number - * @param str String in which operation to be performed - * @param from token - * @param toNumber number to replace token - * @retval position - */ -int replace(std::string &str, const std::string &from, uint64_t toNumber) -{ - return 0; -} - -/** - * @brief Replace matching token with given string - * @param str String in which operation to be performed - * @param from token - * @param toString string to replace token - * @retval position - */ -int replace(std::string &str, const std::string &from, const std::string &toString) -{ - return 0; -} +} \ No newline at end of file diff --git a/test/utests/fakes/FakeAampTimeBasedBufferManager.cpp b/test/utests/fakes/FakeAampTimeBasedBufferManager.cpp deleted file mode 100644 index 2f6443f21..000000000 --- a/test/utests/fakes/FakeAampTimeBasedBufferManager.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "AampTimeBasedBufferManager.hpp" - -namespace aamp -{ - /** - * @brief Constructor for AampTimeBasedBuffer. - * - * @param[in] maxBufferTime Maximum buffer time in seconds. - * @param[in] trickPlayMultiplier Multiplier for trick play mode (default is 1.0). - * @param[in] mediaType Media type for which this buffer is used. - */ - AampTimeBasedBufferManager::AampTimeBasedBufferManager(int maxBufferTime, double trickPlayMultiplier, AampMediaType) - { - } - - /** - * @brief Populate a specified amount of time to the buffer. - * - * @param[in] fragmentDuration Duration of the fragment in seconds. - */ - void AampTimeBasedBufferManager::PopulateBuffer(double fragmentDuration) - { - } - - /** - * @brief Consume a specified amount of time from the buffer. - * - * @param[in] timeToConsume Amount of time to consume from the buffer in seconds. - */ - void AampTimeBasedBufferManager::ConsumeBuffer(double timeToConsume) - { - } - - /** - * @brief Check if the buffer is full. - * - * @return True if buffer is full, false otherwise. - */ - bool AampTimeBasedBufferManager::IsFull() const - { - return false; - } - - /** - * @brief Clear the buffer to its initial state. - */ - void AampTimeBasedBufferManager::ClearBuffer() - { - } -} // namespace aamp diff --git a/test/utests/fakes/FakeAampTrackWorker.cpp b/test/utests/fakes/FakeAampTrackWorker.cpp index 498ef1ecb..c14846ebb 100644 --- a/test/utests/fakes/FakeAampTrackWorker.cpp +++ b/test/utests/fakes/FakeAampTrackWorker.cpp @@ -2,7 +2,7 @@ * If not stated otherwise in this file or this component's license file the * following copyright and licenses apply: * - * Copyright 2025 RDK Management + * Copyright 2022 RDK Management * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,128 +17,11 @@ * limitations under the License. */ -#include "AampTrackWorker.hpp" -#include "MockAampTrackWorker.h" - -MockAampTrackWorker *g_mockAampTrackWorker = nullptr; +#include "AampTrackWorker.h" namespace aamp { - /** - * @brief Default constructor for AampTrackWorkerJob. - * - * Initializes the promise and sets the shared future. - */ - AampTrackWorkerJob::AampTrackWorkerJob() - : mCancelled(false), - mPromise() - { - mSharedFuture = mPromise.get_future().share(); - AAMPLOG_DEBUG("AampTrackWorkerJob constructor"); - } - - /** - * @brief Destructor for AampTrackWorkerJob. - * - * Cleans up resources used by the job. - */ - AampTrackWorkerJob::~AampTrackWorkerJob() = default; - - /** - * @brief Runs the job in the worker thread. - * - * This method is called by the worker thread to execute the job. - * It catches any exceptions thrown during execution and sets them on the promise. - */ - void AampTrackWorkerJob::Run() - { - try - { - if (!mCancelled.load()) - { - Execute(); // calls derived class's Execute method - } - mPromise.set_value(); // Set the promise to indicate job completion - } - catch (...) - { - try - { - mPromise.set_exception(std::current_exception()); // Set the exception on the promise - } - catch (...) - { - AAMPLOG_ERR("Exception in AampTrackWorkerJob::Run: Failed to set exception on promise"); - } - } - } - - /** - * @brief Default implementation of Execute method. - * - * This method does nothing by default and should be overridden in derived classes. - */ - void AampTrackWorkerJob::Execute() - { - // Default implementation does nothing - } - - /** - * @brief Clones the job for worker pool. - * - * This method creates a new instance of AampTrackWorkerJob. - * - * @return std::unique_ptr A unique pointer to the cloned job. - */ - std::unique_ptr AampTrackWorkerJob::Clone() const - { - return aamp_utils::make_unique(); - } - - /** - * @brief Cancels the job by setting the cancelled flag. - * - * If the job is already cancelled, it does nothing. - * If not, it sets the exception on the promise to indicate cancellation. - */ - void AampTrackWorkerJob::SetCancelled() - { - if (!mCancelled.exchange(true)) // Atomically set cancelled to true - { - try - { - mPromise.set_exception(std::make_exception_ptr(std::runtime_error("Job cancelled"))); // Set exception on promise - } - catch (...) - { - AAMPLOG_ERR("Exception in AampTrackWorkerJob::SetCancelled: Failed to set exception on promise"); - } - } - } - - /** - * @brief Checks if the job has been cancelled. - * - * @return true if the job is cancelled, false otherwise. - */ - bool AampTrackWorkerJob::IsCancelled() const - { - return mCancelled.load(); // Return the current value of cancelled flag - } - - /** - * @brief Gets a future to wait for job completion. - * - * This method returns a shared_future that can be used to wait for the job to complete. - * - * @return std::shared_future A future that will be set when the job is completed. - */ - std::shared_future AampTrackWorkerJob::GetFuture() const - { - return mSharedFuture; // Return the shared future for job completion - } - /** * @brief Constructs an AampTrackWorker object. * @@ -149,7 +32,7 @@ namespace aamp * */ AampTrackWorker::AampTrackWorker(PrivateInstanceAAMP *_aamp, AampMediaType _mediaType) - : aamp(_aamp), mMediaType(_mediaType), mStop(false), mWorkerThread(), mJobQueue(), mQueueMutex(), mCondVar(), mPaused(false), mActiveJob(nullptr), mInitialized(false) + : aamp(_aamp), mMediaType(_mediaType), mJobAvailable(false), mStop(false), mWorkerThread(), mJob(), mMutex(), mCondVar(), mCompletionVar() { } @@ -170,101 +53,33 @@ namespace aamp * The job is a function that will be executed by the worker thread. * * @param[in] job The job to be executed by the worker thread. - * @param[in] highPriority Indicates whether the job is high priority. - * - * @return void - */ - std::shared_future AampTrackWorker::SubmitJob(AampTrackWorkerJobSharedPtr job, bool highPriority) - { - if(job) - { - job->Run(); // Execute the job immediately for testing purposes - return job->GetFuture(); // Return the future representing the job completion - } - return std::shared_future(); // Return an empty future if job is null - } - - /** - * @brief The main function executed by the worker thread. - * - * @param[in] weakSelf A weak pointer to the AampTrackWorker instance. - * - * Waits for jobs to be submitted, processes them, and signals their completion. - * The function runs in a loop until the worker is signaled to stop. - * - * @return void - */ - void AampTrackWorker::ProcessJob(AampTrackWorkerWeakPtr weakSelf) - { - } - - /** - * @brief Pauses the worker thread. - * - * Blocks the worker thread until Resume() is called. - * - * @return void - */ - void AampTrackWorker::Pause() - { - } - - /** - * @brief Resumes the worker thread. - * - * Unblocks the worker thread if it is paused. - * - * @return void - */ - void AampTrackWorker::Resume() - { - } - - /** - * @brief Clears the job queue. - * - * Removes all jobs from the queue. - * - * @return void - */ - void AampTrackWorker::ClearJobs() - { - } - - /** - * @brief Reschedules the active job. - * - * Moves the active job to the front of the queue. * * @return void */ - void AampTrackWorker::RescheduleActiveJob() + void AampTrackWorker::SubmitJob(std::function job) { - if(g_mockAampTrackWorker) - { - g_mockAampTrackWorker->RescheduleActiveJob(); - } } /** - * @brief Starts the worker thread. + * @brief Waits for the current job to complete. * - * Creates the worker thread and starts processing jobs. + * Blocks the calling thread until the current job has been processed by the worker thread. * * @return void */ - void AampTrackWorker::StartWorker() + void AampTrackWorker::WaitForCompletion() { } /** - * @brief Stops the worker thread. + * @brief The main function executed by the worker thread. * - * Signals the worker thread to stop and waits for it to finish. + * Waits for jobs to be submitted, processes them, and signals their completion. + * The function runs in a loop until the worker is signaled to stop. * * @return void */ - void AampTrackWorker::StopWorker() + void AampTrackWorker::ProcessJob() { } diff --git a/test/utests/fakes/FakeAampTrackWorkerManager.cpp b/test/utests/fakes/FakeAampTrackWorkerManager.cpp deleted file mode 100644 index b7d04020b..000000000 --- a/test/utests/fakes/FakeAampTrackWorkerManager.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2024 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AampTrackWorkerManager.hpp" - -namespace aamp -{ - /** - * @brief Default constructor. - */ - AampTrackWorkerManager::AampTrackWorkerManager() - { - } - - /** - * @brief Default destructor. - */ - AampTrackWorkerManager::~AampTrackWorkerManager() - { - } - - /** - * @brief Creates an AampTrackWorker instance. - * - * If an instance with the same media type already exists, it returns the existing instance. - * @param[in] aamp Pointer to the PrivateInstanceAAMP. - * @param[in] mediaType The media type for the worker. - * - * @return Shared pointer to the created or existing AampTrackWorker instance. - */ - std::shared_ptr AampTrackWorkerManager::CreateWorker(PrivateInstanceAAMP *aamp, AampMediaType mediaType) - { - return nullptr; - } - - /** - * @brief Gets an existing AampTrackWorker instance. - * - * @param[in] mediaType The media type of the worker. - * - * @return Shared pointer to the AampTrackWorker instance, or nullptr if not found. - */ - std::shared_ptr AampTrackWorkerManager::GetWorker(AampMediaType mediaType) - { - return nullptr; - } - - /** - * @brief Removes all AampTrackWorker instances. - * - * Removes the worker instances - */ - void AampTrackWorkerManager::RemoveWorkers() - { - } - - /** - * @brief Starts all workers. - */ - void AampTrackWorkerManager::StartWorkers() - { - } - - /** - * @brief Stops all workers. - */ - void AampTrackWorkerManager::StopWorkers() - { - } - - /** - * @brief Waits for all workers to complete their jobs. - * - * @param[in] timeInterval The time interval to wait for each onTimeout in milliseconds. - * @param[in] onTimeout callback function - */ - void AampTrackWorkerManager::WaitForCompletionWithTimeout(int timeout, std::function onTimeout) - { - } - - /** - * @brief Checks if there are any workers. - * - * @return True if there are no workers, false otherwise. - */ - bool AampTrackWorkerManager::IsEmpty() - { - return false; - } - - /** - * @brief Submits a job to the specified worker. - * - * @param[in] mediaType The media type of the worker. - * @param[in] job The job to submit. - * @param[in] highPriority Whether the job should be treated as high priority. - * - * @note If the worker is not found, a default-constructed future is returned. - * @return A future representing the submitted job, or a default-constructed future if worker not found. - */ - std::shared_future AampTrackWorkerManager::SubmitJob(AampMediaType mediaType, std::shared_ptr job, bool highPriority) - { - if (job) - { - job->Run(); // Execute the job immediately for testing purposes - return job->GetFuture(); - } - else - { - AAMPLOG_ERR("AampTrackWorkerManager::SubmitJob: Job is null"); - return std::shared_future(); - } - } - - /** - * @brief Reset the worker by clearing all jobs - * - * @param[in] mediaType The media type of the worker to reset. - */ - void AampTrackWorkerManager::ResetWorker(AampMediaType mediaType) - { - } - - /** - * @brief Gets the count of workers. - * - * @return The number of workers. - */ - size_t AampTrackWorkerManager::GetWorkerCount() - { - return 0; - } -} // namespace aamp diff --git a/test/utests/fakes/FakeAdManager.cpp b/test/utests/fakes/FakeAdManager.cpp index 37962bda3..4d4ea843a 100644 --- a/test/utests/fakes/FakeAdManager.cpp +++ b/test/utests/fakes/FakeAdManager.cpp @@ -114,10 +114,6 @@ void PrivateCDAIObjectMPD::NotifyAdLoopWait() { } -void PrivateCDAIObjectMPD::StopFulfillAdLoop() -{ -} - bool PrivateCDAIObjectMPD::WaitForNextAdResolved(int timeoutMs) { if(g_MockPrivateCDAIObjectMPD != nullptr) diff --git a/test/utests/fakes/FakeMediaStreamContext.cpp b/test/utests/fakes/FakeMediaStreamContext.cpp index ab461fa33..bfe87fedb 100644 --- a/test/utests/fakes/FakeMediaStreamContext.cpp +++ b/test/utests/fakes/FakeMediaStreamContext.cpp @@ -27,13 +27,13 @@ bool MediaStreamContext::CacheFragmentChunk(AampMediaType actualType, const char return false; } -bool MediaStreamContext::CacheFragment(std::string fragmentUrl, unsigned int curlInstance, double position, double duration, const char *range, bool initSegment, bool discontinuity, bool playingAd, uint32_t scale) +bool MediaStreamContext::CacheFragment(std::string fragmentUrl, unsigned int curlInstance, double position, double duration, const char *range, bool initSegment, bool discontinuity, bool playingAd, double pto, uint32_t scale, bool overWriteTrackId) { bool rv = true; if (g_mockMediaStreamContext != nullptr) { - rv = g_mockMediaStreamContext->CacheFragment(fragmentUrl, curlInstance, position, duration, range, initSegment, discontinuity, playingAd, scale); + rv = g_mockMediaStreamContext->CacheFragment(fragmentUrl, curlInstance, position, duration, range, initSegment, discontinuity, playingAd, pto, scale, overWriteTrackId); } return rv; @@ -115,25 +115,3 @@ bool MediaStreamContext::CacheTsbFragment(std::shared_ptr fragme return false; } } - -void MediaStreamContext::OnFragmentDownloadFailed(DownloadInfoPtr downloadInfo) -{ -} - -void MediaStreamContext::OnFragmentDownloadSuccess(DownloadInfoPtr downloadInfo) -{ -} - -bool MediaStreamContext::DownloadFragment(DownloadInfoPtr downloadInfo) -{ - if(downloadInfo->uriList.size() > 0) - { - downloadInfo->url = downloadInfo->uriList.begin()->second.url; - } - - if (g_mockMediaStreamContext != nullptr) - { - return g_mockMediaStreamContext->CacheFragment(downloadInfo->url, downloadInfo->curlInstance, downloadInfo->pts, downloadInfo->fragmentDurationSec, downloadInfo->range.c_str(), downloadInfo->isInitSegment, downloadInfo->isDiscontinuity, downloadInfo->isPlayingAd, downloadInfo->timeScale); - } - return false; -} \ No newline at end of file diff --git a/test/utests/fakes/FakePrivateInstanceAAMP.cpp b/test/utests/fakes/FakePrivateInstanceAAMP.cpp index ce290fdb3..633d8883d 100644 --- a/test/utests/fakes/FakePrivateInstanceAAMP.cpp +++ b/test/utests/fakes/FakePrivateInstanceAAMP.cpp @@ -138,8 +138,7 @@ PrivateInstanceAAMP::PrivateInstanceAAMP(AampConfig *config) : mAudioFormat(), mPreviousAudioType(), mAuxFormat(), - mCurlShared(), - mIsChunkMode(false) + mCurlShared() { } diff --git a/test/utests/fakes/FakeStreamAbstractionAamp.cpp b/test/utests/fakes/FakeStreamAbstractionAamp.cpp index 0f34e8bc2..167d14bce 100644 --- a/test/utests/fakes/FakeStreamAbstractionAamp.cpp +++ b/test/utests/fakes/FakeStreamAbstractionAamp.cpp @@ -19,11 +19,9 @@ #include "StreamAbstractionAAMP.h" #include "MockStreamAbstractionAAMP.h" -#include "MockMediaTrack.h" #include MockStreamAbstractionAAMP *g_mockStreamAbstractionAAMP = nullptr; -MockMediaTrack *g_mockMediaTrack = nullptr; StreamAbstractionAAMP::StreamAbstractionAAMP(PrivateInstanceAAMP* aamp, id3_callback_t mID3Handler) : aamp(nullptr), mAudiostateChangeCount(0), mESChangeStatus(false) { @@ -193,14 +191,7 @@ int MediaTrack::GetCurrentBandWidth() CachedFragment* MediaTrack::GetFetchBuffer(bool initialize) { - if (g_mockMediaTrack != nullptr) - { - return g_mockMediaTrack->GetFetchBuffer(initialize); - } - else - { - return NULL; - } + return NULL; } AampMediaType MediaTrack::GetPlaylistMediaTypeFromTrack(TrackType type, bool isIframe) @@ -224,7 +215,7 @@ void MediaTrack::StartPlaylistDownloaderThread() { } -MediaTrack::MediaTrack(TrackType type, PrivateInstanceAAMP* aamp, const char* name) : parsedBufferChunk("parsedBufferChunk"), unparsedBufferChunk("unparsedBufferChunk"), name(name), aamp(aamp), type(type) +MediaTrack::MediaTrack(TrackType type, PrivateInstanceAAMP* aamp, const char* name) : parsedBufferChunk("parsedBufferChunk"), unparsedBufferChunk("unparsedBufferChunk"), name(name) { } @@ -242,10 +233,6 @@ void MediaTrack::StopPlaylistDownloaderThread() void MediaTrack::UpdateTSAfterFetch(bool isInitSegment) { - if(g_mockMediaTrack != nullptr) - { - g_mockMediaTrack->UpdateTSAfterFetch(isInitSegment); - } } bool MediaTrack::WaitForFreeFragmentAvailable( int timeoutMs) @@ -299,26 +286,12 @@ bool MediaTrack::IsLocalTSBInjection() bool StreamAbstractionAAMP::CheckForRampDownLimitReached() { - if (g_mockStreamAbstractionAAMP != nullptr) - { - return g_mockStreamAbstractionAAMP->CheckForRampDownLimitReached(); - } - else - { - return false; - } + return true; } bool StreamAbstractionAAMP::CheckForRampDownProfile(int http_error) { - if (g_mockStreamAbstractionAAMP != nullptr) - { - return g_mockStreamAbstractionAAMP->CheckForRampDownProfile(http_error); - } - else - { - return false; - } + return true; } double StreamAbstractionAAMP::LastVideoFragParsedTimeMS(void) @@ -381,22 +354,11 @@ bool StreamAbstractionAAMP::IsStreamerAtLivePoint(double seekPosition) CachedFragment* MediaTrack::GetFetchChunkBuffer(bool initialize) { - if (g_mockMediaTrack != nullptr) - { - return g_mockMediaTrack->GetFetchChunkBuffer(initialize); - } - else - { - return NULL; - } + return NULL; } void MediaTrack::UpdateTSAfterChunkFetch() { - if(g_mockMediaTrack != nullptr) - { - g_mockMediaTrack->UpdateTSAfterChunkFetch(); - } } void StreamAbstractionAAMP::UpdateRampUpOrDownProfileReason(void) @@ -424,10 +386,6 @@ void MediaTrack::SetCachedFragmentChunksSize(size_t size) } void MediaTrack::UpdateTSAfterInject() { - if(g_mockMediaTrack != nullptr) - { - g_mockMediaTrack->UpdateTSAfterInject(); - } } void StreamAbstractionAAMP::UpdateStreamInfoBitrateData(int profileIndex, StreamInfo &cacheFragStreamInfo) { @@ -470,15 +428,8 @@ void MediaTrack::FlushFragmentChunks() bool MediaTrack::IsInjectionFromCachedFragmentChunks() { - if (g_mockMediaTrack != nullptr) - { - return g_mockMediaTrack->IsInjectionFromCachedFragmentChunks(); - } - else - { - bool ret = false; - return ret; - } + bool ret = false; + return ret; } void MediaTrack::ClearMediaHeaderDuration(CachedFragment* cachedFragment) diff --git a/test/utests/mocks/MockAampTrackWorker.h b/test/utests/mocks/MockAampTrackWorker.h deleted file mode 100644 index f9652947a..000000000 --- a/test/utests/mocks/MockAampTrackWorker.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAMP_MOOCK_TRACK_WORKER_H -#define AAMP_MOOCK_TRACK_WORKER_H - -#include -#include "AampTrackWorker.hpp" - -class MockAampTrackWorker -{ -public: - MOCK_METHOD(void, SubmitJob, (aamp::AampTrackWorkerJobSharedPtr job, bool highPriority)); - MOCK_METHOD(void, RescheduleActiveJob, ()); -}; - -extern MockAampTrackWorker *g_mockAampTrackWorker; - -#endif /* AAMP_MOOCK_TRACK_WORKER_H */ diff --git a/test/utests/mocks/MockMediaStreamContext.h b/test/utests/mocks/MockMediaStreamContext.h index c66f346a3..8e73ba901 100644 --- a/test/utests/mocks/MockMediaStreamContext.h +++ b/test/utests/mocks/MockMediaStreamContext.h @@ -27,7 +27,7 @@ class MockMediaStreamContext { public: - MOCK_METHOD(bool, CacheFragment, (std::string fragmentUrl, unsigned int curlInstance, double position, double duration, const char *range, bool initSegment, bool discontinuity, bool playingAd, uint32_t scale)); + MOCK_METHOD(bool, CacheFragment, (std::string fragmentUrl, unsigned int curlInstance, double position, double duration, const char *range, bool initSegment, bool discontinuity, bool playingAd, double pto, uint32_t scale, bool overWriteTrackId)); MOCK_METHOD(bool, CacheTsbFragment, (std::shared_ptr fragment)); }; diff --git a/test/utests/mocks/MockMediaTrack.h b/test/utests/mocks/MockMediaTrack.h deleted file mode 100644 index 4c484e77e..000000000 --- a/test/utests/mocks/MockMediaTrack.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -* If not stated otherwise in this file or this component's license file the -* following copyright and licenses apply: -* -* Copyright 2025 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#ifndef AAMP_MOCK_MEDIA_TRACK_H -#define AAMP_MOCK_MEDIA_TRACK_H - -#include -#include "StreamAbstractionAAMP.h" - -class MockMediaTrack : public MediaTrack -{ -public: - MockMediaTrack(TrackType type, PrivateInstanceAAMP *aamp, const char *name) - : MediaTrack(type, aamp, name) {} - MockMediaTrack() - : MediaTrack(eTRACK_VIDEO, nullptr, "mock") {} - MOCK_METHOD(CachedFragment*, GetFetchBuffer, (bool initialize)); - MOCK_METHOD(CachedFragment*, GetFetchChunkBuffer, (bool initialize)); - MOCK_METHOD(void, UpdateTSAfterFetch, (bool isInitSegment)); - MOCK_METHOD(void, UpdateTSAfterChunkFetch, ()); - MOCK_METHOD(void, UpdateTSAfterInject, ()); - MOCK_METHOD(bool, IsInjectionFromCachedFragmentChunks, ()); - MOCK_METHOD(bool, IsLocalTSBInjection, ()); - MOCK_METHOD(bool, Enabled, ()); - MOCK_METHOD(void, ProcessPlaylist, (AampGrowableBuffer& newPlaylist, int http_error), (override)); - MOCK_METHOD(std::string&, GetPlaylistUrl, (), (override)); - MOCK_METHOD(std::string&, GetEffectivePlaylistUrl, (), (override)); - MOCK_METHOD(void, SetEffectivePlaylistUrl, (std::string url), (override)); - MOCK_METHOD(long long, GetLastPlaylistDownloadTime, (), (override)); - MOCK_METHOD(void, SetLastPlaylistDownloadTime, (long long time), (override)); - MOCK_METHOD(long, GetMinUpdateDuration, (), (override)); - MOCK_METHOD(int, GetDefaultDurationBetweenPlaylistUpdates, (), (override)); - MOCK_METHOD(void, ABRProfileChanged, (), (override)); - MOCK_METHOD(void, updateSkipPoint, (double position, double duration ), (override)); - MOCK_METHOD(void, setDiscontinuityState, (bool isDiscontinuity), (override)); - MOCK_METHOD(void, abortWaitForVideoPTS, (), (override)); - MOCK_METHOD(double, GetBufferedDuration, (), (override)); - MOCK_METHOD(class StreamAbstractionAAMP*, GetContext, (), (override)); - MOCK_METHOD(void, InjectFragmentInternal, (CachedFragment* cachedFragment, bool &fragmentDiscarded,bool isDiscontinuity), (override)); - MOCK_METHOD(double, GetTotalInjectedDuration, (), (override)); - MOCK_METHOD(void, ResetTrickModePtsRestamping, (), (override)); -}; - -extern MockMediaTrack *g_mockMediaTrack; - -#endif /* AAMP_MOCK_MEDIA_TRACK_H */ diff --git a/test/utests/mocks/MockStreamAbstractionAAMP.h b/test/utests/mocks/MockStreamAbstractionAAMP.h index b75770ff8..bfa51cd99 100644 --- a/test/utests/mocks/MockStreamAbstractionAAMP.h +++ b/test/utests/mocks/MockStreamAbstractionAAMP.h @@ -23,6 +23,33 @@ #include #include "StreamAbstractionAAMP.h" +class MockMediaTrack : public MediaTrack +{ +public: + MockMediaTrack(TrackType type, PrivateInstanceAAMP *aamp, const char *name) + : MediaTrack(type, aamp, name) {} + MOCK_METHOD(bool, IsLocalTSBInjection, ()); + MOCK_METHOD(bool, Enabled, ()); + MOCK_METHOD(void, ProcessPlaylist, (AampGrowableBuffer& newPlaylist, int http_error),(override)); + MOCK_METHOD(std::string&, GetPlaylistUrl, (), (override)); + MOCK_METHOD(std::string&, GetEffectivePlaylistUrl, (), (override)); + MOCK_METHOD(void, SetEffectivePlaylistUrl, (std::string url), (override)); + MOCK_METHOD(long long, GetLastPlaylistDownloadTime, (), (override)); + MOCK_METHOD(void, SetLastPlaylistDownloadTime, (long long time), (override)); + MOCK_METHOD(long, GetMinUpdateDuration, (), (override)); + MOCK_METHOD(int, GetDefaultDurationBetweenPlaylistUpdates, (), (override)); + MOCK_METHOD(void, ABRProfileChanged, (), (override)); + MOCK_METHOD(void, updateSkipPoint, (double position, double duration ), (override)); + MOCK_METHOD(void, setDiscontinuityState, (bool isDiscontinuity), (override)); + MOCK_METHOD(void, abortWaitForVideoPTS, (), (override)); + MOCK_METHOD(double, GetBufferedDuration, (), (override)); + MOCK_METHOD(class StreamAbstractionAAMP*, GetContext, (), (override)); + MOCK_METHOD(void, InjectFragmentInternal, (CachedFragment* cachedFragment, bool &fragmentDiscarded,bool isDiscontinuity), (override)); + + MOCK_METHOD(double, GetTotalInjectedDuration, (), (override)); + MOCK_METHOD(void, ResetTrickModePtsRestamping, (), (override)); +}; + class MockStreamAbstractionAAMP : public StreamAbstractionAAMP { public: @@ -91,10 +118,6 @@ class MockStreamAbstractionAAMP : public StreamAbstractionAAMP MOCK_METHOD(bool, IsEOSReached, (), (override)); - MOCK_METHOD(bool, CheckForRampDownLimitReached, ()); - - MOCK_METHOD(bool, CheckForRampDownProfile, (int http_error)); - MOCK_METHOD(bool, DoEarlyStreamSinkFlush, (bool newTune, float rate), (override)); MOCK_METHOD(void, ReinitializeInjection, (double rate)); diff --git a/test/utests/tests/AampDrmLegacy/CMakeLists.txt b/test/utests/tests/AampDrmLegacy/CMakeLists.txt index 6f39fcf67..59400012d 100644 --- a/test/utests/tests/AampDrmLegacy/CMakeLists.txt +++ b/test/utests/tests/AampDrmLegacy/CMakeLists.txt @@ -76,7 +76,6 @@ set(FAKE_SOURCES ${UTESTS_ROOT}/fakes/FakeAampRfc.cpp set(AAMP_SOURCES ${AAMP_ROOT}/AampConfig.cpp ${AAMP_ROOT}/drm/AampDRMLicManager.cpp - ${AAMP_ROOT}/AampFragmentDescriptor.cpp ${AAMP_ROOT}/downloader/AampCurlStore.cpp ${AAMP_ROOT}/downloader/AampCurlDownloader.cpp ${AAMP_ROOT}/AampDRMLicPreFetcher.cpp diff --git a/test/utests/tests/AampTSBSessionManager/FunctionalTests.cpp b/test/utests/tests/AampTSBSessionManager/FunctionalTests.cpp index 0035ade31..1e7e37042 100644 --- a/test/utests/tests/AampTSBSessionManager/FunctionalTests.cpp +++ b/test/utests/tests/AampTSBSessionManager/FunctionalTests.cpp @@ -336,8 +336,6 @@ TEST_F(FunctionalTests, TSBReadTests) constexpr double FRAG_FIRST_PTS = 69.0; constexpr double FRAG_PTS_OFFSET = -50.0; size_t TEST_DATA_LEN = strlen(TEST_DATA); - EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_MaxDownloadBuffer)) - .WillOnce(Return(DEFAULT_MAX_DOWNLOAD_BUFFER)); class MediaStreamContext videoCtx(eTRACK_VIDEO, NULL, aamp, "video"); std::shared_ptr cachedFragment = std::make_shared(); diff --git a/test/utests/tests/AampTrackWorkerTests/AampTrackWorkerManagerTests.cpp b/test/utests/tests/AampTrackWorkerTests/AampTrackWorkerManagerTests.cpp deleted file mode 100644 index 3a8be1c8d..000000000 --- a/test/utests/tests/AampTrackWorkerTests/AampTrackWorkerManagerTests.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2024 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "AampTrackWorkerManager.hpp" -#include "MediaSegmentDownloadJob.hpp" -#include "priv_aamp.h" - -using namespace aamp; -using ::testing::_; -using ::testing::Return; -using ::testing::StrictMock; - -/** - * @brief Test fixture for AampTrackWorkerManager tests. - */ -class AampTrackWorkerManagerTest : public ::testing::Test -{ -protected: - std::shared_ptr mPrivateInstanceAAMP; - std::shared_ptr mTrackWorkerManager; - - void SetUp() override - { - mPrivateInstanceAAMP = std::make_shared(nullptr); - mTrackWorkerManager = std::make_shared(); - } - - void TearDown() override - { - mTrackWorkerManager->StopWorkers(); - mTrackWorkerManager->RemoveWorkers(); - } -}; - -/** - * @brief Test creating a worker successfully. - */ -TEST_F(AampTrackWorkerManagerTest, CreateWorkerSuccessfully) -{ - auto worker = mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_VIDEO); - EXPECT_NE(worker, nullptr); - EXPECT_EQ(mTrackWorkerManager->GetWorker(AampMediaType::eMEDIATYPE_VIDEO), worker); -} - -/** - * @brief Test that creating a worker with the same media type returns the existing instance. - */ -TEST_F(AampTrackWorkerManagerTest, CreateWorkerReturnsSameInstance) -{ - auto worker1 = mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_AUDIO); - auto worker2 = mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_AUDIO); - EXPECT_EQ(worker1, worker2); // Should return the same instance -} - -/** - * @brief Test getting a worker that does not exist returns nullptr. - */ -TEST_F(AampTrackWorkerManagerTest, GetWorkerReturnsNullIfNotExists) -{ - EXPECT_EQ(mTrackWorkerManager->GetWorker(AampMediaType::eMEDIATYPE_AUX_AUDIO), nullptr); -} - -/** - * @brief Test RemoveWorkers API clears all workers. - */ -TEST_F(AampTrackWorkerManagerTest, RemoveWorkersClearsAllWorkers) -{ - mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_VIDEO); - mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_AUDIO); - mTrackWorkerManager->StartWorkers(); - - mTrackWorkerManager->StopWorkers(); - mTrackWorkerManager->RemoveWorkers(); - EXPECT_EQ(mTrackWorkerManager->GetWorker(AampMediaType::eMEDIATYPE_VIDEO), nullptr); - EXPECT_EQ(mTrackWorkerManager->GetWorker(AampMediaType::eMEDIATYPE_AUDIO), nullptr); -} - -/** - * @brief Test StopWorkers pauses and stops all workers. - */ -TEST_F(AampTrackWorkerManagerTest, StopWorkersPausesAndStopsAllWorkers) -{ - auto worker = mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_VIDEO); - mTrackWorkerManager->StartWorkers(); - EXPECT_NE(worker, nullptr); - - mTrackWorkerManager->StopWorkers(); - EXPECT_TRUE(worker->IsStopped()); -} - -/** - * @brief Test WaitForCompletionWithTimeout works as expected. - */ -TEST_F(AampTrackWorkerManagerTest, WaitForCompletionWorks) -{ - auto worker = mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_VIDEO); - EXPECT_NE(worker, nullptr); - - // Submit a job that will take some time to complete - auto job = std::make_shared(nullptr, []() { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Simulate a long-running job - }); - - // Submit the job to the worker - auto future = worker->SubmitJob(job, false); - EXPECT_TRUE(future.valid()); - - bool timeoutOccurred = false; - // Wait for completion with a timeout - mTrackWorkerManager->StartWorkers(); - mTrackWorkerManager->WaitForCompletionWithTimeout(50, [&]() { timeoutOccurred = true; mTrackWorkerManager->StopWorkers(); }); - EXPECT_TRUE(timeoutOccurred); - EXPECT_TRUE(worker->IsStopped()); -} - -/** - * @brief Test WaitForCompletionWithTimeout skips non-critical workers. - */ -TEST_F(AampTrackWorkerManagerTest, WaitForCompletionSkipsNonCriticalWorkers) -{ - auto videoWorker = mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_VIDEO); - auto textWorker = mTrackWorkerManager->CreateWorker(mPrivateInstanceAAMP.get(), AampMediaType::eMEDIATYPE_SUBTITLE); - EXPECT_NE(videoWorker, nullptr); - EXPECT_NE(textWorker, nullptr); - // Submit a job that will take some time to complete - auto videoJob = std::make_shared(nullptr, []() { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Simulate - }); - auto textJob = std::make_shared(nullptr, []() { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Simulate - }); - // Submit the job to the worker - auto videoFuture = videoWorker->SubmitJob(videoJob, false); - auto textFuture = textWorker->SubmitJob(textJob, false); - EXPECT_TRUE(videoFuture.valid()); - EXPECT_TRUE(textFuture.valid()); - bool timeoutOccurred = false; - // Wait for completion with a timeout - mTrackWorkerManager->StartWorkers(); - mTrackWorkerManager->WaitForCompletionWithTimeout(50, [&]() { timeoutOccurred = true; mTrackWorkerManager->StopWorkers(); }); - EXPECT_TRUE(timeoutOccurred); - EXPECT_TRUE(videoWorker->IsStopped()); - EXPECT_TRUE(textWorker->IsStopped()); // Text worker should be stopped but was skipped in wait -} diff --git a/test/utests/tests/AampTrackWorkerTests/CMakeLists.txt b/test/utests/tests/AampTrackWorkerTests/CMakeLists.txt index b0b777d4c..601ab89f4 100644 --- a/test/utests/tests/AampTrackWorkerTests/CMakeLists.txt +++ b/test/utests/tests/AampTrackWorkerTests/CMakeLists.txt @@ -36,11 +36,10 @@ include_directories(${LibXml2_INCLUDE_DIRS}) include_directories(${UTESTS_ROOT}/mocks) set(TEST_SOURCES FunctionalTests.cpp - AampTrackWorkerManagerTests.cpp AampTrackWorkerTest.cpp) -set(AAMP_SOURCES ${AAMP_ROOT}/AampTrackWorker.cpp ${AAMP_ROOT}/AampTrackWorkerManager.cpp) +set(AAMP_SOURCES ${AAMP_ROOT}/AampTrackWorker.cpp) add_executable(${EXEC_NAME} ${TEST_SOURCES} diff --git a/test/utests/tests/AampTrackWorkerTests/FunctionalTests.cpp b/test/utests/tests/AampTrackWorkerTests/FunctionalTests.cpp index 504f6d7e4..1b1ab16e2 100644 --- a/test/utests/tests/AampTrackWorkerTests/FunctionalTests.cpp +++ b/test/utests/tests/AampTrackWorkerTests/FunctionalTests.cpp @@ -21,8 +21,7 @@ #include #include #include -#include "AampTrackWorker.hpp" -#include "MediaSegmentDownloadJob.hpp" +#include "AampTrackWorker.h" #include "priv_aamp.h" #include "AampUtils.h" @@ -41,16 +40,31 @@ class FunctionalTests : public ::testing::Test class TestableAampTrackWorker : public aamp::AampTrackWorker { public: - using AampTrackWorker::mQueueMutex; // Expose protected member for testing + using AampTrackWorker::mMutex; // Expose protected member for testing TestableAampTrackWorker(PrivateInstanceAAMP *_aamp, AampMediaType _mediaType) : aamp::AampTrackWorker(_aamp, _mediaType) { } + bool GetJobAvailableFlag() + { + return mJobAvailable; + } + + bool GetStopFlag() + { + return mStop; + } + void SetStopFlag(bool stop) { - mStop.store(stop); + mStop = stop; + } + + void SetJobAvailableFlag(bool jobAvailable) + { + mJobAvailable = jobAvailable; } PrivateInstanceAAMP *GetAampInstance() @@ -67,20 +81,10 @@ class FunctionalTests : public ::testing::Test { mCondVar.notify_one(); } - - size_t GetJobQueueSize() - { - return mJobQueue.size(); - } - - bool IsPaused() - { - return mPaused.load(); - } }; PrivateInstanceAAMP *mPrivateInstanceAAMP; AampMediaType mMediaType = AampMediaType::eMEDIATYPE_VIDEO; - std::shared_ptr mTestableAampTrackWorker; + TestableAampTrackWorker *mTestableAampTrackWorker; void SetUp() override { @@ -91,18 +95,13 @@ class FunctionalTests : public ::testing::Test mPrivateInstanceAAMP = new PrivateInstanceAAMP(gpGlobalConfig); - mTestableAampTrackWorker = std::make_shared(mPrivateInstanceAAMP, mMediaType); - mTestableAampTrackWorker->StartWorker(); + mTestableAampTrackWorker = new TestableAampTrackWorker(mPrivateInstanceAAMP, mMediaType); } void TearDown() override { - if(mTestableAampTrackWorker) - { - // Stop worker thread - mTestableAampTrackWorker->StopWorker(); - mTestableAampTrackWorker = nullptr; - } + delete mTestableAampTrackWorker; + mTestableAampTrackWorker = nullptr; delete mPrivateInstanceAAMP; mPrivateInstanceAAMP = nullptr; @@ -123,8 +122,8 @@ class FunctionalTests : public ::testing::Test */ TEST_F(FunctionalTests, ConstructorInitializesFields) { - EXPECT_FALSE(mTestableAampTrackWorker->IsStopped()); - EXPECT_EQ(mTestableAampTrackWorker->GetJobQueueSize(), 0); + EXPECT_FALSE(mTestableAampTrackWorker->GetStopFlag()); + EXPECT_FALSE(mTestableAampTrackWorker->GetJobAvailableFlag()); EXPECT_EQ(mTestableAampTrackWorker->GetAampInstance(), mPrivateInstanceAAMP); EXPECT_EQ(mTestableAampTrackWorker->GetMediaType(), mMediaType); } @@ -139,9 +138,8 @@ TEST_F(FunctionalTests, DestructorCleansUpResources) { try { - mTestableAampTrackWorker->StopWorker(); // Ensure worker thread is stopped - mTestableAampTrackWorker.reset(); // This will call the destructor - mTestableAampTrackWorker = nullptr; + delete mTestableAampTrackWorker; // Explicit delete to check thread join + mTestableAampTrackWorker = nullptr; // Avoid double free // No exceptions or undefined behavior should occur SUCCEED(); } @@ -160,14 +158,9 @@ TEST_F(FunctionalTests, DestructorCleansUpResources) TEST_F(FunctionalTests, SubmitJobExecutesSuccessfully) { bool jobExecuted = false; - auto downloadJob = std::make_shared(nullptr, [&](){ - jobExecuted = true; - }); - auto future = mTestableAampTrackWorker->SubmitJob(downloadJob); - // Verify job was executed - EXPECT_TRUE(future.valid()); - EXPECT_TRUE(future.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - // Check if jobExecuted flag is set + mTestableAampTrackWorker->SubmitJob([&]() + { jobExecuted = true; }); + mTestableAampTrackWorker->WaitForCompletion(); EXPECT_TRUE(jobExecuted); } @@ -180,17 +173,14 @@ TEST_F(FunctionalTests, SubmitJobExecutesSuccessfully) TEST_F(FunctionalTests, MultipleJobsExecution) { int counter = 0; + mTestableAampTrackWorker->SubmitJob([&]() + { counter += 1; }); + mTestableAampTrackWorker->WaitForCompletion(); + + mTestableAampTrackWorker->SubmitJob([&]() + { counter += 2; }); + mTestableAampTrackWorker->WaitForCompletion(); - // Submit multiple independent jobs - for (int i = 0; i < 3; ++i) - { - auto job = std::make_shared(nullptr, [&]() { counter += 1; }); - auto future = mTestableAampTrackWorker->SubmitJob(job); - if (future.valid()) - { - future.wait(); // Wait for job completion - } - } EXPECT_EQ(counter, 3); } @@ -202,26 +192,21 @@ TEST_F(FunctionalTests, MultipleJobsExecution) */ TEST_F(FunctionalTests, ProcessJobExitsGracefully) { - auto downloadJob = std::make_shared(nullptr, [&]() {}); - auto future = mTestableAampTrackWorker->SubmitJob(downloadJob); // Dummy job - try - { - if (future.valid()) - { - future.wait(); // Wait for job to complete - } - } - catch (const std::exception &e) + mTestableAampTrackWorker->SubmitJob([&]() {}); // Dummy job + mTestableAampTrackWorker->WaitForCompletion(); + + // Simulate stop signal { - FAIL() << "Exception caught in AampTrackWorker ProcessJob: " << e.what(); + std::lock_guard lock(mTestableAampTrackWorker->mMutex); + mTestableAampTrackWorker->SetStopFlag(true); + mTestableAampTrackWorker->SetJobAvailableFlag(true); } - - mTestableAampTrackWorker->StopWorker(); + mTestableAampTrackWorker->NotifyConditionVariable(); // Wait for thread to join in destructor try { - mTestableAampTrackWorker.reset(); + delete mTestableAampTrackWorker; mTestableAampTrackWorker = nullptr; SUCCEED(); } @@ -232,12 +217,12 @@ TEST_F(FunctionalTests, ProcessJobExitsGracefully) } /** - * @test FunctionalTests::CreateMultipleWorkers - * @brief Functional tests for AampTrackWorker with multiple instances + * @test FunctionalTests::ConstructorHandlesExceptionsGracefully + * @brief Functional tests for AampTrackWorker constructor exception handling * * The tests check if the constructor handles exceptions gracefully */ -TEST_F(FunctionalTests, CreateMultipleWorkers) +TEST_F(FunctionalTests, ConstructorHandlesExceptionsGracefully) { try { @@ -254,17 +239,17 @@ TEST_F(FunctionalTests, CreateMultipleWorkers) } /** - * @test FunctionalTests::SubmitJobHandlesNullJobs + * @test FunctionalTests::ProcessJobHandlesNullJobs * @brief Functional tests for AampTrackWorker with null job submission * * The tests verify the worker thread does not crash or behave unexpectedly with null job */ -TEST_F(FunctionalTests, SubmitJobHandlesNullJobs) +TEST_F(FunctionalTests, ProcessJobHandlesNullJobs) { try { - auto future = mTestableAampTrackWorker->SubmitJob(nullptr); // Submit an invalid/null job - EXPECT_FALSE(future.valid()); // Future should not be valid + mTestableAampTrackWorker->SubmitJob(nullptr); // Submit an invalid/null job + mTestableAampTrackWorker->WaitForCompletion(); SUCCEED(); // No crashes or unexpected behavior } catch (const std::exception &e) @@ -280,21 +265,17 @@ TEST_F(FunctionalTests, SubmitJobHandlesNullJobs) */ TEST_F(FunctionalTests, SubmitJobHandlesExceptions) { + std::exception ex; try { - auto downloadJob = std::make_shared(nullptr, [&](){throw std::runtime_error("Test exception");}); - auto future = mTestableAampTrackWorker->SubmitJob(downloadJob); - EXPECT_TRUE(future.valid()); - future.get(); // This should throw the exception - FAIL() << "Exception was expected but not thrown"; - } - catch (const std::runtime_error &e) - { - SUCCEED() << "Exception caught in AampTrackWorker job: " << e.what(); + mTestableAampTrackWorker->SubmitJob([&]() + { throw ex; }); + mTestableAampTrackWorker->WaitForCompletion(); + SUCCEED(); // No crashes or unexpected behavior } catch (const std::exception &e) { - FAIL() << "Unexpected exception type caught: " << e.what(); + FAIL() << "Exception caught in AampTrackWorker job: " << e.what(); } } @@ -308,298 +289,11 @@ TEST_F(FunctionalTests, ConstructorHandlesNullAamp) { try { - std::shared_ptr worker = std::make_shared(nullptr, AampMediaType::eMEDIATYPE_VIDEO); - FAIL() << "Expected exception not thrown for null aamp"; - } - catch (const std::invalid_argument &e) - { - SUCCEED() << "Expected exception caught: " << e.what(); - } - catch (const std::exception &e) - { - FAIL() << "Unexpected exception type caught: " << e.what(); - } -} - -/** - * @test FunctionalTests::PauseAndResumeWorker - * @brief Functional tests for AampTrackWorker Pause and Resume - * - * The tests verify the worker thread can be paused and resumed successfully - */ -TEST_F(FunctionalTests, PauseAndResumeWorker) -{ - mTestableAampTrackWorker->Pause(); - EXPECT_TRUE(mTestableAampTrackWorker->IsPaused()); - - mTestableAampTrackWorker->Resume(); - EXPECT_FALSE(mTestableAampTrackWorker->IsPaused()); -} - -/** - * @test FunctionalTests::ClearJobsTest - * @brief Functional tests for AampTrackWorker ClearJobs - * - * The tests verify the job queue is cleared successfully - */ -TEST_F(FunctionalTests, ClearJobsTest) -{ - auto downloadJob = std::make_shared(nullptr, [](){}); - mTestableAampTrackWorker->Pause(); - mTestableAampTrackWorker->SubmitJob(downloadJob); - EXPECT_EQ(mTestableAampTrackWorker->GetJobQueueSize(), 1); - - mTestableAampTrackWorker->ClearJobs(); - EXPECT_EQ(mTestableAampTrackWorker->GetJobQueueSize(), 0); -} - -/** - * @test FunctionalTests::RescheduleActiveJobTest - * @brief Functional tests for AampTrackWorker RescheduleActiveJob - * - * The tests verify the active job is rescheduled successfully - */ -TEST_F(FunctionalTests, RescheduleActiveJobTest) -{ - int counter = 0; - auto downloadJob = std::make_shared(nullptr, [&](){ - if(counter < 1) - { - mTestableAampTrackWorker->RescheduleActiveJob(); - } - counter += 1; - }); - mTestableAampTrackWorker->Pause(); - auto future = mTestableAampTrackWorker->SubmitJob(downloadJob); - EXPECT_TRUE(future.valid()); - mTestableAampTrackWorker->Resume(); - EXPECT_TRUE(future.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - auto job = std::make_shared(); - auto lastJobFuture = mTestableAampTrackWorker->SubmitJob(job); - EXPECT_TRUE(lastJobFuture.valid()); - EXPECT_TRUE(lastJobFuture.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - // Verify the first job was rescheduled and executed twice - EXPECT_EQ(counter, 2); -} - -/** - * @test FunctionalTests::SubmitJobPushToFront - * @brief Functional tests for AampTrackWorker SubmitJob with push to front - * - * The tests verify that the job is pushed to the front of the queue when the second argument is true - */ -TEST_F(FunctionalTests, SubmitJobPushToFront) -{ - std::string result; - auto firstJob = std::make_shared(nullptr, [&](){ - result += "string"; - }); - auto secondJob = std::make_shared(nullptr, [&](){ - result += "test"; - }); - mTestableAampTrackWorker->Pause(); - auto futureFirst = mTestableAampTrackWorker->SubmitJob(firstJob); - EXPECT_TRUE(futureFirst.valid()); - auto futureSecond = mTestableAampTrackWorker->SubmitJob(secondJob, true); // Push to front - EXPECT_TRUE(futureSecond.valid()); - mTestableAampTrackWorker->Resume(); - // Wait for both jobs to complete - EXPECT_TRUE(futureFirst.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - EXPECT_TRUE(futureSecond.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - EXPECT_EQ(result, "teststring"); // secondJob should execute before firstJob -} - -/** - * @test FunctionalTests::SubmitJobPushToFrontAndReschedule - * @brief Functional tests for AampTrackWorker SubmitJob with push to front and reschedule - * - * The tests verify that the job is pushed to the front of the queue and rescheduled successfully - */ -TEST_F(FunctionalTests, SubmitJobPushToFrontAndReschedule) -{ - int counter = 0; - std::string result; - auto downloadJob = std::make_shared(nullptr, [&](){ - if(counter < 1) - { - mTestableAampTrackWorker->RescheduleActiveJob(); - } - result += "test"; - counter += 1; - }); - auto anotherJob = std::make_shared(nullptr, [&](){ - result += "string"; - }); - mTestableAampTrackWorker->Pause(); - auto futureAnotherJob = mTestableAampTrackWorker->SubmitJob(anotherJob); - EXPECT_TRUE(futureAnotherJob.valid()); - auto futureDownloadJob = mTestableAampTrackWorker->SubmitJob(downloadJob, true); // Push to front - EXPECT_TRUE(futureDownloadJob.valid()); - mTestableAampTrackWorker->Resume(); - EXPECT_TRUE(futureAnotherJob.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - EXPECT_TRUE(futureDownloadJob.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - - EXPECT_EQ(counter, 2); - EXPECT_EQ(result, "testteststring"); // downloadJob should execute twice before anotherJob -} - -/** - * @test FunctionalTests::SubmitJobsFromMultipleThreads - * @brief Functional test to verify job submission and execution from multiple threads - * - * This test ensures thread safety and proper job execution when submitted concurrently. - */ -TEST_F(FunctionalTests, SubmitJobsFromMultipleThreads) -{ - const int threadCount = 5; - std::atomic executionCount{0}; - - std::vector threads; - std::vector> futures; - - for (int i = 0; i < threadCount; ++i) - { - threads.emplace_back([&]() { - auto job = std::make_shared(nullptr, [&]() { - executionCount.fetch_add(1); - }); - auto future = mTestableAampTrackWorker->SubmitJob(job); - EXPECT_TRUE(future.valid()); - future.wait(); // Ensure job completes - }); - } - - for (auto &t : threads) - { - if (t.joinable()) - t.join(); - } - - EXPECT_EQ(executionCount.load(), threadCount); -} - -/** - * @test FunctionalTests::JobsSubmittedConcurrentlyWhilePausedAndResumed - * @brief Test jobs submitted while paused and then resumed from different threads - * - * Ensures queued jobs submitted from multiple threads are not lost and get executed post-resume. - */ -TEST_F(FunctionalTests, JobsSubmittedConcurrentlyWhilePausedAndResumed) -{ - mTestableAampTrackWorker->Pause(); - - std::atomic counter{0}; - std::vector> futures; - - const int numJobs = 10; - - // Submit jobs from multiple threads - std::vector threads; - std::mutex futuresMutex; - for (int i = 0; i < numJobs; ++i) - { - threads.emplace_back([&, i]() { - auto job = std::make_shared(nullptr, [&]() { - counter.fetch_add(1); - }); - auto future = mTestableAampTrackWorker->SubmitJob(job); - EXPECT_TRUE(future.valid()); - { - std::lock_guard lock(futuresMutex); - futures.push_back(std::move(future)); - } - }); - } - - for (auto &t : threads) - { - if (t.joinable()) t.join(); - } - - EXPECT_EQ(mTestableAampTrackWorker->GetJobQueueSize(), numJobs); - - mTestableAampTrackWorker->Resume(); - - for (auto &f : futures) - { - EXPECT_TRUE(f.wait_for(std::chrono::seconds(1)) == std::future_status::ready); - } - - EXPECT_EQ(counter.load(), numJobs); -} - -/** - * @test FunctionalTests::StressTestMultipleThreadsSubmittingSimultaneously - * @brief Stress test with many threads submitting jobs simultaneously - * - * Verifies robustness of job queue under high concurrency. - */ -TEST_F(FunctionalTests, StressTestMultipleThreadsSubmittingSimultaneously) -{ - const int threadCount = 20; - std::atomic executedCount{0}; - - std::vector threads; - std::vector> futures; - - for (int i = 0; i < threadCount; ++i) - { - threads.emplace_back([&]() { - auto job = std::make_shared(nullptr, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - executedCount.fetch_add(1); - }); - auto future = mTestableAampTrackWorker->SubmitJob(job); - EXPECT_TRUE(future.valid()); - future.wait(); // Wait for job to complete - }); - } - - for (auto &t : threads) - { - if (t.joinable()) t.join(); - } - - EXPECT_EQ(executedCount.load(), threadCount); -} - -/** - * @test FunctionalTests::StartWorkerTest - * @brief Functional tests for AampTrackWorker StartWorker - * - * The tests verify that the worker thread starts successfully - */ -TEST_F(FunctionalTests, StartWorkerTest) -{ - try - { - mTestableAampTrackWorker->StopWorker(); // Ensure worker is stopped - mTestableAampTrackWorker->StartWorker(); // Start worker again - EXPECT_FALSE(mTestableAampTrackWorker->IsStopped()); - SUCCEED(); - } - catch (const std::exception &e) - { - FAIL() << "Exception caught in AampTrackWorker StartWorker: " << e.what(); - } -} - -/** - * @test FunctionalTests::StopWorkerTest - * @brief Functional tests for AampTrackWorker StopWorker - * - * The tests verify that the worker thread stops successfully - */ -TEST_F(FunctionalTests, StopWorkerTest) -{ - try - { - mTestableAampTrackWorker->StopWorker(); // Stop worker - EXPECT_TRUE(mTestableAampTrackWorker->IsStopped()); + aamp::AampTrackWorker nullAampWorker(nullptr, AampMediaType::eMEDIATYPE_VIDEO); SUCCEED(); } catch (const std::exception &e) { - FAIL() << "Exception caught in AampTrackWorker StopWorker: " << e.what(); + FAIL() << "Exception caught in AampTrackWorker constructor with null aamp: " << e.what(); } } diff --git a/test/utests/tests/AdFallbackTests/FunctionalTests.cpp b/test/utests/tests/AdFallbackTests/FunctionalTests.cpp index 0e5b522b0..5e1a93127 100644 --- a/test/utests/tests/AdFallbackTests/FunctionalTests.cpp +++ b/test/utests/tests/AdFallbackTests/FunctionalTests.cpp @@ -297,7 +297,7 @@ TEST_F(AdFallbackTests, AdInitFailureTest) - + @@ -309,7 +309,7 @@ TEST_F(AdFallbackTests, AdInitFailureTest) - + @@ -324,7 +324,7 @@ TEST_F(AdFallbackTests, AdInitFailureTest) - + @@ -335,7 +335,7 @@ TEST_F(AdFallbackTests, AdInitFailureTest) - + @@ -378,9 +378,10 @@ TEST_F(AdFallbackTests, AdInitFailureTest) )"; - std::string adInitFragmentUrl = std::string(TEST_AD_BASE_URL) + std::string("video_init.mp4"); - std::string sourceVideoInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - std::string sourceAudioInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("audio_p0_init.mp4"); + std::string AdInitFragmentUrl = std::string(TEST_AD_BASE_URL) + std::string("video_init.mp4"); + std::string AdAudioInitFragmentUrl = std::string(TEST_AD_BASE_URL) + std::string("audio_init.mp4"); + std::string SourceInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); + std::string SourceAudioInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("audio_init.mp4"); AAMPStatusType status; mPrivateInstanceAAMP->rate = 1.0; @@ -416,16 +417,12 @@ TEST_F(AdFallbackTests, AdInitFailureTest) // Ad manifest InitializeAdMPD(adManifest); - // Need to fail ad init fragment, This will be called from FetchAndCacheInitHeaders in admanager during fulfilling ad - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitFragmentUrl, eMEDIATYPE_INIT_VIDEO, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(false)); // Called again to populate mAdBreaks and other variables mStreamAbstractionAAMP_MPD->mCdaiObject->SetAlternateContents(periodId, adId, adurl, startMS, breakdur); std::this_thread::sleep_for(std::chrono::milliseconds(50)); EXPECT_EQ(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdBreaks[periodId].ads->size(), 1); EXPECT_EQ(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdBreaks[periodId].ads->at(0).adId, adId); - // After ad init failure, the ad break should not have mpd set - EXPECT_EQ(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdBreaks[periodId].ads->at(0).mpd, nullptr); + EXPECT_NE(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdBreaks[periodId].ads->at(0).mpd, nullptr); EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()) .Times(AnyNumber()) @@ -435,19 +432,32 @@ TEST_F(AdFallbackTests, AdInitFailureTest) return (++counter < 10); }); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .Times(4) // 2 audio + 2 video init fragments - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _)) - .WillRepeatedly(Return(true)); // Media fragments + // Need to fail ad Video init fragment + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(AdInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) + .Times(1) + .WillOnce(Return(false)); + + //Need to fail ad audio init fragment + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(AdAudioInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(SourceInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) + .Times(1) + .WillOnce(Return(true)); + + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(SourceAudioInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) + .Times(1) + .WillOnce(Return(true)); TuneType tuneType = TuneType::eTUNETYPE_NEW_NORMAL; // Will start fetching the ad, but fails in ad init fragment and should fallback to source period and its init fragment status = Init(tuneType); EXPECT_EQ(status, eAAMPSTATUS_OK); - EXPECT_EQ(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdState, AdState::IN_ADBREAK_AD_NOT_PLAYING); + EXPECT_EQ(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdState, AdState::IN_ADBREAK_AD_PLAYING); + mStreamAbstractionAAMP_MPD->InvokeFetcherLoop(); // Gets updated in FetcherLoop - EXPECT_EQ(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdState, AdState::OUTSIDE_ADBREAK); - EXPECT_DOUBLE_EQ(mStreamAbstractionAAMP_MPD->mPTSOffset.inSeconds(), 60.0); + EXPECT_EQ(mStreamAbstractionAAMP_MPD->mCdaiObject->mAdState, AdState::IN_ADBREAK_AD_NOT_PLAYING); + EXPECT_DOUBLE_EQ(mStreamAbstractionAAMP_MPD->mPTSOffset.inSeconds(), 0.0); } diff --git a/test/utests/tests/AdManagerMPDTests/FunctionalTests.cpp b/test/utests/tests/AdManagerMPDTests/FunctionalTests.cpp index dbf33301c..70cc5c2cd 100644 --- a/test/utests/tests/AdManagerMPDTests/FunctionalTests.cpp +++ b/test/utests/tests/AdManagerMPDTests/FunctionalTests.cpp @@ -64,9 +64,7 @@ class AdManagerMPDTests : public ::testing::Test IMPD *mMPD; AampMPDParseHelperPtr mAdMPDParseHelper; static constexpr const char *TEST_AD_MANIFEST_URL = "http://host/ad/manifest.mpd"; - static constexpr const char *TEST_AD_MANIFEST_HOST = "http://host/ad/"; static constexpr const char *TEST_FOG_AD_MANIFEST_URL = "http://127.0.0.1:9080/adrec?clientId=FOG_AAMP&recordedUrl=http%3A%2F%2Fhost%2Fad%2Fmanifest.mpd"; - static constexpr const char *TEST_FOG_AD_MANIFEST_HOST = "http://127.0.0.1:9080/"; static constexpr const char *TEST_FOG_MAIN_MANIFEST_URL = "http://127.0.0.1:9080/recording/manifest.mpd"; void SetUp() @@ -137,7 +135,6 @@ class AdManagerMPDTests : public ::testing::Test if (manifest) { mManifest = manifest; - mPrivateInstanceAAMP->rate = AAMP_NORMAL_PLAY_RATE; // remoteUrl, manifest, effectiveUrl EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adManifestUrl, _,_ , _, _, _, _, _, _, _, _, _, _, _)) .Times(count) @@ -202,17 +199,6 @@ class AdManagerMPDTests : public ::testing::Test } } - /** - * @brief Constructs the ad initialization URI based on the host and path. - * @param host The host URL - * @param path The path to the ad manifest - * - * @return The complete ad initialization URL - */ - std::string GetFullURI(const std::string &host, const std::string &path) - { - return host + path; - } }; /** @@ -435,12 +421,6 @@ R"( { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - std::string adInitUrl = GetFullURI(TEST_AD_MANIFEST_HOST, "manifest/track-video-repid-LE5-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); - adInitUrl = GetFullURI(TEST_AD_MANIFEST_HOST, "manifest-eac3/track-audio-repid-DDen-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); mPrivateCDAIObjectMPD->SetAlternateContents(periodId, adId, url, startMS, breakdur); std::this_thread::sleep_for(std::chrono::milliseconds(50)); t.join(); @@ -512,13 +492,6 @@ R"( // mIsFogTSB is true, so downloaded from CDN and redirected to FOG and ad resolved event is sent EXPECT_CALL(*g_mockPrivateInstanceAAMP, SendAdResolvedEvent(adId, true, startMS, 10000, adErrorCode)).Times(1); - - std::string adInitUrl = GetFullURI(TEST_FOG_AD_MANIFEST_HOST, "manifest/track-video-repid-LE5-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); - adInitUrl = GetFullURI(TEST_FOG_AD_MANIFEST_HOST, "manifest-eac3/track-audio-repid-DDen-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); mPrivateCDAIObjectMPD->SetAlternateContents(periodId, adId, url, startMS, breakdur); std::this_thread::sleep_for(std::chrono::milliseconds(100)); @@ -641,12 +614,6 @@ R"( // mIsFogTSB is true, so downloaded from CDN and redirected to FOG which fails. // Here, ad resolved event is sent with true and CDN url is cached EXPECT_CALL(*g_mockPrivateInstanceAAMP, SendAdResolvedEvent(adId, true, startMS, 10000, eCDAI_ERROR_NONE)).Times(1); - std::string adInitUrl = GetFullURI(TEST_AD_MANIFEST_HOST, "manifest/track-video-repid-LE5-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); - adInitUrl = GetFullURI(TEST_AD_MANIFEST_HOST, "manifest-eac3/track-audio-repid-DDen-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); mPrivateCDAIObjectMPD->SetAlternateContents(periodId, adId, url, startMS, breakdur); std::this_thread::sleep_for(std::chrono::milliseconds(50)); @@ -718,12 +685,6 @@ R"( // Here, ad resolved event is sent with true and CDN url is cached EXPECT_CALL(*g_mockPrivateInstanceAAMP, SendAdResolvedEvent(adId1, true, startMS, 10000, adErrorCode)).Times(1); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SendAdResolvedEvent(adId2, true, startMS + adDuration, 10000, adErrorCode)).Times(1); - std::string adInitUrl = GetFullURI(TEST_AD_MANIFEST_HOST, "manifest/track-video-repid-LE5-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillRepeatedly(Return(true)); - adInitUrl = GetFullURI(TEST_AD_MANIFEST_HOST, "manifest-eac3/track-audio-repid-DDen-tc--header.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile (adInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillRepeatedly(Return(true)); mPrivateCDAIObjectMPD->SetAlternateContents(periodId, adId1, url, startMS, adDuration); mPrivateCDAIObjectMPD->SetAlternateContents(periodId, adId2, url, startMS, adDuration); std::this_thread::sleep_for(std::chrono::milliseconds(50)); @@ -960,17 +921,10 @@ TEST_F(AdManagerMPDTests, SetAlternateContentsTests_13) AAMPCDAIError expectedError = eCDAI_ERROR_DELIVERY_TIMEOUT; const char *manifest = "" - "" - ""; + "" + ""; mPrivateCDAIObjectMPD->SetAlternateContents(periodId, "", "", startMS, breakdur); - // Set up GetFile expectations for video and audio init segments - std::string videoInitUrl = TEST_AD_MANIFEST_HOST + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile(videoInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); - std::string audioInitUrl = TEST_AD_MANIFEST_HOST + std::string("audio_init.mp4"); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile(audioInitUrl, _, _, _, _, _, _, _, _, _, _, _, _, _)) - .WillOnce(Return(true)); // Set up the mock for GetFile before any SetAlternateContents calls EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile(url, _, _, _, _, _, _, _, _, _, _, _, _, _)) .WillOnce(WithArgs<0,2,3,4>(Invoke([this, periodId, manifest](std::string remoteUrl, AampGrowableBuffer *buffer, std::string& effectiveUrl, int *httpError) diff --git a/test/utests/tests/CacheFragmentTests/CMakeLists.txt b/test/utests/tests/CacheFragmentTests/CMakeLists.txt index eccc3409c..c61ded51b 100644 --- a/test/utests/tests/CacheFragmentTests/CMakeLists.txt +++ b/test/utests/tests/CacheFragmentTests/CMakeLists.txt @@ -44,7 +44,7 @@ include_directories(SYSTEM ${UTESTS_ROOT}/mocks) set(TEST_SOURCES CacheFragmentTests.cpp CacheFragmentAampTests.cpp) -set(AAMP_SOURCES ${AAMP_ROOT}/streamabstraction.cpp ${AAMP_ROOT}/CachedFragment.cpp ${AAMP_ROOT}/MediaStreamContext.cpp ${AAMP_ROOT}/AampGrowableBuffer.cpp ${AAMP_ROOT}/AampMPDUtils.cpp) +set(AAMP_SOURCES ${AAMP_ROOT}/streamabstraction.cpp ${AAMP_ROOT}/CachedFragment.cpp ${AAMP_ROOT}/MediaStreamContext.cpp ${AAMP_ROOT}/AampGrowableBuffer.cpp) add_executable(${EXEC_NAME} ${TEST_SOURCES} ${AAMP_SOURCES}) @@ -60,5 +60,5 @@ if (COVERAGE_ENABLED) APPEND_COVERAGE_COMPILER_FLAGS() endif() -target_link_libraries(${EXEC_NAME} -pthread ${OS_LD_FLAGS} ${GMOCK_LINK_LIBRARIES} ${GTEST_LINK_LIBRARIES} ${LIBDASH_LINK_LIBRARIES} ${GLIB_LINK_LIBRARIES} fakes) +target_link_libraries(${EXEC_NAME} -pthread ${OS_LD_FLAGS} ${GMOCK_LINK_LIBRARIES} ${GTEST_LINK_LIBRARIES} ${GLIB_LINK_LIBRARIES} fakes ${LIBCJSON_LINK_LIBRARIES}) gtest_discover_tests(${EXEC_NAME} TEST_PREFIX ${EXEC_NAME}:) diff --git a/test/utests/tests/CacheFragmentTests/CacheFragmentTests.cpp b/test/utests/tests/CacheFragmentTests/CacheFragmentTests.cpp index 8f0984260..6c17bf682 100644 --- a/test/utests/tests/CacheFragmentTests/CacheFragmentTests.cpp +++ b/test/utests/tests/CacheFragmentTests/CacheFragmentTests.cpp @@ -30,7 +30,6 @@ #include "MockAampConfig.h" #include "MockIsoBmffBuffer.h" #include "StreamAbstractionAAMP.h" -#include "AampDownloadInfo.hpp" #include "MockPrivateInstanceAAMP.h" #include "MockStreamAbstractionAAMP_MPD.h" #include "MockTSBSessionManager.h" @@ -42,7 +41,6 @@ using ::testing::Return; using ::testing::StrictMock; using ::testing::SetArgReferee; using ::testing::AtLeast; -using ::testing::DoAll; AampConfig *gpGlobalConfig{nullptr}; struct TestParams @@ -150,8 +148,7 @@ class MediaStreamContextTest : public ::testing::TestWithParam {eAAMPConfig_EnableIgnoreEosSmallFragment, false}, {eAAMPConfig_EnablePTSReStamp, false}, {eAAMPConfig_LocalTSBEnabled, false}, - {eAAMPConfig_EnableIFrameTrackExtract, false}, - {eAAMPConfig_EnableABR, true}, + {eAAMPConfig_EnableIFrameTrackExtract, false} }; BoolConfigSettings mBoolConfigSettings; @@ -168,7 +165,6 @@ class MediaStreamContextTest : public ::testing::TestWithParam {eAAMPConfig_PrePlayBufferCount, DEFAULT_PREBUFFER_COUNT}, {eAAMPConfig_VODTrickPlayFPS, TRICKPLAY_VOD_PLAYBACK_FPS}, {eAAMPConfig_ABRBufferCounter,DEFAULT_ABR_BUFFER_COUNTER}, - {eAAMPConfig_MaxDownloadBuffer, DEFAULT_MAX_DOWNLOAD_BUFFER}, {eAAMPConfig_MaxFragmentChunkCached,DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK} }; @@ -321,12 +317,10 @@ class MediaStreamContextTest : public ::testing::TestWithParam } EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetTSBSessionManager()).WillOnce(Return(tsbSessionManager)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile(_, _, _, _, _, _, _, _, _, _, _, _, _, _)).WillOnce(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()).WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetLLDashChunkMode()).WillRepeatedly(Return(chunk)); if(init) { EXPECT_CALL(*g_mockIsoBmffBuffer, isInitSegment()).WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockIsoBmffBuffer, getTimeScale(_)).WillOnce(DoAll(SetArgReferee<0>(90000), Return(true))); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetVidTimeScale(_)).Times(AtLeast(1)); } } @@ -352,19 +346,7 @@ TEST_P(MediaStreamContextTest, CacheFragment) testParam.expectedFragmentChunksCached, testParam.expectedFragmentCached); Initialize(testParam.lowlatency, testParam.chunk, testParam.tsb, testParam.eos, testParam.paused, testParam.underflow, testParam.init, testParam.rate); - URIInfo uriInfo; - uriInfo.url = "remoteUrl"; - URLBitrateMap urlList = { { 0, uriInfo } }; - mMediaStreamContext->mActiveDownloadInfo = std::make_shared(eMEDIATYPE_VIDEO, eCURLINSTANCE_VIDEO, 10, 2, "", -1, 0, testParam.init, false, false, false, 0.0, 0, 1, 0, 0, urlList); - bool retResult = mMediaStreamContext->CacheFragment("remoteUrl", 0, 10, 0, NULL, testParam.init, false, false, 0); - if(retResult) - { - mMediaStreamContext->OnFragmentDownloadSuccess(mMediaStreamContext->mActiveDownloadInfo); - } - else - { - mMediaStreamContext->OnFragmentDownloadFailed(mMediaStreamContext->mActiveDownloadInfo); - } + bool retResult = mMediaStreamContext->CacheFragment("remoteUrl", 0, 10, 0, NULL, testParam.init, false, false, 0, 0, false); if (testParam.eos && !testParam.paused) { diff --git a/test/utests/tests/DrmOcdm/CMakeLists.txt b/test/utests/tests/DrmOcdm/CMakeLists.txt deleted file mode 100755 index 383da58c4..000000000 --- a/test/utests/tests/DrmOcdm/CMakeLists.txt +++ /dev/null @@ -1,148 +0,0 @@ -# If not stated otherwise in this file or this component's license file the -# following copyright and licenses apply: -# -# Copyright 2024 RDK Management -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include(GoogleTest) - -pkg_check_modules(UUID REQUIRED uuid) -pkg_check_modules(GOBJECT REQUIRED gobject-2.0) - -set(AAMP_ROOT "../../../..") -set(UTESTS_ROOT "../..") -set(DRM_ROOT ${UTESTS_ROOT}/drm) -set(RFC_ROOT ${UTESTS_ROOT}/rfc) -set(EXEC_NAME DrmOcdm) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DREALTEKCE=1") - -include_directories(${AAMP_ROOT} ${AAMP_ROOT}/drm ${AAMP_ROOT}/middleware/externals ${AAMP_ROOT}/middleware/drm/helper ${AAMP_ROOT}/middleware ${AAMP_ROOT}/middleware/drm/ocdm ${AAMP_ROOT}/middleware/drm/ ${AAMP_ROOT}/drm/helper ${AAMP_ROOT}/subtitle ${AAMP_ROOT}/middleware/subtitle ${AAMP_ROOT}/downloader ${AAMP_ROOT}/isobmff ${AAMP_ROOT}/middleware/subtec/subtecparser ${AAMP_ROOT}/middleware/playerjsonobject ${AAMP_ROOT}/middleware/subtec/libsubtec ${AAMP_ROOT}/middleware/externals/contentsecuritymanager) -include_directories(${AAMP_ROOT}/middleware/baseConversion) -include_directories(${AAMP_ROOT}/tsb/api) -include_directories(${AAMP_ROOT}/middleware) -include_directories(${AAMP_ROOT}/middleware/play) -include_directories(${GTEST_INCLUDE_DIRS}) -include_directories(${GMOCK_INCLUDE_DIRS}) -include_directories(${GLIB_INCLUDE_DIRS}) -include_directories(${GSTREAMER_INCLUDE_DIRS}) -include_directories(${LIBCJSON_INCLUDE_DIRS}) -include_directories(${UUID_INCLUDE_DIRS}) -include_directories(${DRM_ROOT}) -include_directories(${RFC_ROOT}) -include_directories(${DRM_ROOT}/ocdm) -include_directories(${DRM_ROOT}/mocks) -include_directories(${AAMP_ROOT}/middleware) -include_directories(${AAMP_ROOT}/middleware/vendor) -include_directories(${AAMP_ROOT}/test/utests/mocks) -include_directories(${AAMP_ROOT}/middleware/playerLogManager) - -message(GSTREAMER_INCLUDE_DIRS=${GSTREAMER_INCLUDE_DIRS}) - -set(TEST_SOURCES DrmTestsRun.cpp - DrmSessionTests.cpp - DrmHelperTests.cpp - DrmUtilsTests.cpp) - -set(MOCK_SOURCES ${DRM_ROOT}/mocks/aampMocks.cpp - ${DRM_ROOT}/mocks/FakeID3Metadata.cpp - ${DRM_ROOT}/mocks/FakeABRManager.cpp - ${DRM_ROOT}/mocks/FakeAampStreamSinkManager.cpp - ${DRM_ROOT}/mocks/curlMocks.c - ${AAMP_ROOT}/middleware/test/utests/drm/mocks/gstMocks.c - ${DRM_ROOT}/mocks/pthreadMocks.c - ${DRM_ROOT}/mocks/openSslMocks.c - ${DRM_ROOT}/mocks/Fakeopencdm.cpp) - -set(FAKE_SOURCES ${UTESTS_ROOT}/fakes/FakeAampRfc.cpp - ${UTESTS_ROOT}/fakes/FakePlayerExternalsInterface.cpp - ${UTESTS_ROOT}/fakes/FakeProcessProtectionHls.cpp - ${AAMP_ROOT}/middleware/test/utests/fakes/FakeSocUtils.cpp - ${AAMP_ROOT}/test/utests/fakes/FakePlayerSecInterface.cpp - ${AAMP_ROOT}/test/utests/fakes/FakeContentSecurityManager.cpp - ${AAMP_ROOT}/test/utests/fakes/FakeContentSecurityManagerSession.cpp - ${AAMP_ROOT}/test/utests/fakes/FakePlayerScheduler.cpp - ${AAMP_ROOT}/test/utests/fakes/FakeBase64.cpp - ${AAMP_ROOT}/test/utests/fakes/FakePlayerLogManager.cpp) - -set(AAMP_SOURCES ${AAMP_ROOT}/AampConfig.cpp - ${AAMP_ROOT}/drm/AampDRMLicManager.cpp - ${AAMP_ROOT}/ProcessHandler.cpp - ${AAMP_ROOT}/middleware/drm/DrmUtils.cpp - ${AAMP_ROOT}/downloader/AampCurlStore.cpp - ${AAMP_ROOT}/downloader/AampCurlDownloader.cpp - ${AAMP_ROOT}/AampDRMLicPreFetcher.cpp - ${AAMP_ROOT}/AampEvent.cpp - ${AAMP_ROOT}/AampJsonObject.cpp - ${AAMP_ROOT}/middleware/drm/DrmJsonObject.cpp - ${AAMP_ROOT}/AampProfiler.cpp - ${AAMP_ROOT}/scte35/AampSCTE35.cpp - ${AAMP_ROOT}/AampFragmentDescriptor.cpp - ${AAMP_ROOT}/AampUtils.cpp - ${AAMP_ROOT}/iso639map.cpp - ${AAMP_ROOT}/middleware/drm/DrmUtils.cpp - ${AAMP_ROOT}/middleware/drm/base64.cpp - ${AAMP_ROOT}/middleware/PlayerUtils.cpp - ${AAMP_ROOT}/middleware/drm/DrmSessionFactory.cpp - ${AAMP_ROOT}/middleware/drm/helper/DrmHelper.cpp - ${AAMP_ROOT}/middleware/drm/helper/DrmHelperFactory.cpp - ${AAMP_ROOT}/middleware/drm/DrmSessionManager.cpp - ${AAMP_ROOT}/middleware/drm/DrmSession.cpp - ${AAMP_ROOT}/middleware/drm/ocdm/opencdmsessionadapter.cpp - ${AAMP_ROOT}/middleware/drm/ocdm/opencdmsessionadapter.cpp - ${AAMP_ROOT}/middleware/drm/ocdm/OcdmBasicSessionAdapter.cpp - ${AAMP_ROOT}/middleware/drm/ocdm/OcdmGstSessionAdapter.cpp - - ${AAMP_ROOT}/middleware/drm/HlsOcdmBridge.cpp - ${AAMP_ROOT}/middleware/drm/HlsDrmSessionManager.cpp - ${AAMP_ROOT}/middleware/drm/helper/ClearKeyHelper.cpp - ${AAMP_ROOT}/middleware/drm/helper/WidevineDrmHelper.cpp - ${AAMP_ROOT}/middleware/drm/helper/PlayReadyHelper.cpp - ${AAMP_ROOT}/isobmff/isobmffbox.h - ${AAMP_ROOT}/isobmff/isobmffbox.cpp - ${AAMP_ROOT}/isobmff/isobmffbuffer.h - ${AAMP_ROOT}/isobmff/isobmffbuffer.cpp) - -add_definitions(-DUSE_SHARED_MEMORY) -add_definitions(-DUSE_OPENCDM -DUSE_OPENCDM_ADAPTER) -add_definitions(-DUSE_THUNDER_OCDM_API_0_2) - -add_executable(${EXEC_NAME} - ${DRM_ROOT}/DrmTestUtils.cpp - ${TEST_SOURCES} - ${MOCK_SOURCES} - ${AAMP_SOURCES} - ${FAKE_SOURCES}) - -set_target_properties(${EXEC_NAME} PROPERTIES FOLDER "utests") - - -if (CMAKE_XCODE_BUILD_SYSTEM) - # XCode schema target - xcode_define_schema(${EXEC_NAME}) -endif() - -if (COVERAGE_ENABLED) - include(CodeCoverage) - APPEND_COVERAGE_COMPILER_FLAGS() - #Set NO_EXCLUDE_DIR to the location of this test so it doesn't get excluded & include common exclude files: - set(NO_EXCLUDE_DIR "${PROJECT_SOURCE_DIR}/tests/DrmOcdm/*") - include("${PROJECT_SOURCE_DIR}/cmake_exclude_file.list") - SETUP_TARGET_FOR_COVERAGE_LCOV(NAME ${EXEC_NAME}_coverage - EXECUTABLE ${EXEC_NAME} - DEPENDENCIES ${EXEC_NAME}) -endif() - -target_link_libraries(${EXEC_NAME} ${UUID_LINK_LIBRARIES} ${OS_LD_FLAGS} pthread -ldl ${GLIB_LINK_LIBRARIES} ${LIBCJSON_LINK_LIBRARIES} ${GMOCK_LINK_LIBRARIES} ${GTEST_LINK_LIBRARIES} ${GOBJECT_LINK_LIBRARIES} ) - -aamp_utest_run_add(${EXEC_NAME}) diff --git a/test/utests/tests/FragmentCollectorAdTests/AdSelectionTests.cpp b/test/utests/tests/FragmentCollectorAdTests/AdSelectionTests.cpp index d5860de3d..39269ad48 100644 --- a/test/utests/tests/FragmentCollectorAdTests/AdSelectionTests.cpp +++ b/test/utests/tests/FragmentCollectorAdTests/AdSelectionTests.cpp @@ -342,7 +342,6 @@ class AdSelectionTests : public ::testing::Test {eAAMPConfig_StallTimeoutMS, DEFAULT_STALL_DETECTION_TIMEOUT}, {eAAMPConfig_AdFulfillmentTimeout, DEFAULT_AD_FULFILLMENT_TIMEOUT}, {eAAMPConfig_AdFulfillmentTimeoutMax, MAX_AD_FULFILLMENT_TIMEOUT}, - {eAAMPConfig_MaxDownloadBuffer, DEFAULT_MAX_DOWNLOAD_BUFFER}, {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK} }; @@ -602,7 +601,7 @@ TEST_F(AdSelectionTests, WaitForAdFallbackTest) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mLiveManifest, eTUNETYPE_SEEK, 10); @@ -671,7 +670,7 @@ TEST_F(AdSelectionTests, onAdEventTest_1) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -717,7 +716,7 @@ TEST_F(AdSelectionTests, onAdEventTest_2) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -759,7 +758,7 @@ TEST_F(AdSelectionTests, onAdEventTest_3) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -798,7 +797,7 @@ TEST_F(AdSelectionTests, onAdEventTest_4) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -859,7 +858,7 @@ TEST_F(AdSelectionTests, onAdEventTest_5) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -923,7 +922,7 @@ TEST_F(AdSelectionTests, onAdEventTest_6) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -989,7 +988,7 @@ TEST_F(AdSelectionTests, onAdEventTest_7) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -1052,7 +1051,7 @@ TEST_F(AdSelectionTests, onAdEventTest_8) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -1125,7 +1124,7 @@ TEST_F(AdSelectionTests, onAdEventTest_9) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -1194,7 +1193,7 @@ TEST_F(AdSelectionTests, onAdEventTest_10) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -1242,7 +1241,7 @@ TEST_F(AdSelectionTests, onAdEventTest_11) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -1286,7 +1285,7 @@ TEST_F(AdSelectionTests, onAdEventTest_12) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL); @@ -1355,7 +1354,7 @@ TEST_F(AdSelectionTests, onAdEventTest_13) // Initialize MPD. The video initialization segment is cached. fragmentUrl = std::string(TEST_BASE_URL) + std::string("dash/iframe_init.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL, 0.0, -1.0); @@ -1425,7 +1424,7 @@ TEST_F(AdSelectionTests, onAdEventTest_14) AAMPStatusType status; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("dash/iframe_init.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL, 0.0, -1.0); @@ -1492,7 +1491,7 @@ TEST_F(AdSelectionTests, onAdEventTest_15) AAMPStatusType status; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("dash/iframe_init.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_NEW_NORMAL, 0.0, 2); @@ -1565,7 +1564,7 @@ TEST_F(AdSelectionTests, AdTransitionTest) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mLiveManifest, eTUNETYPE_SEEK, 10); @@ -1674,7 +1673,7 @@ TEST_F(AdSelectionTests, AdTransitionTest_PausedWithAampTSB) fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mLiveManifest, eTUNETYPE_SEEK, 10); @@ -1893,10 +1892,10 @@ R"( videoInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); audioInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("audio_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoInitFragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioInitFragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); diff --git a/test/utests/tests/MediaStreamContextTests/CMakeLists.txt b/test/utests/tests/MediaStreamContextTests/CMakeLists.txt index fcdd8d279..7b1aba7b1 100644 --- a/test/utests/tests/MediaStreamContextTests/CMakeLists.txt +++ b/test/utests/tests/MediaStreamContextTests/CMakeLists.txt @@ -42,9 +42,8 @@ include_directories(${AAMP_ROOT}/tsb/api) include_directories(${AAMP_ROOT}/middleware) set(TEST_SOURCES MediaStreamContextTests.cpp - FragmentDownloadTests.cpp MediaStreamContextAampTests.cpp) -set(AAMP_SOURCES ${AAMP_ROOT}/MediaStreamContext.cpp ${AAMP_ROOT}/AampMPDUtils.cpp ${AAMP_ROOT}/CachedFragment.cpp) +set(AAMP_SOURCES ${AAMP_ROOT}/MediaStreamContext.cpp ${AAMP_ROOT}/CachedFragment.cpp) add_executable(${EXEC_NAME} ${TEST_SOURCES} ${AAMP_SOURCES}) @@ -59,5 +58,5 @@ if (COVERAGE_ENABLED) include(CodeCoverage) APPEND_COVERAGE_COMPILER_FLAGS() endif() -target_link_libraries(${EXEC_NAME} fakes -pthread ${GLIB_LINK_LIBRARIES} ${LIBDASH_LINK_LIBRARIES} ${OS_LD_FLAGS} ${GMOCK_LINK_LIBRARIES} ${GTEST_LINK_LIBRARIES}) +target_link_libraries(${EXEC_NAME} fakes -pthread ${GLIB_LINK_LIBRARIES} ${OS_LD_FLAGS} ${GMOCK_LINK_LIBRARIES} ${GTEST_LINK_LIBRARIES}) aamp_utest_run_add(${EXEC_NAME}) diff --git a/test/utests/tests/MediaStreamContextTests/FragmentDownloadTests.cpp b/test/utests/tests/MediaStreamContextTests/FragmentDownloadTests.cpp deleted file mode 100644 index 84aa08544..000000000 --- a/test/utests/tests/MediaStreamContextTests/FragmentDownloadTests.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2025 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include "MediaStreamContext.h" -#include "fragmentcollector_mpd.h" -#include "isobmff/isobmffbuffer.h" -#include "AampCacheHandler.h" -#include "priv_aamp.h" -#include "AampDRMLicPreFetcherInterface.h" -#include "AampConfig.h" -#include "MockAampConfig.h" -#include "MockMediaTrack.h" -#include "MockStreamAbstractionAAMP.h" -#include "MockPrivateInstanceAAMP.h" -#include "fragmentcollector_mpd.h" -#include "StreamAbstractionAAMP.h" - -using namespace testing; - -/* - * Test cases for FragmentDownloadTests - * These tests are designed to validate the behavior of the MediaStreamContext class - * when handling fragment downloads. - */ -class FragmentDownloadTests : public testing::Test -{ -protected: - void SetUp() override - { - if (gpGlobalConfig == nullptr) - { - gpGlobalConfig = new AampConfig(); - } - mPrivateInstanceAAMP = new PrivateInstanceAAMP(gpGlobalConfig); - mStreamAbstractionAAMP_MPD = new StreamAbstractionAAMP_MPD(mPrivateInstanceAAMP, 123.45, 12.34); - mMediaStreamContext = new MediaStreamContext(eTRACK_VIDEO, mStreamAbstractionAAMP_MPD, mPrivateInstanceAAMP, "SAMPLETEXT"); - g_mockAampConfig = new NiceMock(); - g_mockMediaTrack = new StrictMock(); - g_mockStreamAbstractionAAMP = new NiceMock(mPrivateInstanceAAMP); - g_mockPrivateInstanceAAMP = new StrictMock(); - } - - void TearDown() override - { - delete mPrivateInstanceAAMP; - mPrivateInstanceAAMP = nullptr; - - delete mStreamAbstractionAAMP_MPD; - mStreamAbstractionAAMP_MPD = nullptr; - - delete mMediaStreamContext; - mMediaStreamContext = nullptr; - - delete g_mockAampConfig; - g_mockAampConfig = nullptr; - - delete g_mockMediaTrack; - g_mockMediaTrack = nullptr; - - delete g_mockStreamAbstractionAAMP; - g_mockStreamAbstractionAAMP = nullptr; - - delete g_mockPrivateInstanceAAMP; - g_mockPrivateInstanceAAMP = nullptr; - } - -public: - StreamAbstractionAAMP_MPD *mStreamAbstractionAAMP_MPD; - PrivateInstanceAAMP *mPrivateInstanceAAMP; - MediaStreamContext *mMediaStreamContext; -}; - - -/** - * @brief Test case for OnFragmentDownloadSuccess with null active download info - */ -TEST_F(FragmentDownloadTests, OnFragmentDownloadSuccess_NullActiveDownloadInfo) -{ - mMediaStreamContext->mActiveDownloadInfo = nullptr; - DownloadInfoPtr dlInfo = std::make_shared(); - mMediaStreamContext->OnFragmentDownloadSuccess(dlInfo); - // Expect no crash or exception -} - -/** - * @brief Test case for OnFragmentDownloadSuccess with a valid download info - */ -TEST_F(FragmentDownloadTests, OnFragmentDownloadSuccess_ValidDownloadInfo) -{ - mMediaStreamContext->mActiveDownloadInfo = std::make_shared(); - DownloadInfoPtr dlInfo = std::make_shared(); - dlInfo->pts = 123.45; - dlInfo->fragmentDurationSec = 5.0; - dlInfo->isDiscontinuity = false; - - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetTSBSessionManager()).WillRepeatedly(Return(nullptr)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillRepeatedly(Return(false)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()).WillRepeatedly(Return(true)); - - // Mock the behavior of GetFetchBuffer, create a dummy CachedFragment - // and append some data to it to simulate a buffer - auto cachedFragment = std::make_shared(); - cachedFragment->fragment.AppendBytes("test", 4); - EXPECT_CALL(*g_mockMediaTrack, GetFetchBuffer(false)) - .WillOnce(Return(cachedFragment.get())); - - // Mock the behavior of IsInjectionFromCachedFragmentChunks, return as non-chunk/non-TSB mode - EXPECT_CALL(*g_mockMediaTrack, IsInjectionFromCachedFragmentChunks()) - .WillRepeatedly(Return(false)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetLLDashChunkMode()).WillRepeatedly(Return(false)); - - // Test the behavior of OnFragmentDownloadSuccess in non-chunk mode - EXPECT_CALL(*g_mockMediaTrack, UpdateTSAfterFetch(_)); - EXPECT_NO_THROW(mMediaStreamContext->OnFragmentDownloadSuccess(dlInfo)); - // Free the cached fragment - cachedFragment->fragment.Free(); -} - -/** - * @brief Test case for OnFragmentDownloadSuccess with a valid download info as chunk mode - */ -TEST_F(FragmentDownloadTests, OnFragmentDownloadSuccess_ValidDownloadInfoChunk) -{ - mMediaStreamContext->mActiveDownloadInfo = std::make_shared(); - DownloadInfoPtr dlInfo = std::make_shared(); - dlInfo->pts = 123.45; - dlInfo->fragmentDurationSec = 5.0; - dlInfo->isDiscontinuity = false; - - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetTSBSessionManager()).WillRepeatedly(Return(nullptr)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillRepeatedly(Return(false)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()).WillRepeatedly(Return(true)); - - // Mock the behavior of GetFetchBuffer, create a dummy CachedFragment - // and append some data to it to simulate a buffer - auto cachedFragment = std::make_shared(); - cachedFragment->fragment.AppendBytes("test", 4); - EXPECT_CALL(*g_mockMediaTrack, GetFetchBuffer(false)) - .WillOnce(Return(cachedFragment.get())); - - // Mock the behavior of IsInjectionFromCachedFragmentChunks, return as chunk mode - EXPECT_CALL(*g_mockMediaTrack, IsInjectionFromCachedFragmentChunks()) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetLLDashChunkMode()).WillRepeatedly(Return(true)); - - // Tes the behaviour of OnFragmentDownloadSuccess in chunk mode - EXPECT_CALL(*g_mockMediaTrack, UpdateTSAfterFetch(_)); - EXPECT_CALL(*g_mockMediaTrack, UpdateTSAfterInject()); - EXPECT_NO_THROW(mMediaStreamContext->OnFragmentDownloadSuccess(dlInfo)); - cachedFragment->fragment.Free(); -} - -/** - * @brief Test case for onFragmentDownloadFailed with null active download info - */ -TEST_F(FragmentDownloadTests, OnFragmentDownloadFailed_NullActiveDownloadInfo) -{ - mMediaStreamContext->mActiveDownloadInfo = nullptr; - DownloadInfoPtr dlInfo = std::make_shared(); - EXPECT_NO_THROW(mMediaStreamContext->OnFragmentDownloadFailed(dlInfo)); -} - -/** - * @brief Test case for onFragmentDownloadFailed with a ramp down attempt - */ -TEST_F(FragmentDownloadTests, OnFragmentDownloadFailed_RampDownAttempt) -{ - mMediaStreamContext->mActiveDownloadInfo = std::make_shared(); - DownloadInfoPtr dlInfo = std::make_shared(); - dlInfo->url = "http://example.com/fragment"; - dlInfo->isInitSegment = false; - - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()).WillRepeatedly(Return(true)); - - // Set segDLFailCount to 0 and set the fail threshold as 10(default) - mMediaStreamContext->segDLFailCount = 0; - EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_FragmentDownloadFailThreshold)).WillRepeatedly(Return(10)); - - // Mock the behavior of GetFetchBuffer, create a dummy CachedFragment - auto cachedFragment = std::make_shared(); - EXPECT_CALL(*g_mockMediaTrack, GetFetchBuffer(false)) - .WillOnce(Return(cachedFragment.get())); - - // Return false for CheckForRampDownLimitReached to allow ramp down, and true for CheckForRampDownProfile - EXPECT_CALL(*g_mockStreamAbstractionAAMP, CheckForRampDownLimitReached()) - .WillOnce(Return(false)); - EXPECT_CALL(*g_mockStreamAbstractionAAMP, CheckForRampDownProfile(_)) - .WillOnce(Return(true)); - - // Test the behavior of OnFragmentDownloadFailed - EXPECT_NO_THROW({ - mMediaStreamContext->OnFragmentDownloadFailed(dlInfo); - EXPECT_TRUE(mMediaStreamContext->mCheckForRampdown); - }); -} - -TEST_F(FragmentDownloadTests, OnFragmentDownloadFailed_ValidDownloadInfoLowestProfile) -{ - mMediaStreamContext->mActiveDownloadInfo = std::make_shared(); - DownloadInfoPtr dlInfo = std::make_shared(); - dlInfo->url = "http://example.com/fragment"; - dlInfo->isInitSegment = false; - - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()).WillRepeatedly(Return(true)); - - // Set segDLFailCount to 1 for showing the ramp down histiry and set the fail threshold as 10(default) - mMediaStreamContext->segDLFailCount = 1; - mMediaStreamContext->mSkipSegmentOnError = false; - EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_FragmentDownloadFailThreshold)).WillRepeatedly(Return(10)); - - // Mock the behavior of GetFetchBuffer, create a dummy CachedFragment - auto cachedFragment = std::make_shared(); - EXPECT_CALL(*g_mockMediaTrack, GetFetchBuffer(false)) - .WillOnce(Return(cachedFragment.get())); - - // Return true for CheckForRampDownLimitReached to indicate that the limit is reached - EXPECT_CALL(*g_mockStreamAbstractionAAMP, CheckForRampDownLimitReached()) - .WillOnce(Return(true)); - - // Test the behavior of OnFragmentDownloadFailed, mCheckForRampdown should be set to false - // and mSkipSegmentOnError should be set to true - EXPECT_NO_THROW({ - mMediaStreamContext->OnFragmentDownloadFailed(dlInfo); - EXPECT_EQ(mMediaStreamContext->segDLFailCount, 1); - EXPECT_FALSE(mMediaStreamContext->mCheckForRampdown); - EXPECT_TRUE(mMediaStreamContext->mSkipSegmentOnError); - }); -} - -/** - * @brief Test case for OnFragmentDownloadFailed with a retry attempt threshold - */ -TEST_F(FragmentDownloadTests, OnFragmentDownloadFailed_RetryAttemptThreshold) -{ - mMediaStreamContext->mActiveDownloadInfo = std::make_shared(); - DownloadInfoPtr dlInfo = std::make_shared(); - dlInfo->url = "http://example.com/fragment"; - dlInfo->isInitSegment = false; - - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()).WillRepeatedly(Return(true)); - - // Set segDLFailCount to 10 and set the fail threshold as 10(default) - // This should not trigger a ramp down - mMediaStreamContext->segDLFailCount = 10; - EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_FragmentDownloadFailThreshold)).WillRepeatedly(Return(10)); - - // Mock the behavior of GetFetchBuffer, create a dummy CachedFragment - auto cachedFragment = std::make_shared(); - EXPECT_CALL(*g_mockMediaTrack, GetFetchBuffer(false)) - .WillOnce(Return(cachedFragment.get())); - - //Ensure proper error event is sent - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SendDownloadErrorEvent(AAMP_TUNE_FRAGMENT_DOWNLOAD_FAILURE, _)) - .Times(1); - - // Test the behavior of OnFragmentDownloadFailed, mCheckForRampdown should be set to false - EXPECT_NO_THROW({ - mMediaStreamContext->OnFragmentDownloadFailed(dlInfo); - EXPECT_FALSE(mMediaStreamContext->mCheckForRampdown); - }); -} - -/** - * @brief Test case for DownloadFragment with null download info - */ -TEST_F(FragmentDownloadTests, DownloadFragment_NullDownloadInfo) -{ - DownloadInfoPtr dlInfo = nullptr; - bool result = mMediaStreamContext->DownloadFragment(dlInfo); - EXPECT_FALSE(result); -} - -/** - * @brief Test case for DownloadFragment with empty media URL - */ -TEST_F(FragmentDownloadTests, DownloadFragment_EmptyMediaUrl) -{ - DownloadInfoPtr dlInfo = std::make_shared(); - dlInfo->uriList[0].url = ""; - bool result = mMediaStreamContext->DownloadFragment(dlInfo); - EXPECT_FALSE(result); -} - -/** - * @brief Test case for DownloadFragment with valid DownloadInfo - */ -TEST_F(FragmentDownloadTests, DownloadFragment_ValidDownloadInfo) -{ - DownloadInfoPtr dlInfo = std::make_shared(); - dlInfo->uriList[0].url = "http://example.com/fragment"; - dlInfo->url = "http://example.com/fragment"; - dlInfo->isInitSegment = false; - - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()).WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetFile(_, _, _, _, _, _, _, _, _, _, _, _, _, _)).WillOnce(Return(true)); - - auto cachedFragment = std::make_shared(); - EXPECT_CALL(*g_mockMediaTrack, GetFetchBuffer(true)) - .WillOnce(Return(cachedFragment.get())); - - EXPECT_NO_THROW({ - bool result = mMediaStreamContext->DownloadFragment(dlInfo); - EXPECT_TRUE(result); - }); -} diff --git a/test/utests/tests/MediaStreamContextTests/MediaStreamContextAampTests.cpp b/test/utests/tests/MediaStreamContextTests/MediaStreamContextAampTests.cpp index 24e323388..dba525b48 100644 --- a/test/utests/tests/MediaStreamContextTests/MediaStreamContextAampTests.cpp +++ b/test/utests/tests/MediaStreamContextTests/MediaStreamContextAampTests.cpp @@ -19,8 +19,6 @@ #include #include "MediaStreamContext.h" -AampConfig *gpGlobalConfig{nullptr}; - int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); diff --git a/test/utests/tests/MediaStreamContextTests/MediaStreamContextTests.cpp b/test/utests/tests/MediaStreamContextTests/MediaStreamContextTests.cpp index b0ca301f5..d72d06f0d 100644 --- a/test/utests/tests/MediaStreamContextTests/MediaStreamContextTests.cpp +++ b/test/utests/tests/MediaStreamContextTests/MediaStreamContextTests.cpp @@ -32,6 +32,8 @@ using namespace testing; +AampConfig *gpGlobalConfig{nullptr}; + class MediaStreamContextTest : public testing::Test { protected: diff --git a/test/utests/tests/PrivAampTests/PrivAampTests_TsbInjection.cpp b/test/utests/tests/PrivAampTests/PrivAampTests_TsbInjection.cpp index e657d2689..ef338a300 100644 --- a/test/utests/tests/PrivAampTests/PrivAampTests_TsbInjection.cpp +++ b/test/utests/tests/PrivAampTests/PrivAampTests_TsbInjection.cpp @@ -26,7 +26,6 @@ #include "priv_aamp.h" #include "MockStreamAbstractionAAMP.h" -#include "MockMediaTrack.h" using ::testing::NiceMock; using ::testing::Return; diff --git a/test/utests/tests/StreamAbstractionAAMP/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP/FunctionalTests.cpp index 1ce086d2c..01c7e6b25 100644 --- a/test/utests/tests/StreamAbstractionAAMP/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP/FunctionalTests.cpp @@ -27,7 +27,7 @@ #include "MockAampConfig.h" #include "MockAampUtils.h" #include "MockPrivateInstanceAAMP.h" -#include "MockMediaTrack.h" +#include "MockStreamAbstractionAAMP.h" #include "MockMediaProcessor.h" using ::testing::_; diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/AudioOnlyTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/AudioOnlyTests.cpp index 7f5a09452..c13faf161 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/AudioOnlyTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/AudioOnlyTests.cpp @@ -109,7 +109,6 @@ class AudioOnlyTests : public ::testing::Test {eAAMPConfig_PrePlayBufferCount, DEFAULT_PREBUFFER_COUNT}, {eAAMPConfig_VODTrickPlayFPS, TRICKPLAY_VOD_PLAYBACK_FPS}, {eAAMPConfig_ABRBufferCounter,DEFAULT_ABR_BUFFER_COUNTER}, - {eAAMPConfig_MaxDownloadBuffer, DEFAULT_MAX_DOWNLOAD_BUFFER}, {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK} }; @@ -150,7 +149,6 @@ class AudioOnlyTests : public ::testing::Test { if (mStreamAbstractionAAMP_MPD) { - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; } @@ -345,7 +343,7 @@ R"( .WillRepeatedly(Return(true)); /* Initialize MPD. The audio initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("opus/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); @@ -356,7 +354,7 @@ R"( /* Push the first audio segment to present. Here, video is replaced with audio track in audio only case */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("opus/audio_1.mp3"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); // Call for video track PushNextFragment(eTRACK_VIDEO); @@ -419,7 +417,7 @@ R"( fragmentUrl = std::string(TEST_BASE_URL) + std::string("audio.mp4"); // On a first look, this is a bug in the code. initialization is returned as empty with this manifest std::string indexRange = "0-1363"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq(indexRange.c_str()), true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq(indexRange.c_str()), true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); @@ -430,7 +428,7 @@ R"( /* Push the first audio segment to present.*/ indexRange = "1364-61619"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq(indexRange.c_str()), false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq(indexRange.c_str()), false, _, _, _, _, _)) .WillOnce(Return(true)); // Call for video track PushNextFragment(eTRACK_VIDEO); @@ -491,7 +489,7 @@ R"( fragmentUrl = std::string(TEST_BASE_URL) + std::string("audio.mp4"); // On a first look, this is a bug in the code. initialization is returned as empty with this manifest std::string indexRange = "0-1363"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq("0-1363"), true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq("0-1363"), true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest, TuneType::eTUNETYPE_NEW_NORMAL, 240); @@ -502,7 +500,7 @@ R"( /* Push the first audio segment to present.*/ indexRange = "1529861-1584033"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq("1529861-1584033"), false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, testing::StrEq("1529861-1584033"), false, _, _, _, _, _)) .WillOnce(Return(true)); // Call for video track PushNextFragment(eTRACK_VIDEO); diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSelectionTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSelectionTests.cpp index d5c1f1147..847264e70 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSelectionTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSelectionTests.cpp @@ -126,7 +126,6 @@ class SelectAudioTrackTests : public ::testing::Test { if (mStreamAbstractionAAMP_MPD) { - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; } @@ -279,7 +278,7 @@ R"( )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("opus/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); @@ -327,7 +326,7 @@ R"( )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("h264/video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); EXPECT_EQ(status, eAAMPSTATUS_OK); @@ -376,7 +375,7 @@ R"( )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("aac/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); @@ -434,7 +433,7 @@ R"( )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("ac4/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); EXPECT_EQ(status, eAAMPSTATUS_OK); @@ -500,7 +499,7 @@ R"( )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("ac4/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); EXPECT_EQ(status, eAAMPSTATUS_OK); diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSwitchTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSwitchTests.cpp index 22488d0b9..ced0b1551 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSwitchTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/AudioTrackSwitchTests.cpp @@ -127,7 +127,6 @@ class SwitchAudioTrackTests : public ::testing::Test { if (mStreamAbstractionAAMP_MPD) { - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; } @@ -295,7 +294,7 @@ R"( .WillRepeatedly(Return(true)); fragmentUrl = std::string(TEST_BASE_URL) + std::string("opus/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); @@ -432,7 +431,7 @@ R"( .WillRepeatedly(Return(true)); fragmentUrl = std::string(TEST_BASE_URL) + std::string("opus/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); @@ -478,7 +477,7 @@ R"( )"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); EXPECT_EQ(status, eAAMPSTATUS_OK); diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/CMakeLists.txt b/test/utests/tests/StreamAbstractionAAMP_MPD/CMakeLists.txt index 650353ef6..3a53250e6 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/CMakeLists.txt +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/CMakeLists.txt @@ -65,7 +65,7 @@ set(TEST_SOURCES FunctionalTests.cpp StreamSelectionTest.cpp MonitorLatencyTests.cpp) - set(AAMP_SOURCES ${AAMP_ROOT}/streamabstraction.cpp ${AAMP_ROOT}/CachedFragment.cpp ${AAMP_ROOT}/fragmentcollector_mpd.cpp ${AAMP_ROOT}/AampMPDParseHelper.cpp ${AAMP_ROOT}/AampMPDUtils.cpp ${AAMP_ROOT}/AampFragmentDescriptor.cpp ${AAMP_ROOT}/downloader/AampCurlDownloader.cpp ${DASH_PARSER_SOURCES}) + set(AAMP_SOURCES ${AAMP_ROOT}/streamabstraction.cpp ${AAMP_ROOT}/CachedFragment.cpp ${AAMP_ROOT}/fragmentcollector_mpd.cpp ${AAMP_ROOT}/AampMPDParseHelper.cpp ${AAMP_ROOT}/AampMPDUtils.cpp ${AAMP_ROOT}/AampTrackWorker.cpp ${AAMP_ROOT}/downloader/AampCurlDownloader.cpp ${DASH_PARSER_SOURCES}) add_executable(${EXEC_NAME} ${TEST_SOURCES} diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FetcherLoopTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FetcherLoopTests.cpp index 873d5146b..9f892b589 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FetcherLoopTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FetcherLoopTests.cpp @@ -35,7 +35,7 @@ #include "MockAampStreamSinkManager.h" #include "MockTSBSessionManager.h" #include "MockAdManager.h" -#include "AampTrackWorker.hpp" +#include "AampTrackWorker.h" using ::testing::_; using ::testing::AnyNumber; @@ -50,7 +50,7 @@ using ::testing::WithoutArgs; /** * @brief LinearTests tests common base class. */ -class FetcherLoopTests : public ::testing::Test +class FetcherLoopTests : public testing::TestWithParam { protected: class TestableStreamAbstractionAAMP_MPD : public StreamAbstractionAAMP_MPD @@ -279,7 +279,6 @@ class FetcherLoopTests : public ::testing::Test {eAAMPConfig_StallTimeoutMS, DEFAULT_STALL_DETECTION_TIMEOUT}, {eAAMPConfig_AdFulfillmentTimeout, DEFAULT_AD_FULFILLMENT_TIMEOUT}, {eAAMPConfig_AdFulfillmentTimeoutMax, MAX_AD_FULFILLMENT_TIMEOUT}, - {eAAMPConfig_MaxDownloadBuffer, DEFAULT_MAX_DOWNLOAD_BUFFER}, {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK} }; @@ -319,7 +318,6 @@ class FetcherLoopTests : public ::testing::Test { if (mTestableStreamAbstractionAAMP_MPD) { - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); delete mTestableStreamAbstractionAAMP_MPD; mTestableStreamAbstractionAAMP_MPD = nullptr; } @@ -550,7 +548,7 @@ TEST_F(FetcherLoopTests, SelectSourceOrAdPeriodTests1) bool ret = false; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest); @@ -595,7 +593,7 @@ TEST_F(FetcherLoopTests, SelectSourceOrAdPeriodTests2) bool ret = false; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p1_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_SEEK, 35); @@ -640,7 +638,7 @@ TEST_F(FetcherLoopTests, IndexSelectedPeriodTests1) /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p1_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mLiveManifest); @@ -679,7 +677,7 @@ TEST_F(FetcherLoopTests, IndexSelectedPeriodTests2) bool ret = false; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_SEEK, 15); @@ -722,7 +720,7 @@ TEST_F(FetcherLoopTests, DetectDiscotinuityAndFetchInitTests1) mPrivateInstanceAAMP->rate = 1.0; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_SEEK, 0); @@ -763,7 +761,7 @@ TEST_F(FetcherLoopTests, DetectDiscotinuityAndFetchInitTests2) mPrivateInstanceAAMP->rate = 1.0; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mVodManifest, eTUNETYPE_SEEK, 15); @@ -791,7 +789,7 @@ TEST_F(FetcherLoopTests, DetectDiscotinuityAndFetchInitTests2) * for the next period. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p1_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, true, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, true, _, _, _, _)) .Times(1) .WillOnce(Return(true)); @@ -814,9 +812,9 @@ TEST_F(FetcherLoopTests, BasicFetcherLoop) mPrivateInstanceAAMP->rate = AAMP_NORMAL_PLAY_RATE; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillRepeatedly(Return(false)); status = InitializeMPD(mVodManifest); @@ -826,9 +824,9 @@ TEST_F(FetcherLoopTests, BasicFetcherLoop) * The segment starts at time 40.0s and has a duration of 2.0s. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p1_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()) @@ -863,7 +861,7 @@ TEST_F(FetcherLoopTests, BasicFetcherLoopLive) mPrivateInstanceAAMP->rate = AAMP_NORMAL_PLAY_RATE; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillRepeatedly(Return(false)); @@ -882,10 +880,10 @@ TEST_F(FetcherLoopTests, BasicFetcherLoopLive) static int counter = 0; return (++counter < 20); }); fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p1_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _, _, _)) .WillRepeatedly(Return(true)); /* Invoke the fetcher loop. */ @@ -909,7 +907,7 @@ TEST_F(FetcherLoopTests, SelectSourceOrAdPeriodTests3) bool ret = false; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); status = InitializeMPD(mLiveManifest, eTUNETYPE_SEEK, 10); @@ -1040,7 +1038,7 @@ R"( )"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _, _, _)) .WillRepeatedly(Return(true)); AAMPStatusType status = InitializeMPD(manifest, eTUNETYPE_NEW_NORMAL, 10.0); @@ -1053,7 +1051,7 @@ R"( mTestableStreamAbstractionAAMP_MPD->PushNextFragment(pMediaStreamContext,eCURLINSTANCE_AUDIO); pMediaStreamContext->freshManifest=true; //when skipfetch sets to true, fetchfragment will be avoided - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, eCURLINSTANCE_AUDIO, _,_, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, eCURLINSTANCE_AUDIO, _,_, _, _, _, _, _, _, _)) .Times(0); EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetPositionMilliseconds()).WillRepeatedly(Return(0.0)); @@ -1062,6 +1060,99 @@ R"( } +/** + * @brief BasicFetcherLoop tests. + * + * The tests verify the basic fetcher loop functionality for a Live multi-period MPD. + */ +TEST_F(FetcherLoopTests, BasicFetcherLoopLiveWithParallelDownload) +{ + std::string videoFragmentUrl; + std::string audioFragmentUrl; + AAMPStatusType status; + mPrivateInstanceAAMP->rate = AAMP_NORMAL_PLAY_RATE; + static const char *multiTrackManifest = R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; + + /* Initialize MPD. The video initialization segment is cached. */ + videoFragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); + audioFragmentUrl = std::string(TEST_BASE_URL) + std::string("audio_p0_init.mp4"); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoFragmentUrl, _, _, _, _, true, _, _, _, _, _)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioFragmentUrl, _, _, _, _, true, _, _, _, _, _)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillRepeatedly(Return(false)); + + status = InitializeMPD(multiTrackManifest, eTUNETYPE_SEEK, 24.0); + + /* Invoke Worker threads */ + mTestableStreamAbstractionAAMP_MPD->InvokeInitializeWorkers(); + + EXPECT_EQ(status, eAAMPSTATUS_OK); + + /* Push the first video segment to present. + * The segment starts at time 40.0s and has a duration of 2.0s. + */ + // Add the new EXPECT_CALL for DownloadsAreEnabled + EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()) + .Times(AnyNumber()) + .WillRepeatedly([]() + { + static int counter = 0; + return (++counter < 20); }); + videoFragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p1_init.mp4"); + audioFragmentUrl = std::string(TEST_BASE_URL) + std::string("audio_p1_init.mp4"); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoFragmentUrl, _, _, _, _, true, _, _, _, _, _)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioFragmentUrl, _, _, _, _, true, _, _, _, _, _)).Times(1).WillOnce(Return(true)); + // Expect the segments to be downloaded from track + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _, _, _)).WillRepeatedly(Return(true)); + + /* Invoke the fetcher loop. */ + mTestableStreamAbstractionAAMP_MPD->InvokeFetcherLoop(); + EXPECT_EQ(mTestableStreamAbstractionAAMP_MPD->GetCurrentPeriodIdx(), 1); + EXPECT_EQ(mTestableStreamAbstractionAAMP_MPD->GetIteratorPeriodIdx(), 1); +} + /** * @brief SelectSourceOrAdPeriod tests. * @@ -1163,10 +1254,10 @@ R"( /* Initialize MPD. The video/audio initialization segment is cached. */ videoInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); audioInitFragmentUrl = std::string(TEST_BASE_URL) + std::string("audio_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoInitFragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioInitFragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioInitFragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); // Seek to Period 1 @@ -1239,7 +1330,7 @@ TEST_F(FetcherLoopTests, SelectSourceOrAdPeriodTests5) // Expect initialization fragment to be cached fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .Times(1) .WillOnce(Return(true)); @@ -1334,329 +1425,3 @@ TEST_F(FetcherLoopTests, SelectSourceOrAdPeriodTests5) EXPECT_TRUE(ret); EXPECT_EQ(cdaiObj->mAdState, AdState::IN_ADBREAK_AD_PLAYING); // Validate expected state transition } -// Structure to hold test parameters -struct TestParams -{ - const char *manifest; - double seekPos; - const char *videoInitFragment; - const char *audioInitFragment; - const char *videoFragmentP1; - const char *audioFragmentP1; -}; - -// Test cases -TestParams testCases[] = { - { - R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - )", - 24.0, - "video_p0_init.mp4", - "audio_p0_init.mp4", - "video_p1_init.mp4", - "audio_p1_init.mp4"}, - { - R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - )", - 0, - "video_p0_init.m4s", - "audio_p0_init.m4s", - "video_p1_init.m4s", - "audio_p1_init.m4s"}, - { - R"( - - - - - http://host/asset/video_p0.m4s - - - - - - - - - - http://host/asset/audio_p0.m4s - - - - - - - - - - - - http://host/asset/video_p1.m4s - - - - - - - - - - http://host/asset/audio_p1.m4s - - - - - - - - - - )", - 0, - "video_p0.m4s", - "audio_p0.m4s", - "video_p1.m4s", - "audio_p1.m4s"}, - { - R"( - - - - - http://host/asset/video_p0.m4s - - - - - - http://host/asset/audio_p0.m4s - - - - - - - - http://host/asset/video_p1.m4s - - - - - - http://host/asset/audio_p1.m4s - - - - - - )", - 0, - "video_p0.m4s", - "audio_p0.m4s", - "video_p1.m4s", - "audio_p1.m4s"}, - { - R"( - - - - - http://host/asset/video_p0.m4s - - - - - - - - http://host/asset/audio_p0.m4s - - - - - - - - - - http://host/asset/video_p1.m4s - - - - - - - - http://host/asset/audio_p1.m4s - - - - - - - - )", - 0, - "video_p0.m4s", - "audio_p0.m4s", - "video_p1.m4s", - "audio_p1.m4s"}}; - -class AdvancedFetcherLoopTests : public FetcherLoopTests, public ::testing::WithParamInterface -{ -public: - void SetUp() override - { - counter = 0; - FetcherLoopTests::SetUp(); - } - - void TearDown() override - { - FetcherLoopTests::TearDown(); - } - int counter; -}; - -/** - * @brief FetcherLoopTests - * Verifies the fetcher loop with different formats of MPDs - */ -TEST_P(AdvancedFetcherLoopTests, FetcherLoopTestsWithDifferentMPD) -{ - std::string videoFragmentUrl; - std::string audioFragmentUrl; - AAMPStatusType status; - mPrivateInstanceAAMP->rate = AAMP_NORMAL_PLAY_RATE; - bool ret = false; - - // Access struct elements - TestParams param = GetParam(); - const char *manifest = param.manifest; - double seekPos = param.seekPos; - const char *videoInitFragment = param.videoInitFragment; - const char *audioInitFragment = param.audioInitFragment; - const char *videoFragmentP1 = param.videoFragmentP1; - const char *audioFragmentP1 = param.audioFragmentP1; - - /* Initialize MPD. The video/audio initialization segment is cached. */ - videoFragmentUrl = std::string(TEST_BASE_URL) + std::string(videoInitFragment); - audioFragmentUrl = std::string(TEST_BASE_URL) + std::string(audioInitFragment); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoFragmentUrl, _, _, _, _, true, _, _, _)).Times(1).WillOnce(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioFragmentUrl, _, _, _, _, true, _, _, _)).Times(1).WillOnce(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, IsLocalAAMPTsbInjection()).WillRepeatedly(Return(false)); - - status = InitializeMPD(manifest, eTUNETYPE_SEEK, seekPos); - - /* Invoke Worker threads */ - mTestableStreamAbstractionAAMP_MPD->InvokeInitializeWorkers(); - - EXPECT_EQ(status, eAAMPSTATUS_OK); - - /* Push the first video segment to present. - * The segment starts at time 40.0s and has a duration of 2.0s. - */ - EXPECT_CALL(*g_mockPrivateInstanceAAMP, DownloadsAreEnabled()) - .Times(AnyNumber()) - .WillRepeatedly([this]() { return (++counter < 20); }); - videoFragmentUrl = std::string(TEST_BASE_URL) + std::string(videoFragmentP1); - audioFragmentUrl = std::string(TEST_BASE_URL) + std::string(audioFragmentP1); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(videoFragmentUrl, _, _, _, _, true, _, _, _)).Times(1).WillOnce(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(audioFragmentUrl, _, _, _, _, true, _, _, _)).Times(1).WillOnce(Return(true)); - - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, false, _, _, _)).WillRepeatedly(Return(true)); - - /* Invoke the fetcher loop. */ - mTestableStreamAbstractionAAMP_MPD->InvokeFetcherLoop(); - EXPECT_EQ(mTestableStreamAbstractionAAMP_MPD->GetCurrentPeriodIdx(), 1); - EXPECT_EQ(mTestableStreamAbstractionAAMP_MPD->GetIteratorPeriodIdx(), 1); -} - -INSTANTIATE_TEST_SUITE_P( - BasicFetcherLoopMPDTests, - AdvancedFetcherLoopTests, - ::testing::ValuesIn(testCases)); diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index a5b2920e0..40eff1dce 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -118,7 +118,6 @@ class FunctionalTestsBase {eAAMPConfig_PrePlayBufferCount, DEFAULT_PREBUFFER_COUNT}, {eAAMPConfig_VODTrickPlayFPS, TRICKPLAY_VOD_PLAYBACK_FPS}, {eAAMPConfig_ABRBufferCounter, DEFAULT_ABR_BUFFER_COUNTER}, - {eAAMPConfig_MaxDownloadBuffer, DEFAULT_MAX_DOWNLOAD_BUFFER}, {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK} }; @@ -167,7 +166,6 @@ class FunctionalTestsBase if (mStreamAbstractionAAMP_MPD) { - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; } @@ -458,9 +456,11 @@ class StreamAbstractionAAMP_MPDTest : public FunctionalTestsBase, printSelectedTrack(trackIndex, media); } - void CallAdvanceTrack(int trackIdx, bool trickPlay, double &delta) + void CallAdvanceTrack(int trackIdx, bool trickPlay, double *delta, bool &waitForFreeFrag, bool &bCacheFullState) { - AdvanceTrack(trackIdx, trickPlay, delta); + bool throttleAudioDownload = false; + bool isDiscontinuity = false; + AdvanceTrack(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState, throttleAudioDownload, isDiscontinuity ); } void CallFetcherLoop() @@ -531,6 +531,10 @@ class StreamAbstractionAAMP_MPDTest : public FunctionalTestsBase, { ProcessStreamRestrictionList(node, AdID, startMS, isInit, reportBulkMeta); } + void CallTrackDownloader(int trackIdx, std::string initialization) + { + TrackDownloader(trackIdx, initialization); + } void CallFetchAndInjectInitFragments(bool discontinuity = false) { @@ -694,7 +698,6 @@ class StreamAbstractionAAMP_MPDTest : public FunctionalTestsBase, delete g_mockTSBSessionManager; g_mockTSBSessionManager = nullptr; - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; @@ -807,17 +810,18 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); EXPECT_EQ(status, eAAMPSTATUS_OK); + /* Push the first video segment to present. The segment time identifier ("$Time$") is zero. The * segment starts at time 0.0s and has a duration of 2.0s. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_0.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 0.0, 2.0, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 0.0, 2.0, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -827,7 +831,7 @@ R"( * starts at time 2.0s and has a duration of 2.0s. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_5000.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 2.0, 2.0, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 2.0, 2.0, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -862,7 +866,7 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -874,7 +878,7 @@ R"( * and has a duration of 2.0s. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_0.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 0.0, 2.0, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 0.0, 2.0, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -884,7 +888,7 @@ R"( * starts at time 2.0s and has a duration of 2.0s. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_5000.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 2.0, 2.0, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, 2.0, 2.0, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -939,7 +943,7 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); (void)status; @@ -957,7 +961,7 @@ R"( /* Push the first video segment to present. */ (void)snprintf(url, sizeof(url), "%svideo_%lld.m4s", TEST_BASE_URL, fragmentNumber); fragmentUrl = std::string(url); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -966,7 +970,7 @@ R"( fragmentNumber++; (void)snprintf(url, sizeof(url), "%svideo_%lld.m4s", TEST_BASE_URL, fragmentNumber); fragmentUrl = std::string(url); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -1049,7 +1053,7 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p1_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, NotifyOnEnteringLive()).Times(1); @@ -1137,7 +1141,7 @@ R"( /* Push the first video segment to present. */ (void)snprintf(url, sizeof(url), "%svideo_p1_%lld.m4s", TEST_BASE_URL, fragmentNumber); fragmentUrl = std::string(url); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -1146,7 +1150,7 @@ R"( fragmentNumber++; (void)snprintf(url, sizeof(url), "%svideo_p1_%lld.m4s", TEST_BASE_URL, fragmentNumber); fragmentUrl = std::string(url); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -1180,7 +1184,7 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_p0_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, NotifyOnEnteringLive()).Times(1); @@ -1240,11 +1244,11 @@ R"( /* Initialize MPD. The initialization segments are cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("vp8/video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); fragmentUrl = std::string(TEST_BASE_URL) + std::string("vorbis/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -1253,14 +1257,14 @@ R"( /* Push the first video segment to present. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("vp8/video_1.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); /* Push the first audio segment to present. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("vorbis/audio_1.mp3"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_AUDIO); @@ -1303,11 +1307,11 @@ R"( /* Initialize MPD. The initialization segments are cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("vp9/video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); fragmentUrl = std::string(TEST_BASE_URL) + std::string("opus/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -1316,14 +1320,14 @@ R"( /* Push the first video segment to present. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("vp9/video_1.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); /* Push the first audio segment to present. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("opus/audio_1.mp3"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_AUDIO); @@ -1388,11 +1392,11 @@ R"( * Opus. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("h264/video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); fragmentUrl = std::string(TEST_BASE_URL) + std::string("aac/audio_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -1401,14 +1405,14 @@ R"( /* Push the first video segment to present. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("h264/video_1.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); /* Push the first audio segment to present. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("aac/audio_1.mp3"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_AUDIO); @@ -1466,7 +1470,7 @@ R"( /* Initialize MPD. The first video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -1515,7 +1519,7 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); @@ -1554,7 +1558,7 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -1615,7 +1619,7 @@ R"( /* Initialize MPD. The first video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -1675,7 +1679,7 @@ R"( // Initialize MPD. The video initialization segment is cached. std::string expectedCData = R"({"version":1,"identifiers":[{"scheme":"urn:smpte:ul:060E2B34.01040101.01200900.00000000","value":"5493003","ad_position":"_PT0S_0","ad_type":"avail","tracking_uri":"../../../../../../../../../../tracking/99247e89c7677df85a85aabdd3256ffe02a60196/example-dash-vod-2s-generic/f38d0147-7bee-480f-83a7-fec49fda39b9","custom_vast_data":null}]})"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); @@ -1731,7 +1735,7 @@ R"( * segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("dash/iframe_init.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest, TuneType::eTUNETYPE_NEW_NORMAL, seekPosition, rate); @@ -1741,7 +1745,7 @@ R"( /* Push the first video segment to present. */ (void)snprintf(url, sizeof(url), "%sdash/iframe_%03d.m4s", TEST_BASE_URL, fragmentNumber); fragmentUrl = std::string(url); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -1762,7 +1766,7 @@ R"( (void)snprintf(url, sizeof(url), "%sdash/iframe_%03d.m4s", TEST_BASE_URL, fragmentNumber); fragmentUrl = std::string(url); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); PushNextFragment(eTRACK_VIDEO); @@ -1861,7 +1865,7 @@ R"( /* Initialize MPD with seek position and play rate. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("dash/iframe_init.m4s"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest, TuneType::eTUNETYPE_NEW_NORMAL, seekPosition, rate); @@ -1875,7 +1879,7 @@ R"( char number[50]; (void)snprintf(number, sizeof(number), "dash/iframe_%d.m4s", trickplay_time_tbl[idx].expected_seg_time[j]); fragmentUrl = std::string(TEST_BASE_URL) + std::string(number); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, false, _, _, _, _, _)) .WillOnce(Return(true)); } @@ -2403,7 +2407,7 @@ R"( // Initialize MPD. The video initialization segment is cached. std::string expectedCData = R"({"version":1,"identifiers":[{"scheme":"urn:smpte:ul:060E2B34.01040101.01200900.00000000","value":"5493003","ad_position":"_PT0S_0","ad_type":"avail","tracking_uri":"../../../../../../../../../../tracking/99247e89c7677df85a85aabdd3256ffe02a60196/example-dash-vod-2s-generic/f38d0147-7bee-480f-83a7-fec49fda39b9","custom_vast_data":null}]})"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); @@ -2445,7 +2449,7 @@ R"( )"; // Initialize MPD. The video initialization segment is cached. fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); @@ -2484,7 +2488,7 @@ R"( )"; // Initialize MPD. The video initialization segment is cached. fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); @@ -2521,7 +2525,7 @@ TEST_F(FunctionalTests, ChunkMode_NonLLD) )"; float seekPosition = 0; int rate = 1 ; //Normal playrate test - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, NotifyOnEnteringLive()).Times(1); @@ -2583,7 +2587,7 @@ TEST_F(FunctionalTests, ChunkMode_LLD) EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetLLDashAdjustSpeed()) .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _, _, _)) .WillRepeatedly(Return(true)); //For this test case we need EnableLowLatencyDash as true EXPECT_CALL(*g_mockAampConfig, IsConfigSet(_)) @@ -2651,7 +2655,7 @@ TEST_F(FunctionalTests, ChunkMode_LLD_ForMaxLatency_Case) EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetLLDashAdjustSpeed()) .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockAampMPDDownloader, IsMPDLowLatency (_)) @@ -2707,7 +2711,7 @@ R"( )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); (void)status; @@ -2746,7 +2750,7 @@ TEST_F(FunctionalTests, SetThumbnailTrack) )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); (void)status; @@ -2783,7 +2787,7 @@ TEST_F(FunctionalTests, GetThumbnailRangeDataTest1) )"; fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest); (void)status; @@ -2828,7 +2832,7 @@ TEST_F(FunctionalTests, FindServerUTCTimeTest) EXPECT_CALL(*g_mockAampUtils, aamp_GetCurrentTimeMS()) .Times(AnyNumber()) .WillRepeatedly(Return(timeMS)); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _, _, _)) .WillRepeatedly(Return(true)); // Verify that the parameters from the manifest URL are not added to the time request URL EXPECT_CALL(*g_mockAampUtils, GetNetworkTime("http://host/timing", _, _)).WillOnce(Return(currentTime)); @@ -2862,7 +2866,7 @@ R"( g_mockAampUtils = new NiceMock(); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); @@ -2941,7 +2945,7 @@ R"( /* Set the eAAMPConfig_useRialtoSink flag to true */ mBoolConfigSettings[eAAMPConfig_useRialtoSink] = true; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); EXPECT_CALL(*g_mockAampStreamSinkManager, AddMediaHeader(2, _)) @@ -3473,7 +3477,7 @@ R"( )"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _, _, _)) .WillRepeatedly(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); status = InitializeMPD(manifest, eTUNETYPE_NEW_NORMAL, 0.0, AAMP_NORMAL_PLAY_RATE, false); @@ -3521,7 +3525,7 @@ R"( /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/LinearFOGTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/LinearFOGTests.cpp index 384f56e58..f9b18cb0a 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/LinearFOGTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/LinearFOGTests.cpp @@ -33,7 +33,6 @@ #include "MockMediaStreamContext.h" #include "MockAampMPDDownloader.h" #include "MockAampStreamSinkManager.h" -#include "MockAampTrackWorker.h" using ::testing::_; using ::testing::SetArgReferee; @@ -121,7 +120,7 @@ class LinearFOGTests : public testing::TestWithParam /** @brief Integer AAMP configuration settings. */ const IntConfigSettings mDefaultIntConfigSettings = - { + { {eAAMPConfig_ABRCacheLength, DEFAULT_ABR_CACHE_LENGTH}, {eAAMPConfig_MaxABRNWBufferRampUp, AAMP_HIGH_BUFFER_BEFORE_RAMPUP}, {eAAMPConfig_MinABRNWBufferRampDown, AAMP_LOW_BUFFER_BEFORE_RAMPDOWN}, @@ -131,8 +130,8 @@ class LinearFOGTests : public testing::TestWithParam {eAAMPConfig_PrePlayBufferCount, DEFAULT_PREBUFFER_COUNT}, {eAAMPConfig_VODTrickPlayFPS, TRICKPLAY_VOD_PLAYBACK_FPS}, {eAAMPConfig_ABRBufferCounter, DEFAULT_ABR_BUFFER_COUNTER}, - {eAAMPConfig_MaxDownloadBuffer, DEFAULT_MAX_DOWNLOAD_BUFFER}, - {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK}}; + {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK} + }; IntConfigSettings mIntConfigSettings; @@ -160,8 +159,6 @@ class LinearFOGTests : public testing::TestWithParam g_mockAampMPDDownloader = new StrictMock(); - g_mockAampTrackWorker = new StrictMock(); - g_mockAampStreamSinkManager = new NiceMock(); mStreamAbstractionAAMP_MPD = nullptr; @@ -174,51 +171,47 @@ class LinearFOGTests : public testing::TestWithParam void TearDown() { - if (mStreamAbstractionAAMP_MPD) - { - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); - delete mStreamAbstractionAAMP_MPD; - mStreamAbstractionAAMP_MPD = nullptr; - } - - delete mPrivateInstanceAAMP; - mPrivateInstanceAAMP = nullptr; + if (mStreamAbstractionAAMP_MPD) + { + delete mStreamAbstractionAAMP_MPD; + mStreamAbstractionAAMP_MPD = nullptr; + } - delete mCdaiObj; - mCdaiObj = nullptr; + delete mPrivateInstanceAAMP; + mPrivateInstanceAAMP = nullptr; - delete gpGlobalConfig; - gpGlobalConfig = nullptr; + delete mCdaiObj; + mCdaiObj = nullptr; - if (g_mockAampUtils) - { - delete g_mockAampUtils; - g_mockAampUtils = nullptr; - } + delete gpGlobalConfig; + gpGlobalConfig = nullptr; - delete g_mockAampConfig; - g_mockAampConfig = nullptr; + if (g_mockAampUtils) + { + delete g_mockAampUtils; + g_mockAampUtils = nullptr; + } - delete g_mockAampGstPlayer; - g_mockAampGstPlayer = nullptr; + delete g_mockAampConfig; + g_mockAampConfig = nullptr; - delete g_mockPrivateInstanceAAMP; - g_mockPrivateInstanceAAMP = nullptr; + delete g_mockAampGstPlayer; + g_mockAampGstPlayer = nullptr; - delete g_mockMediaStreamContext; - g_mockMediaStreamContext = nullptr; + delete g_mockPrivateInstanceAAMP; + g_mockPrivateInstanceAAMP = nullptr; - delete g_mockAampMPDDownloader; - g_mockAampMPDDownloader = nullptr; + delete g_mockMediaStreamContext; + g_mockMediaStreamContext = nullptr; - delete g_mockAampStreamSinkManager; - g_mockAampStreamSinkManager = nullptr; + delete g_mockAampMPDDownloader; + g_mockAampMPDDownloader = nullptr; - delete g_mockAampTrackWorker; - g_mockAampTrackWorker = nullptr; + delete g_mockAampStreamSinkManager; + g_mockAampStreamSinkManager = nullptr; - mManifest = nullptr; - mResponse = nullptr; + mManifest = nullptr; + mResponse = nullptr; } public: @@ -374,9 +367,8 @@ R"( bool ret = false; /* Initialize MPD. The video initialization segment is cached. */ fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_init.mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - .Times(2) - .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _, _, _)) + .WillOnce(Return(true)); status = InitializeMPD(manifest, eTUNETYPE_NEW_SEEK, seekPos, 1.0, true); EXPECT_EQ(status, eAAMPSTATUS_OK); @@ -385,23 +377,37 @@ R"( EXPECT_NE(track, nullptr); MediaStreamContext *pMediaStreamContext = static_cast(track); - /* Push the first video segment to present. - * The segment starts at time 40.0s and has a duration of 2.0s. - */ - fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_") + std::to_string(fragNum) + std::string(".mp4"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, seekPos, 2.0, _, false, _, _, _)) - .WillOnce([pMediaStreamContext]() - { - pMediaStreamContext->mDownloadedFragment.AppendBytes("0x0a", 2); - return false; - }); - EXPECT_CALL(*g_mockAampTrackWorker, RescheduleActiveJob()) - .Times(1) - .WillOnce([pMediaStreamContext]() - { pMediaStreamContext->mDownloadedFragment.Free(); }); + /* Push the first video segment to present. + * The segment starts at time 40.0s and has a duration of 2.0s. + */ + fragmentUrl = std::string(TEST_BASE_URL) + std::string("video_") + std::to_string(fragNum) + std::string(".mp4"); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, seekPos, 2.0, _, false, _, _, _, _, _)) + .WillOnce([pMediaStreamContext]() { + pMediaStreamContext->mDownloadedFragment.AppendBytes("0x0a", 2); + return false; + }); ret = PushNextFragment(eTRACK_VIDEO); - EXPECT_EQ(ret, true); + EXPECT_EQ(ret, false); + + // Invoke UpdateMPD to mimic a playlist refresh, it will internally call UpdateTrackInfo and reset all variables + mStreamAbstractionAAMP_MPD->InvokeUpdateMPD(false); + + // The same fragment should be attempted again + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, seekPos, 2.0, _, false, _, _, _, _, _)) + .WillOnce([pMediaStreamContext]() { + pMediaStreamContext->mDownloadedFragment.Free(); + return true; + }); + + // This seeks in the current playlist and reaches the lastSegmentTime + ret = PushNextFragment(eTRACK_VIDEO); + if (seekPos != 0) + { + // Downloads the segment this time + ret = PushNextFragment(eTRACK_VIDEO); + } + EXPECT_EQ(ret, true); } // Run LinearFOGTests tests at various position values diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/MonitorLatencyTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/MonitorLatencyTests.cpp index c62cfd407..d8757e47e 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/MonitorLatencyTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/MonitorLatencyTests.cpp @@ -284,7 +284,6 @@ class MonitorLatencyTests : public ::testing::TestWithParamGetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; } diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/StreamSelectionTest.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/StreamSelectionTest.cpp index 184a6b137..47d9728bf 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/StreamSelectionTest.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/StreamSelectionTest.cpp @@ -383,7 +383,6 @@ class StreamSelectionTests : public testing::TestWithParamGetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; } @@ -573,7 +571,7 @@ TEST_P(StreamSelectionTests, TestCorrectTrackSelection) { const auto& params = GetParam(); /*Retrieve the parameter values */ mPrivateInstanceAAMP->rate = AAMP_NORMAL_PLAY_RATE; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, _, _, _, _, _, _)) .Times(AnyNumber()) .WillOnce(Return(true)); AAMPStatusType status = InitializeMPD(params.manifestUsed, TuneType::eTUNETYPE_NEW_NORMAL, params.position); diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/subtitleTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/subtitleTests.cpp index 043ccbd71..ab12ced51 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/subtitleTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/subtitleTests.cpp @@ -158,7 +158,6 @@ class SubtitleTrackTests : public ::testing::Test void TearDown() override { - mPrivateInstanceAAMP->GetAampTrackWorkerManager()->RemoveWorkers(); delete mStreamAbstractionAAMP_MPD; mStreamAbstractionAAMP_MPD = nullptr; delete mPrivateInstanceAAMP; @@ -344,7 +343,7 @@ TEST_F(SubtitleTrackTests, Nosubtitletracks) )"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _, _, _)) .WillOnce(Return(true)); status = InitializeMPD(manifest); EXPECT_EQ(status, eAAMPSTATUS_OK); @@ -522,12 +521,10 @@ TEST_F(SubtitleTrackTests, SkipSubtitleFetchTests) )"; - EXPECT_CALL(*g_mockMediaStreamContext,CacheFragment(_, _, _, _, _, true, _, _, _)) - .Times(2)//init segment is available for audio and video so set to true - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockMediaStreamContext,CacheFragment(_, _, _, _, _, false, _, _, _)) - .Times(1)//init segment is not available for subtitle so set to false - .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockMediaStreamContext,CacheFragment(_, _, _, _, _, true, _, _, _, _, _)) + .Times(2);//init segment is available for audio and video so set to true + EXPECT_CALL(*g_mockMediaStreamContext,CacheFragment(_, _, _, _, _, false, _, _, _, _, _)) + .Times(1);//init segment is not available for subtitle so set to false status = InitializeMPD(manifest); EXPECT_EQ(status, eAAMPSTATUS_OK); @@ -538,7 +535,7 @@ TEST_F(SubtitleTrackTests, SkipSubtitleFetchTests) mStreamAbstractionAAMP_MPD->PushNextFragment(pMediaStreamContext,eCURLINSTANCE_SUBTITLE); pMediaStreamContext->freshManifest=true; //when skipfetch sets to true, fetchfragment will be avoided - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, eCURLINSTANCE_SUBTITLE, _,_, _, _, _, _, _)) + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, eCURLINSTANCE_SUBTITLE, _,_, _, _, _, _, _, _, _)) .Times(0); CallSwitchSubtitleTrack(true);