HLSStream DRAFT

This commit is contained in:
pschatzmann 2023-09-22 12:52:38 +02:00
parent ba4376e2db
commit dcc2b36d24
21 changed files with 361 additions and 142 deletions

View File

@ -1,35 +0,0 @@
#include "AudioTools.h"
#include "AudioCodecs/CodecMTS.h"
#include "AudioCodecs/CodecAACHelix.h"
// HLSStream hls(out,"SSID", "PWD");
const int buffer_size = 1024;
CsvOutput<int16_t> out(Serial, 2); // Or use StdOuput
MTSDecoder decoder;
AACDecoderHelix aac;
EncodedAudioStream dec_str(&out, &aac); // Decoding stream
EncodedAudioStream mts_str(&dec_str, &decoder);
FILE *file;
uint8_t buffer[buffer_size];
// Arduino Setup
void setup(void) {
// Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Debug);
file = fopen("/home/pschatzmann/Downloads/test.ts", "rb");
dec_str.begin();
mts_str.begin();
}
// Arduino loop
void loop() {
size_t fileSize = fread(buffer, 1, buffer_size, file);
if (fileSize > 0) {
int written = mts_str.write(buffer, fileSize);
LOGI("written %d, ", written);
fileSize = fread(buffer, 1, buffer_size, file);
}
}

View File

@ -0,0 +1,89 @@
#include "AudioTools.h"
void print(const char *title, Vector<int> &vector) {
Serial.println(title);
for (int j = 0; j < vector.size(); j++) {
Serial.print(vector[j]);
Serial.print(" ");
}
Serial.println();
Serial.println();
}
void testPushBack() {
Vector<int> vector;
for (int j = 0; j < 10; j++) {
vector.push_back(j);
}
print("testPushBack", vector);
for (int j = 0; j < 10; j++) {
assert(vector[j] == j);
}
}
void testPushFront() {
Vector<int> vector;
for (int j = 0; j < 10; j++) {
vector.push_front(j);
}
print("testPushFront", vector);
for (int j = 0; j < 10; j++) {
assert(vector[9 - j] == j);
}
}
void testPopFront() {
Vector<int> vector;
for (int j = 0; j < 10; j++) {
vector.push_back(j);
}
vector.pop_front();
print("testPopFront", vector);
assert(vector.size() == 9);
for (int j = 0; j < 9; j++) {
assert(vector[j] == j + 1);
}
}
void testPopBack() {
Vector<int> vector;
for (int j = 0; j < 10; j++) {
vector.push_back(j);
}
vector.pop_back();
print("testPopBack", vector);
assert(vector.size() == 9);
for (int j = 0; j < 9; j++) {
assert(vector[j] == j);
}
}
void testErase() {
Vector<int> vector;
for (int j = 0; j < 10; j++) {
vector.push_back(j);
}
vector.erase(0);
print("testErase", vector);
assert(vector.size() == 9);
for (int j = 0; j < 9; j++) {
assert(vector[j] == j + 1);
}
}
void setup() {
Serial.begin(115200);
testPushBack();
testPushFront();
testPopFront();
testPopBack();
testErase();
Serial.print("All tests passed");
}
void loop() {}

View File

@ -188,8 +188,7 @@ class Vector {
}
inline void pop_front(){
erase(0);
erase(0);
}

View File

