globocom / m3u8

Python m3u8 Parser for HTTP Live Streaming (HLS) Transmissions
Other
2.03k stars 471 forks source link

Updating m3u8 from 0.3.7 to 3.3.0 breaks frame_rate format #306

Closed rafrafek closed 1 year ago

rafrafek commented 1 year ago

I'm trying to bump version of m3u8 from 0.3.7 to 3.3.0 in one of my projects, but it breaks frame_rate format.

Before:

>>> m3u8_obj.data["playlists"][0]["stream_info"]["frame_rate"]
'25.000'

After:

>>> m3u8_obj.data["playlists"][0]["stream_info"]["frame_rate"]
25.0

Raw data from curl:

FRAME-RATE=25.000

Is it a bug or a desired change?

bbayles commented 1 year ago

Likely changed in https://github.com/globocom/m3u8/pull/149.

The difference is str (old) vs. float (new). I think the new behavior is desirable.

The spec says:

      FRAME-RATE

      The value is a decimal-floating-point describing the maximum frame
      rate for all the video in the Variant Stream, rounded to three
      decimal places.

decimal-floating-point is described as:

   *  decimal-floating-point: an unquoted string of characters from the
      set [0..9] and '.' that expresses a non-negative floating-point
      number in decimal positional notation.

So it makes sense for the parser to hand back a float instance.

rafrafek commented 1 year ago

Thanks for your quick reply!

The problem in my case is that I have a map of possible fps values in mp4box format, e. g. 25.000 means 25/1. For raw values it is easy, because I know all possible raw values of my streams. Some of them have non-float raw value, e.g. 24000/1001. I don't know how should I map it with float values.

Is there any way to preserve raw FRAME-RATE value of playlists with the latest version of m3u8?

rokam commented 1 year ago

Can't you just divide 24000 / 1001?

rafrafek commented 1 year ago

Can't you just divide 24000 / 1001?

Should I map 23.976023976023978 or 23.976 in this case?

rokam commented 1 year ago

I think you don't need to care. Both will be stored as 23.976 inside the m3u8 file.

rafrafek commented 1 year ago

I don't use raw m3u8 file. I use object created by m3u8 Python module.

I need a map of frame_rate m3u8 format to mp4box format.

bbayles commented 1 year ago

You can patch in the old behavior like this:

import m3u8

def _custom_parse_stream_inf(line, data, state):
    data['is_variant'] = True
    data['media_sequence'] = None
    atribute_parser = remove_quotes_parser('codecs', 'audio', 'video', 'video_range', 'subtitles', 'pathway_id', 'stable_variant_id')
    atribute_parser["program_id"] = int
    atribute_parser["bandwidth"] = lambda x: int(float(x))
    atribute_parser["average_bandwidth"] = int
    atribute_parser["frame_rate"] = str  # Here is the change!
    atribute_parser["video_range"] = str
    atribute_parser["hdcp_level"] = str
    state['stream_info'] = _parse_attribute_list(protocol.ext_x_stream_inf, line, atribute_parser)

m3u8.parser._parse_stream_inf = _custom_parse_stream_inf

playlist_text = '...'  # Go get this from wherever
parsed_playlist = m3u8.loads(playlist_text)
parsed_playlist.data["playlists"][0]["stream_info"]["frame_rate"]  # Should be the original value

Alternatively, you can probably map the existing values with:

current_value = m3u8_obj.data["playlists"][0]["stream_info"]["frame_rate"]
mapped_value = format(current_value, '.03f')
rafrafek commented 1 year ago

Thanks!