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

Support for EXT-X-MAP (basic) #131

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 105 additions & 35 deletions src/hls.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,31 @@ static int extend_url(char **url, const char *baseurl)
}
}

static void semgemnt_list_append(hls_media_segment_t** first, hls_media_segment_t** last, hls_media_segment_t* ms)
{
if (*first == NULL)
{
*first = ms;
*last = ms;
}
else
{
(*last)->next = ms;
ms->prev = *last;
*last = ms;
}
}

static char* parse_byterange(char *tag, int64_t *seg_offset, int64_t *seg_size)
{
*seg_size = strtoll(tag, &tag, 10);
tag = strchr(tag, '@');
if (tag) {
*seg_offset = strtoll(tag+1, &tag, 10);
}
return tag;
}

static int parse_tag(hls_media_playlist_t *me, struct hls_media_segment *ms, char *tag, int64_t *seg_offset, int64_t *seg_size)
{
int enc_type;
Expand Down Expand Up @@ -270,11 +295,27 @@ static int parse_tag(hls_media_playlist_t *me, struct hls_media_segment *ms, cha
me->target_duration_ms = get_duration_ms(tag+22);
return 0;
} else if (!strncmp(tag, "#EXT-X-BYTERANGE:", 17)) {
*seg_size = strtoll(tag+17, NULL, 10);
tag = strchr(tag+17, '@');
if (tag) {
*seg_offset = strtoll(tag+1, NULL, 10);
parse_byterange(tag+17, seg_offset, seg_size);
return 0;
} else if (!strncmp(tag, "#EXT-X-MAP:URI=\"", 16)) {
tag += 16;
char* end_pos = strchr(tag, '"');
if (!end_pos) {
return 0;
}

hls_media_segment_t* map = malloc(sizeof(struct hls_media_segment));
memset(map, 0x00, sizeof(struct hls_media_segment));
semgemnt_list_append(&me->first_media_segment, &me->last_media_segment, map);

map->url = strndup(tag, end_pos - tag);
map->is_map = true;
map->size = -1;

if (!strncmp(end_pos+1, ",BYTERANGE=", 11)) {
parse_byterange(end_pos+1+11, &map->offset, &map->size);
}

return 0;
}
return 1;
Expand Down Expand Up @@ -307,11 +348,28 @@ static int parse_tag(hls_media_playlist_t *me, struct hls_media_segment *ms, cha
return 0;
}

static void setup_segment_aes(hls_media_playlist_t *me, hls_media_segment_t *ms)
{
if (me->encryptiontype == ENC_AES128 || me->encryptiontype == ENC_AES_SAMPLE) {
memcpy(ms->enc_aes.key_value, me->enc_aes.key_value, KEYLEN);
memcpy(ms->enc_aes.iv_value, me->enc_aes.iv_value, KEYLEN);
ms->enc_aes.key_url = strdup(me->enc_aes.key_url);
if (me->enc_aes.iv_is_static == false) {
char iv_str[STRLEN_BTS(KEYLEN)];
snprintf(iv_str, STRLEN_BTS(KEYLEN), "%032x\n", ms->sequence_number); // is this correct for map segments?
uint8_t *iv_bin[KEYLEN];
str_to_bin(iv_bin, iv_str, KEYLEN);
memcpy(ms->enc_aes.iv_value, iv_bin, KEYLEN);
}
}
}

static int media_playlist_get_links(hls_media_playlist_t *me)
{
struct hls_media_segment *map = NULL;
struct hls_media_segment *ms = NULL;
struct hls_media_segment *curr_ms = NULL;
char *src = me->source;
char* map_uri = NULL;
int64_t seg_offset = 0;
int64_t seg_size = -1;

Expand All @@ -335,7 +393,18 @@ static int media_playlist_get_links(hls_media_playlist_t *me)
}
if (*src == '#') {
parse_tag(me, ms, src, &seg_offset, &seg_size);
continue;

if (me->last_media_segment && me->last_media_segment->is_map) { // new map was given
map = me->last_media_segment;

map->sequence_number = i + me->first_media_sequence;
setup_segment_aes(me, map);

/* Get full url */
extend_url(&(map->url), me->url);
}

continue;
}
if (*src == '\0') {
goto finish;
Expand All @@ -350,20 +419,9 @@ static int media_playlist_get_links(hls_media_playlist_t *me)
ms->url = malloc(url_size);
strncpy(ms->url, src, url_size-1);
ms->url[url_size-1] = '\0';

ms->sequence_number = i + me->first_media_sequence;
if (me->encryptiontype == ENC_AES128 || me->encryptiontype == ENC_AES_SAMPLE) {
memcpy(ms->enc_aes.key_value, me->enc_aes.key_value, KEYLEN);
memcpy(ms->enc_aes.iv_value, me->enc_aes.iv_value, KEYLEN);
ms->enc_aes.key_url = strdup(me->enc_aes.key_url);
if (me->enc_aes.iv_is_static == false) {
char iv_str[STRLEN_BTS(KEYLEN)];
snprintf(iv_str, STRLEN_BTS(KEYLEN), "%032x\n", ms->sequence_number);
uint8_t *iv_bin = malloc(KEYLEN);
str_to_bin(iv_bin, iv_str, KEYLEN);
memcpy(ms->enc_aes.iv_value, iv_bin, KEYLEN);
free(iv_bin);
}
}
setup_segment_aes(me, ms);

/* Get full url */
extend_url(&(ms->url), me->url);
Expand All @@ -379,17 +437,7 @@ static int media_playlist_get_links(hls_media_playlist_t *me)
}

/* Add new segment to segment list */
if (me->first_media_segment == NULL)
{
me->first_media_segment = ms;
curr_ms = ms;
}
else
{
curr_ms->next = ms;
ms->prev = curr_ms;
curr_ms = ms;
}
semgemnt_list_append(&(me->first_media_segment), &(me->last_media_segment), ms);
ms = NULL;
i += 1;
break;
Expand All @@ -398,7 +446,6 @@ static int media_playlist_get_links(hls_media_playlist_t *me)
}

finish:
me->last_media_segment = curr_ms;

if (i > 0) {
me->last_media_sequence = me->first_media_sequence + i - 1;
Expand Down Expand Up @@ -1233,7 +1280,7 @@ static void *hls_playlist_update_thread(void *arg)
// add new segments
struct hls_media_segment *ms = new_me.first_media_segment;
while (ms) {
if (ms->sequence_number > me->last_media_sequence) {
if (ms->sequence_number > me->last_media_sequence || ms->is_map) {
if (ms->prev) {
ms->prev->next = NULL;
}
Expand Down Expand Up @@ -1353,12 +1400,15 @@ int download_live_hls(write_ctx_t *out_ctx, hls_media_playlist_t *me)

void *session = init_hls_session();
set_timeout_session(session, 2L, 3L);
char* current_map_url = NULL;
uint64_t current_map_offset = 0;
uint64_t current_map_size = -1;
uint64_t downloaded_duration_ms = 0;
int64_t download_size = 0;
time_t repTime = 0;
bool download = true;
while(download) {

while(download) {
pthread_mutex_lock(&media_playlist_mtx);
struct hls_media_segment *ms = me->first_media_segment;
if (ms != NULL) {
Expand All @@ -1382,7 +1432,15 @@ int download_live_hls(write_ctx_t *out_ctx, hls_media_playlist_t *me)
continue;
}

MSG_PRINT("Downloading part %d\n", ms->sequence_number);
if (ms->is_map) {
// don't duplicate map (initial) segment if we have already written it
if (current_map_url && !strcmp(ms->url, current_map_url) && ms->offset == current_map_offset && ms->size == current_map_size)
goto loop_cleanup;

MSG_PRINT("Downloading init segment %s\n", ms->url);
} else
MSG_PRINT("Downloading part %d\n", ms->sequence_number);

int retries = 0;
do {
struct ByteBuffer seg;
Expand All @@ -1401,7 +1459,7 @@ int download_live_hls(write_ctx_t *out_ctx, hls_media_playlist_t *me)
first_media_sequence = me->first_media_sequence;
pthread_mutex_unlock(&media_playlist_mtx);

if(http_code != 403 && http_code != 401 && http_code != 410 && retries <= hls_args.segment_download_retries && ms->sequence_number > first_media_sequence) {
if(http_code != 403 && http_code != 401 && http_code != 410 && (retries <= hls_args.segment_download_retries && ms->sequence_number > first_media_sequence || ms->is_map)) {
clean_http_session(session);
sleep(1);
session = init_hls_session();
Expand Down Expand Up @@ -1445,9 +1503,21 @@ int download_live_hls(write_ctx_t *out_ctx, hls_media_playlist_t *me)

break;
} while(true);

// remember last written map
if (ms->is_map) {
free(current_map_url);
current_map_url = ms->url;
ms->url = NULL;
current_map_offset = ms->offset;
current_map_size = ms->size;
}

loop_cleanup:
media_segment_cleanup(ms);
}

free(current_map_url);

pthread_join(thread, &ret);
pthread_mutex_destroy(&media_playlist_mtx);
Expand Down
1 change: 1 addition & 0 deletions src/hls.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ typedef struct hls_media_segment {
int64_t offset;
int64_t size;
int sequence_number;
bool is_map;
uint64_t duration_ms;
struct enc_aes128 enc_aes;
struct hls_media_segment *next;
Expand Down