|
- // https://tools.ietf.org/html/rfc8216
- // https://developer.apple.com/documentation/http_live_streaming/about_the_ext-x-version_tag
- //
- // audio/mpegurl
- // application/vnd.apple.mpegurl
-
- #include "hls-parser.h"
- #include "hls-string.h"
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
-
- struct hls_parser_t
- {
- size_t media_capacity;
- size_t variant_capacity;
- size_t session_key_capacity;
- size_t session_data_capacity;
- struct hls_variant_t* variant;
- struct hls_master_t* master;
-
- size_t segment_capacity;
- struct hls_segment_t* segment;
- struct hls_segment_t* key_segment; // last segment has key
- struct hls_playlist_t* playlist;
- };
-
- enum
- {
- ATTR_VALUE_TYPE_UINT32,
- ATTR_VALUE_TYPE_UINT64,
- ATTR_VALUE_TYPE_FLOAT32,
- ATTR_VALUE_TYPE_FLOAT64,
- ATTR_VALUE_TYPE_STRING,
- ATTR_VALUE_TYPE_STRING_BOOL,
- };
-
- struct hls_tag_attr_t
- {
- int cls;
- const char* name;
- void* ptr;
- };
-
- #define HLS_TAG_ATTR_VALUE(a, cls0, name0, ptr0) {a.cls=cls0; a.name=name0; a.ptr=ptr0; }
-
- static int hls_parser_realloc(void** ptr, size_t* capacity, size_t len, size_t incr, size_t size);
-
- static int hls_attr_read(const char* value, size_t n, int cls, void* ptr)
- {
- switch (cls)
- {
- case ATTR_VALUE_TYPE_STRING_BOOL:
- *(int*)ptr = (3 == n && 0 == strncasecmp(value, "YES", 3)) ? 1 : 0;
- return 0;
-
- case ATTR_VALUE_TYPE_UINT32:
- *(uint32_t*)ptr = (uint32_t)strtoul(value, NULL, 10);
- break;
-
- case ATTR_VALUE_TYPE_UINT64:
- *(uint64_t*)ptr = (uint64_t)strtoull(value, NULL, 10);
- break;
-
- case ATTR_VALUE_TYPE_FLOAT32:
- *(float*)ptr = (float)strtod(value, NULL);
- break;
-
- case ATTR_VALUE_TYPE_FLOAT64:
- *(double*)ptr = strtod(value, NULL);
- break;
-
- case ATTR_VALUE_TYPE_STRING:
- *((char**)ptr) = (char*)value;
- ((char*)value)[n] = 0;
- break;
-
- default:
- assert(0);
- return -1;
- }
-
- return 0;
- }
-
- static int hls_parse_attrs(const char* data, size_t bytes, struct hls_tag_attr_t* attrs, size_t nattrs)
- {
- int r;
- size_t i, n, nn, nv;
- const char* ptr, *next;
- const char* name, *value;
-
- r = 0;
- for (ptr = data; ptr && ptr < data + bytes && 0 == r; ptr = next)
- {
- n = hls_strsplit(ptr, data + bytes, ",", "\"", &next);
-
- nn = hls_strsplit(ptr, ptr + n, "=", "", &value);
- name = hls_strtrim(ptr, &nn, " \t", " \t"); // trim SP/HTAB
- nv = ptr + n - value;
- value = hls_strtrim(value, &nv, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
-
- for (i = 0; i < nattrs; i++)
- {
- if (nn == strlen(attrs[i].name) && 0 == strncasecmp(attrs[i].name, name, nn))
- {
- r = hls_attr_read(value, nv, attrs[i].cls, attrs[i].ptr);
- break;
- }
- }
- }
-
- return r;
- }
-
- static int hls_ext_inf(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- size_t n;
- const char* duration;
- struct hls_segment_t* segment;
- segment = parser->segment;
-
- n = hls_strsplit(data, data + bytes, ",", "\"", (const char**)&segment->title);
- duration = hls_strtrim(data, &n, " \t", " \t"); // trim SP/HTAB
-
- // decimal-floating-point or decimal-integer number
- segment->duration = strtod(duration, NULL);
- n = data + bytes - segment->title;
- segment->title = (char*)hls_strtrim(segment->title, &n, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- segment->title[n] = 0;
- return 0;
- }
-
- static int hls_ext_x_byterange(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- size_t n;
- const char* ptr, *next;
- struct hls_segment_t* segment;
- segment = parser->segment;
-
- n = hls_strsplit(data, data + bytes, "@", "", &next);
- ptr = hls_strtrim(data, &n, " \t", " \t"); // trim SP/HTAB
- segment->bytes = (int64_t)strtoull(ptr, NULL, 10); // decimal-integer
-
- n = data + bytes - next;
- next = hls_strtrim(next, &n, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- segment->offset = n > 0 ? (int64_t)strtoull(next, NULL, 10) : segment->offset;
- return 0;
- }
-
- static int hls_ext_x_discontinuity(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- parser->segment->discontinuity = 1;
- (void)data, (void)bytes;
- return 0;
- }
-
- static int hls_ext_x_key(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- int r;
- struct hls_segment_t* segment;
- struct hls_tag_attr_t attrs[5];
- char* iv;
-
- iv = "";
- segment = parser->segment;
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "METHOD", &segment->key.method);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "URI", &segment->key.uri);
- HLS_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "IV", &iv);
- HLS_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "KEYFORMAT", &segment->key.keyformat);
- HLS_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "KEYFORMATVERSIONS", &segment->key.keyformatversions);
-
- r = hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- if (0 != r)
- return r;
-
- if (strlen(iv) == 34) // 0x + 32bits
- hls_base16_decode(segment->key.iv, iv + 2, 32);
-
- // It applies to every Media Segment and to every Media
- // Initialization Section declared by an EXT-X-MAP tag that appears
- // between it and the next EXT-X-KEY tag in the Playlist file with the
- // same KEYFORMAT attribute (or the end of the Playlist file).
- parser->key_segment = parser->segment; // save key_segment
- return 0;
- }
-
- static int hls_ext_x_map(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- int r;
- size_t n;
- struct hls_segment_t* segment;
- struct hls_tag_attr_t attrs[2];
- char* byterange;
- const char* ptr, *next;
-
- byterange = "";
- segment = parser->segment;
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "URI", &segment->map.uri);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "BYTERANGE", &byterange);
-
- r = hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- if (0 != r)
- return r;
-
- n = strlen(byterange);
- if (n > 2)
- {
- n = hls_strsplit(byterange, byterange + n, "@", "", &next);
- ptr = hls_strtrim(byterange, &n, " \t", " \t"); // trim SP/HTAB
- segment->map.bytes = (int64_t)strtoull(ptr, NULL, 10); // decimal-integer
-
- n = byterange + n - next;
- next = hls_strtrim(next, &n, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- segment->map.offset = n > 0 ? (int64_t)strtoull(next, NULL, 10) : segment->map.offset;
- }
- return 0;
- }
-
- static int hls_ext_x_program_date_time(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- struct hls_segment_t* segment;
- segment = parser->segment;
- segment->program_date_time = (char*)hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- return 0;
- }
-
- static int hls_ext_x_daterange(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- struct hls_segment_t* segment;
- struct hls_tag_attr_t attrs[7];
-
- segment = parser->segment;
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "ID", &segment->daterange.id);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "CLASS", &segment->daterange.cls);
- HLS_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "START-DATE", &segment->daterange.start_date);
- HLS_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "END-DATE", &segment->daterange.end_date);
- HLS_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_FLOAT64, "DURATION", &segment->daterange.duration);
- HLS_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_FLOAT64, "PLANNED-DURATION", &segment->daterange.planned_duration);
- HLS_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING_BOOL, "END-ON-NEXT", &segment->daterange.end_on_next);
-
- return hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- }
-
- static int hls_ext_x_targetduration(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- data = hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- parser->playlist->target_duration = (uint64_t)strtoull(data, NULL, 10); // decimal-integer
- return 0;
- }
-
- static int hls_ext_x_media_sequence(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- data = hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- parser->playlist->media_sequence = (uint64_t)strtoull(data, NULL, 10); // decimal-integer
- return 0;
- }
-
- static int hls_ext_x_discontinuity_sequence(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- data = hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- parser->playlist->discontinuity_sequence = (uint64_t)strtoull(data, NULL, 10); // decimal-integer
- return 0;
- }
-
- static int hls_ext_x_endlist(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- data = hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- parser->playlist->endlist = 1;
- return 0;
- }
-
- static int hls_ext_x_playlist_type(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- struct hls_playlist_t* playlist;
- playlist = parser->playlist;
-
- data = hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- if (5 == bytes && 0 == strncasecmp("EVENT", data, 5))
- playlist->type = HLS_PLAYLIST_TYPE_EVENT;
- else if (3 == bytes && 0 == strncasecmp("VOD", data, 3))
- playlist->type = HLS_PLAYLIST_TYPE_VOD;
- else
- playlist->type = HLS_PLAYLIST_TYPE_LIVE;
- return 0;
- }
-
- static int hls_ext_x_i_frames_only(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- data = hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- parser->playlist->i_frames_only = 1;
- return 0;
- }
-
- // If the media type is VIDEO or AUDIO, a missing URI attribute
- // indicates that the media data for this Rendition is included in the
- // Media Playlist of any EXT-X-STREAM-INF tag referencing this EXT-X-MEDIA tag.
- static int hls_ext_x_media(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- struct hls_media_t* media;
- struct hls_master_t* master;
- struct hls_tag_attr_t attrs[12];
-
- master = parser->master;
- if (0 != hls_parser_realloc((void**)&master->medias, &parser->media_capacity, master->media_count, 4, sizeof(struct hls_media_t)))
- return -ENOMEM;
-
- media = &master->medias[master->media_count++];
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "TYPE", &media->type);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "URI", &media->uri);
- HLS_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "GROUP-ID", &media->group_id);
- HLS_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "LANGUAGE", &media->language);
- HLS_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "ASSOC-LANGUAGE", &media->assoc_language);
- HLS_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING, "NAME", &media->name);
- HLS_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING_BOOL, "DEFAULT", &media->is_default);
- HLS_TAG_ATTR_VALUE(attrs[7], ATTR_VALUE_TYPE_STRING_BOOL, "AUTOSELECT", &media->autoselect);
- HLS_TAG_ATTR_VALUE(attrs[8], ATTR_VALUE_TYPE_STRING_BOOL, "FORCED", &media->forced);
- HLS_TAG_ATTR_VALUE(attrs[9], ATTR_VALUE_TYPE_STRING, "INSTREAM-ID", &media->instream_id);
- HLS_TAG_ATTR_VALUE(attrs[10], ATTR_VALUE_TYPE_STRING, "CHARACTERISTICS", &media->characteristics);
- HLS_TAG_ATTR_VALUE(attrs[11], ATTR_VALUE_TYPE_STRING, "CHANNELS", &media->channels);
-
- return hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- }
-
- static int hls_ext_x_stream_inf(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- int r;
- struct hls_variant_t* variant;
- struct hls_tag_attr_t attrs[10];
- char* resolution;
-
- resolution = "";
- variant = parser->variant;
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_UINT32, "BANDWIDTH", &variant->bandwidth);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_UINT32, "AVERAGE-BANDWIDTH", &variant->average_bandwidth);
- HLS_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "CODECS", &variant->codecs);
- HLS_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "RESOLUTION", &resolution);
- HLS_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_FLOAT64, "FRAME-RATE", &variant->fps);
- HLS_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING, "HDCP-LEVEL", &variant->hdcp_level);
- HLS_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING, "AUDIO", &variant->audio);
- HLS_TAG_ATTR_VALUE(attrs[7], ATTR_VALUE_TYPE_STRING, "VIDEO", &variant->video);
- HLS_TAG_ATTR_VALUE(attrs[8], ATTR_VALUE_TYPE_STRING, "SUBTITLES", &variant->subtitle);
- HLS_TAG_ATTR_VALUE(attrs[9], ATTR_VALUE_TYPE_STRING, "CLOSED-CAPTIONS", &variant->closed_captions);
-
- r = hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- if (0 != r)
- return r;
-
- if (2 != sscanf(resolution, "%dx%d", &variant->width, &variant->height))
- return 0; // ignore
- return 0;
- }
-
- static int hls_ext_x_i_frame_stream_inf(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- int r;
- struct hls_variant_t* variant;
- struct hls_variant_t* iframestream;
- struct hls_tag_attr_t attrs[7];
- char* resolution;
-
- resolution = "";
- variant = parser->variant;
- if (!variant->i_frame_stream_inf)
- {
- variant->i_frame_stream_inf = calloc(1, sizeof(*variant->i_frame_stream_inf));
- if (!variant->i_frame_stream_inf)
- return -ENOMEM;
- }
- iframestream = variant->i_frame_stream_inf;
-
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "URI", &iframestream->uri);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_UINT32, "BANDWIDTH", &iframestream->bandwidth);
- HLS_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_UINT32, "AVERAGE-BANDWIDTH", &iframestream->average_bandwidth);
- HLS_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "CODECS", &iframestream->codecs);
- HLS_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "RESOLUTION", &resolution);
- HLS_TAG_ATTR_VALUE(attrs[5], ATTR_VALUE_TYPE_STRING, "HDCP-LEVEL", &iframestream->hdcp_level);
- HLS_TAG_ATTR_VALUE(attrs[6], ATTR_VALUE_TYPE_STRING, "VIDEO", &iframestream->video);
-
- r = hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- if (0 != r)
- return r;
-
- if (2 != sscanf(resolution, "%dx%d", &iframestream->width, &iframestream->height))
- return 0; // ignore
- return 0;
- }
-
- static int hls_ext_x_session_data(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- struct hls_master_t* master;
- struct hls_tag_attr_t attrs[4];
-
- master = parser->master;
- if (0 != hls_parser_realloc((void**)&master->session_data, &parser->session_data_capacity, master->session_data_count, 2, sizeof(master->session_data[0])))
- return -ENOMEM;
-
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "DATA-ID", &master->session_data[master->session_data_count].data_id);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "VALUE", &master->session_data[master->session_data_count].value);
- HLS_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "URI", &master->session_data[master->session_data_count].uri);
- HLS_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "LANGUAGE", &master->session_data[master->session_data_count].language);
-
- ++master->session_data_count;
- return hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- }
-
- static int hls_ext_x_session_key(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- int r;
- struct hls_master_t* master;
- struct hls_tag_attr_t attrs[5];
- char* iv;
-
- iv = "";
- master = parser->master;
- if (0 != hls_parser_realloc((void**)&master->session_key, &parser->session_key_capacity, master->session_key_count, 2, sizeof(master->session_key[0])))
- return -ENOMEM;
-
- HLS_TAG_ATTR_VALUE(attrs[0], ATTR_VALUE_TYPE_STRING, "METHOD", &master->session_key[master->session_key_count].method);
- HLS_TAG_ATTR_VALUE(attrs[1], ATTR_VALUE_TYPE_STRING, "URI", &master->session_key[master->session_key_count].uri);
- HLS_TAG_ATTR_VALUE(attrs[2], ATTR_VALUE_TYPE_STRING, "IV", &iv);
- HLS_TAG_ATTR_VALUE(attrs[3], ATTR_VALUE_TYPE_STRING, "KEYFORMAT", &master->session_key[master->session_key_count].keyformat);
- HLS_TAG_ATTR_VALUE(attrs[4], ATTR_VALUE_TYPE_STRING, "KEYFORMATVERSIONS", &master->session_key[master->session_key_count].keyformatversions);
-
- r = hls_parse_attrs(data, bytes, attrs, sizeof(attrs) / sizeof(attrs[0]));
- if (0 != r)
- return r;
-
- if (strlen(iv) == 34) // 0x + 32bits
- hls_base16_decode(master->session_key[master->session_key_count].iv, iv + 2, 32);
-
- ++master->session_key_count;
- return 0;
- }
-
- static int hls_ext_x_independent_segments(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- data = hls_strtrim(data, &bytes, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- if (parser->playlist)
- parser->playlist->independent_segments = 1;
- else
- parser->master->independent_segments = 1;
- return 0;
- }
-
- static int hls_ext_x_start(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- size_t n;
- int start_precise;
- double start_time_offset;
- const char* ptr, * next;
-
- n = hls_strsplit(data, data + bytes, ",", "", &next);
- ptr = hls_strtrim(data, &n, " \t", " \t"); // trim SP/HTAB
- start_time_offset = strtod(ptr, NULL); // signed-decimal-floating-point
-
- n = data + bytes - next;
- next = hls_strtrim(next, &n, " \t'\"", " \t'\""); // trim SP/HTAB/'/"
- start_precise = 3 == n && 0 == strncasecmp("YES", next, 3) ? 1 : 0;
-
- if (parser->playlist)
- {
- parser->playlist->start_time_offset = start_time_offset;
- parser->playlist->start_precise = start_precise;
- }
- else
- {
- parser->master->start_time_offset = start_time_offset;
- parser->master->start_precise = start_precise;
- }
- return 0;
- }
-
- static int hls_ext_x_version(struct hls_parser_t* parser, const char* data, size_t bytes)
- {
- int version;
- data = hls_strtrim(data, &bytes, " \t", " \t"); // trim SP/HTAB
- version = (int)strtoul(data, NULL, 10);
- if (parser->playlist)
- parser->playlist->version = version;
- else
- parser->master->version = version;
- return 0;
- }
-
- static int hls_parser_onuri(struct hls_parser_t* parser, const char* uri, size_t len)
- {
- //printf("uri: %.*s\n", (int)len, uri);
-
- uri = hls_strtrim(uri, &len, " \t", " \t"); // trim SP/HTAB
-
- if (parser->playlist)
- {
- if (!parser->segment)
- {
- assert(0);
- return -1;
- }
- parser->segment->uri = (char*)uri;
- parser->segment->uri[len] = 0;
- parser->segment = NULL;
- parser->playlist->count++;
- }
- else
- {
- if (!parser->variant)
- {
- assert(0);
- return -1;
- }
- parser->variant->uri = (char*)uri;
- parser->variant->uri[len] = 0;
- parser->variant = NULL;
- parser->master->variant_count++;
- }
-
- return 0;
- }
-
- static const struct
- {
- int cls; // 0-any, 1-playlist, 2-master
- const char* name;
- int (*parser)(struct hls_parser_t* parser, const char* attrs, size_t bytes);
- } s_tags[] = {
- // 4.3.2. Media Segment Tags
- { HLS_M3U8_PLAYLIST, "EXTINF", hls_ext_inf },
- { HLS_M3U8_PLAYLIST, "EXT-X-BYTERANGE", hls_ext_x_byterange },
- { HLS_M3U8_PLAYLIST, "EXT-X-DISCONTINUITY", hls_ext_x_discontinuity },
- { HLS_M3U8_PLAYLIST, "EXT-X-KEY", hls_ext_x_key },
- { HLS_M3U8_PLAYLIST, "EXT-X-MAP", hls_ext_x_map },
- { HLS_M3U8_PLAYLIST, "EXT-X-PROGRAM-DATE-TIME", hls_ext_x_program_date_time },
- { HLS_M3U8_PLAYLIST, "EXT-X-DATERANGE", hls_ext_x_daterange },
-
- // 4.3.3. Media Playlist Tags
- { HLS_M3U8_PLAYLIST, "EXT-X-TARGETDURATION", hls_ext_x_targetduration },
- { HLS_M3U8_PLAYLIST, "EXT-X-MEDIA-SEQUENCE", hls_ext_x_media_sequence },
- { HLS_M3U8_PLAYLIST, "EXT-X-DISCONTINUITY-SEQUENCE",hls_ext_x_discontinuity_sequence },
- { HLS_M3U8_PLAYLIST, "EXT-X-ENDLIST", hls_ext_x_endlist },
- { HLS_M3U8_PLAYLIST, "EXT-X-PLAYLIST-TYPE", hls_ext_x_playlist_type },
- { HLS_M3U8_PLAYLIST, "EXT-X-I-FRAMES-ONLY", hls_ext_x_i_frames_only },
-
- // 4.3.4. Master Playlist Tags
- { HLS_M3U8_MASTER, "EXT-X-MEDIA", hls_ext_x_media },
- { HLS_M3U8_MASTER, "EXT-X-STREAM-INF", hls_ext_x_stream_inf },
- { HLS_M3U8_MASTER, "EXT-X-I-FRAME-STREAM-INF", hls_ext_x_i_frame_stream_inf },
- { HLS_M3U8_MASTER, "EXT-X-SESSION-DATA", hls_ext_x_session_data },
- { HLS_M3U8_MASTER, "EXT-X-SESSION-KEY", hls_ext_x_session_key },
-
- // 4.3.5. Media or Master Playlist Tags
- { HLS_M3U8_UNKNOWN, "EXT-X-INDEPENDENT-SEGMENTS", hls_ext_x_independent_segments },
- { HLS_M3U8_UNKNOWN, "EXT-X-START", hls_ext_x_start },
-
- { HLS_M3U8_UNKNOWN, "EXT-X-VERSION", hls_ext_x_version},
- };
-
- static int hls_parser_realloc(void** ptr, size_t* capacity, size_t len, size_t incr, size_t size)
- {
- size_t n;
- void* ptr1;
- if (len >= *capacity)
- {
- n = len / 4;
- n = n > incr ? n : incr;
- ptr1 = realloc(*ptr, (len + 1 + n) * size);
- if (!ptr1)
- return -ENOMEM;
-
- memset((uint8_t*)ptr1 + len * size, 0, (1 + n) * size);
- *capacity = len + 1 + n;
- *ptr = ptr1;
- }
- return 0;
- }
-
- static int hls_parser_fetch_segment(struct hls_parser_t* parser)
- {
- if (parser->segment)
- return 0;
-
- if(0 != hls_parser_realloc((void**)&parser->playlist->segments, &parser->segment_capacity, parser->playlist->count, 8, sizeof(struct hls_segment_t)))
- return -ENOMEM;
-
- parser->segment = &parser->playlist->segments[parser->playlist->count];
- //memset(parser->segment, 0, sizeof(*parser->segment));
- parser->segment->bytes = -1;
- parser->segment->offset = -1;
- parser->segment->map.bytes = -1;
- parser->segment->map.offset = -1;
-
- // copy default key
- if (parser->key_segment)
- memcpy(&parser->segment->key, &parser->key_segment->key, sizeof(parser->segment->key));
- return 0;
- }
-
- static int hls_parser_fetch_variant(struct hls_parser_t* parser)
- {
- if (parser->variant)
- return 0;
-
- if (0 != hls_parser_realloc((void**)&parser->master->variants, &parser->variant_capacity, parser->master->variant_count, 4, sizeof(struct hls_variant_t)))
- return -ENOMEM;
-
- parser->variant = &parser->master->variants[parser->master->variant_count];
- //memset(parser->variant, 0, sizeof(*parser->variant));
- return 0;
- }
-
- static int hls_parser_ontag(struct hls_parser_t* parser, const char* tag, size_t len, const char* attrs, size_t bytes)
- {
- int r;
- size_t i;
-
- //printf("%.*s: %.*s\n", (int)len, tag, (int)bytes, attrs);
- r = parser->playlist ? hls_parser_fetch_segment(parser) : hls_parser_fetch_variant(parser);
- if (0 != r)
- return r;
-
- for (i = 0; i < sizeof(s_tags) / sizeof(s_tags[0]); i++)
- {
- if (len == strlen(s_tags[i].name)+1 && 0 == strncasecmp(s_tags[i].name, tag+1, len-1))
- {
- if ((HLS_M3U8_PLAYLIST == s_tags[i].cls && !parser->playlist)
- || (HLS_M3U8_MASTER == s_tags[i].cls && !parser->master))
- return -EINVAL;
-
- r = s_tags[i].parser(parser, attrs, bytes);
- break;
- }
- }
-
- return r;
- }
-
- static int hls_parser_input(struct hls_parser_t* parser, const char* m3u8, size_t len)
- {
- int r;
- size_t n, ntag;
- const char* ptr, *next;
- const char* attr;
-
- r = 0;
- for(ptr = m3u8; ptr && ptr < m3u8 + len; ptr = next)
- {
- n = hls_strsplit(ptr, m3u8 + len, "\r\n", "", &next);
- ptr = hls_strtrim(ptr, &n, " \t", " \t"); // trim SP/HTAB
- if (n < 1)
- continue; // blank line
-
- if ('#' == *ptr)
- {
- if (n <= 4 || strncmp("#EXT", ptr, 4))
- {
- // ignore comment
- //assert(0);
- continue;
- }
- else
- {
- // tags
- attr = strpbrk(ptr, ": \t\r\n");
- assert(attr <= ptr + n);
- ntag = attr ? attr - ptr : n;
- attr = attr ? attr + strspn(attr, ": \t") : ptr + n;
- r = hls_parser_ontag(parser, ptr, ntag, attr, ptr + n - attr);
- }
- }
- else
- {
- // uri
- r = hls_parser_onuri(parser, ptr, n);
- }
- }
-
- return r;
- }
-
- int hls_master_parse(struct hls_master_t** master, const char* m3u8, size_t len)
- {
- int r;
- char* ptr;
- struct hls_parser_t parser;
-
- memset(&parser, 0, sizeof(parser));
- parser.master = (struct hls_master_t*)calloc(1, sizeof(struct hls_master_t) + len + 1);
- if (!parser.master)
- return -ENOMEM;
-
- ptr = (char*)(parser.master + 1);
- memcpy(ptr, m3u8, len);
-
- r = hls_parser_input(&parser, ptr, len);
- if (0 != r)
- {
- hls_master_free(&parser.master);
- return r;
- }
-
- *master = parser.master;
- return 0;
- }
-
- int hls_playlist_parse(struct hls_playlist_t** playlist, const char* m3u8, size_t len)
- {
- int r;
- char* ptr;
- struct hls_parser_t parser;
-
- memset(&parser, 0, sizeof(parser));
- parser.playlist = (struct hls_playlist_t*)calloc(1, sizeof(struct hls_playlist_t) + len + 1);
- if (!parser.playlist)
- return -ENOMEM;
-
- ptr = (char*)(parser.playlist + 1);
- memcpy(ptr, m3u8, len);
-
- r = hls_parser_input(&parser, ptr, len);
- if (0 != r)
- {
- hls_playlist_free(&parser.playlist);
- return r;
- }
-
- *playlist = parser.playlist;
- return 0;
- }
-
- int hls_master_free(struct hls_master_t** master)
- {
- size_t i;
- struct hls_master_t* p;
- if (master && *master)
- {
- p = *master;
- if (p->medias)
- free(p->medias);
- if (p->session_data)
- free(p->session_data);
- if (p->session_key)
- free(p->session_key);
- if (p->variants)
- {
- for (i = 0; i < p->variant_count; i++)
- {
- if (p->variants[i].i_frame_stream_inf)
- free(p->variants[i].i_frame_stream_inf);
- }
- free(p->variants);
- }
- free(p);
- *master = NULL;
- return 0;
- }
- return -1;
- }
-
- int hls_playlist_free(struct hls_playlist_t** playlist)
- {
- struct hls_playlist_t* p;
- if (playlist && *playlist)
- {
- p = *playlist;
- if (p->segments)
- free(p->segments);
- free(p);
- *playlist = NULL;
- return 0;
- }
- return -1;
- }
-
- int hls_parser_probe(const char* m3u8, size_t len)
- {
- size_t n;
- const char* ptr, *next;
-
- for (ptr = m3u8; ptr && ptr < m3u8 + len; ptr = next)
- {
- n = hls_strsplit(ptr, m3u8 + len, "\r\n", "", &next);
- ptr = hls_strtrim(ptr, &n, " \t", " \t"); // trim SP/HTAB
- if (n >= 7 && 0 == strncasecmp("#EXTINF", ptr, 7))
- return HLS_M3U8_PLAYLIST;
- else if (n >= 17 && 0 == strncasecmp("#EXT-X-STREAM-INF", ptr, 17))
- return HLS_M3U8_MASTER;
- }
-
- return HLS_M3U8_UNKNOWN;
- }
-
- #if defined(_DEBUG) || defined(DEBUG)
- void hls_parser_test(const char* m3u8)
- {
- static char data[2 * 1024 * 1024];
- FILE* fp = fopen(m3u8, "rb");
- int n = (int)fread(data, 1, sizeof(data), fp);
- fclose(fp);
-
- int v = hls_parser_probe(data, n);
- if (HLS_M3U8_MASTER == v)
- {
- struct hls_master_t* master;
- assert(0 == hls_master_parse(&master, data, n));
- hls_master_free(&master);
- }
- else if (HLS_M3U8_PLAYLIST == v)
- {
- struct hls_playlist_t* playlist;
- assert(0 == hls_playlist_parse(&playlist, data, n));
- hls_playlist_free(&playlist);
- }
- else
- {
- assert(0);
- }
- }
- #endif
|