kim-company / kim_hls

M3U8 marshaler and unmarshaler. Playlist tracker
Apache License 2.0
2 stars 6 forks source link

`EXT-X-MAP` tags are not marshaled or unmarshaled #13

Open samrat opened 1 month ago

samrat commented 1 month ago

Looks like the EXT-X-MAP, which is used to specify the initial segment in fMP4 streams, don't get marshaled or unmarshaled:

alias HLS.Playlist
alias HLS.Playlist.{Master, Media}

content = 
  """
  #EXTM3U
  #EXT-X-VERSION:7
  #EXT-X-TARGETDURATION:6
  #EXT-X-MEDIA-SEQUENCE:0
  #EXT-X-DISCONTINUITY-SEQUENCE:0
  #EXT-X-MAP:URI="muxed_header_video_track_part_0.mp4"
  #EXT-X-PROGRAM-DATE-TIME:2024-07-05T20:12:34.845Z
  #EXTINF:5.007755555,
  muxed_segment_0_video_track.m4s
  #EXT-X-PROGRAM-DATE-TIME:2024-07-05T20:12:35.422Z
  #EXTINF:4.996144444,
  muxed_segment_1_video_track.m4s
  """

manifest = Playlist.unmarshal(content, %Media{})
%HLS.Playlist.Media{
  target_segment_duration: 6,
  uri: nil,
  finished: false,
  type: nil,
  media_sequence_number: 0,
  version: 7,
  segments: [
    %HLS.Segment{
      uri: %URI{
        scheme: nil,
        userinfo: nil,
        host: nil,
        port: nil,
        path: "muxed_segment_0_video_track.m4s",
        query: nil,
        fragment: nil
      },
      duration: 5.007755555,
      relative_sequence: 0,
      absolute_sequence: 0,
      from: 0,
      ref: #Reference<0.640784525.3053715459.151562>,
      discontinuity: true
    },
    %HLS.Segment{
      uri: %URI{
        scheme: nil,
        userinfo: nil,
        host: nil,
        port: nil,
        path: "muxed_segment_1_video_track.m4s",
        query: nil,
        fragment: nil
      },
      duration: 4.996144444,
      relative_sequence: 1,
      absolute_sequence: 1,
      from: 5.007755555,
      ref: #Reference<0.640784525.3053715459.151563>,
      discontinuity: false
    }
  ]
}

I took a stab at implementing this and made some progress but wasn't sure how to handle the following part of the RFC:

It applies to every Media Segment that appears after it in the Playlist until the next EXT-X-MAP tag or until the end of the Playlist.

Are there other tags that behave similarly that I could look at? Or ideas on how this should be implemented?

dmorn commented 1 month ago

Hi @samrat! Promising. So one similar tag is the x-discontinuity one. On key difference is that we need to associate segments to their x-map tag, as when we download the bytes of that segment we also need to prepend the ones of the header provided by x-map. The discontinuity tag is associated with one segment only, whereas this one is implicitly associated to each of the segments following the tag, so while decoding we need to preserve this mapping, which is a new thing for this library.

I would add that logic here, add a new field in the segments (something like header_uri or similar) and add that value, so each segment is independent. When marshalling, we emit that tag only when it changes.

The responsibility of downloading, caching and prepending the bytes of the header to the segment when the header_uri field is not nil is left to the HLS plugin.

samrat commented 1 month ago

Thanks for the pointers @dmorn ! I've created a draft PR with the changes I've made so far. Happy to make any changes if you already have some feedback.

add a new field in the segments (something like header_uri or similar)

I called it map as this seems to follow the convention of how the tag names are mapped to field names in the Segment struct. But I also get the "map" is a bit generic and might be confusing. Do you think header_uri still makes sense given that it can have both URI and byterange?