@@ -94,9 +94,12 @@ typedef struct http_stream {
9494 esp_err_t (* crt_bundle_attach )(void * conf ); /* Function pointer to esp_crt_bundle_attach*/
9595#endif
9696 bool gzip_encoding ; /* Content is encoded */
97- gzip_miniz_handle_t gzip ; /* GZIP instance */
97+ gzip_miniz_handle_t gzip ; /* GZIP instance */
9898 http_stream_hls_key_t * hls_key ;
9999 hls_handle_t * hls_media ;
100+ int request_range_size ;
101+ int64_t request_range_end ;
102+ bool is_last_range ;
100103} http_stream_t ;
101104
102105static esp_err_t http_stream_auto_connect_next_track (audio_element_handle_t el );
@@ -174,6 +177,22 @@ static esp_err_t _http_event_handle(esp_http_client_event_t *evt)
174177 return ESP_FAIL ;
175178 }
176179 }
180+ else if (strcasecmp (evt -> header_key , "Content-Range" ) == 0 ) {
181+ http_stream_t * http = (http_stream_t * )audio_element_getdata (el );
182+ if (http -> request_range_size ) {
183+ char * end_pos = strchr (evt -> header_value , '-' );
184+ http -> is_last_range = true;
185+ if (end_pos ) {
186+ end_pos ++ ;
187+ int64_t range_end = atoll (end_pos );
188+ if (range_end == http -> request_range_end ) {
189+ http -> is_last_range = false;
190+ }
191+ // Update total bytes to range end
192+ audio_element_set_total_bytes (el , range_end + 1 );
193+ }
194+ }
195+ }
177196 return ESP_OK ;
178197}
179198
@@ -413,80 +432,40 @@ static char *_playlist_get_next_track(audio_element_handle_t self)
413432 return NULL ;
414433}
415434
416- static esp_err_t _http_open ( audio_element_handle_t self )
435+ static void _prepare_range ( http_stream_t * http , int64_t pos )
417436{
418- http_stream_t * http = (http_stream_t * )audio_element_getdata (self );
419- esp_err_t err ;
420- char * uri = NULL ;
421- audio_element_info_t info ;
422- ESP_LOGD (TAG , "_http_open" );
423-
424- if (http -> is_open ) {
425- ESP_LOGE (TAG , "already opened" );
426- return ESP_OK ;
427- }
428- http -> _errno = 0 ;
429- audio_element_getinfo (self , & info );
430- _stream_open_begin :
431- if (http -> hls_key && http -> hls_key -> key_loaded == false) {
432- uri = http -> hls_key -> key_url ;
433- } else if (info .byte_pos == 0 ) {
434- uri = _playlist_get_next_track (self );
435- } else if (http -> is_playlist_resolved ) {
436- uri = http_playlist_get_last_track (http -> playlist );
437- }
438- if (uri == NULL ) {
439- if (http -> is_playlist_resolved && http -> enable_playlist_parser ) {
440- if (dispatch_hook (self , HTTP_STREAM_FINISH_PLAYLIST , NULL , 0 ) != ESP_OK ) {
441- ESP_LOGE (TAG , "Failed to process user callback" );
442- return ESP_FAIL ;
437+ if (http -> request_range_size > 0 || pos != 0 ) {
438+ char range_header [64 ] = {0 };
439+ if (http -> request_range_size == 0 ) {
440+ snprintf (range_header , sizeof (range_header ), "bytes=%lld-" , pos );
441+ } else {
442+ int64_t end_pos = pos + http -> request_range_size - 1 ;
443+ if (pos < 0 && end_pos > 0 ) {
444+ end_pos = 0 ;
443445 }
444- goto _stream_open_begin ;
446+ snprintf (range_header , sizeof (range_header ), "bytes=%lld-%lld" , pos , end_pos );
447+ http -> request_range_end = end_pos ;
445448 }
446- uri = audio_element_get_uri (self );
447- }
448-
449- if (uri == NULL ) {
450- ESP_LOGE (TAG , "Error open connection, uri = NULL" );
451- return ESP_FAIL ;
452- }
453- audio_element_getinfo (self , & info );
454- ESP_LOGD (TAG , "URI=%s" , uri );
455- // if not initialize http client, initial it
456- if (http -> client == NULL ) {
457- esp_http_client_config_t http_cfg = {
458- .url = uri ,
459- .event_handler = _http_event_handle ,
460- .user_data = self ,
461- .timeout_ms = 30 * 1000 ,
462- .buffer_size = HTTP_STREAM_BUFFER_SIZE ,
463- #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (4 , 1 , 0 )
464- .buffer_size_tx = 1024 ,
465- #endif
466- .cert_pem = http -> cert_pem ,
467- #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (4 , 3 , 4 )) && defined CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
468- .crt_bundle_attach = http -> crt_bundle_attach ,
469- #endif // (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)) && defined CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
470- };
471- http -> client = esp_http_client_init (& http_cfg );
472- AUDIO_MEM_CHECK (TAG , http -> client , return ESP_ERR_NO_MEM );
473- } else {
474- esp_http_client_set_url (http -> client , uri );
475- }
476-
477- if (info .byte_pos ) {
478- char rang_header [32 ];
479- snprintf (rang_header , 32 , "bytes=%d-" , (int )info .byte_pos );
480- esp_http_client_set_header (http -> client , "Range" , rang_header );
449+ esp_http_client_set_header (http -> client , "Range" , range_header );
481450 } else {
482451 esp_http_client_delete_header (http -> client , "Range" );
483452 }
453+ }
454+
455+ static esp_err_t _http_load_uri (audio_element_handle_t self , audio_element_info_t * info )
456+ {
457+ esp_err_t err ;
458+ http_stream_t * http = (http_stream_t * )audio_element_getdata (self );
459+
460+ esp_http_client_close (http -> client );
484461
485462 if (dispatch_hook (self , HTTP_STREAM_PRE_REQUEST , NULL , 0 ) != ESP_OK ) {
486463 ESP_LOGE (TAG , "Failed to process user callback" );
487464 return ESP_FAIL ;
488465 }
489466
467+ _prepare_range (http , info -> byte_pos );
468+
490469 if (http -> stream_type == AUDIO_STREAM_WRITER ) {
491470 err = esp_http_client_open (http -> client , -1 );
492471 if (err == ESP_OK ) {
@@ -530,32 +509,95 @@ static esp_err_t _http_open(audio_element_handle_t self)
530509 * Due to the total byte of content has been changed after seek, set info.total_bytes at beginning only.
531510 */
532511 int64_t cur_pos = esp_http_client_fetch_headers (http -> client );
533- audio_element_getinfo (self , & info );
534- if (info .byte_pos <= 0 ) {
535- info .total_bytes = cur_pos ;
512+ audio_element_getinfo (self , info );
513+ if (info -> byte_pos <= 0 ) {
514+ info -> total_bytes = cur_pos ;
515+ ESP_LOGI (TAG , "total_bytes=%d" , (int )info -> total_bytes );
516+ audio_element_set_total_bytes (self , info -> total_bytes );
536517 }
537-
538- ESP_LOGI (TAG , "total_bytes=%d" , (int )info .total_bytes );
539518 int status_code = esp_http_client_get_status_code (http -> client );
540519 if (status_code == 301 || status_code == 302 ) {
541520 esp_http_client_set_redirection (http -> client );
542521 goto _stream_redirect ;
543522 }
544523 if (status_code != 200
545- && (esp_http_client_get_status_code (http -> client ) != 206 )) {
524+ && (esp_http_client_get_status_code (http -> client ) != 206 )
525+ && (esp_http_client_get_status_code (http -> client ) != 416 )) {
546526 ESP_LOGE (TAG , "Invalid HTTP stream, status code = %d" , status_code );
547527 if (http -> enable_playlist_parser ) {
548528 http_playlist_clear (http -> playlist );
549529 http -> is_playlist_resolved = false;
550530 }
551531 return ESP_FAIL ;
552532 }
553- /**
554- * `audio_element_setinfo` is risky affair.
555- * It overwrites URI pointer as well. Pay attention to that!
556- */
557- audio_element_set_total_bytes (self , info .total_bytes );
533+ return err ;
534+ }
535+
536+ static esp_err_t _http_open (audio_element_handle_t self )
537+ {
538+ http_stream_t * http = (http_stream_t * )audio_element_getdata (self );
539+ char * uri = NULL ;
540+ audio_element_info_t info ;
541+ ESP_LOGD (TAG , "_http_open" );
542+
543+ if (http -> is_open ) {
544+ ESP_LOGE (TAG , "already opened" );
545+ return ESP_OK ;
546+ }
547+ http -> _errno = 0 ;
548+ audio_element_getinfo (self , & info );
549+ _stream_open_begin :
550+ if (http -> hls_key && http -> hls_key -> key_loaded == false) {
551+ uri = http -> hls_key -> key_url ;
552+ } else if (info .byte_pos == 0 ) {
553+ uri = _playlist_get_next_track (self );
554+ } else if (http -> is_playlist_resolved ) {
555+ uri = http_playlist_get_last_track (http -> playlist );
556+ }
557+ if (uri == NULL ) {
558+ if (http -> is_playlist_resolved && http -> enable_playlist_parser ) {
559+ if (dispatch_hook (self , HTTP_STREAM_FINISH_PLAYLIST , NULL , 0 ) != ESP_OK ) {
560+ ESP_LOGE (TAG , "Failed to process user callback" );
561+ return ESP_FAIL ;
562+ }
563+ goto _stream_open_begin ;
564+ }
565+ uri = audio_element_get_uri (self );
566+ }
567+
568+ if (uri == NULL ) {
569+ ESP_LOGE (TAG , "Error open connection, uri = NULL" );
570+ return ESP_FAIL ;
571+ }
558572
573+ ESP_LOGD (TAG , "URI=%s" , uri );
574+ // if not initialize http client, initial it
575+ if (http -> client == NULL ) {
576+ esp_http_client_config_t http_cfg = {
577+ .url = uri ,
578+ .event_handler = _http_event_handle ,
579+ .user_data = self ,
580+ .timeout_ms = 30 * 1000 ,
581+ .buffer_size = HTTP_STREAM_BUFFER_SIZE ,
582+ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (4 , 1 , 0 )
583+ .buffer_size_tx = 1024 ,
584+ #endif
585+ .cert_pem = http -> cert_pem ,
586+ #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (4 , 3 , 0 )) && defined CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
587+ .crt_bundle_attach = http -> crt_bundle_attach ,
588+ #endif // (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)) && defined CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
589+ };
590+ http -> client = esp_http_client_init (& http_cfg );
591+ AUDIO_MEM_CHECK (TAG , http -> client , return ESP_ERR_NO_MEM );
592+ } else {
593+ esp_http_client_set_url (http -> client , uri );
594+ }
595+ audio_element_getinfo (self , & info );
596+
597+ if (_http_load_uri (self , & info ) != ESP_OK ) {
598+ return ESP_FAIL ;
599+ }
600+
559601 if (_is_playlist (& info , uri ) == true) {
560602 /**
561603 * `goto _stream_open_begin` blocks on http_open until it gets valid URL.
@@ -594,20 +636,22 @@ static esp_err_t _http_close(audio_element_handle_t self)
594636{
595637 http_stream_t * http = (http_stream_t * )audio_element_getdata (self );
596638 ESP_LOGD (TAG , "_http_close" );
597- while (http -> is_open ) {
639+ if (http -> is_open ) {
598640 http -> is_open = false;
599- if (http -> stream_type != AUDIO_STREAM_WRITER ) {
600- break ;
601- }
602- if (dispatch_hook (self , HTTP_STREAM_POST_REQUEST , NULL , 0 ) < 0 ) {
603- break ;
604- }
605- esp_http_client_fetch_headers (http -> client );
606-
607- if (dispatch_hook (self , HTTP_STREAM_FINISH_REQUEST , NULL , 0 ) < 0 ) {
608- break ;
609- }
641+ do {
642+ if (http -> stream_type != AUDIO_STREAM_WRITER ) {
643+ break ;
644+ }
645+ if (dispatch_hook (self , HTTP_STREAM_POST_REQUEST , NULL , 0 ) < 0 ) {
646+ break ;
647+ }
648+ esp_http_client_fetch_headers (http -> client );
649+ if (dispatch_hook (self , HTTP_STREAM_FINISH_REQUEST , NULL , 0 ) < 0 ) {
650+ break ;
651+ }
652+ } while (0 );
610653 }
654+
611655 if (AEL_STATE_PAUSED != audio_element_get_state (self )) {
612656 if (http -> enable_playlist_parser ) {
613657 http_playlist_clear (http -> playlist );
@@ -645,6 +689,19 @@ static esp_err_t _http_reconnect(audio_element_handle_t self)
645689 return err ;
646690}
647691
692+ static bool _check_range_done (audio_element_handle_t self )
693+ {
694+ http_stream_t * http = (http_stream_t * )audio_element_getdata (self );
695+ bool last_range = http -> is_last_range ;
696+ audio_element_info_t info = {};
697+ audio_element_getinfo (self , & info );
698+ // If not last range need reload uri from last position
699+ if (last_range == false && _http_load_uri (self , & info ) != ESP_OK ) {
700+ return true;
701+ }
702+ return last_range ;
703+ }
704+
648705static int _http_read (audio_element_handle_t self , char * buffer , int len , TickType_t ticks_to_wait , void * context )
649706{
650707 http_stream_t * http = (http_stream_t * )audio_element_getdata (self );
@@ -655,6 +712,11 @@ static int _http_read(audio_element_handle_t self, char *buffer, int len, TickTy
655712 if (rlen == 0 ) {
656713 rlen = _http_read_data (http , buffer , len );
657714 }
715+ if (rlen <= 0 && http -> request_range_size ) {
716+ if (_check_range_done (self ) == false) {
717+ rlen = _http_read_data (http , buffer , len );
718+ }
719+ }
658720 if (rlen <= 0 && http -> auto_connect_next_track ) {
659721 if (http_stream_auto_connect_next_track (self ) == ESP_OK ) {
660722 rlen = _http_read_data (http , buffer , len );
@@ -841,6 +903,10 @@ audio_element_handle_t http_stream_init(http_stream_cfg_t *config)
841903 } else if (config -> type == AUDIO_STREAM_WRITER ) {
842904 cfg .write = _http_write ;
843905 }
906+ http -> request_range_size = config -> request_range_size ;
907+ if (config -> request_size ) {
908+ cfg .buffer_len = config -> request_size ;
909+ }
844910
845911 el = audio_element_init (& cfg );
846912 AUDIO_MEM_CHECK (TAG , el , {
0 commit comments