Skip to content

Commit 7690d2e

Browse files
committed
Merge branch 'feature/hls_support_multiple_key' into 'master'
feat(http_stream): Add multiple key support for HLS See merge request adf/esp-adf-internal!1432
2 parents 8b61b32 + 39d603b commit 7690d2e

File tree

4 files changed

+175
-63
lines changed

4 files changed

+175
-63
lines changed

components/audio_stream/http_stream.c

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ typedef struct {
6969
uint8_t key_cache[HLS_KEY_CACHE_SIZE];
7070
uint8_t key_size;
7171
hls_stream_key_t key;
72-
uint64_t sequence_no;
7372
esp_aes_context aes_ctx;
7473
bool aes_used;
7574
} http_stream_hls_key_t;
@@ -96,14 +95,17 @@ typedef struct http_stream {
9695
bool gzip_encoding; /* Content is encoded */
9796
gzip_miniz_handle_t gzip; /* GZIP instance */
9897
http_stream_hls_key_t *hls_key;
98+
uint64_t hls_sequence_no;
9999
hls_handle_t *hls_media;
100+
int url_index;
100101
int request_range_size;
101102
int64_t request_range_end;
102103
bool is_last_range;
103104
const char *user_agent;
104105
} http_stream_t;
105106

106107
static esp_err_t http_stream_auto_connect_next_track(audio_element_handle_t el);
108+
static int _update_hls_info(http_stream_t *http);
107109

108110
// `errno` is not thread safe in multiple HTTP-clients,
109111
// so it is necessary to save the errno number of HTTP clients to avoid reading and writing exceptions of HTTP-clients caused by errno exceptions
@@ -272,14 +274,13 @@ static esp_err_t _prepare_crypt(http_stream_t *http)
272274
if (ret < 0) {
273275
return ESP_FAIL;
274276
}
275-
ret = hls_playlist_get_key(http->hls_media, http->hls_key->sequence_no, &http->hls_key->key);
277+
ret = hls_playlist_get_key(http->hls_media, http->hls_sequence_no, &http->hls_key->key);
276278
if (ret != 0) {
277279
return ESP_FAIL;
278280
}
279281
esp_aes_init(&hls_key->aes_ctx);
280282
esp_aes_setkey(&hls_key->aes_ctx, (unsigned char*)hls_key->key.key, 128);
281283
hls_key->aes_used = true;
282-
http->hls_key->sequence_no++;
283284
return ESP_OK;
284285
}
285286

@@ -387,6 +388,15 @@ static esp_err_t _resolve_playlist(audio_element_handle_t self, const char *uri)
387388
}
388389
} while (0);
389390
if (hls) {
391+
http->hls_sequence_no = hls_playlist_get_sequence_no(hls);
392+
for (int i = 0; i < http->url_index; i++) {
393+
char *next_url = http_playlist_get_next_track(http->playlist);
394+
if (next_url == NULL) {
395+
ESP_LOGE(TAG, "Url index %d over limited %d", http->url_index, i);
396+
return ESP_FAIL;
397+
}
398+
}
399+
http->hls_sequence_no += http->url_index;
390400
if (hls_playlist_is_encrypt(hls) == false) {
391401
_free_hls_key(http);
392402
hls_playlist_close(hls);
@@ -398,27 +408,9 @@ static esp_err_t _resolve_playlist(audio_element_handle_t self, const char *uri)
398408
ESP_LOGE(TAG, "Hls do not have key url");
399409
return ESP_FAIL;
400410
}
401-
if (http->hls_key == NULL) {
402-
http->hls_key = (http_stream_hls_key_t *) calloc(1, sizeof(http_stream_hls_key_t));
403-
if (http->hls_key == NULL) {
404-
ESP_LOGE(TAG, "No memory for hls key");
405-
return ESP_FAIL;
406-
}
407-
}
408-
if (http->hls_key->key_url && strcmp(http->hls_key->key_url, key_url) == 0) {
409-
http->hls_key->key_loaded = true;
410-
} else {
411-
if (http->hls_key->key_url) {
412-
audio_free(http->hls_key->key_url);
413-
}
414-
http->hls_key->key_loaded = false;
415-
http->hls_key->key_url = audio_strdup(key_url);
416-
if (http->hls_key->key_url == NULL) {
417-
ESP_LOGE(TAG, "No memory for hls key url");
418-
return ESP_FAIL;
419-
}
411+
if (_update_hls_info(http) != ESP_OK) {
412+
return ESP_FAIL;
420413
}
421-
http->hls_key->sequence_no = hls_playlist_get_sequence_no(hls);
422414
}
423415
}
424416
return http->is_valid_playlist ? ESP_OK : ESP_FAIL;
@@ -570,7 +562,7 @@ static esp_err_t _http_open(audio_element_handle_t self)
570562
ESP_LOGE(TAG, "Error open connection, uri = NULL");
571563
return ESP_FAIL;
572564
}
573-
565+
574566
ESP_LOGD(TAG, "URI=%s", uri);
575567
// if not initialize http client, initial it
576568
if (http->client == NULL) {
@@ -600,7 +592,7 @@ static esp_err_t _http_open(audio_element_handle_t self)
600592
return ESP_FAIL;
601593
}
602594

