Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse <SegmentList> and <SegmentBase> #18

Merged
merged 15 commits into from
Feb 5, 2018
Merged

Parse <SegmentList> and <SegmentBase> #18

merged 15 commits into from
Feb 5, 2018

Conversation

OshinKaramian
Copy link
Contributor

This adds the ability for us to parse SegmentList and SegmentBase.

There were some minor reshuffling as well (I don't think it's a huge deal to keep it in here, although I can do it as a seperate PR if that's desirable).

As a note the reshuffling includes:

  1. Moving some of the segment duration/timeline parsing out of segmentTemplate.js into its own file since SegmentBase shares that logic with SegmentTemplate.
  2. Putting all <Segment*> tag parsing into its own folder.
  3. Moving resolveUrl.js into the utils folder.

@@ -18,16 +13,13 @@ export const generateSegments = (segmentInfo, attributes) => {

// TODO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not a TODO anymore

src/errors.js Outdated
@@ -2,5 +2,8 @@ export default {
INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
DASH_INVALID_XML: 'DASH_INVALID_XML',
UNSUPPORTED_SEGMENTATION_TYPE: 'UNSUPPORTED_SEGMENTATION_TYPE'
UNSUPPORTED_SEGMENTATION_TYPE: 'UNSUPPORTED_SEGMENTATION_TYPE',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this error

shallowMerge(getAttributes(segmentList),
{
segmentUrls: segmentUrls &&
segmentUrls.map(s =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could move this map to findChildren(segmentList, 'SegmentURL').map(..., then this just becomes { segmentUrls }

// Since we're mapping we should get rid of any blank segments (in case
// the given SegmentTimeline is handling for more elements than we have
// SegmentURLs for).
}).filter(segment => segment);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this common/supposed/allowed to happen? What cases cause this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... this shouldn't happen if the mpd is well constructed. But, let's say you have a segment timeline that defines r=10, but you only have 5 segments listed. We will have too many duration objects for the number of segments we have. I guess we could throw an error, but I felt like handling it and truncating made more sense in this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I suppose its either we crash up front, or play what we can parse and just have an incomplete video, which is better than no video


// return segmentsFromList(attributes);
return segmentsFromList(
shallowMerge(segmentInfo.list, attributes), segmentInfo.timeline
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can anything in attributes overlap with segmentInfo.list (and segmentInfo.base for above)? If so we probably want to swap the order of the arguments so any overlapping values take from segmentInfo. If not then no need

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which makes me realize, I should double check for segment template

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In either case it seems safer to swap the order.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like there will be any overlap. Depending on how Initialization tag is handled, will have to keep that in mind for SegmentTemplate since it can have both the tag and attribute. I don't think we will encounter a manifest with SegmentTemplate that has both the attribute and tag, but if we do, we will want to make a choice on which takes precedence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't encounter that scenario, but it's a possibility. I didn't see anything in the spec with guidance where @initialization and <Initialization> relate to the same <SegmentTemplate>. So it seems it's up to our discretion. I made an assumption that if both are provided that the <Initialization> will take precedence (and added a comment noting as much). Given the tree structure I think that makes sense.

The other options are to just error if this is the case (less permissive, and it is probably easier to move from less permissive -> more permissive) or @initialization taking precedence.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me 👍

Copy link
Contributor

@mjneil mjneil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need <Initialization> support. SegmentBase and SegmentList do not have an initialization attribute and require the tag. SegmentTemplate can have the tag or as an attribute. Right now template doesn't support the tag, you can add it to this pr if you want, or we can add it into another, but I think for List and Base support, we should have it. See Table 13 (5.3.9.2.2) for semantics, and 5.3.9.5.2 to see where it mentions List and Template can have the tag since the spec does not mention it in their respective definitions.

Edit: They also all seem to be able to have a <RepresentationIndex>, but I'm not quite sure what that is for or if we need it just yet.

@OshinKaramian
Copy link
Contributor Author

OshinKaramian commented Jan 30, 2018

Ah, good catch, I missed that bit of nuance. I'll handle that in this PR.

Edit: I'm going to leave <RepresentationIndex> alone here, until we have a better idea of how it fits into how things are structured in the m3u8-parser.

};
}

if (attributes.initialization && typeof attributes.initialization === 'object') {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on what I picked up off the spec, <Initialization> doesn't get templated, but @initialization does, which is why there is a difference here.

const template = segmentTemplate && getAttributes(segmentTemplate);

if (template && segmentInitialization) {
template.initialization = getAttributes(segmentInitialization);
Copy link
Contributor

@mjneil mjneil Jan 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good if we can account for the string vs object here and have template.initialization always be an object.

if (template) {
  template.initialization = (segmentInitialization && getAttributes(segmentIntitialization) || { sourceURL: template.initialization || '' };
}

This way in segmentsFromTemplate you do not need the extra code path for the string

if (attributes.initialization && typeof attributes.initialization === 'object') {
mapSegment = urlTypeToSegment({
baseUrl: attributes.baseUrl,
source: attributes.initialization.sourceURL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the changes proposed in my other comment to handle the string to object conversion in getSegmentInformation, I think you can safely do

source: constructTemplateUrl(attributes.initialization.sourceURL, templateValues)

In the case that the <Initialization> node took precedence, there shouldn't be any template identifiers, so constructTemplateUrl here should just return the sourceURL unchanged

resolvedUri: resolveUrl(baseUrl || '', source)
};

if (source && range) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think range should be parsed even if source is not available. According to the spec for @sourceURL,

If not present, then any BaseURL element is mapped to the @sourceURL attribute and the range attribute shall be present.

The BaseURL mapping is already handled by the resolvedUri above, but this check would prevent parsing of the range that "shall be present"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I made an assumption that both were required but the existence of baseUrl makes that a poor assumption.

const segmentTimelineNode = segmentList || segmentTemplate;
const segmentTimeline =
segmentTimelineNode && findChildren(segmentTimelineNode, 'SegmentTimeline')[0];
const segmentInitializationNode = segmentList || segmentBase || segmentTemplate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These variable names are a bit misleading. segmentTimelineNode and segmentInitializationNode are actually the parent nodes of those nodes

findChildren(segmentTimeline, 'S').map(s => getAttributes(s)),
list: segmentList && getAttributes(segmentList),
base: segmentBase && getAttributes(segmentBase)
findChildren(segmentTimeline, 'S').map(s => getAttributes(s)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that this line was added by me, including the indentation, but do you mind fixing the indentation here?

Copy link
Contributor Author

@OshinKaramian OshinKaramian Jan 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder why the linter didn't catch this one, I think it gave me a warning or error for the indentation under list: and base:.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya I'm surprised I added it myself, and didn't catch it myself or with the linter. Its not even lined up with anything ha

Copy link
Contributor

@mjneil mjneil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for future PR:

In 5.3.9.5.2 of the spec, this is specified

If neither Initialization element nor SegmentTemplate@initialization attribute are present for a Representation then each Media Segment within the Representation shall be self-initializing.

Right now when this scenario is encountered, this PR will still create a segment.map = { uri: '', resolvedUri: baseUrl } with baseUrl not being the location of an initialization segment.

In VHS, the existence of segment.map is used to determine if the segments are fmp4 or ts, and VHS will automatically request the initialization segment specified in segment.map without any checks, and appends the response to the source buffer.

We will have to think about how to handle this case, which can potentially require changes in both this project and VHS.

@forbesjo forbesjo merged commit 71b8976 into videojs:master Feb 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants