X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fstream_filter%2Fhttplive.c;h=27839d2fba9b74ef6770dc17a0efb055f615dd2a;hb=6a24a618294ba84b569b32131eb3117b505ca4f0;hp=71730f4dccdd6f743b86d957d1aff20bd7a84f4e;hpb=74f02326dc168aa632fe68c29951cf3824dfce06;p=vlc diff --git a/modules/stream_filter/httplive.c b/modules/stream_filter/httplive.c index 71730f4dcc..27839d2fba 100644 --- a/modules/stream_filter/httplive.c +++ b/modules/stream_filter/httplive.c @@ -6,19 +6,19 @@ * * Author: Jean-Paul Saman * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -29,12 +29,12 @@ #endif #include -#include #include #include #include +#include #include #include @@ -70,7 +70,7 @@ typedef struct segment_s char *url; char *psz_key_path; /* url key path */ - uint8_t psz_AES_key[16]; /* AES-128 */ + uint8_t aes_key[16]; /* AES-128 */ bool b_key_loaded; vlc_mutex_t lock; @@ -210,7 +210,7 @@ static bool isHTTPLiveStreaming(stream_t *s) for (size_t i = 0; i < ARRAY_SIZE(ext); i++) { size_t len = strlen(ext[i]); - if (size < len) + if (size < 0 || (size_t)size < len) continue; if (!memcmp(peek, ext[i], len)) return true; @@ -484,7 +484,8 @@ static char *parse_Attributes(const char *line, const char *attr) begin = p; do { - if (strncasecmp(begin, attr, strlen(attr)) == 0) + if (strncasecmp(begin, attr, strlen(attr)) == 0 + && begin[strlen(attr)] == '=') { /* =[,]* */ p = strchr(begin, ','); @@ -502,33 +503,38 @@ static char *parse_Attributes(const char *line, const char *attr) return NULL; } -static int hex2int(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - return -1; -} - -static int string_to_IV(const char *string_hexa, uint8_t iv[AES_BLOCK_SIZE]) +static int string_to_IV(char *string_hexa, uint8_t iv[AES_BLOCK_SIZE]) { - const char *p = string_hexa; - uint8_t *d = iv; - unsigned int c; - - if (*p++ != '0') + unsigned long long iv_hi, iv_lo; + char *end = NULL; + if (*string_hexa++ != '0') return VLC_EGENERIC; - if (*p++ != 'x') + if (*string_hexa != 'x' && *string_hexa != 'X') return VLC_EGENERIC; - while (*p && *(p+1)) - { - c = hex2int(*p++) << 4; - c |= hex2int(*p++); - *d++ = c; + string_hexa++; + + size_t len = strlen(string_hexa); + if (len <= 16) { + iv_hi = 0; + iv_lo = strtoull(string_hexa, &end, 16); + if (end) + return VLC_EGENERIC; + } else { + iv_lo = strtoull(&string_hexa[len-16], NULL, 16); + if (end) + return VLC_EGENERIC; + string_hexa[len-16] = '\0'; + iv_hi = strtoull(string_hexa, NULL, 16); + if (end) + return VLC_EGENERIC; + } + + for (int i = 8; i ; --i) { + iv[ i] = iv_hi & 0xff; + iv[8+i] = iv_lo & 0xff; + iv_hi >>= 8; + iv_lo >>= 8; } return VLC_SUCCESS; @@ -569,20 +575,23 @@ static int parse_SegmentInformation(hls_stream_t *hls, char *p_read, int *durati return VLC_EGENERIC; int value; + char *endptr; if (hls->version < 3) { - value = strtol(token, NULL, 10); - if (errno == ERANGE) - { - *duration = -1; - return VLC_EGENERIC; - } - *duration = value; + errno = 0; + value = strtol(token, &endptr, 10); + if (token == endptr || errno == ERANGE) + { + *duration = -1; + return VLC_EGENERIC; + } + *duration = value; } else { - double d = strtod(token, (char **) NULL); - if (errno == ERANGE) + errno = 0; + double d = strtof(token, &endptr); + if (token == endptr || errno == ERANGE) { *duration = -1; return VLC_EGENERIC; @@ -591,6 +600,7 @@ static int parse_SegmentInformation(hls_stream_t *hls, char *p_read, int *durati value = ((int)d) + 1; else value = ((int)d); + *duration = value; } /* Ignore the rest of the line */ @@ -790,7 +800,10 @@ static int parse_Key(stream_t *s, hls_stream_t *hls, char *p_read) */ if (string_to_IV(iv, hls->psz_AES_IV) == VLC_EGENERIC) + { + msg_Err(s, "IV invalid"); err = VLC_EGENERIC; + } else hls->b_iv_loaded = true; free(value); @@ -1083,9 +1096,6 @@ static int parse_M3U8(stream_t *s, vlc_array_t *streams, uint8_t *buffer, const static int hls_DownloadSegmentKey(stream_t *s, segment_t *seg) { - uint8_t aeskey[32]; /* AES-512 can use up to 32 bytes */ - ssize_t len; - stream_t *p_m3u8 = stream_UrlNew(s, seg->psz_key_path); if (p_m3u8 == NULL) { @@ -1093,18 +1103,14 @@ static int hls_DownloadSegmentKey(stream_t *s, segment_t *seg) return VLC_EGENERIC; } - len = stream_Read(p_m3u8, aeskey, sizeof(aeskey)); + int len = stream_Read(p_m3u8, seg->aes_key, sizeof(seg->aes_key)); + stream_Delete(p_m3u8); if (len != AES_BLOCK_SIZE) { - msg_Err(s, "The AES key loaded doesn't have the right size (%zd)", len); - stream_Delete(p_m3u8); + msg_Err(s, "The AES key loaded doesn't have the right size (%d)", len); return VLC_EGENERIC; } - memcpy(seg->psz_AES_key, aeskey, AES_BLOCK_SIZE); - - stream_Delete(p_m3u8); - return VLC_SUCCESS; } @@ -1129,7 +1135,7 @@ static int hls_ManageSegmentKeys(stream_t *s, hls_stream_t *hls) * try to copy it, and don't load the key */ if (prev_seg && prev_seg->b_key_loaded && strcmp(seg->psz_key_path, prev_seg->psz_key_path) == 0) { - memcpy(seg->psz_AES_key, prev_seg->psz_AES_key, AES_BLOCK_SIZE); + memcpy(seg->aes_key, prev_seg->aes_key, AES_BLOCK_SIZE); seg->b_key_loaded = true; continue; } @@ -1168,8 +1174,8 @@ static int hls_DecodeSegmentData(stream_t *s, hls_stream_t *hls, segment_t *segm } /* Set key */ - i_gcrypt_err = gcry_cipher_setkey(aes_ctx, segment->psz_AES_key, - sizeof(segment->psz_AES_key)); + i_gcrypt_err = gcry_cipher_setkey(aes_ctx, segment->aes_key, + sizeof(segment->aes_key)); if (i_gcrypt_err) { msg_Err(s, "gcry_cipher_setkey failed: %s", gpg_strerror(i_gcrypt_err)); @@ -1311,7 +1317,8 @@ static int hls_UpdatePlaylist(stream_t *s, hls_stream_t *hls_new, hls_stream_t * continue; } /* We must free the content, because if the key was not downloaded, content can't be decrypted */ - if (segment->data) + if ((p->psz_key_path || p->b_key_loaded) && + segment->data) { block_Release(segment->data); segment->data = NULL; @@ -1488,12 +1495,7 @@ static int hls_DownloadSegmentData(stream_t *s, hls_stream_t *hls, segment_t *se msg_Info(s, "downloaded segment %d from stream %d", segment->sequence, *cur_stream); - /* check for division by zero */ - double ms = (double)duration / 1000.0; /* ms */ - if (ms <= 0.0) - return VLC_SUCCESS; - - uint64_t bw = ((double)(segment->size * 8) / ms) * 1000; /* bits / s */ + uint64_t bw = segment->size * 8 * 1000000 / __MAX(1, duration); /* bits / s */ p_sys->bandwidth = bw; if (p_sys->b_meta && (hls->bandwidth != bw)) { @@ -1650,8 +1652,13 @@ static int Prefetch(stream_t *s, int *current) if (hls == NULL) return VLC_EGENERIC; - /* Download first 2 segments of this HLS stream */ - for (int i = 0; i < 2; i++) + if (vlc_array_count(hls->segments) == 0) + return VLC_EGENERIC; + else if (vlc_array_count(hls->segments) == 1 && p_sys->b_live) + msg_Warn(s, "Only 1 segment available to prefetch in live stream; may stall"); + + /* Download first 2 segments of this HLS stream if they exist */ + for (int i = 0; i < __MIN(vlc_array_count(hls->segments), 2); i++) { segment_t *segment = segment_GetSegment(hls, p_sys->download.segment); if (segment == NULL ) @@ -1711,6 +1718,10 @@ static int hls_Download(stream_t *s, segment_t *segment) uint64_t size; do { + /* NOTE: Beware the size reported for a segment by the HLS server may not + * be correct, when downloading the segment data. Therefore check the size + * and enlarge the segment data block if necessary. + */ size = stream_Size(p_ts); if (size > segment->size) { @@ -1807,21 +1818,27 @@ static char *ReadLine(uint8_t *buffer, uint8_t **pos, const size_t len) while (p < end) { - if ((*p == '\n') || (*p == '\0')) + if ((*p == '\r') || (*p == '\n') || (*p == '\0')) break; p++; } - /* copy line excluding \n or \0 */ + /* copy line excluding \r \n or \0 */ line = strndup((char *)begin, p - begin); - if (*p == '\0') - *pos = end; - else + while ((*p == '\r') || (*p == '\n') || (*p == '\0')) { - /* next pass start after \n */ - p++; - *pos = p; + if (*p == '\0') + { + *pos = end; + break; + } + else + { + /* next pass start after \r and \n */ + p++; + *pos = p; + } } return line; @@ -2337,16 +2354,12 @@ static int segment_Seek(stream_t *s, const uint64_t pos) uint64_t size = hls->size; int count = vlc_array_count(hls->segments); - /* restore current segment to start position */ - segment_t *segment = segment_GetSegment(hls, p_sys->playback.segment); - if (segment == NULL) + segment_t *currentSegment = segment_GetSegment(hls, p_sys->playback.segment); + if (currentSegment == NULL) { vlc_mutex_unlock(&hls->lock); return VLC_EGENERIC; } - vlc_mutex_lock(&segment->lock); - segment_RestorePos(segment); - vlc_mutex_unlock(&segment->lock); for (int n = 0; n < count; n++) { @@ -2385,7 +2398,13 @@ static int segment_Seek(stream_t *s, const uint64_t pos) /* */ if (b_found) { - /* restore segment to start position */ + + /* restore current segment to start position */ + vlc_mutex_lock(¤tSegment->lock); + segment_RestorePos(currentSegment); + vlc_mutex_unlock(¤tSegment->lock); + + /* restore seeked segment to start position */ segment_t *segment = segment_GetSegment(hls, p_sys->playback.segment); if (segment == NULL) {