@ -555,7 +555,8 @@ class Str {
/// remove leading spaces
virtual void ltrim(){
int n = count(' ',0);
*this << n;
if (n > 0)
*this << n;
}
/// remove trailing spaces

View File

@ -27,6 +27,7 @@ class StrExt : public Str {
StrExt(int initialAllocatedLength) : Str() {
maxlen = initialAllocatedLength;
is_const = false;
}
StrExt(Str &source) : Str() {
@ -52,7 +53,10 @@ class StrExt : public Str {
StrExt (StrExt &&obj) = default;
// move assignment
StrExt& operator = (StrExt &&obj) = default;
StrExt& operator = (StrExt &&obj) {
set(obj.c_str());
return *this;
}
// copy assingment
StrExt& operator = (StrExt &obj) {
@ -61,19 +65,14 @@ class StrExt : public Str {
};
~StrExt() {
if (chars!=nullptr){
LOGD("delete %d",maxlen);
delete [] chars;
chars = nullptr;
}
~StrExt() {
}
bool isOnHeap() {
bool isOnHeap() override {
return true;
}
bool isConst() {
bool isConst() override {
return false;
}
@ -134,9 +133,11 @@ class StrExt : public Str {
}
protected:
Vector<char> vector;
bool grow(int newMaxLen){
bool grown = false;
assert(newMaxLen<1024*10);
if (chars==nullptr || newMaxLen > maxlen ){
LOGD("grow(%d)",newMaxLen);
@ -144,18 +145,8 @@ class StrExt : public Str {
grown = true;
// we use at minimum the defined maxlen
int newSize = newMaxLen > maxlen ? newMaxLen : maxlen;
if (chars!=nullptr){
char* tmp = chars;
chars = new char[newSize+1];
if (chars!=nullptr){
strcpy(chars,tmp);
}
delete [] tmp;
} else {
chars = new char[newSize+1];
if (chars!=nullptr)
chars[0] = 0;
}
vector.resize(newSize+1);
chars = &vector[0];
maxlen = newSize;
}

View File

@ -380,7 +380,7 @@ class EncodedAudioOutput : public AudioStream {
return result;
}
int availableForWrite() override { return ptr_out->availableForWrite(); }
int availableForWrite() override { return min(ptr_out->availableForWrite(), DEFAULT_BUFFER_SIZE); }
/// Returns true if status is active and we still have data to be processed
operator bool() { return active; }

View File

@ -104,8 +104,7 @@ class AACDecoderHelix : public AudioDecoder {
}
void setAudioInfo(AudioInfo info) override {
AudioDecoder::setAudioInfo(info);
//aac->setAudioInfo(info.channels, info.sample_rate);
this->info = info;
if(p_notify!=nullptr && info_notifications_active){
p_notify->setAudioInfo(info);
}
@ -114,7 +113,16 @@ class AACDecoderHelix : public AudioDecoder {
/// Write AAC data to decoder
size_t write(const void* aac_data, size_t len) override {
LOGD("AACDecoderHelix::write: %d", (int)len);
return aac==nullptr ? 0 : aac->write(aac_data, len);
if (aac==nullptr) return 0;
int open = len;
int processed = 0;
uint8_t *data = (uint8_t*)aac_data;
while(open>0){
int act_write = aac->write(data+processed, min(open, DEFAULT_BUFFER_SIZE));
open -= act_write;
processed += act_write;
}
return processed;
}
/// checks if the class is active

View File

@ -133,7 +133,7 @@ class ADTSDecoder : public AudioDecoder {
/// Write AAC data to decoder
size_t write(const void *dataIn, size_t len) override {
LOGD("ADTSDecoder::write: %d", len);
LOGD("AACDecoderADTS::write: %d", (int)len);
// make sure that we can hold at least the len
if (buffer.size() < len) {
@ -225,7 +225,7 @@ class ADTSDecoder : public AudioDecoder {
void resizeBuffer() {
if (parser.size() > buffer.size()) {
LOGI("resize buffer %d to %d", buffer.size(), parser.size());
LOGI("resize buffer %d to %d", (int)buffer.size(), (int)parser.size());
buffer.resize(parser.size());
buffer_write_size = parser.size();
}

View File

@ -252,7 +252,7 @@ class MTSDecoder : public AudioDecoder {
TSDPESPacket *pes = (TSDPESPacket *)data;
// This is where we write the PES data into our buffer.
LOGD("====================");
LOGD("PID %d PES Packet, Size: %ld, stream_id=%u, pts=%lu, dts=%lu", pid,
LOGD("PID %x PES Packet, Size: %ld, stream_id=%u, pts=%lu, dts=%lu", pid,
pes->data_bytes_length, pes->stream_id, pes->pts, pes->dts);
// print out the PES Packet data if it's in our print list
int i;
@ -261,7 +261,7 @@ class MTSDecoder : public AudioDecoder {
if (print_pids[i] == pid) {
// log data
if (logger.isLogging(AudioLogger::Debug)) {
logger.print(" PES data ");
logger.print(" PES data");
logger.print(is_write_active? "active:":"inactive:");
int j = 0;
while (j < pes->data_bytes_length) {
@ -862,24 +862,24 @@ class MTSDecoder : public AudioDecoder {
static void* log_malloc (size_t size) {
void *result = malloc(size);
LOGI("malloc(%d) -> %p %s\n", (int)size,result, result!=NULL?"OK":"ERROR");
LOGI("malloc(%d) -> %p %s", (int)size,result, result!=NULL?"OK":"ERROR");
return result;
}
static void* log_calloc(size_t num, size_t size){
void *result = calloc(num, size);
LOGI("calloc(%d) -> %p %s\n", (int)(num*size),result, result!=NULL?"OK":"ERROR");
LOGI("calloc(%d) -> %p %s", (int)(num*size),result, result!=NULL?"OK":"ERROR");
return result;
}
static void* log_realloc(void *ptr, size_t size){
void *result = realloc(ptr, size);
LOGI("realloc(%d) -> %p %s\n", (int)size, result, result!=NULL?"OK":"ERROR");
LOGI("realloc(%d) -> %p %s", (int)size, result, result!=NULL?"OK":"ERROR");
return result;
}
static void log_free (void *mem){
LOGD("free(%p)\n", mem);
LOGD("free(%p)", mem);
free(mem);
}

View File

@ -51,7 +51,7 @@
#define LOG_STREAM Serial
#endif
#define LOG_PRINTF_BUFFER_SIZE 256
#define LOG_PRINTF_BUFFER_SIZE 303
#define LOG_METHOD __PRETTY_FUNCTION__
// cheange USE_CHECK_MEMORY to 1 to activate memory checks
@ -648,6 +648,7 @@ using WiFiServerSecure = BearSSL::WiFiServerSecure;
#ifdef IS_DESKTOP
# include <Client.h>
# include <WiFi.h>
# define USE_WIFI
# define USE_URL_ARDUINO
# define USE_STREAM_WRITE_OVERRIDE
# define USE_STREAM_READ_OVERRIDE

View File

@ -6,7 +6,7 @@
#include "AudioBasic/StrExt.h"
#include "AudioHttp/URLStream.h"
#define MAX_HLS_LINE 400
#define MAX_HLS_LINE 512
namespace audio_tools {
@ -27,6 +27,7 @@ class URLLoader {
bool begin() {
TRACED();
buffer.resize(buffer_size, buffer_count);
active = true;
return true;
}
@ -74,15 +75,22 @@ class URLLoader {
return stream.httpRequest().reply().get(CONTENT_TYPE);
}
const char *contentLength() {
if (!stream) return nullptr;
return stream.httpRequest().reply().get(CONTENT_LENGTH);
int contentLength() {
if (!stream) return 0;
return stream.contentLength();
}
void setBuffer(int size, int count){
buffer_size = size;
buffer_count = count;
}
protected:
Vector<const char*> urls{10};
NBuffer<uint8_t> buffer{DEFAULT_BUFFER_SIZE, 50};
NBuffer<uint8_t> buffer{DEFAULT_BUFFER_SIZE, 10};
bool active = false;
int buffer_size = DEFAULT_BUFFER_SIZE;
int buffer_count = 10;
URLStream stream;
@ -90,12 +98,18 @@ protected:
void bufferRefill() {
TRACED();
// we have nothing to do
if (urls.empty()) return;
if (buffer.availableForWrite()==0) return;
if (urls.empty()) {
LOGD("urls empty");
return;
}
if (buffer.availableForWrite()==0) {
LOGD("buffer full");
return;
}
// switch current stream if we have no more data
if ((!stream || stream.totalRead()==stream.contentLength()) && !urls.empty()) {
assert(stream.available()==0);
LOGD("Refilling");
const char* url = urls[0];
urls.pop_front();
assert(urls[0]!=url);
@ -106,26 +120,26 @@ protected:
LOGI("Playing %s of %d", url, urls.size());
stream.clear();
stream.setWaitForData(false);
stream.setWaitForData(true);
if (!stream.begin(url)){
TRACEE();
}
// free memory
delete(url);
return;
}
// copy data to buffer
stream.waitForData();
//LOGD("waitForData");
//stream.waitForData();
int to_write = min(buffer.availableForWrite(),DEFAULT_BUFFER_SIZE);
if (to_write>0){
int total = 0;
while(to_write>0){
if (stream.totalRead()==stream.contentLength()) break;
uint8_t tmp[to_write]={0};
int read = stream.readBytes(tmp, to_write);
total += read;
if (read>0){
if (stream.totalRead()==stream.contentLength()) break;
buffer.writeArray(tmp, read);
to_write = min(buffer.availableForWrite(),DEFAULT_BUFFER_SIZE);
} else {
@ -242,14 +256,13 @@ class HLSParser {
return url_loader.contentType();
}
const char *contentLength() {
int contentLength() {
return url_loader.contentLength();
}
/// Closes the processing
void end() {
TRACEI();
//timer.end();
codec.clear();
segments_url_str.clear();
url_stream.end();
@ -267,6 +280,10 @@ class HLSParser {
custom_log_level.set(level);
}
void setBuffer(int size, int count){
url_loader.setBuffer(size, count);
}
protected:
CustomLogLevel custom_log_level;
int bandwidth = 0;
@ -509,7 +526,7 @@ class HLSStream : public AudioStream {
return parser.contentType();
}
const char *contentLength() {
int contentLength() {
return parser.contentLength();
}
@ -528,6 +545,11 @@ class HLSStream : public AudioStream {
parser.setLogLevel(level);
}
/// Defines the buffer size
void setBuffer(int size, int count){
parser.setBuffer(size, count);
}
protected:
HLSParser parser;
const char* ssid = nullptr;
@ -547,8 +569,6 @@ class HLSStream : public AudioStream {
LOGW("login not supported");
#endif
}
};
} // namespace audio_tools

View File

@ -80,7 +80,8 @@ class HttpHeader {
if (activeFlag){
(*it)->active = false;
} else {
delete *it;
(*it)->active = false;
//delete *it; // this leads to exceptions
}
}
if (!activeFlag){
@ -195,8 +196,8 @@ class HttpHeader {
msg[len-2] = 0;
LOGI(" -> %s ", msg);
// marke as processed
header->active = false;
// mark as processed
// header->active = false;
}
const char* urlPath() {
@ -243,6 +244,7 @@ class HttpHeader {
}
LOGI("Data availble");
}
readLine(in, line, MAX_HTTP_HEADER_LINE_LENGTH);
parse1stLine(line);
while (in.available()){
@ -254,11 +256,9 @@ class HttpHeader {
break;
}
put(line);
}
}
}
} else {
LOGW("connection lost");
}
}
}
/// writes the full header to the indicated HttpStreamedMultiOutput stream
@ -274,6 +274,12 @@ class HttpHeader {
is_written = true;
}
void setProcessed() {
for (auto it = lines.begin() ; it != lines.end(); ++it){
(*it)->active = false;
}
}
/// automatically create new lines
void setAutoCreateLines(bool is_auto_line){
create_new_lines = is_auto_line;

View File

@ -237,7 +237,7 @@ class HttpRequest {
if (!this->connected()){
LOGI("process connecting to host %s port %d", url.host(), url.port());
int is_connected = connect(url.host(), url.port(), clientTimeout);
if (is_connected!=1){
if (!is_connected){
LOGE("Connect failed");
return false;
}
@ -249,6 +249,9 @@ class HttpRequest {
LOGI("Free heap: %u", ESP.getFreeHeap());
#endif
reply_header.setProcessed();
host_name = url.host();
request_header.setValues(action, url.path());
if (lenData>0){
@ -330,7 +333,7 @@ class HttpRequest {
LOGI("connected %d timeout %d", is_connected, (int) timeout);
return is_connected;
}
};
}

View File

@ -52,6 +52,9 @@ class URLStream : public AbstractURLStream {
setPassword(password);
}
URLStream(const URLStream&) = delete;
~URLStream(){
TRACED();
end();
@ -93,7 +96,7 @@ class URLStream : public AbstractURLStream {
url.setUrl(url_str.c_str());
int result = -1;
// close it - if we have an active connection
// close it - if we have an active connection
if (active) end();
// optional: login if necessary
@ -232,8 +235,8 @@ class URLStream : public AbstractURLStream {
wait_for_data = flag;
}
size_t contentLength() {
return request.contentLength();
int contentLength() {
return size;
}
size_t totalRead() {
@ -387,7 +390,6 @@ class URLStream : public AbstractURLStream {
delay(500);
}
#else
LOGE("login not supported");
#endif
}

View File

@ -82,13 +82,13 @@ public:
}
virtual int print(char c, PrintCharFmt spec){
char result[3];
char result[5];
switch(spec){
case DEC:
snprintf(result, 3,"%c", c);
return print(result);
case HEX:
snprintf(result, 3,"%x", c & 0xff);
snprintf(result, 3,"%x", c);
return print(result);
}
return -1;

View File

@ -189,10 +189,6 @@ class PortAudioStream : public AudioStream {
return len;
}
int available() override {
return DEFAULT_BUFFER_SIZE;
}
protected:
PaStream *stream = nullptr;
@ -219,7 +215,7 @@ class PortAudioStream : public AudioStream {
/// automatically start the stream when we start to get data
void startStream() {
if (!stream_started) {
if (!stream_started && stream != nullptr) {
TRACED();
err = Pa_StartStream( stream );
if( err == paNoError ) {

View File

@ -1582,6 +1582,87 @@ class CallbackStream : public AudioStream {
size_t (*cb_read)(uint8_t* data, size_t len) = nullptr;
};
/**
* @brief Provides data from a concatenation of Streams. Please note that the provided
* Streams can be played only once! You will need to reset them (e.g. moving the file pointer to the beginning)
* and readd them back if you want to process them a second time. The default timeout on the available() method
* is set to 0. This might be not good if you use e.g. a URLStream.
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class CatStream : public AudioStream {
CatStream(){
_timeout = 0;
}
void add(Stream *stream){
input_streams.push_back(stream);
}
void add(Stream &stream){
input_streams.push_back(&stream);
}
bool begin() override {
is_active = true;
return AudioStream::begin();
}
void end() override {
is_active = false;
return AudioStream::end();
}
int available() override {
if (!is_active) return 0;
if (!moveToNextStreamOnEnd()){
return 0;
}
return availableWithTimout();
}
size_t readBytes(uint8_t* data, size_t len) override {
if (!is_active) return 0;
if (!moveToNextStreamOnEnd()){
return 0;
}
return p_current_stream->readBytes(data, len);
}
/// Returns true if active and we still have data
operator bool(){
return is_active && available()>0;
}
protected:
Vector<Stream*> input_streams;
Stream *p_current_stream = nullptr;
bool is_active = false;
/// moves to the next stream if necessary: returns true if we still have a valid stream
bool moveToNextStreamOnEnd(){
if ((p_current_stream==nullptr || availableWithTimout()) && !input_streams.empty()){
p_current_stream = input_streams[0];
input_streams.pop_front();
}
// returns true if we have a valid stream
return p_current_stream!=nullptr;
}
int availableWithTimout(){
int result = p_current_stream->available();
if (result==0){
for (int j=0; j <_timeout/10;j++){
delay(10);
result = p_current_stream->available();
if (result!=0) break;
}
}
return result;
}
};
/**
* @brief Stream to which we can apply Filters for each channel. The filter
* might change the result size!

View File

@ -549,35 +549,11 @@ template <typename T>
class NBuffer : public BaseBuffer<T> {
public:
NBuffer(int size, int count) {
filled_buffers.resize(count);
avaliable_buffers.resize(count);
write_buffer_count = 0;
buffer_count = count;
buffer_size = size;
for (int j = 0; j < count; j++) {
avaliable_buffers[j] = new SingleBuffer<T>(size);
if (avaliable_buffers[j] == nullptr) {
LOGE("Not Enough Memory for buffer %d", j);
}
}
resize(size, count);
}
virtual ~NBuffer() {
delete actual_write_buffer;
delete actual_read_buffer;
BaseBuffer<T> *ptr = getNextAvailableBuffer();
while (ptr != nullptr) {
delete ptr;
ptr = getNextAvailableBuffer();
}
ptr = getNextFilledBuffer();
while (ptr != nullptr) {
delete ptr;
ptr = getNextFilledBuffer();
}
freeMemory();
}
// reads an entry from the buffer
@ -724,7 +700,23 @@ class NBuffer : public BaseBuffer<T> {
return result;
}
void setBufferSize(int size) { buffer_size = size; }
void resize(int size, int count) {
if (buffer_size==size && buffer_count == count)
return;
freeMemory();
filled_buffers.resize(count);
avaliable_buffers.resize(count);
write_buffer_count = 0;
buffer_count = count;
buffer_size = size;
for (int j = 0; j < count; j++) {
avaliable_buffers[j] = new SingleBuffer<T>(size);
if (avaliable_buffers[j] == nullptr) {
LOGE("Not Enough Memory for buffer %d", j);
}
}
}
protected:
int buffer_size = 0;
@ -740,6 +732,25 @@ class NBuffer : public BaseBuffer<T> {
// empty constructor only allowed by subclass
NBuffer() = default;
void freeMemory() {
delete actual_write_buffer;
actual_write_buffer = nullptr;
delete actual_read_buffer;
actual_read_buffer = nullptr;
BaseBuffer<T> *ptr = getNextAvailableBuffer();
while (ptr != nullptr) {
delete ptr;
ptr = getNextAvailableBuffer();
}
ptr = getNextFilledBuffer();
while (ptr != nullptr) {
delete ptr;
ptr = getNextFilledBuffer();
}
}
void resetCurrent() {
if (actual_read_buffer != nullptr) {
actual_read_buffer->reset();

View File

@ -9,12 +9,12 @@ set (DCMAKE_CXX_FLAGS "-Werror")
include(FetchContent)
# Deactivate Emulator and Portaudio
set(ADD_ARDUINO_EMULATOR OFF CACHE BOOL "Add Arduino Emulator Library")
set(ADD_ARDUINO_EMULATOR ON CACHE BOOL "Add Arduino Emulator Library")
set(ADD_PORTAUDIO OFF CACHE BOOL "Add Portaudio Library")
# Build with arduino-audio-tools
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools )
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools )
endif()
# Build with tms
@ -36,13 +36,13 @@ endif()
# build sketch as executable
set_source_files_properties(hls.ino PROPERTIES LANGUAGE CXX)
add_executable (hls hls.ino)
add_executable (hls hls.cpp ../main.cpp )
# set preprocessor defines
target_compile_definitions(hls PUBLIC -DIS_MIN_DESKTOP -DEXIT_ON_STOP -DHELIX_PRINT)
target_compile_definitions(hls PUBLIC -DARDUINO -DIS_DESKTOP -DEXIT_ON_STOP -DHELIX_PRINT)
# specify libraries
target_link_libraries(hls arduino-audio-tools tsdemux arduino_helix)
target_link_libraries(hls arduino-audio-tools arduino_emulator tsdemux arduino_helix portaudio)

46
tests-cmake/hls/hls.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "AudioTools.h"
#include "AudioCodecs/CodecMTS.h"
#include "AudioCodecs/CodecADTS.h"
#include "AudioCodecs/CodecAACHelix.h"
#include "AudioLibs/PortAudioStream.h"
AudioInfo info(48000,2,16);
HLSStream hls_stream("NA", "NA");
// HexDumpOutput hex(Serial);
// NullStream null;
//CsvOutput<int16_t> out(Serial, 2); // Or use StdOuput
PortAudioStream out;
MTSDecoder mts;
AACDecoderADTS adts;
AACDecoderHelix aac;
EncodedAudioStream aac_stream(&out, &aac);
EncodedAudioStream adts_stream(&aac_stream, &adts);
EncodedAudioStream mts_stream(&adts_stream, &mts);
StreamCopy copier(mts_stream, hls_stream);
// Arduino Setup
void setup(void) {
//Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Info);
//hls_stream.setLogLevel(AudioLogger::Debug); // hls_stream is quite chatty at Info
//adts_stream.setLogLevel(AudioLogger::Debug);
mts_stream.setLogLevel(AudioLogger::Debug);
aac.setAudioInfoNotifications(false);
auto cfg = out.defaultConfig(TX_MODE);
cfg.copyFrom(info);
out.begin();
mts_stream.begin();
aac_stream.begin();
adts_stream.begin();
hls_stream.begin("http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_vlow/ak/bbc_world_service.m3u8");
Serial.println("playing...");
}
// Arduino loop
void loop() {
copier.copy();
}

View File

@ -9,7 +9,7 @@ StreamCopy copier(null_out, url); // copy url to decoder
void setup(){
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Debug);
AudioLogger::instance().begin(Serial, AudioLogger::Info);
// mp3 radio
url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3");
}