603-
if (_is_playlist(&info, uri) == true) {
595+
if (_is_playlist(&info, uri) == true && http->is_playlist_resolved == false) {
604596
/**
605597
* `goto _stream_open_begin` blocks on http_open until it gets valid URL.
606598
* Ensure that the stop command is processed
@@ -704,6 +696,43 @@ static bool _check_range_done(audio_element_handle_t self)
704696
return last_range;
705697
}
706698

699+
static int _update_hls_info(http_stream_t *http)
700+
{
701+
char *key_url = hls_playlist_get_key_uri_by_seq(http->hls_media, http->hls_sequence_no);
702+
if (key_url && http->hls_key == NULL) {
703+
http->hls_key = audio_calloc(1, sizeof(http_stream_hls_key_t));
704+
if (http->hls_key == NULL) {
705+
ESP_LOGE(TAG, "No mem for HLS key");
706+
return ESP_FAIL;
707+
}
708+
}
709+
if (http->hls_key) {
710+
if (key_url == NULL) {
711+
ESP_LOGE(TAG, "Hls do not have key url");
712+
return ESP_FAIL;
713+
}
714+
// Same key no need reload
715+
if (http->hls_key->key_url && strcmp(http->hls_key->key_url, key_url) == 0) {
716+
http->hls_key->key_loaded = true;
717+
audio_free(key_url);
718+
} else {
719+
if (http->hls_key->key_url) {
720+
audio_free(http->hls_key->key_url);
721+
}
722+
http->hls_key->key_loaded = false;
723+
http->hls_key->key_url = key_url;
724+
}
725+
}
726+
return ESP_OK;
727+
}
728+
729+
static int _update_next_hls_info(http_stream_t *http)
730+
{
731+
// Check HLS key need update or not
732+
http->hls_sequence_no++;
733+
return _update_hls_info(http);
734+
}
735+
707736
static int _http_read(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context)
708737
{
709738
http_stream_t *http = (http_stream_t *)audio_element_getdata(self);
@@ -719,6 +748,9 @@ static int _http_read(audio_element_handle_t self, char *buffer, int len, TickTy
719748
rlen = _http_read_data(http, buffer, len);
720749
}
721750
}
751+
if (rlen <= 0) {
752+
_update_next_hls_info(http);
753+
}
722754
if (rlen <= 0 && http->auto_connect_next_track) {
723755
if (http_stream_auto_connect_next_track(self) == ESP_OK) {
724756
rlen = _http_read_data(http, buffer, len);
@@ -727,7 +759,7 @@ static int _http_read(audio_element_handle_t self, char *buffer, int len, TickTy
727759
if (rlen <= 0) {
728760
http->_errno = esp_http_client_get_errno(http->client);
729761
ESP_LOGW(TAG, "No more data,errno:%d, total_bytes:%llu, rlen = %d", http->_errno, info.byte_pos, rlen);
730-
if (http->_errno != 0) { // Error occuered, reset connection
762+
if (http->_errno != 0) { // Error occurs, reset connection
731763
ESP_LOGW(TAG, "Got %d errno(%s)", http->_errno, strerror(http->_errno));
732764
return http->_errno;
733765
}
@@ -745,8 +777,8 @@ static int _http_read(audio_element_handle_t self, char *buffer, int len, TickTy
745777
return ESP_OK;
746778
} else {
747779
if (http->hls_key) {
748-
int ret = esp_aes_crypt_cbc(&http->hls_key->aes_ctx, ESP_AES_DECRYPT,
749-
rlen, (unsigned char*)http->hls_key->key.iv,
780+
int ret = esp_aes_crypt_cbc(&http->hls_key->aes_ctx, ESP_AES_DECRYPT,
781+
rlen, (unsigned char*)http->hls_key->key.iv,
750782
(unsigned char*)buffer, (unsigned char*)buffer);
751783
if (rlen % 16 != 0) {
752784
ESP_LOGE(TAG, "Data length %d not aligned", rlen);
@@ -873,6 +905,7 @@ audio_element_handle_t http_stream_init(http_stream_cfg_t *config)
873905
http->user_data = config->user_data;
874906
http->cert_pem = config->cert_pem;
875907
http->user_agent = config->user_agent;
908+
http->url_index = config->url_index;
876909

877910
if (config->crt_bundle_attach) {
878911
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
@@ -971,6 +1004,19 @@ esp_err_t http_stream_auto_connect_next_track(audio_element_handle_t el)
9711004
return ESP_FAIL;
9721005
}
9731006

1007+
esp_err_t http_stream_get_url_index(audio_element_handle_t el, int *url_idx)
1008+
{
1009+
http_stream_t *http = (http_stream_t *)audio_element_getdata(el);
1010+
if (http->playlist == NULL || http->hls_media == NULL || http->playlist->is_incomplete) {
1011+
ESP_LOGI(TAG, "Only support for VOD HLS stream");
1012+
return ESP_ERR_NOT_SUPPORTED;
1013+
}
1014+
uint64_t start_seq = hls_playlist_get_sequence_no(http->hls_media);
1015+
*(url_idx) = (int)(http->hls_sequence_no - start_seq);
1016+
ESP_LOGI(TAG, "Current url index %d\n", *(url_idx));
1017+
return ESP_OK;
1018+
}
1019+
9741020
esp_err_t http_stream_fetch_again(audio_element_handle_t el)
9751021
{
9761022
http_stream_t *http = (http_stream_t *)audio_element_getdata(el);

components/audio_stream/include/http_stream.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,12 @@ typedef struct {
9090
bundle for server verification, must be enabled in menuconfig */
9191
int request_size; /*!< Request data size each time from `http_client`
9292
Defaults use DEFAULT_ELEMENT_BUFFER_LENGTH if set to 0
93-
Need care this setting if audio frame size is small and want low latency playback */
93+
Need care this setting if audio frame size is small and want low latency playback */
9494
int request_range_size; /*!< Range size setting for header `Range: bytes=start-end`
9595
Request full range of resource if set to 0
9696
Range size bigger than request size is recommended */
9797
const char *user_agent; /*!< The User Agent string to send with HTTP requests */
98+
int url_index; /*!< The url index to be played in playlist */
9899
} http_stream_cfg_t;
99100

100101
#define HTTP_STREAM_TASK_STACK (6 * 1024)
@@ -158,6 +159,19 @@ esp_err_t http_stream_restart(audio_element_handle_t el);
158159
*/
159160
esp_err_t http_stream_fetch_again(audio_element_handle_t el);
160161

162+
/**
163+
* @brief Get url index in current playlist
164+
*
165+
*
166+
* @param el The http_stream element handle
167+
* @param url_idx Url index in playlist
168+
*
169+
* @return
170+
* - ESP_OK on success
171+
* - ESP_ERR_NOT_SUPPORTED if playlist not existed
172+
*/
173+
esp_err_t http_stream_get_url_index(audio_element_handle_t el, int *url_idx);
174+
161175
/**
162176
* @brief Set SSL server certification
163177
* @note EM format as string, if the client requires to verify server

components/audio_stream/lib/hls/hls_playlist.c

Lines changed: 73 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,48 @@ static void hls_hex_to_bin(uint8_t* v, char* s, int n)
260260
}
261261
}
262262

263+
static int hls_append_key(hls_media_playlist_t* media, hls_tag_info_t* tag_info)
264+
{
265+
if ((media->key_num % 5) == 0) {
266+
int key_arr_size = sizeof(hls_key_t) * (media->key_num + 5);
267+
hls_key_t* new_key = audio_realloc(media->key, key_arr_size);
268+
if (new_key == NULL) {
269+
return -1;
270+
}
271+
memset(&new_key[media->key_num], 0, sizeof(hls_key_t) * 5);
272+
media->key = new_key;
273+
}
274+
hls_key_t *key_info = &media->key[media->key_num];
275+
for (int i = 0; i < tag_info->attr_num; i++) {
276+
switch (tag_info->k[i]) {
277+
case HLS_ATTR_METHOD:
278+
key_info->method = (hls_encrypt_method_t)tag_info->v[i].v;
279+
break;
280+
case HLS_ATTR_IV: {
281+
char* v = tag_info->v[i].s;
282+
// Skip leading 0x
283+
if (strncasecmp(v, "0x", 2) == 0) {
284+
v += 2;
285+
}
286+
hls_hex_to_bin(key_info->iv, v, strlen(v));
287+
break;
288+
}
289+
case HLS_ATTR_URI:
290+
key_info->uri = strdup(tag_info->v[i].s);
291+
break;
292+
default:
293+
break;
294+
}
295+
}
296+
key_info->range[0] = media->url_num;
297+
key_info->range[1] = 0xFFFF;
298+
if (media->key_num > 0) {
299+
key_info[-1].range[1] = media->url_num ? media->url_num - 1 : 0;
300+
}
301+
media->key_num++;
302+
return 0;
303+
}
304+
263305
static int hls_media_tag_cb(hls_tag_info_t* tag_info, void* ctx)
264306
{
265307
hls_t* hls = (hls_t*)ctx;
@@ -293,36 +335,9 @@ static int hls_media_tag_cb(hls_tag_info_t* tag_info, void* ctx)
293335
break;
294336
case HLS_TAG_KEY:
295337
if (tag_info->attr_num) {
296-
// Only support one key currently
297-
media->key_num = 1;
298-
if (media->key == NULL) {
299-
media->key = (hls_key_t*) audio_calloc(1, sizeof(hls_key_t));
300-
}
301-
if (media->key == NULL) {
338+
if (hls_append_key(media, tag_info) != 0) {
302339
ESP_LOGE(TAG, "No memory for key");
303-
break;
304-
}
305-
for (int i = 0; i < tag_info->attr_num; i++) {
306-
switch (tag_info->k[i]) {
307-
case HLS_ATTR_METHOD:
308-
media->key[0].method = (hls_encrypt_method_t)tag_info->v[i].v;
309-
break;
310-
case HLS_ATTR_IV: {
311-
char* v = tag_info->v[i].s;
312-
// Skip leading 0x
313-
if (strncasecmp(v, "0x", 2) == 0) {
314-
v += 2;
315-
}
316-
hls_hex_to_bin(media->key[0].iv, v, strlen(v));
317-
break;
318-
}
319-
case HLS_ATTR_URI:
320-
HLS_FREE(media->key[0].uri);
321-
media->key[0].uri = join_url(media->uri, tag_info->v[i].s);
322-
break;
323-
default:
324-
break;
325-
}
340+
return -1;
326341
}
327342
}
328343
break;
@@ -333,6 +348,8 @@ static int hls_media_tag_cb(hls_tag_info_t* tag_info, void* ctx)
333348
case HLS_ATTR_URI: {
334349
char* url = join_url(media->uri, tag_info->v[i].s);
335350
if (url) {
351+
// Inc url number
352+
media->url_num++;
336353
hls->cfg.cb(url, hls->cfg.ctx);
337354
audio_free(url);
338355
}
@@ -429,10 +446,12 @@ static int hls_close_media_playlist(hls_t* hls, hls_media_playlist_t* media)
429446
}
430447
media->key_num = 0;
431448
HLS_FREE(media->key);
432-
// release url
433-
for (i = 0; i < media->url_num; i++) {
434-
hls_url_t* url = &media->url_items[i];
435-
HLS_FREE(url->uri);
449+
// Release url
450+
if (media->url_items) {
451+
for (i = 0; i < media->url_num; i++) {
452+
hls_url_t* url = &media->url_items[i];
453+
HLS_FREE(url->uri);
454+
}
436455
}
437456
HLS_FREE(media->url_items);
438457
HLS_FREE(media->uri);
@@ -585,7 +604,7 @@ const char* hls_playlist_get_key_uri(hls_handle_t h)
585604
}
586605
hls_media_playlist_t* media = hls->media_playlist;
587606
if (media->key_num && media->key[0].uri) {
588-
return media->key[0].uri;
607+
return join_url(media->uri, media->key[0].uri);
589608
}
590609
return NULL;
591610
}
@@ -600,6 +619,26 @@ uint64_t hls_playlist_get_sequence_no(hls_handle_t h)
600619
return media->media_sequence;
601620
}
602621

622+
char *hls_playlist_get_key_uri_by_seq(hls_handle_t h, uint64_t sequence_no)
623+
{
624+
hls_t* hls = (hls_t*)h;
625+
if (hls == NULL || hls->media_playlist == NULL) {
626+
return NULL;
627+
}
628+
hls_media_playlist_t* media = hls->media_playlist;
629+
if (media->key_num == 0) {
630+
return NULL;
631+
}
632+
uint16_t start_idx = sequence_no - media->media_sequence;
633+
for (int i = 0; i < media->key_num; i++) {
634+
if (start_idx > media->key[i].range[1]) {
635+
continue;
636+
}
637+
return join_url(media->uri, media->key[i].uri);
638+
}
639+
return NULL;
640+
}
641+
603642
int hls_playlist_get_key(hls_handle_t h, uint64_t sequence_no, hls_stream_key_t* key)
604643
{
605644
hls_t* hls = (hls_t*)h;

0 commit comments

Comments
 (0)