From b989784fb92e528f257ac8deb58cb72227e06552 Mon Sep 17 00:00:00 2001 From: Fabian Schlenz Date: Wed, 20 Nov 2019 06:13:15 +0100 Subject: [PATCH] You can now also play MP3s streamed from the internet. (Very rough & wonky code. More or less proof-of-concept right now.) --- include/data_sources.h | 25 ++++++++++++++++-- include/playlist.h | 2 +- src/data_sources.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++ src/player.cpp | 10 +++++-- src/playlist.cpp | 6 ++++- 5 files changed, 96 insertions(+), 6 deletions(-) diff --git a/include/data_sources.h b/include/data_sources.h index 2649e1d..ddfe023 100644 --- a/include/data_sources.h +++ b/include/data_sources.h @@ -3,19 +3,21 @@ #include #include #include "config.h" +#include class DataSource { private: public: DataSource() {}; - ~DataSource() {}; + virtual ~DataSource() {}; virtual size_t read(uint8_t* buf, size_t len) = 0; virtual uint8_t read() = 0; virtual size_t position() = 0; virtual void seek(size_t position) = 0; virtual size_t size() = 0; virtual void close() = 0; - virtual void skip_id3_tag(); + virtual void skip_id3_tag() {}; + virtual bool usable() = 0; }; class SDDataSource : public DataSource { @@ -31,4 +33,23 @@ public: size_t size(); void close(); void skip_id3_tag(); + bool usable(); }; + +class HTTPSDataSource : public DataSource { +private: + WiFiClient* _stream = NULL; + HTTPClient* _http = NULL; + uint32_t _length; + uint32_t _position; +public: + HTTPSDataSource(String url, uint32_t offset=0); + ~HTTPSDataSource(); + size_t read(uint8_t* buf, size_t len); + uint8_t read(); + size_t position(); + void seek(size_t position); + size_t size(); + void close(); + bool usable(); +}; \ No newline at end of file diff --git a/include/playlist.h b/include/playlist.h index ba7f81e..545a309 100644 --- a/include/playlist.h +++ b/include/playlist.h @@ -11,7 +11,7 @@ private: bool _shuffled = false; std::vector _files; public: - Playlist(String path); + Playlist(String path, bool is_url=false); void start(); bool has_track_next(); bool has_track_prev(); diff --git a/src/data_sources.cpp b/src/data_sources.cpp index 281b0bf..cd6aa45 100644 --- a/src/data_sources.cpp +++ b/src/data_sources.cpp @@ -9,6 +9,7 @@ size_t SDDataSource::position() { return _file.position(); } void SDDataSource::seek(size_t position) { _file.seek(position); } size_t SDDataSource::size() { return _file.size(); } void SDDataSource::close() { _file.close(); } +bool SDDataSource::usable() { return _file; } void SDDataSource::skip_id3_tag() { uint32_t original_position = _file.position(); @@ -33,3 +34,61 @@ void SDDataSource::skip_id3_tag() { _file.seek(original_position); } } + + + +////////////// HTTPSDataSource ////////////// +HTTPSDataSource::HTTPSDataSource(String url, uint32_t offset) { + uint8_t tries_left = 5; + int status; + do { + if (tries_left == 0) { + ERROR("Redirection loop? Cancelling!\n"); + return; + } + tries_left--; + DEBUG("Connecting to %s...\n", url.c_str()); + if (_http) delete _http; + _http = new HTTPClient(); + const char* headers[] = {"location"}; + _http->collectHeaders(headers, 1); + bool result = _http->begin(url); + DEBUG("HTTP->begin result: %d\n", result); + if (!result) return; + status = _http->GET(); + DEBUG("Status code: %d\n", status); + if (status == HTTP_CODE_FOUND || status==HTTP_CODE_MOVED_PERMANENTLY || status==HTTP_CODE_TEMPORARY_REDIRECT) { + if (_http->hasHeader("Location")) { + url = _http->header("Location"); + } else if (_http->hasHeader("location")) { + url = _http->header("location"); + } else { + ERROR("Got redirection HTTP code, but could not find Location header.\n"); + for(int i=0; i<_http->headers(); i++) { + DEBUG(" Header: %s=%s\n", _http->headerName(i).c_str(), _http->header(i).c_str()); + } + return; + } + } else if (status != HTTP_CODE_OK) { + DEBUG("Unexpected HTTP return code. Cancelling.\n"); + return; + } + } while (status != HTTP_CODE_OK); + _length = _http->getSize(); + DEBUG("Content-Length: %d\n", _length); + _stream = _http->getStreamPtr(); +} + +HTTPSDataSource::~HTTPSDataSource() { + if (_stream) _stream->stop(); + _http->end(); + delete _stream; + delete _http; +} +bool HTTPSDataSource::usable() { return _http && _stream; } +size_t HTTPSDataSource::read(uint8_t* buf, size_t len) { size_t result = _stream->read(buf, len); _position += result; return result; } +uint8_t HTTPSDataSource::read() { _position++; return _stream->read(); } +size_t HTTPSDataSource::position() { return _position; } +void HTTPSDataSource::seek(size_t position) { return; /* TODO */ } +size_t HTTPSDataSource::size() { return _length; } +void HTTPSDataSource::close() { _stream->stop(); } \ No newline at end of file diff --git a/src/player.cpp b/src/player.cpp index 463bc55..014e007 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -545,10 +545,16 @@ bool Player::play() { void Player::_play_file(String file, uint32_t file_offset) { INFO("play_file('%s', %d)\n", file.c_str(), file_offset); _spi->select_sd(); - _file = new SDDataSource(file); + if (file.startsWith("/")) { + _file = new SDDataSource(file); + } else if (file.startsWith("https://")) { + _file = new HTTPSDataSource(file); + } else { + return; + } _file_size = _file->size(); _spi->select_sd(false); - if (!_file) { + if (!_file || !_file->usable()) { DEBUG("Could not open file %s", file.c_str()); return; } diff --git a/src/playlist.cpp b/src/playlist.cpp index 16ece6b..0292a33 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -5,7 +5,11 @@ #include #include -Playlist::Playlist(String path) { +Playlist::Playlist(String path, bool is_url) { + if (is_url) { + _files.push_back(path); + return; + } // Add files to _files SPIMaster::select_sd(); TRACE("Examining folder %s...\n", path.c_str());