You can now also play MP3s streamed from the internet. (Very rough & wonky code. More or less proof-of-concept right now.)
This commit is contained in:
parent
94489618ca
commit
b989784fb9
@ -3,19 +3,21 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
class DataSource {
|
class DataSource {
|
||||||
private:
|
private:
|
||||||
public:
|
public:
|
||||||
DataSource() {};
|
DataSource() {};
|
||||||
~DataSource() {};
|
virtual ~DataSource() {};
|
||||||
virtual size_t read(uint8_t* buf, size_t len) = 0;
|
virtual size_t read(uint8_t* buf, size_t len) = 0;
|
||||||
virtual uint8_t read() = 0;
|
virtual uint8_t read() = 0;
|
||||||
virtual size_t position() = 0;
|
virtual size_t position() = 0;
|
||||||
virtual void seek(size_t position) = 0;
|
virtual void seek(size_t position) = 0;
|
||||||
virtual size_t size() = 0;
|
virtual size_t size() = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
virtual void skip_id3_tag();
|
virtual void skip_id3_tag() {};
|
||||||
|
virtual bool usable() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SDDataSource : public DataSource {
|
class SDDataSource : public DataSource {
|
||||||
@ -31,4 +33,23 @@ public:
|
|||||||
size_t size();
|
size_t size();
|
||||||
void close();
|
void close();
|
||||||
void skip_id3_tag();
|
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();
|
||||||
};
|
};
|
@ -11,7 +11,7 @@ private:
|
|||||||
bool _shuffled = false;
|
bool _shuffled = false;
|
||||||
std::vector<String> _files;
|
std::vector<String> _files;
|
||||||
public:
|
public:
|
||||||
Playlist(String path);
|
Playlist(String path, bool is_url=false);
|
||||||
void start();
|
void start();
|
||||||
bool has_track_next();
|
bool has_track_next();
|
||||||
bool has_track_prev();
|
bool has_track_prev();
|
||||||
|
@ -9,6 +9,7 @@ size_t SDDataSource::position() { return _file.position(); }
|
|||||||
void SDDataSource::seek(size_t position) { _file.seek(position); }
|
void SDDataSource::seek(size_t position) { _file.seek(position); }
|
||||||
size_t SDDataSource::size() { return _file.size(); }
|
size_t SDDataSource::size() { return _file.size(); }
|
||||||
void SDDataSource::close() { _file.close(); }
|
void SDDataSource::close() { _file.close(); }
|
||||||
|
bool SDDataSource::usable() { return _file; }
|
||||||
|
|
||||||
void SDDataSource::skip_id3_tag() {
|
void SDDataSource::skip_id3_tag() {
|
||||||
uint32_t original_position = _file.position();
|
uint32_t original_position = _file.position();
|
||||||
@ -33,3 +34,61 @@ void SDDataSource::skip_id3_tag() {
|
|||||||
_file.seek(original_position);
|
_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(); }
|
@ -545,10 +545,16 @@ bool Player::play() {
|
|||||||
void Player::_play_file(String file, uint32_t file_offset) {
|
void Player::_play_file(String file, uint32_t file_offset) {
|
||||||
INFO("play_file('%s', %d)\n", file.c_str(), file_offset);
|
INFO("play_file('%s', %d)\n", file.c_str(), file_offset);
|
||||||
_spi->select_sd();
|
_spi->select_sd();
|
||||||
|
if (file.startsWith("/")) {
|
||||||
_file = new SDDataSource(file);
|
_file = new SDDataSource(file);
|
||||||
|
} else if (file.startsWith("https://")) {
|
||||||
|
_file = new HTTPSDataSource(file);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_file_size = _file->size();
|
_file_size = _file->size();
|
||||||
_spi->select_sd(false);
|
_spi->select_sd(false);
|
||||||
if (!_file) {
|
if (!_file || !_file->usable()) {
|
||||||
DEBUG("Could not open file %s", file.c_str());
|
DEBUG("Could not open file %s", file.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
Playlist::Playlist(String path) {
|
Playlist::Playlist(String path, bool is_url) {
|
||||||
|
if (is_url) {
|
||||||
|
_files.push_back(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Add files to _files
|
// Add files to _files
|
||||||
SPIMaster::select_sd();
|
SPIMaster::select_sd();
|
||||||
TRACE("Examining folder %s...\n", path.c_str());
|
TRACE("Examining folder %s...\n", path.c_str());
|
||||||
|
Loading…
Reference in New Issue
Block a user