From 30c4379e6b738d15636f5987dc4bbc1df37e2ce2 Mon Sep 17 00:00:00 2001 From: Lysander Date: Thu, 23 Nov 2023 05:42:36 +0800 Subject: [PATCH] support optional URI in EXT-X-MEDIA tag (#104) * URI is OPTIONAL in EXT-X-MEDIA tag * make URI mandatory in case of SUBTITLES * adapt client in order to work with renditions without URLs --------- Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com> --- client_downloader_primary.go | 42 ++++++++++++++------------ pkg/playlist/multivariant_rendition.go | 34 +++++++++++++++------ pkg/playlist/multivariant_test.go | 2 +- 3 files changed, 47 insertions(+), 31 deletions(-) diff --git a/client_downloader_primary.go b/client_downloader_primary.go index d36ca20..898b578 100644 --- a/client_downloader_primary.go +++ b/client_downloader_primary.go @@ -209,27 +209,29 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error { return fmt.Errorf("audio playlist with id \"%s\" not found", leadingPlaylist.Audio) } - u, err := clientAbsoluteURL(d.primaryPlaylistURL, audioPlaylist.URI) - if err != nil { - return err + if audioPlaylist.URI != "" { + u, err := clientAbsoluteURL(d.primaryPlaylistURL, audioPlaylist.URI) + if err != nil { + return err + } + + ds := newClientDownloaderStream( + false, + d.httpClient, + d.onDownloadStreamPlaylist, + d.onDownloadSegment, + d.onDecodeError, + u, + nil, + d.rp, + d.onStreamTracks, + d.onSetLeadingTimeSync, + d.onGetLeadingTimeSync, + d.onData, + ) + d.rp.add(ds) + streamCount++ } - - ds := newClientDownloaderStream( - false, - d.httpClient, - d.onDownloadStreamPlaylist, - d.onDownloadSegment, - d.onDecodeError, - u, - nil, - d.rp, - d.onStreamTracks, - d.onSetLeadingTimeSync, - d.onGetLeadingTimeSync, - d.onData, - ) - d.rp.add(ds) - streamCount++ } default: diff --git a/pkg/playlist/multivariant_rendition.go b/pkg/playlist/multivariant_rendition.go index e83295d..fe3d4ac 100644 --- a/pkg/playlist/multivariant_rendition.go +++ b/pkg/playlist/multivariant_rendition.go @@ -111,22 +111,32 @@ func (t *MultivariantRendition) unmarshal(v string) error { return fmt.Errorf("GROUP-ID missing") } - if t.Type != MultivariantRenditionTypeClosedCaptions { - if t.URI == "" { - return fmt.Errorf("missing URI") - } - - if t.InstreamID != "" { - return fmt.Errorf("INSTREAM-ID is forbidden with type %s", t.Type) - } - } else { + // If the TYPE is CLOSED-CAPTIONS, the URI + // attribute MUST NOT be present. + // The URI attribute of the EXT-X-MEDIA tag is REQUIRED if the media + // type is SUBTITLES, but OPTIONAL if the media type is VIDEO or AUDIO. + switch t.Type { + case MultivariantRenditionTypeClosedCaptions: if t.URI != "" { return fmt.Errorf("URI is forbidden for type CLOSED-CAPTIONS") } + case MultivariantRenditionTypeSubtitles: + if t.URI == "" { + return fmt.Errorf("URI is required for type SUBTITLES") + } + + default: + } + + // This attribute is REQUIRED if the TYPE attribute is CLOSED-CAPTIONS + // For all other TYPE values, the INSTREAM-ID MUST NOT be specified. + if t.Type == MultivariantRenditionTypeClosedCaptions { if t.InstreamID == "" { return fmt.Errorf("missing INSTREAM-ID") } + } else if t.InstreamID != "" { + return fmt.Errorf("INSTREAM-ID is forbidden with type %s", t.Type) } return nil @@ -164,7 +174,11 @@ func (t MultivariantRendition) marshal() string { ret += ",CHANNELS=\"" + t.Channels + "\"" } - ret += ",URI=\"" + t.URI + "\"\n" + if t.URI != "" { + ret += ",URI=\"" + t.URI + "\"" + } + + ret += "\n" return ret } diff --git a/pkg/playlist/multivariant_test.go b/pkg/playlist/multivariant_test.go index bf9620d..9d98581 100644 --- a/pkg/playlist/multivariant_test.go +++ b/pkg/playlist/multivariant_test.go @@ -256,7 +256,7 @@ v2/prog_index.m3u8 #EXT-X-MEDIA:TYPE="AUDIO",GROUP-ID="aud1",LANGUAGE="en",NAME="English",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="a1/prog_index.m3u8" #EXT-X-MEDIA:TYPE="AUDIO",GROUP-ID="aud2",LANGUAGE="en",NAME="English",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="6",URI="a2/prog_index.m3u8" #EXT-X-MEDIA:TYPE="AUDIO",GROUP-ID="aud3",LANGUAGE="en",NAME="English",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="6",URI="a3/prog_index.m3u8" -#EXT-X-MEDIA:TYPE="CLOSED-CAPTIONS",GROUP-ID="cc1",LANGUAGE="en",NAME="English",DEFAULT=YES,AUTOSELECT=YES,URI="" +#EXT-X-MEDIA:TYPE="CLOSED-CAPTIONS",GROUP-ID="cc1",LANGUAGE="en",NAME="English",DEFAULT=YES,AUTOSELECT=YES #EXT-X-MEDIA:TYPE="SUBTITLES",GROUP-ID="sub1",LANGUAGE="en",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,URI="s1/en/prog_index.m3u8" `, Multivariant{