update cspot

This commit is contained in:
philippe44
2022-11-17 14:06:00 -08:00
parent a81d0e0513
commit 7e5f27af12
137 changed files with 6046 additions and 836 deletions

Binary file not shown.

View File

@@ -1,88 +0,0 @@
#include "BaseHTTPServer.h"
#include <sstream>
unsigned char bell::BaseHTTPServer::h2int(char c)
{
if (c >= '0' && c <= '9')
{
return ((unsigned char)c - '0');
}
if (c >= 'a' && c <= 'f')
{
return ((unsigned char)c - 'a' + 10);
}
if (c >= 'A' && c <= 'F')
{
return ((unsigned char)c - 'A' + 10);
}
return (0);
}
std::string bell::BaseHTTPServer::urlDecode(std::string str)
{
std::string encodedString = "";
char c;
char code0;
char code1;
for (int i = 0; i < str.length(); i++)
{
c = str[i];
if (c == '+')
{
encodedString += ' ';
}
else if (c == '%')
{
i++;
code0 = str[i];
i++;
code1 = str[i];
c = (h2int(code0) << 4) | h2int(code1);
encodedString += c;
}
else
{
encodedString += c;
}
}
return encodedString;
}
std::vector<std::string> bell::BaseHTTPServer::splitUrl(const std::string &url, char delimiter)
{
std::stringstream ssb(url);
std::string segment;
std::vector<std::string> seglist;
while (std::getline(ssb, segment, delimiter))
{
seglist.push_back(segment);
}
return seglist;
}
std::map<std::string, std::string> bell::BaseHTTPServer::parseQueryString(const std::string &queryString)
{
std::map<std::string, std::string> query;
auto prefixedString = "&" + queryString;
while (prefixedString.find("&") != std::string::npos)
{
auto keyStart = prefixedString.find("&");
auto keyEnd = prefixedString.find("=");
// Find second occurence of "&" in prefixedString
auto valueEnd = prefixedString.find("&", keyStart + 1);
if (valueEnd == std::string::npos)
{
valueEnd = prefixedString.size();
}
auto key = prefixedString.substr(keyStart + 1, keyEnd - 1);
auto value = prefixedString.substr(keyEnd + 1, valueEnd - keyEnd - 1);
query[key] = urlDecode(value);
prefixedString = prefixedString.substr(valueEnd);
}
return query;
}

View File

@@ -1,9 +1,9 @@
#include "BellLogger.h"
std::shared_ptr<bell::AbstractLogger> bell::bellGlobalLogger;
bell::AbstractLogger* bell::bellGlobalLogger;
void bell::setDefaultLogger() {
bell::bellGlobalLogger = std::make_shared<bell::BellLogger>();
bell::bellGlobalLogger = new bell::BellLogger();
}
void bell::enableSubmoduleLogging() {

View File

@@ -18,8 +18,8 @@ void bell::BinaryReader::close() {
}
void bell::BinaryReader::skip(size_t pos) {
uint8_t b[pos];
stream->read((uint8_t *)b, pos);
std::vector<uint8_t> b(pos);
stream->read(&b[0], pos);
}
int32_t bell::BinaryReader::readInt() {

View File

@@ -0,0 +1,174 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-7.
#include "BufferedStream.h"
#include <cstring>
BufferedStream::BufferedStream(
const std::string &taskName,
uint32_t bufferSize,
uint32_t readThreshold,
uint32_t readSize,
uint32_t readyThreshold,
uint32_t notReadyThreshold,
bool waitForReady)
: bell::Task(taskName, 4096, 5, 0) {
this->bufferSize = bufferSize;
this->readAt = bufferSize - readThreshold;
this->readSize = readSize;
this->readyThreshold = readyThreshold;
this->notReadyThreshold = notReadyThreshold;
this->waitForReady = waitForReady;
this->buf = static_cast<uint8_t *>(malloc(bufferSize));
this->bufEnd = buf + bufferSize;
reset();
}
BufferedStream::~BufferedStream() {
this->close();
free(buf);
}
void BufferedStream::close() {
this->terminate = true;
this->readSem.give(); // force a read operation
const std::lock_guard lock(runningMutex);
if (this->source)
this->source->close();
this->source = nullptr;
}
void BufferedStream::reset() {
this->bufReadPtr = this->buf;
this->bufWritePtr = this->buf;
this->readTotal = 0;
this->bufferTotal = 0;
this->readAvailable = 0;
this->terminate = false;
}
bool BufferedStream::open(const std::shared_ptr<bell::ByteStream> &stream) {
if (this->running)
this->close();
reset();
this->source = stream;
startTask();
return source.get();
}
bool BufferedStream::open(const StreamReader &newReader, uint32_t initialOffset) {
if (this->running)
this->close();
reset();
this->reader = newReader;
this->bufferTotal = initialOffset;
startTask();
return source.get();
}
bool BufferedStream::isReady() const {
return readAvailable >= readyThreshold;
}
bool BufferedStream::isNotReady() const {
return readAvailable < notReadyThreshold;
}
size_t BufferedStream::skip(size_t len) {
return read(nullptr, len);
}
size_t BufferedStream::position() {
return readTotal;
}
size_t BufferedStream::size() {
return source->size();
}
uint32_t BufferedStream::lengthBetween(uint8_t *me, uint8_t *other) {
const std::lock_guard lock(readMutex);
if (other <= me) {
// buf .... other ...... me ........ bufEnd
// buf .... me/other ........ bufEnd
return bufEnd - me;
} else {
// buf ........ me ........ other .... bufEnd
return other - me;
}
}
size_t BufferedStream::read(uint8_t *dst, size_t len) {
if (waitForReady && isNotReady()) {
while ((source || reader) && !isReady()) {} // end waiting after termination
}
if (!running && !readAvailable) {
reset();
return 0;
}
uint32_t read = 0;
uint32_t toReadTotal = std::min(readAvailable.load(), static_cast<uint32_t>(len));
while (toReadTotal > 0) {
uint32_t toRead = std::min(toReadTotal, lengthBetween(bufReadPtr, bufWritePtr));
if (dst) {
memcpy(dst, bufReadPtr, toRead);
dst += toRead;
}
readAvailable -= toRead;
bufReadPtr += toRead;
if (bufReadPtr >= bufEnd)
bufReadPtr = buf;
toReadTotal -= toRead;
read += toRead;
readTotal += toRead;
}
this->readSem.give();
return read;
}
void BufferedStream::runTask() {
const std::lock_guard lock(runningMutex);
running = true;
if (!source && reader) {
// get the initial request on the task's thread
source = reader(this->bufferTotal);
}
while (!terminate) {
if (!source)
break;
if (isReady()) {
// buffer ready, wait for any read operations
this->readSem.wait();
}
if (terminate)
break;
if (readAvailable > readAt)
continue;
// here, the buffer needs re-filling
uint32_t len;
bool wasReady = isReady();
do {
uint32_t toRead = std::min(readSize, lengthBetween(bufWritePtr, bufReadPtr));
if (!source) {
len = 0;
break;
}
len = source->read(bufWritePtr, toRead);
readAvailable += len;
bufferTotal += len;
bufWritePtr += len;
if (bufWritePtr >= bufEnd) // TODO is == enough here?
bufWritePtr = buf;
} while (len && readSize < bufferSize - readAvailable); // loop until there's no more free space in the buffer
if (!len && reader)
source = reader(bufferTotal);
else if (!len)
terminate = true;
// signal that buffer is ready for reading
if (!wasReady && isReady()) {
this->readySem.give();
}
}
source = nullptr;
reader = nullptr;
running = false;
}

View File

@@ -1,5 +1,4 @@
#ifdef BELL_USE_MBEDTLS
#include "CryptoMbedTLS.h"
#include "Crypto.h"
CryptoMbedTLS::CryptoMbedTLS()
{
@@ -224,4 +223,3 @@ std::vector<uint8_t> CryptoMbedTLS::generateVectorWithRandomData(size_t length)
return randomVector;
}
#endif

View File

@@ -1,184 +0,0 @@
#include "CryptoOpenSSL.h"
namespace
{
struct BIOFreeAll
{
void operator()(BIO *p) { BIO_free_all(p); }
};
} // namespace
CryptoOpenSSL::CryptoOpenSSL()
{
// OpenSSL init
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
this->publicKey = std::vector<uint8_t>(DH_KEY_SIZE);
this->privateKey = generateVectorWithRandomData(DH_KEY_SIZE);
}
CryptoOpenSSL::~CryptoOpenSSL()
{
if (this->dhContext != nullptr)
{
DH_free(this->dhContext);
}
}
std::vector<uint8_t> CryptoOpenSSL::base64Decode(const std::string& data)
{
// base64 in openssl is an absolute mess lmao
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
BIO *source = BIO_new_mem_buf(data.c_str(), -1); // read-only source
BIO_push(b64.get(), source);
const int maxlen = data.size() / 4 * 3 + 1;
std::vector<uint8_t> decoded(maxlen);
const int len = BIO_read(b64.get(), decoded.data(), maxlen);
decoded.resize(len);
return decoded;
}
std::string CryptoOpenSSL::base64Encode(const std::vector<uint8_t>& data)
{
// base64 in openssl is an absolute mess lmao x 2
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
// No newline mode, put all the data into sink
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
BIO *sink = BIO_new(BIO_s_mem());
BIO_push(b64.get(), sink);
BIO_write(b64.get(), data.data(), data.size());
BIO_flush(b64.get());
const char *encoded;
const long len = BIO_get_mem_data(sink, &encoded);
return std::string(encoded, len);
}
// Sha1
void CryptoOpenSSL::sha1Init()
{
SHA1_Init(&sha1Context);
}
void CryptoOpenSSL::sha1Update(const std::string& s)
{
sha1Update(std::vector<uint8_t>(s.begin(), s.end()));
}
void CryptoOpenSSL::sha1Update(const std::vector<uint8_t>& vec)
{
SHA1_Update(&sha1Context, vec.data(), vec.size());
}
std::vector<uint8_t> CryptoOpenSSL::sha1FinalBytes()
{
std::vector<uint8_t> digest(20); // 20 is 160 bits
SHA1_Final(digest.data(), &sha1Context);
return digest;
}
std::string CryptoOpenSSL::sha1Final()
{
auto digest = sha1FinalBytes();
return std::string(digest.begin(), digest.end());
}
// HMAC SHA1
std::vector<uint8_t> CryptoOpenSSL::sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message)
{
std::vector<uint8_t> digest(20); // 20 is 160 bits
auto hmacContext = HMAC_CTX_new();
HMAC_Init_ex(hmacContext, inputKey.data(), inputKey.size(), EVP_sha1(), NULL);
HMAC_Update(hmacContext, message.data(), message.size());
unsigned int resLen = 0;
HMAC_Final(hmacContext, digest.data(), &resLen);
HMAC_CTX_free(hmacContext);
return digest;
}
// AES CTR
void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* buffer, size_t nbytes)
{
// Prepare AES_KEY
auto cryptoKey = AES_KEY();
AES_set_encrypt_key(key.data(), 128, &cryptoKey);
// Needed for openssl internal cache
unsigned char ecountBuf[16] = {0};
unsigned int offsetInBlock = 0;
CRYPTO_ctr128_encrypt(
buffer,
buffer,
nbytes,
&cryptoKey,
iv.data(),
ecountBuf,
&offsetInBlock,
block128_f(AES_encrypt));
}
void CryptoOpenSSL::aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data)
{
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(ctx);
int len = 0;
EVP_DecryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key.data(), NULL);
EVP_CIPHER_CTX_set_padding(ctx, 0); // disable padding
EVP_DecryptUpdate(ctx, data.data(), &len, data.data(), data.size());
EVP_DecryptFinal_ex(ctx, data.data() + len, &len);
EVP_CIPHER_CTX_free(ctx);
}
// PBKDF2
std::vector<uint8_t> CryptoOpenSSL::pbkdf2HmacSha1(const std::vector<uint8_t>& password, const std::vector<uint8_t>& salt, int iterations, int digestSize)
{
std::vector<uint8_t> digest(digestSize);
// Generate PKDF2 digest
PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.size(),
(const unsigned char *)salt.data(), salt.size(), iterations,
digestSize, digest.data());
return digest;
}
void CryptoOpenSSL::dhInit()
{
// Free old context
if (this->dhContext != nullptr)
{
DH_free(this->dhContext);
}
this->dhContext = DH_new();
// Set prime and the generator
DH_set0_pqg(this->dhContext, BN_bin2bn(DHPrime, DH_KEY_SIZE, NULL), NULL, BN_bin2bn(DHGenerator, 1, NULL));
// Generate public and private keys and copy them to vectors
DH_generate_key(this->dhContext);
BN_bn2bin(DH_get0_pub_key(dhContext), this->publicKey.data());
}
std::vector<uint8_t> CryptoOpenSSL::dhCalculateShared(const std::vector<uint8_t>& remoteKey)
{
auto sharedKey = std::vector<uint8_t>(DH_KEY_SIZE);
// Convert remote key to bignum and compute shared key
auto pubKey = BN_bin2bn(&remoteKey[0], DH_KEY_SIZE, NULL);
DH_compute_key(sharedKey.data(), pubKey, this->dhContext);
BN_free(pubKey);
return sharedKey;
}
// Random stuff
std::vector<uint8_t> CryptoOpenSSL::generateVectorWithRandomData(size_t length)
{
std::vector<uint8_t> randomVec(length);
if(RAND_bytes(randomVec.data(), length) == 0)
{
}
return randomVec;
}

View File

@@ -1,8 +0,0 @@
#include "DecoderGlobals.h"
std::shared_ptr<bell::DecodersInstance> bell::decodersInstance;
void bell::createDecoders()
{
bell::decodersInstance = std::make_shared<bell::DecodersInstance>();
}

View File

@@ -55,14 +55,14 @@ std::vector<std::string> bell::HTTPServer::splitUrl(const std::string &url,
void bell::HTTPServer::registerHandler(RequestType requestType,
const std::string &routeUrl,
httpHandler handler) {
httpHandler handler,
bool readBodyToStr) {
if (routes.find(routeUrl) == routes.end()) {
routes.insert({routeUrl, std::vector<HTTPRoute>()});
}
this->routes[routeUrl].push_back(HTTPRoute{
.requestType = requestType,
.handler = handler,
});
this->routes[routeUrl].push_back(HTTPRoute{.requestType = requestType,
.handler = handler,
.readBodyToStr = readBodyToStr});
}
void bell::HTTPServer::listen() {
@@ -126,8 +126,8 @@ void bell::HTTPServer::listen() {
HTTPConnection conn = { .buffer = std::vector<uint8_t>(128),
.httpMethod = "" };
this->connections.insert({ newFd, conn });
}
this->connections.insert({newFd, conn});
}
/* Service other sockets and update set & max */
maxfd = sockfd;
@@ -140,8 +140,7 @@ void bell::HTTPServer::listen() {
FD_CLR(fd, &activeFdSet);
this->connections.erase(
it++); // or "it = m.erase(it)" since C++11
}
else {
} else {
if (fd != sockfd && FD_ISSET(fd, &readFdSet)) {
/* Data arriving on an already-connected socket. */
readFromClient(fd);
@@ -156,6 +155,10 @@ void bell::HTTPServer::listen() {
void bell::HTTPServer::readFromClient(int clientFd) {
HTTPConnection &conn = this->connections[clientFd];
if (conn.headersRead) {
return;
}
conn.fd = clientFd;
int nbytes = recv(clientFd, (char*) &conn.buffer[0], conn.buffer.size(), 0);
if (nbytes < 0) {
@@ -165,49 +168,55 @@ void bell::HTTPServer::readFromClient(int clientFd) {
} else if (nbytes == 0) {
this->closeConnection(clientFd);
} else {
conn.currentLine +=
std::string(conn.buffer.data(), conn.buffer.data() + nbytes);
READBODY:
if (!conn.isReadingBody) {
while (conn.currentLine.find("\r\n") != std::string::npos) {
auto line =
conn.currentLine.substr(0, conn.currentLine.find("\r\n"));
conn.currentLine = conn.currentLine.substr(
conn.currentLine.find("\r\n") + 2, conn.currentLine.size());
if (line.find("GET ") != std::string::npos ||
line.find("POST ") != std::string::npos ||
line.find("OPTIONS ") != std::string::npos) {
conn.httpMethod = line;
}
// append buffer to partialBuffer
conn.partialBuffer.insert(conn.partialBuffer.end(), conn.buffer.begin(),
conn.buffer.begin() + nbytes);
auto stringifiedBuffer =
std::string(conn.partialBuffer.data(),
conn.partialBuffer.data() + conn.partialBuffer.size());
if (line.find("Content-Length: ") != std::string::npos) {
conn.contentLength =
std::stoi(line.substr(16, line.size() - 1));
}
// detect hostname for captive portal
if (line.find("Host: connectivitycheck.gstatic.com") !=
std::string::npos) {
conn.isCaptivePortal = true;
BELL_LOG(info, "http", "Captive portal request detected");
}
if (line.size() == 0) {
if (conn.contentLength != 0) {
conn.isReadingBody = true;
goto READBODY;
READBODY:
auto readSize = 0;
while (stringifiedBuffer.find("\r\n") != std::string::npos) {
auto line =
stringifiedBuffer.substr(0, stringifiedBuffer.find("\r\n"));
readSize += stringifiedBuffer.find("\r\n") + 2;
stringifiedBuffer = stringifiedBuffer.substr(
stringifiedBuffer.find("\r\n") + 2, stringifiedBuffer.size());
if (line.find("GET ") != std::string::npos ||
line.find("POST ") != std::string::npos ||
line.find("OPTIONS ") != std::string::npos) {
conn.httpMethod = line;
}
if (line.find("Content-Length: ") != std::string::npos) {
conn.contentLength =
std::stoi(line.substr(16, line.size() - 1));
}
// detect hostname for captive portal
if (line.find("Host: connectivitycheck.gstatic.com") !=
std::string::npos) {
conn.isCaptivePortal = true;
BELL_LOG(info, "http", "Captive portal request detected");
}
if (line.size() == 0) {
if (conn.contentLength != 0) {
// conn.isReadingBody = true;
// remove readSize bytes from partialBuffer
conn.partialBuffer.erase(conn.partialBuffer.begin(),
conn.partialBuffer.begin() +
readSize);
findAndHandleRoute(conn);
// goto READBODY;
} else {
if (!conn.isCaptivePortal) {
findAndHandleRoute(conn);
} else {
if (!conn.isCaptivePortal) {
findAndHandleRoute(conn.httpMethod,
conn.currentLine, clientFd);
} else {
this->redirectCaptivePortal(clientFd);
}
this->redirectCaptivePortal(clientFd);
}
}
}
} else {
if (conn.currentLine.size() >= conn.contentLength) {
findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);
}
}
}
}
@@ -367,8 +376,11 @@ bell::HTTPServer::parseQueryString(const std::string &queryString) {
return query;
}
void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
int connectionFd) {
void bell::HTTPServer::findAndHandleRoute(HTTPConnection &conn) {
conn.headersRead = true;
auto connectionFd = conn.fd;
// auto body = conn.partialBuffer;
auto url = conn.httpMethod;
std::map<std::string, std::string> pathParams;
std::map<std::string, std::string> queryParams;
@@ -451,18 +463,36 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
}
if (matches) {
if (body.find('&') != std::string::npos) {
queryParams = this->parseQueryString(body);
auto reader = std::make_unique<RequestBodyReader>(
conn.contentLength, conn.fd, conn.partialBuffer);
auto body = std::string();
if (route.readBodyToStr) {
body.resize(conn.contentLength);
auto read = 0;
while (read < conn.contentLength) {
auto readBytes = reader->read(
body.data() + read, conn.contentLength - read);
read += readBytes;
}
body.resize(read);
if (body.find('&') != std::string::npos) {
queryParams = this->parseQueryString(body);
}
}
HTTPRequest req = {.urlParams = pathParams,
.queryParams = queryParams,
.body = body,
.url = path,
.handlerId = 0,
.connection = connectionFd};
std::unique_ptr<HTTPRequest> req =
std::make_unique<HTTPRequest>();
req->queryParams = queryParams;
req->urlParams = pathParams;
req->url = path;
req->body = body;
req->connection = connectionFd;
req->handlerId = 0;
req->responseReader = std::move(reader);
req->contentLength = conn.contentLength;
route.handler(req);
route.handler(std::move(req));
return;
}
}

View File

@@ -8,13 +8,19 @@
#include <cstring>
#include <stdlib.h>
#include <sys/types.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include "win32shim.h"
#else
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netinet/tcp.h>
#endif
#include <sstream>
#include <fstream>
#include <netinet/tcp.h>
bell::HTTPStream::HTTPStream()
{

View File

@@ -1,4 +1,4 @@
#include "platform/TLSSocket.h"
#include "TLSSocket.h"
/**
* Platform TLSSocket implementation for the mbedtls

View File

@@ -0,0 +1,37 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
#include "AACDecoder.h"
AACDecoder::AACDecoder() {
aac = AACInitDecoder();
pcmData = (int16_t *)malloc(AAC_MAX_NSAMPS * AAC_MAX_NCHANS * sizeof(int16_t));
}
AACDecoder::~AACDecoder() {
AACFreeDecoder(aac);
free(pcmData);
}
bool AACDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
frame.sampRateCore = (int)sampleRate;
frame.nChans = channelCount;
frame.bitsPerSample = bitDepth;
return AACSetRawBlockParams(aac, 0, &frame) == 0;
}
uint8_t *AACDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
if (!inData)
return nullptr;
int status = AACDecode(
aac,
static_cast<unsigned char **>(&inData),
reinterpret_cast<int *>(&inLen),
static_cast<short *>(this->pcmData));
AACGetLastFrameInfo(aac, &frame);
if (status != ERR_AAC_NONE) {
lastErrno = status;
return nullptr;
}
outLen = frame.outputSamps * sizeof(int16_t);
return (uint8_t *)pcmData;
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
#include "ALACDecoder.h"
ALACDecoder::ALACDecoder() {
// aac = AACInitDecoder();
// pcmData = (int16_t *)malloc(AAC_MAX_NSAMPS * AAC_MAX_NCHANS * sizeof(int16_t));
}
ALACDecoder::~ALACDecoder() {
// AACFreeDecoder(aac);
// free(pcmData);
}
bool ALACDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
// frame.sampRateCore = (int)sampleRate;
// frame.nChans = channelCount;
// frame.bitsPerSample = bitDepth;
// return AACSetRawBlockParams(aac, 0, &frame) == 0;
}
uint8_t *ALACDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
// if (!inData)
// return nullptr;
// int status = AACDecode(
// aac,
// static_cast<unsigned char **>(&inData),
// reinterpret_cast<int *>(&inLen),
// static_cast<short *>(this->pcmData));
// AACGetLastFrameInfo(aac, &frame);
// if (status != ERR_AAC_NONE) {
// lastErrno = status;
// return nullptr;
// }
// outLen = frame.outputSamps * sizeof(int16_t);
// return (uint8_t *)pcmData;
}

View File

@@ -0,0 +1,71 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
#include "AudioCodecs.h"
#include <map>
#ifdef BELL_CODEC_AAC
#include "AACDecoder.h"
static std::shared_ptr<AACDecoder> codecAac;
#endif
#ifdef BELL_CODEC_MP3
#include "MP3Decoder.h"
static std::shared_ptr<MP3Decoder> codecMp3;
#endif
#ifdef BELL_CODEC_VORBIS
#include "VorbisDecoder.h"
static std::shared_ptr<VorbisDecoder> codecVorbis;
#endif
#ifdef BELL_CODEC_OPUS
#include "OPUSDecoder.h"
static std::shared_ptr<OPUSDecoder> codecOpus;
#endif
std::map<AudioCodec, std::shared_ptr<BaseCodec>> customCodecs;
std::shared_ptr<BaseCodec> AudioCodecs::getCodec(AudioCodec type) {
if (customCodecs.find(type) != customCodecs.end())
return customCodecs[type];
switch (type) {
#ifdef BELL_CODEC_AAC
case AudioCodec::AAC:
if (codecAac)
return codecAac;
codecAac = std::make_shared<AACDecoder>();
return codecAac;
#endif
#ifdef BELL_CODEC_MP3
case AudioCodec::MP3:
if (codecMp3)
return codecMp3;
codecMp3 = std::make_shared<MP3Decoder>();
return codecMp3;
#endif
#ifdef BELL_CODEC_VORBIS
case AudioCodec::VORBIS:
if (codecVorbis)
return codecVorbis;
codecVorbis = std::make_shared<VorbisDecoder>();
return codecVorbis;
#endif
#ifdef BELL_CODEC_OPUS
case AudioCodec::OPUS:
if (codecOpus)
return codecOpus;
codecOpus = std::make_shared<OPUSDecoder>();
return codecOpus;
#endif
default:
return nullptr;
}
}
std::shared_ptr<BaseCodec> AudioCodecs::getCodec(BaseContainer *container) {
return getCodec(container->codec);
}
void AudioCodecs::addCodec(AudioCodec type, const std::shared_ptr<BaseCodec> &codec) {
customCodecs[type] = codec;
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
#include "BaseCodec.h"
bool BaseCodec::setup(BaseContainer *container) {
return this->setup(container->sampleRate, container->channelCount, container->bitDepth);
}
uint8_t *BaseCodec::decode(BaseContainer *container, uint32_t &outLen) {
uint32_t len;
auto *data = container->readSample(len);
return decode(data, len, outLen);
}

View File

@@ -0,0 +1,8 @@
#include "DecoderGlobals.h"
bell::DecodersInstance* bell::decodersInstance;
void bell::createDecoders()
{
bell::decodersInstance = new bell::DecodersInstance();
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
#include "MP3Decoder.h"
MP3Decoder::MP3Decoder() {
mp3 = MP3InitDecoder();
pcmData = (int16_t *)malloc(MAX_NSAMP * MAX_NGRAN * MAX_NCHAN * sizeof(int16_t));
}
MP3Decoder::~MP3Decoder() {
MP3FreeDecoder(mp3);
free(pcmData);
}
bool MP3Decoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
return true;
}
uint8_t *MP3Decoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
if (!inData)
return nullptr;
int status = MP3Decode(
mp3,
static_cast<unsigned char **>(&inData),
reinterpret_cast<int *>(&inLen),
static_cast<short *>(this->pcmData),
/* useSize */ 0);
MP3GetLastFrameInfo(mp3, &frame);
if (status != ERR_MP3_NONE) {
lastErrno = status;
return nullptr;
}
outLen = frame.outputSamps * sizeof(int16_t);
return (uint8_t *)pcmData;
}

View File

@@ -0,0 +1,46 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
#include "OPUSDecoder.h"
#include "opus.h"
#define MAX_FRAME_SIZE 6 * 960
#define MAX_CHANNELS 2
// dummy structure, just to get access to channels
struct OpusDecoder {
int dummy1;
int dummy2;
int channels;
};
OPUSDecoder::OPUSDecoder() {
opus = nullptr;
pcmData = (int16_t *)malloc(MAX_FRAME_SIZE * MAX_CHANNELS * sizeof(int16_t));
}
OPUSDecoder::~OPUSDecoder() {
if (opus)
opus_decoder_destroy(opus);
free(pcmData);
}
bool OPUSDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
if (opus)
opus_decoder_destroy(opus);
opus = opus_decoder_create((int32_t)sampleRate, channelCount, &lastErrno);
return !lastErrno;
}
uint8_t *OPUSDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
if (!inData)
return nullptr;
outLen = opus_decode(
opus,
static_cast<unsigned char *>(inData),
static_cast<int32_t>(inLen),
pcmData,
MAX_FRAME_SIZE,
false);
outLen *= opus->channels * sizeof(int16_t);
return (uint8_t *)pcmData;
}

View File

@@ -0,0 +1,120 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
#include "VorbisDecoder.h"
#include "AudioCodecs.h"
extern "C" {
extern vorbis_dsp_state *vorbis_dsp_create(vorbis_info *vi);
extern void vorbis_dsp_destroy(vorbis_dsp_state *v);
extern int vorbis_dsp_restart(vorbis_dsp_state *v);
extern int vorbis_dsp_headerin(vorbis_info *vi, vorbis_comment *vc, ogg_packet *op);
extern int vorbis_dsp_synthesis(vorbis_dsp_state *vd, ogg_packet *op, int decodep);
extern int vorbis_dsp_pcmout(vorbis_dsp_state *v, ogg_int16_t *pcm, int samples);
extern int vorbis_dsp_read(vorbis_dsp_state *v, int samples);
}
#define VORBIS_BUF_SAMPLES 1024
#define VORBIS_BUF_CHANNELS 2
VorbisDecoder::VorbisDecoder() {
vi = new vorbis_info;
vorbis_info_init(vi);
vc = new vorbis_comment;
vorbis_comment_init(vc);
op.packet = new ogg_reference;
op.packet->buffer = new ogg_buffer;
op.packet->buffer->refcount = 0;
op.packet->buffer->ptr.owner = nullptr;
op.packet->buffer->ptr.next = nullptr;
op.packet->begin = 0;
op.packet->next = nullptr;
op.granulepos = -1;
op.packetno = 10;
pcmData = (int16_t *)malloc(VORBIS_BUF_SAMPLES * VORBIS_BUF_CHANNELS * sizeof(uint16_t));
}
VorbisDecoder::~VorbisDecoder() {
vorbis_info_clear(vi);
vorbis_comment_clear(vc);
if (vd)
vorbis_dsp_destroy(vd);
vd = nullptr;
free(pcmData);
}
bool VorbisDecoder::setup(BaseContainer *container) {
uint32_t setupLen;
uint8_t *setup = container->getSetupData(setupLen, AudioCodec::VORBIS);
if (!setup)
return false;
op.b_o_s = true; // mark this page as beginning of stream
uint32_t bytesLeft = setupLen - 1; // minus header count length (8 bit)
std::vector<uint32_t> headers(setup[0]);
for (uint8_t i = 0; i < setup[0]; i++) {
uint8_t *sizeByte = (uint8_t *)setup + 1 + i;
headers[i] = 0;
while (*sizeByte == 255) {
headers[i] += *(sizeByte++);
bytesLeft--;
}
headers[i] += *sizeByte;
bytesLeft--;
}
// parse all headers from the setup data
for (const auto &headerSize : headers) {
setPacket(setup + setupLen - bytesLeft, headerSize);
bytesLeft -= headerSize;
lastErrno = vorbis_dsp_headerin(vi, vc, &op);
if (lastErrno < 0) {
bytesLeft = 0;
break;
}
}
// parse last header, not present in header table (seems to happen for MP4 containers)
if (bytesLeft) {
setPacket(setup + setupLen - bytesLeft, bytesLeft);
lastErrno = vorbis_dsp_headerin(vi, vc, &op);
}
// disable BOS to allow reading audio data
op.b_o_s = false;
// set up the codec
if (vd)
vorbis_dsp_restart(vd);
else
vd = vorbis_dsp_create(vi);
return !lastErrno;
}
bool VorbisDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
// manual setup is not allowed
return false;
}
uint8_t *VorbisDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
if (!inData || !vi)
return nullptr;
setPacket(inData, inLen);
// sources:
// - vorbisfile.c:556
// - vorbisfile.c:1557
lastErrno = vorbis_dsp_synthesis(vd, &op, 1);
if (lastErrno < 0)
return nullptr;
int samples = vorbis_dsp_pcmout(vd, pcmData, VORBIS_BUF_SAMPLES);
outLen = samples;
if (samples) {
if (samples > 0) {
vorbis_dsp_read(vd, samples);
outLen = samples * 2 * vi->channels;
}
}
return (uint8_t *)pcmData;
}
void VorbisDecoder::setPacket(uint8_t *inData, uint32_t inLen) const {
op.packet->buffer->data = static_cast<unsigned char *>(inData);
op.packet->buffer->size = static_cast<long>(inLen);
op.packet->length = static_cast<long>(inLen);
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-15.
#include "AudioContainers.h"
#include "Mpeg4Container.h"
#ifdef _WIN32
#include "win32shim.h"
#endif
std::unique_ptr<BaseContainer> AudioContainers::create(const char *mimeType) {
char *type = strchr((char *)mimeType, '/');
if (!type || *(++type) == '\0')
return nullptr;
if (strncasecmp(type, "mp4", 3) == 0)
return std::make_unique<Mpeg4Container>();
return nullptr;
}

View File

@@ -0,0 +1,78 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-7.
#include "BaseContainer.h"
void BaseContainer::feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) {
this->reader = std::make_unique<bell::BinaryReader>(stream);
this->source = stream;
this->pos = position;
}
// TODO move source stream reading here, and set closed = true when stream ends
uint8_t BaseContainer::readUint8() {
pos += 1;
return reader->readByte();
}
uint16_t BaseContainer::readUint16() {
pos += 2;
return reader->readShort();
}
uint32_t BaseContainer::readUint24() {
uint8_t b[3];
readBytes(b, 3);
return static_cast<int32_t>((b[2]) | (b[1] << 8) | (b[0] << 16));
}
uint32_t BaseContainer::readUint32() {
pos += 4;
return reader->readUInt();
}
uint64_t BaseContainer::readUint64() {
pos += 8;
return reader->readLong();
}
uint32_t BaseContainer::readVarint32() {
uint8_t b = readUint8();
uint32_t result = b & 0x7f;
while (b & 0b10000000) {
b = readUint8();
result <<= 7;
result |= b & 0x7f;
}
return result;
}
uint32_t BaseContainer::readBytes(uint8_t *dst, uint32_t num) {
if (!num)
return 0;
uint32_t len, total = 0;
do {
if (dst) {
len = source->read(dst, num);
dst += len; // increment destination pointer
} else {
len = source->skip(num);
}
total += len; // increment total read count
pos += len; // increment absolute source position
num -= len; // decrement bytes left to read
} while (len && num);
if (!len) // source->read() returned 0, it's closed
closed = true;
return len;
}
uint32_t BaseContainer::skipBytes(uint32_t num) {
return readBytes(nullptr, num);
}
uint32_t BaseContainer::skipTo(uint32_t offset) {
if (offset <= pos)
return 0;
return readBytes(nullptr, offset - pos);
}

View File

@@ -0,0 +1,359 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-8.
#include "Mpeg4Container.h"
#include "AudioCodecs.h"
#include "Mpeg4Atoms.h"
#include "Mpeg4Types.h"
Mpeg4Container::~Mpeg4Container() {
freeAll();
this->source->close();
}
void Mpeg4Container::feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) {
BaseContainer::feed(stream, position);
if (isParsed) {
// this is needed to support seeking backwards, as goToData() always moves forward only
setCurrentFragment();
isInData = false;
goToData();
}
}
bool Mpeg4Container::parse() {
freeAll();
if (pos != 0) {
isParsed = false;
return false;
}
uint32_t size;
AtomType type;
uint32_t moovEnd = 0, mdiaEnd = 0;
bool parsed = false, error = false, hasMoov = false, hasHdlr = false;
while (!parsed && !error && !closed) {
readAtomHeader(size, (uint32_t &)type);
switch (type) {
/*case AtomType::ATOM_FTYP:
readBytes(mediaBrand, 4);
mediaBrand[4] = '\0';
skipBytes(size - 4);
break;*/
case AtomType::ATOM_MVEX:
case AtomType::ATOM_TRAK:
case AtomType::ATOM_MINF:
case AtomType::ATOM_STBL:
// this causes the next iteration to read the next direct child atom
continue;
case AtomType::ATOM_MOOV:
moovEnd = pos + size;
hasMoov = true;
continue;
case AtomType::ATOM_MDIA:
mdiaEnd = pos + size;
continue;
case AtomType::ATOM_TREX:
readTrex();
break;
case AtomType::ATOM_TKHD:
skipBytes(12);
if (audioTrackId == -1) {
audioTrackId = (int8_t)readUint32();
skipBytes(size - 16);
} else {
// new track header, but audio track already found
skipTo(moovEnd);
}
break;
case AtomType::ATOM_MDHD:
skipBytes(12);
timescale = readUint32();
totalDuration = readUint32();
totalDurationPresent = true;
durationMs = totalDuration * 1000LL / timescale;
if (!sampleRate)
sampleRate = timescale;
hasHdlr = false;
skipBytes(size - 20);
break;
case AtomType::ATOM_HDLR:
if (hasHdlr) {
skipBytes(size);
continue;
}
hasHdlr = true;
skipBytes(8);
if (readUint32() != (uint32_t)AtomType::ATOM_SOUN) {
skipTo(mdiaEnd); // skip the rest of mdia atom
audioTrackId = -1; // unset the track ID, so the next tkhd can set it
} else {
skipBytes(size - 12);
}
break;
case AtomType::ATOM_STSD:
readStsd();
break;
case AtomType::ATOM_STTS:
readStts();
break;
case AtomType::ATOM_STSC:
readStsc();
break;
case AtomType::ATOM_STCO:
readStco();
break;
case AtomType::ATOM_STSZ:
readStsz();
break;
case AtomType::ATOM_SIDX:
readSidx(size);
break;
case AtomType::ATOM_MOOF:
case AtomType::ATOM_MDAT:
// the track can be accessed randomly if all the tables are set before parsing the first fragment
isSeekable = fragmentsLen || (chunksLen && chunkOffsetsLen && samplesLen && sampleSizesLen);
if (type == AtomType::ATOM_MOOF) {
// this will seek to the start of mdat header
error = !parseMoof(size);
} else {
// pos already points to sample data
isInData = true;
}
parsed = true;
break;
default:
// ignore unknown atoms
skipBytes(size);
break;
}
}
if (sampleDescLen) {
codec = getCodec(sampleDesc);
}
if (!hasMoov || audioTrackId == -1 || codec == AudioCodec::UNKNOWN) {
// this is not a progressive MP4, can't be played
// or has no audio tracks
// or has an unknown audio codec
freeAll();
isParsed = false;
return false;
}
if (isInData) {
// [pos] points to mdat, create a dummy fragment for it
createFragment()->duration = totalDuration;
}
isParsed = !error && !closed;
setCurrentFragment();
setCurrentSample();
return isParsed;
}
bool Mpeg4Container::parseMoof(uint32_t moofSize) {
freeFragment();
uint32_t size;
AtomType type;
uint32_t moofOffset = pos - 8;
uint32_t moofEnd = pos + moofSize;
uint32_t trafEnd = 0;
bool hasFragment = false;
Mpeg4Fragment *fragment;
for (fragment = fragments; fragment < fragments + fragmentsLen; fragment++) {
if (isInFragment(fragment, pos)) {
hasFragment = true;
break;
}
}
while (pos < moofEnd) {
readAtomHeader(size, (uint32_t &)type);
switch (type) {
case AtomType::ATOM_TRAF:
trafEnd = pos + size;
continue;
case AtomType::ATOM_TFHD:
readTfhd(trafEnd, moofOffset);
break;
case AtomType::ATOM_TRUN:
readTrun(size, moofOffset);
break;
default:
skipBytes(size);
break;
}
}
// this moof is not in the fragments table
if (!hasFragment) {
fragment = createFragment();
}
if (!totalDurationPresent) {
// total duration was not found or a new fragment was created
uint32_t duration = 0;
for (Mpeg4SampleRange *sr = samples; sr < samples + samplesLen; sr++) {
duration += sr->count * sr->duration;
}
fragment->duration = duration;
totalDuration += duration;
durationMs = totalDuration * 1000LL / timescale;
}
isFragmented = true;
return true;
}
int32_t Mpeg4Container::getLoadingOffset(uint32_t timeMs) {
if (!isParsed)
return SAMPLE_NOT_LOADED;
if (!isSeekable)
return SAMPLE_NOT_SEEKABLE;
// timeScaled - specified time in the media time coordinate system
uint64_t timeScaled = (uint64_t)timeMs * timescale / 1000LL;
uint64_t timeAbs = 0;
Mpeg4Fragment *fragment = fragments;
for (; fragment < fragments + fragmentsLen; fragment++) {
timeAbs += fragment->duration; // timeAbs holds the fragment end time
if (timeScaled < timeAbs) {
timeAbs -= fragment->duration; // set timeAbs to fragment start time
break;
}
}
if (!fragment)
return SAMPLE_NOT_FOUND;
if (fragment != curFragment)
return (int32_t)fragment->start;
// get the position in bytes
return (int32_t)findSample((int64_t)timeScaled, -1, timeAbs);
}
bool Mpeg4Container::goToData() {
if (!isParsed || !curFragment)
return false;
if (isInData)
return true;
uint32_t size;
AtomType type;
if (pos == curFragment->start || pos >= curFragment->end) {
// fragment ended, or a new one just loaded
while (pos >= curFragment->end && curFragment < fragments + fragmentsLen - 1) {
// skip to the next fragment header
curFragment++;
} // else, no more **loaded** fragments
if (pos < curFragment->start && !skipTo(curFragment->start))
return false;
// [pos] is either a fragment header, EOF or unknown data
readAtomHeader(size, (uint32_t &)type);
if (type == AtomType::ATOM_MOOF) {
// fragment header found, try to parse it
parseMoof(size);
// update [curFragment]
setCurrentFragment();
// read mdat header
readAtomHeader(size, (uint32_t &)type);
}
if (type != AtomType::ATOM_MDAT)
return false;
} else if (pos >= curChunk.end) {
// chunk ended, but still in [curFragment]
if (!curChunk.nextStart) // no more chunks but fragment not ended ??
return false;
if (pos != curChunk.nextStart && !skipTo(curChunk.nextStart))
return false;
} /* else {
readAtomHeader(size, (uint32_t &)type);
return false;
}*/
// update [isInData], [curChunk] and [curSampleSize]
setCurrentSample();
if (pos < curChunk.start) {
// chunk not started yet, probably a multi-track movie
if (!skipTo(curChunk.start))
return false;
// update [isInData], [curChunk] and [curSampleSize]
setCurrentSample();
}
return true;
}
bool Mpeg4Container::seekTo(uint32_t timeMs) {
if (!isParsed || !isSeekable)
return false;
// try to go to nearest mdat data
if (!goToData())
return false;
uint32_t offset = getLoadingOffset(timeMs);
// check if the required [offset] is in the currently loaded fragment
// - if it is, [offset] points to the required sample
// - if it isn't, [offset] points to a moof header
if (!isInFragment(curFragment, offset)) {
// try to seek to moof header, fail if not possible
if (offset != pos && !skipTo(offset))
return false;
// [pos] points to a moof header
isInData = false;
// parse the just loaded atom header (pos >= curFragment->end)
if (!goToData())
return false;
// get the actual sample's offset
offset = getLoadingOffset(timeMs);
// ...or give up if still not loaded
if (!isInFragment(curFragment, offset))
return false;
}
if (!isInData) // something is really not ok
return false;
// [pos] points to mdat data
// [offset] points to the required sample
if (!skipTo(offset))
return false;
// update the current chunk range and sample sizes
setCurrentSample();
return true;
}
int32_t Mpeg4Container::getCurrentTimeMs() {
if (!curFragment || !isParsed)
return 0;
int64_t time = 0;
// get time offset of the current fragment
Mpeg4Fragment *f = fragments;
while (f != curFragment) {
time += (f++)->duration;
}
time = findSample(-1, (int32_t)pos, time);
if (time < 0)
return (int32_t)time;
return (int32_t)(time * 1000LL / timescale);
}
uint8_t *Mpeg4Container::readSample(uint32_t &len) {
if (!curFragment || !isParsed)
return nullptr;
if (!sampleData) {
allocSampleData();
}
// go to mdat
if (!isInData && !goToData())
return nullptr;
len = *curSampleSize;
len = readBytes(sampleData, len);
skipBytes(*curSampleSize - len); // skip the rest if something went wrong
if (sampleSizesLen > 1)
curSampleSize++;
if (pos >= curChunk.end) {
// chunk ended, make goToData() read the next one
isInData = false;
}
return sampleData;
}
uint8_t *Mpeg4Container::getSetupData(uint32_t &len, AudioCodec matchCodec) {
for (SampleDescription *desc = sampleDesc; desc < sampleDesc + sampleDescLen; desc++) {
if (matchCodec != getCodec(desc))
continue;
len = desc->dataLength;
return desc->data;
}
return nullptr;
}

View File

@@ -0,0 +1,171 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-10.
#include "BellUtils.h"
#include "Mpeg4Container.h"
#include "Mpeg4Types.h"
using namespace bell;
/** Populate [chunks] using the Sample-to-chunk Table */
void Mpeg4Container::readStsc() {
skipBytes(4); // skip version and flags
chunksLen = readUint32();
chunks = (Mpeg4ChunkRange *)malloc(chunksLen * sizeof(Mpeg4ChunkRange));
for (uint32_t i = 0; i < chunksLen; i++) {
chunks[i].count = readUint32();
chunks[i].samples = readUint32();
chunks[i].sampleDescriptionId = readUint32();
if (i > 0) {
chunks[i - 1].count = chunks[i].count - chunks[i - 1].count;
}
}
if (chunkOffsetsLen) {
chunks[chunksLen - 1].count = chunkOffsetsLen - chunks[chunksLen - 1].count + 1;
}
}
/** Populate [chunkOffsets] using the Chunk Offset Table */
void Mpeg4Container::readStco() {
skipBytes(4); // skip version and flags
chunkOffsetsLen = readUint32();
chunkOffsets = (Mpeg4ChunkOffset *)malloc(chunkOffsetsLen * sizeof(Mpeg4ChunkOffset));
for (uint32_t i = 0; i < chunkOffsetsLen; i++) {
chunkOffsets[i] = readUint32();
}
if (chunksLen) {
chunks[chunksLen - 1].count = chunkOffsetsLen - chunks[chunksLen - 1].count + 1;
}
}
/** Populate [samples] using the Time-to-sample Table */
void Mpeg4Container::readStts() {
skipBytes(4); // skip version and flags
samplesLen = readUint32();
samples = (Mpeg4SampleRange *)malloc(samplesLen * sizeof(Mpeg4SampleRange));
for (uint32_t i = 0; i < samplesLen; i++) {
samples[i].count = readUint32();
samples[i].duration = readUint32();
}
}
/** Populate [sampleSizes] using the Sample Size Table */
void Mpeg4Container::readStsz() {
skipBytes(4); // skip version and flags
uint32_t sampleSize = readUint32();
sampleSizesLen = readUint32();
if (sampleSize) {
sampleSizesLen = 1;
}
sampleSizes = (Mpeg4SampleSize *)malloc(sampleSizesLen * sizeof(Mpeg4SampleSize));
if (sampleSize) {
sampleSizes[0] = sampleSize;
if (sampleSize > sampleSizeMax)
sampleSizeMax = sampleSize;
return;
}
for (uint32_t i = 0; i < sampleSizesLen; i++) {
sampleSize = readUint32();
if (sampleSize > sampleSizeMax)
sampleSizeMax = sampleSize;
sampleSizes[i] = sampleSize;
}
// reallocate sampleData if the max size changes
allocSampleData();
}
/** Populate [sampleDesc] using the Sample Description Table */
void Mpeg4Container::readStsd() {
// Helpful resources:
// - STSD atom structure - ISO/IEC 14496-1 (page 277) - seems to cover QT desc ver.0
// - ESDS atom structure - ISO/IEC 14496-1 (page 28)
freeAndNull((void *&)sampleDesc);
skipBytes(4); // skip version and flags
sampleDescLen = readUint32();
sampleDesc = (SampleDescription *)malloc(sampleDescLen * sizeof(SampleDescription));
for (SampleDescription *desc = sampleDesc; desc < sampleDesc + sampleDescLen; desc++) {
uint32_t entryEnd = readUint32() - 4 + pos;
uint32_t esdsEnd = entryEnd;
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-BBCHHGBH
// General Structure of a Sample Description
desc->format = (AudioSampleFormat)readUint32();
desc->mp4aObjectType = MP4AObjectType::UNDEFINED;
desc->mp4aProfile = MP4AProfile::UNDEFINED;
skipBytes(6); // reserved
desc->dataReferenceIndex = readUint16();
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-75770
// Sound Sample Description (Version 0)
uint16_t version = readUint16();
skipBytes(6); // skip Revision level(2), Vendor(4)
channelCount = readUint16();
bitDepth = readUint16();
skipBytes(4); // skip Compression ID(2), Packet size(2)
sampleRate = readUint16();
skipBytes(2); // decimal part of sample rate
if (version >= 1) {
// Sound Sample Description (Version 1)
skipBytes(16); // skip Samples per packet(4), Bytes per packet(4), Bytes per frame(4), Bytes per sample(4)
}
// read the child atom
uint32_t atomSize;
AtomType atomType;
readAtomHeader(atomSize, (uint32_t &)atomType);
if (atomType == AtomType::ATOM_WAVE) {
do {
readAtomHeader(atomSize, (uint32_t &)atomType);
if (atomType == AtomType::ATOM_ESDS) {
esdsEnd = pos + atomSize;
break;
}
skipBytes(atomSize);
} while (pos < entryEnd);
if (pos >= entryEnd) // something went wrong
continue;
}
if (atomType != AtomType::ATOM_ESDS) {
desc->dataType = (uint32_t)atomType;
desc->dataLength = atomSize;
desc->data = (uint8_t *)malloc(desc->dataLength);
readBytes(desc->data, desc->dataLength);
continue;
}
// read ESDS
skipBytes(4); // skip esds flags
while (pos < esdsEnd) {
uint8_t tag = readUint8();
uint32_t size = readVarint32();
uint8_t flags;
switch (tag) {
case 0x03: // ES_Descriptor
skipBytes(2);
flags = readUint8();
if (flags & 0b10000000)
skipBytes(2);
if (flags & 0b01000000)
skipBytes(readUint8());
if (flags & 0b00100000)
skipBytes(2);
break;
case 0x04: // DecoderConfigDescriptor
desc->mp4aObjectType = (MP4AObjectType)readUint8();
skipBytes(12);
break;
case 0x05: // DecoderSpecificInfo
if (desc->mp4aObjectType == MP4AObjectType::MP4A) {
desc->mp4aProfile = (MP4AProfile)(readUint8() >> 3);
skipBytes(size - 1);
} else {
desc->dataType = 0;
desc->dataLength = size;
desc->data = (uint8_t *)malloc(desc->dataLength);
readBytes(desc->data, desc->dataLength);
}
break;
default:
skipBytes(size);
break;
}
}
// skip leftover atoms for version 1 QuickTime descriptors
skipTo(entryEnd);
}
}

View File

@@ -0,0 +1,162 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-10.
#include "Mpeg4Container.h"
/** Populate [fragments] using the Segment Index Table */
void Mpeg4Container::readSidx(uint32_t atomSize) {
// https://b.goeswhere.com/ISO_IEC_14496-12_2015.pdf (page 121)
uint32_t offset = pos + atomSize;
uint8_t version = readUint8();
skipBytes(3); // skip flags
if (audioTrackId != readUint32()) { // check reference_ID
skipBytes(atomSize - 8); // skip the rest
return;
}
skipBytes(4); // skip uint(32) timescale
skipBytes(version ? 12 : 4); // skip zeroes, depending on version
offset += readUint32(); // read first_offset
skipBytes(2); // skip uint(16) reserved
fragmentsLen = readUint16();
fragments = (Mpeg4Fragment *)malloc(fragmentsLen * sizeof(Mpeg4Fragment));
for (uint32_t i = 0; i < fragmentsLen; i++) {
auto size = readUint32();
if (size & (1 << 31)) {
skipBytes(8);
continue; // ignore references to sidx, for now
}
fragments[i].start = offset;
fragments[i].end = offset + size;
fragments[i].duration = readUint32();
offset += size;
skipBytes(4); // skip SAP info
}
isFragmented = true;
}
/** Populate [sampleDefs] using Track Extends */
void Mpeg4Container::readTrex() {
// https://b.goeswhere.com/ISO_IEC_14496-12_2015.pdf (page 69)
skipBytes(4); // skip version and flags
sampleDefsLen++;
sampleDefs = (SampleDefaults *)realloc(sampleDefs, sampleDefsLen * sizeof(SampleDefaults));
sampleDefTracks = (uint32_t *)realloc(sampleDefTracks, sampleDefsLen * sizeof(uint32_t));
uint32_t i = sampleDefsLen - 1;
sampleDefTracks[i] = readUint32();
sampleDefs[i].offset = 0;
sampleDefs[i].sampleDescriptionId = readUint32();
sampleDefs[i].duration = readUint32();
sampleDefs[i].size = readUint32();
sampleDefs[i].flags = readUint32();
}
/** Populate [sampleDefs] using Track Fragment Header */
void Mpeg4Container::readTfhd(uint32_t trafEnd, uint32_t moofOffset) {
skipBytes(1); // skip version
TfFlags tfhdFlags = {};
readBytes((uint8_t *)&tfhdFlags, 3);
if (audioTrackId != readUint32()) {
skipTo(trafEnd); // skip the rest of traf
return;
}
auto *def = getSampleDef(audioTrackId);
if (!def) {
skipTo(trafEnd); // error?
return;
}
def->offset = 0;
if (tfhdFlags.baseDataOffsetPresent)
def->offset = readUint64();
if (tfhdFlags.sampleDescriptionIndexPresent)
def->sampleDescriptionId = readUint32();
if (tfhdFlags.defaultSampleDurationPresent)
def->duration = readUint32();
if (tfhdFlags.defaultSampleSizePresent)
def->size = readUint32();
if (tfhdFlags.defaultSampleFlagsPresent)
def->flags = readUint32();
if (tfhdFlags.defaultBaseIsMoof)
def->offset += moofOffset;
}
/** Populate [chunks, chunkOffsets, samples, sampleSizes] using Track Fragment Run Table */
void Mpeg4Container::readTrun(uint32_t atomSize, uint32_t moofOffset) {
skipBytes(1); // skip version
TrFlags trunFlags = {};
readBytes((uint8_t *)&trunFlags, 3);
// audioTrackId is guaranteed to match this trun's track ID
auto *def = getSampleDef(audioTrackId);
if (!def) {
skipBytes(atomSize - 4); // error?
return;
}
uint32_t i, j = 0;
uint32_t sampleCnt = readUint32();
uint32_t offset = def->offset ? def->offset : moofOffset; // base offset is baseDataOffset or moofOffset
// SampleFlags flags = def->flags;
if (trunFlags.dataOffsetPresent)
offset += readUint32();
// if (trunFlags.firstSampleFlagsPresent)
// flags = readUint32();
// treat every trun as a single new chunk
i = chunksLen++;
chunks = (Mpeg4ChunkRange *)realloc(chunks, chunksLen * sizeof(Mpeg4ChunkRange));
chunks[i].count = 1;
chunks[i].samples = sampleCnt;
chunks[i].sampleDescriptionId = def->sampleDescriptionId;
i = chunkOffsetsLen++;
chunkOffsets = (Mpeg4ChunkOffset *)realloc(chunkOffsets, chunkOffsetsLen * sizeof(Mpeg4ChunkOffset));
chunkOffsets[i] = offset;
// add all samples' sizes from this trun
i = sampleSizesLen;
sampleSizesLen += sampleCnt;
sampleSizes = (Mpeg4SampleSize *)realloc(sampleSizes, sampleSizesLen * sizeof(Mpeg4SampleSize));
// count duration changes for Mpeg4SampleRanges
auto *durations = (uint32_t *)malloc(sampleCnt * sizeof(uint32_t));
uint32_t prevDuration = 0, durationChanges = 0;
// TODO optimize memory usage for when all samples are of equal sizes
for (; i < sampleSizesLen; i++) {
durations[j] = trunFlags.sampleDurationPresent ? readUint32() : def->duration;
sampleSizes[i] = trunFlags.sampleSizePresent ? readUint32() : def->size;
if (sampleSizes[i] > sampleSizeMax)
sampleSizeMax = sampleSizes[i];
if (trunFlags.sampleFlagsPresent)
skipBytes(4); // skip flags, for now
if (trunFlags.sampleCompositionTimeOffsetsPresent)
skipBytes(4); // skip sample_composition_time_offset
// count duration changes
if (durations[j] != prevDuration) {
prevDuration = durations[j];
durationChanges++;
}
j++;
}
// add each duration change as a sample range
i = samplesLen;
samplesLen += durationChanges;
samples = (Mpeg4SampleRange *)realloc(samples, samplesLen * sizeof(Mpeg4SampleRange));
prevDuration = 0;
uint32_t durationCnt = 0; // how many consecutive samples have this duration
for (j = 0; j < sampleCnt; j++) {
if (durations[j] != prevDuration) {
if (prevDuration) {
samples[i].count = durationCnt;
samples[i].duration = prevDuration;
}
prevDuration = durations[j];
durationCnt = 1;
} else {
durationCnt++;
}
}
samples[samplesLen - 1].count = durationCnt;
samples[samplesLen - 1].duration = prevDuration;
// free temp array
free(durations);
// reallocate sampleData if the max size changes
allocSampleData();
}

View File

@@ -0,0 +1,215 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-10.
#include "AudioCodecs.h"
#include "BellUtils.h"
#include "Mpeg4Container.h"
#include "Mpeg4Types.h"
using namespace bell;
void Mpeg4Container::readAtomHeader(uint32_t &size, uint32_t &type) {
size = readUint32() - 8;
type = readUint32();
}
void Mpeg4Container::allocSampleData() {
if (sampleSizeMax != sampleDataLen) {
freeAndNull((void *&)sampleData);
sampleData = (uint8_t *)realloc(sampleData, sampleSizeMax);
sampleDataLen = sampleSizeMax;
}
}
void Mpeg4Container::freeAll() {
freeAndNull((void *&)fragments);
fragmentsLen = 0;
freeAndNull((void *&)sampleDefs);
freeAndNull((void *&)sampleDefTracks);
sampleDefsLen = 0;
for (SampleDescription *desc = sampleDesc; desc < sampleDesc + sampleDefsLen; desc++) {
free(desc->data);
}
freeAndNull((void *&)sampleDesc);
sampleDescLen = 0;
freeAndNull((void *&)sampleData);
freeFragment();
}
void Mpeg4Container::freeFragment() {
freeAndNull((void *&)chunks);
chunksLen = 0;
freeAndNull((void *&)chunkOffsets);
chunkOffsetsLen = 0;
freeAndNull((void *&)samples);
samplesLen = 0;
freeAndNull((void *&)sampleSizes);
sampleSizesLen = 0;
}
SampleDefaults *Mpeg4Container::getSampleDef(uint32_t trackId) {
for (uint32_t i = 0; i < sampleDefsLen; i++) {
if (sampleDefTracks[i] == trackId)
return sampleDefs + i;
}
return nullptr;
}
bool Mpeg4Container::isInFragment(Mpeg4Fragment *f, uint32_t offset) {
return offset >= f->start && offset < f->end;
}
void Mpeg4Container::setCurrentFragment() {
if (!isParsed)
return;
for (Mpeg4Fragment *f = fragments; f < fragments + fragmentsLen; f++) {
if (isInFragment(f, pos)) {
curFragment = f;
return;
}
}
curFragment = nullptr;
}
void Mpeg4Container::setCurrentSample() {
if (!isParsed)
return;
Mpeg4ChunkRange *chunk = chunks;
Mpeg4ChunkOffset *chunkOffset = chunkOffsets;
uint32_t chunkCnt = chunk->count;
uint32_t chunkSampleCnt = chunk->samples;
curChunk.start = 0;
curChunk.end = 0;
uint32_t offset = *chunkOffset;
Mpeg4SampleSize *ss = sampleSizes;
while (ss < sampleSizes + sampleSizesLen) {
// for (Mpeg4SampleSize *ss = sampleSizes; ss < sampleSizes + sampleSizesLen; ss++) {
offset += *ss;
if (!curChunk.start && pos < offset) { // sample found
curChunk.start = chunkOffset ? *chunkOffset : 0; // set chunk beginning
curSampleSize = ss; // store reference to current sample
}
chunkSampleCnt--; // decrease remaining samples in chunk
if (!chunkSampleCnt) { // no more samples
chunkOffset++; // get next chunk offset
if (chunkOffset >= chunkOffsets + chunkOffsetsLen)
chunkOffset = nullptr;
if (curChunk.start) { // chunk ended and beginning already found
curChunk.end = offset; // set chunk end
curChunk.nextStart = chunkOffset ? *chunkOffset : 0; // set next chunk offset
break;
}
if (chunkOffset)
offset = *chunkOffset;
chunkCnt--; // decrease remaining chunks in range
if (!chunkCnt) { // no more chunks
chunk++; // get next chunk range
if (chunk >= chunks + chunksLen) // something is not ok
return; // -> fail
chunkCnt = chunk->count; // update new chunk count from range
}
chunkSampleCnt = chunk->samples; // update new sample count from range
}
if (sampleSizesLen > 1)
ss++;
}
isInData = pos >= curChunk.start && pos < curChunk.end;
}
Mpeg4Fragment *Mpeg4Container::createFragment() {
uint32_t i = fragmentsLen++;
fragments = (Mpeg4Fragment *)realloc(fragments, fragmentsLen * sizeof(Mpeg4Fragment));
fragments[i].start = pos - 8;
uint32_t fragmentEnd = chunkOffsets[chunkOffsetsLen - 1];
uint32_t lastRangeSamples = chunks[chunksLen - 1].samples;
if (sampleSizesLen == 1)
fragmentEnd += *sampleSizes * lastRangeSamples;
else {
for (uint32_t j = sampleSizesLen - lastRangeSamples; j < sampleSizesLen; j++) {
fragmentEnd += sampleSizes[j];
}
}
fragments[i].end = fragmentEnd;
fragments[i].duration = 0;
totalDurationPresent = false;
return fragments + i;
}
AudioCodec Mpeg4Container::getCodec(SampleDescription *desc) {
switch (desc->format) {
case AudioSampleFormat::OPUS:
return AudioCodec::OPUS;
case AudioSampleFormat::FLAC:
return AudioCodec::FLAC;
case AudioSampleFormat::MP4A:
switch (desc->mp4aObjectType) {
case MP4AObjectType::AAC_LC:
return AudioCodec::AAC;
case MP4AObjectType::OPUS:
return AudioCodec::OPUS;
case MP4AObjectType::VORBIS:
return AudioCodec::VORBIS;
case MP4AObjectType::MPEG1:
return AudioCodec::MP3;
case MP4AObjectType::MP4A:
switch (desc->mp4aProfile) {
case MP4AProfile::AAC_LC:
return AudioCodec::AAC;
case MP4AProfile::LAYER_3:
return AudioCodec::MP3;
default:
return AudioCodec::UNKNOWN;
}
default:
return AudioCodec::UNKNOWN;
}
default:
return AudioCodec::UNKNOWN;
}
}
int64_t Mpeg4Container::findSample(int64_t byTime, int32_t byPos, uint64_t startTime) {
Mpeg4ChunkRange *chunk = chunks;
Mpeg4SampleRange *sample = samples;
Mpeg4ChunkOffset *chunkOffset = chunkOffsets;
Mpeg4SampleSize *sampleSize = sampleSizes;
uint32_t chunkCnt = chunk->count;
uint32_t chunkSampleCnt = chunk->samples;
uint32_t sampleRangeCnt = sample->count;
uint64_t timeAbs = startTime;
uint32_t offsetAbs = *chunkOffset;
while (sampleSize < sampleSizes + sampleSizesLen) {
if (byTime >= 0 && byTime <= timeAbs) {
return offsetAbs;
}
if (byPos >= 0 && byPos <= offsetAbs) {
return (int64_t)timeAbs;
}
timeAbs += sample->duration;
sampleRangeCnt--;
if (!sampleRangeCnt) {
sample++;
if (sample > samples + samplesLen)
return SAMPLE_NOT_FOUND;
sampleRangeCnt = sample->count;
}
chunkSampleCnt--;
if (!chunkSampleCnt) {
chunkCnt--;
chunkOffset++;
if (chunkOffset > chunkOffsets + chunkOffsetsLen)
return SAMPLE_NOT_FOUND;
offsetAbs = *chunkOffset;
if (!chunkCnt) {
chunk++;
if (chunk > chunks + chunksLen)
return SAMPLE_NOT_FOUND;
chunkCnt = chunk->count;
}
chunkSampleCnt = chunk->samples;
} else {
offsetAbs += sampleSizesLen > 1 ? *(sampleSize++) : *sampleSize;
}
}
return SAMPLE_NOT_FOUND;
}

View File

@@ -0,0 +1,172 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
#include "WebmContainer.h"
#include "AudioCodecs.h"
#include "BellUtils.h"
#include "WebmElements.h"
using namespace bell;
#define BLOCK_LACING_MASK 0b110
WebmContainer::~WebmContainer() {
freeAndNull((void *&)docType);
freeAndNull((void *&)codecId);
freeAndNull((void *&)codecPrivate);
freeAndNull((void *&)cues);
freeAndNull((void *&)sampleData);
freeAndNull((void *&)laceSizes);
}
void WebmContainer::feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) {
BaseContainer::feed(stream, position);
freeAndNull((void *&)laceSizes);
laceLeft = 0;
readOutFrameSize = 0;
}
bool WebmContainer::parse() {
bool segmentFound = false;
do {
readElem();
switch (eid) {
case ElementId::EBML:
continue;
case ElementId::DocType:
docType = static_cast<char *>(malloc(esize + 1));
docType[esize] = '\0';
readBytes((uint8_t *)docType, esize);
break;
case ElementId::Segment:
segmentFound = true;
parseSegment(pos); // webm can only have a single segment
break;
default:
skipBytes(esize);
}
} while (!segmentFound);
if (strncmp(codecId, "A_OPUS", 6) == 0)
codec = AudioCodec::OPUS;
else if (strncmp(codecId, "A_VORBIS", 8) == 0)
codec = AudioCodec::VORBIS;
else
codec = AudioCodec::UNKNOWN;
isSeekable = cuesLen;
return isParsed && codec != AudioCodec::UNKNOWN;
}
int32_t WebmContainer::getLoadingOffset(uint32_t timeMs) {
if (!isSeekable || !cuesLen)
return SAMPLE_NOT_SEEKABLE;
auto offset = (int32_t)cues[0].offset;
auto reqTime = (uint32_t)((float)timeMs * 1000000.0f / timescale);
for (CuePoint *cue = cues + 1; cue < cues + cuesLen; cue++) {
if (reqTime <= cue->time)
return offset;
offset = (int32_t)cue->offset;
}
return offset;
}
bool WebmContainer::seekTo(uint32_t timeMs) {
auto reqTime = (uint32_t)((float)timeMs * 1000000.0f / timescale);
if (reqTime <= currentTime)
return false;
// seeking ¯\_(ツ)_/¯
readOutFrameSize = readCluster(reqTime);
return !closed;
}
int32_t WebmContainer::getCurrentTimeMs() {
return (int32_t)((float)currentTime * timescale / 1000000.0f);
}
uint8_t *WebmContainer::readSample(uint32_t &len) {
if (readOutFrameSize)
return readFrame(readOutFrameSize, len);
if (laceLeft && laceLeft--)
return readFrame(*(laceCurrent++), len);
return readFrame(readCluster(), len);
}
uint32_t WebmContainer::readCluster(uint32_t untilTime) {
uint32_t end;
do {
readElem();
end = pos + esize;
switch (eid) {
case ElementId::Cluster:
continue;
case ElementId::Timestamp:
clusterTime = readUint(esize);
break;
case ElementId::BlockGroup:
continue;
case ElementId::Block:
case ElementId::SimpleBlock:
if (readVarNum32() != audioTrackId) {
skipTo(end);
continue;
}
currentTime = clusterTime + readUint16();
if (!untilTime || currentTime >= untilTime)
return readBlock(end);
skipTo(end); // skip all unneeded frames
break;
default:
skipBytes(esize);
}
} while (!closed);
return 0;
}
uint32_t WebmContainer::readBlock(uint32_t end) {
uint8_t lacing = readUint8() & BLOCK_LACING_MASK;
// https://www.matroska.org/technical/basics.html#simpleblock-structure
if (!lacing) // no lacing (0b000)
return end - pos;
// use lacing
laceLeft = readUint8() + 1;
freeAndNull((void *&)laceSizes);
laceSizes = static_cast<uint32_t *>(malloc(laceLeft * sizeof(uint32_t)));
auto *size = laceSizes;
for (uint8_t i = 0; i < laceLeft; i++) {
if (lacing == 0b010) { // Xiph lacing (0b010)
uint8_t sizeByte = readUint8();
*size = sizeByte;
while (sizeByte == 255) {
sizeByte = readUint8();
*size += sizeByte;
}
} else if (lacing == 0b110) { // EBML lacing (0b110)
*size = readVarNum32();
} else { // fixed-size lacing (0b100)
*size = (end - pos) / laceLeft;
}
size++;
}
laceCurrent = laceSizes + 1;
laceLeft--;
return laceSizes[0];
}
uint8_t *WebmContainer::readFrame(uint32_t size, uint32_t &outLen) {
if (!size)
return nullptr;
if (size > sampleLen) {
free(sampleData);
sampleData = static_cast<uint8_t *>(malloc(size));
sampleLen = size;
}
outLen = readBytes(sampleData, size);
readOutFrameSize = 0;
return sampleData;
}
uint8_t *WebmContainer::getSetupData(uint32_t &len, AudioCodec matchCodec) {
if (codec != matchCodec)
return nullptr;
len = codecPrivateLen;
return codecPrivate;
}

View File

@@ -0,0 +1,126 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
#include "WebmContainer.h"
#include "WebmElements.h"
void WebmContainer::parseSegment(uint32_t start) {
uint16_t cueIdx = 0;
do {
readElem();
switch (eid) {
case ElementId::Info:
case ElementId::Tracks:
continue;
case ElementId::TimestampScale:
timescale = (float)readUint(esize);
break;
case ElementId::Duration:
durationMs = (uint32_t)(readFloat(esize) * timescale / 1000000.0f);
break;
case ElementId::TrackEntry:
if (audioTrackId == 255)
parseTrack(pos + esize);
else // skip other tracks if audio is already found
skipBytes(esize);
break;
case ElementId::Cues:
// try to guess the amount of CuePoints from the total size:
// - CuePoint = id(1) + size(1) + CueTime + CueTrackPositions
// - CueTime = id(1) + size(1) + value(2) # avg. 16 bit
// - CueTrackPositions = id(1) + size(1) + CueTrack + CueClusterPosition
// - CueTrack = id(1) + size(1) + value(1)
// - CueClusterPosition = id(1) + size(1) + value(3) # avg. 24 bit
// total: approx. 16 bytes
cuesLen += esize / 16;
cues = static_cast<CuePoint *>(realloc(cues, cuesLen * sizeof(CuePoint)));
continue; // read the next child
case ElementId::CuePoint:
if (cueIdx >= cuesLen) {
cuesLen++;
cues = static_cast<CuePoint *>(realloc(cues, cuesLen * sizeof(CuePoint)));
}
parseCuePoint(cueIdx++, pos + esize, start);
break;
case ElementId::Cluster:
isParsed = audioTrackId != 255;
clusterEnd = pos + esize;
return;
default:
skipBytes(esize);
}
} while (!isParsed);
}
void WebmContainer::parseTrack(uint32_t end) {
uint8_t trackId = 255;
uint32_t trackRate = 0;
uint8_t trackChannels = 0;
uint8_t trackBits = 0;
char *trackCodecId = nullptr;
uint8_t *trackCodecPrivate = nullptr;
uint32_t trackCodecPrivateLen = 0;
do {
readElem();
switch (eid) {
case ElementId::TrackNumber:
trackId = readUint(esize);
break;
case ElementId::TrackType:
if (readUint8() != 0x02) { // allow only audio tracks
skipTo(end);
return;
}
break;
case ElementId::CodecID:
trackCodecId = static_cast<char *>(malloc(esize + 1));
trackCodecId[esize] = '\0';
readBytes((uint8_t *)trackCodecId, esize);
break;
case ElementId::CodecPrivate:
trackCodecPrivate = static_cast<uint8_t *>(malloc(esize));
trackCodecPrivateLen = esize;
readBytes(trackCodecPrivate, esize);
break;
case ElementId::Audio:
continue;
case ElementId::SamplingFrequency:
trackRate = (uint32_t)readFloat(esize);
break;
case ElementId::Channels:
trackChannels = readUint(esize);
break;
case ElementId::BitDepth:
trackBits = readUint(esize);
break;
default:
skipBytes(esize);
}
} while (pos < end);
// not-audio tracks do not even get to this point
audioTrackId = trackId;
sampleRate = trackRate;
channelCount = trackChannels;
bitDepth = trackBits;
codecId = trackCodecId;
codecPrivate = trackCodecPrivate;
codecPrivateLen = trackCodecPrivateLen;
}
void WebmContainer::parseCuePoint(uint16_t idx, uint32_t end, uint32_t segmentStart) {
CuePoint *cue = cues + idx;
do {
readElem();
switch (eid) {
case ElementId::CueTime:
cue->time = readUint(esize);
break;
case ElementId::CueTrackPositions:
continue;
case ElementId::CueClusterPosition:
cue->offset = segmentStart + readUint(esize);
break;
default:
skipBytes(esize);
}
} while (pos < end);
}

View File

@@ -0,0 +1,67 @@
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
#include "WebmContainer.h"
uint32_t WebmContainer::readVarNum32(bool raw) {
uint32_t result = readUint8();
if (!result) {
closed = true;
return 0;
}
uint8_t len = 0;
for (; !(result >> (7 - len)); len++) {}
if (!raw)
result &= ~(1 << (7 - len));
for (uint8_t i = 0; i < len; i++) {
result <<= 8;
result |= readUint8();
}
return result;
}
uint64_t WebmContainer::readVarNum64() {
uint64_t result = readUint8();
if (!result) {
closed = true;
return 0;
}
uint8_t len = 0;
for (; !(result >> (7 - len)); len++) {}
result &= ~(1 << (7 - len));
for (uint8_t i = 0; i < len; i++) {
result <<= 8;
result |= readUint8();
}
return result;
}
uint32_t WebmContainer::readUint(uint8_t len) {
if (len >= 4) {
skipBytes(len - 4);
return readUint32();
}
if (len == 3)
return readUint24();
if (len == 2)
return readUint16();
return readUint8();
}
uint64_t WebmContainer::readUlong(uint8_t len) {
if (len == 8)
return readUint64();
return readUint(len);
}
float WebmContainer::readFloat(uint8_t len) {
double result = 0;
auto *b = (uint8_t *)&result;
for (uint8_t i = 0; i < len; i++)
b[len - i - 1] = readUint8();
return (float)result;
}
void WebmContainer::readElem() {
eid = (ElementId)readVarNum32(true);
esize = readVarNum32();
}

View File

@@ -0,0 +1,105 @@
#include "ES8311AudioSink.h"
extern "C" {
#include "es8311.h"
}
ES8311AudioSink::ES8311AudioSink()
{
this->softwareVolumeControl = false;
esp_err_t ret_val = ESP_OK;
Es8311Config cfg = {
.esMode = ES_MODE_SLAVE,
.i2c_port_num = I2C_NUM_0,
.i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 1,
.scl_io_num = 2,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
},
.dacOutput = (DacOutput) (DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2),
.adcInput = ADC_INPUT_LINPUT1_RINPUT1,
};
cfg.i2c_cfg.master.clk_speed = 100000;
Es8311Init(&cfg);
Es8311SetBitsPerSample(ES_MODULE_DAC, BIT_LENGTH_16BITS);
Es8311ConfigFmt(ES_MODULE_DAC, ES_I2S_NORMAL);
Es8311SetVoiceVolume(60);
Es8311Start(ES_MODULE_DAC);
ES8311WriteReg(ES8311_CLK_MANAGER_REG01, 0xbf);
ES8311WriteReg(ES8311_CLK_MANAGER_REG02, 0x18);
// .codec_mode = AUDIO_HAL_CODEC_MODE_DECODE,
// .i2s_iface = {
// .mode = AUDIO_HAL_MODE_SLAVE,
// .fmt = AUDIO_HAL_I2S_NORMAL,
// .samples = AUDIO_HAL_44K_SAMPLES,
// .bits = AUDIO_HAL_BIT_LENGTH_16BITS,
// },
// };
// ret_val |= es8311_codec_init(&cfg);
// ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits);
// ret_val |= es8311_config_fmt((es_i2s_fmt_t) cfg.i2s_iface.fmt);
// ret_val |= es8311_codec_set_voice_volume(60);
// ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START);
// ret_val |= es8311_codec_set_clk();
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
.sample_rate = 44100,
.bits_per_sample = (i2s_bits_per_sample_t)16,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // 2-channels
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = 0, // Default interrupt priority
.dma_buf_count = 8,
.dma_buf_len = 512,
.use_apll = false,
.tx_desc_auto_clear = true, // Auto clear tx descriptor on underflow
.fixed_mclk = 256 * 44100};
i2s_pin_config_t pin_config = {
.mck_io_num = 42,
.bck_io_num = 40,
.ws_io_num = 41,
.data_out_num = 39,
.data_in_num = -1,
};
int err;
err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
if (err != ESP_OK)
{
ESP_LOGE("OI", "i2s driver installation error: %d", err);
}
err = i2s_set_pin((i2s_port_t)0, &pin_config);
if (err != ESP_OK)
{
ESP_LOGE("OI", "i2s set pin error: %d", err);
}
// PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
// REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0);
// ESP_LOGI("OI", "MCLK output on CLK_OUT1");
startI2sFeed();
}
void ES8311AudioSink::volumeChanged(uint16_t volume)
{
// int volAdj = (int)((((float)volume) / 65536) * 100);
// es8311_codec_set_voice_vo0lume(volAdj);
if (volume > 79) {
volume = 79;
}
Es8311SetVoiceVolume(volume);
}
void ES8311AudioSink::writeReg(uint8_t reg_add, uint8_t data)
{
}
ES8311AudioSink::~ES8311AudioSink()
{
}

View File

@@ -51,11 +51,6 @@ ES8388AudioSink::ES8388AudioSink()
ESP_LOGE("OI", "i2s set pin error: %d", err);
}
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0);
ESP_LOGI("OI", "MCLK output on CLK_OUT1");
err = i2c_param_config(0, &i2c_config);
if (err != ESP_OK) {
ESP_LOGE("OI", "i2c param config error: %d", err);

View File

@@ -28,10 +28,6 @@ ES9018AudioSink::ES9018AudioSink()
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
i2s_set_pin((i2s_port_t)0, &pin_config);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0);
ESP_LOGI("OI", "MCLK output on CLK_OUT1");
startI2sFeed();
}

View File

@@ -5,6 +5,7 @@ InternalAudioSink::InternalAudioSink()
{
softwareVolumeControl = true;
usign = true;
#ifdef I2S_MODE_DAC_BUILT_IN
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), // Only TX
@@ -24,6 +25,7 @@ InternalAudioSink::InternalAudioSink()
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
//init DAC
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
#endif
startI2sFeed();
}

View File

@@ -44,10 +44,6 @@ TAS5711AudioSink::TAS5711AudioSink()
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
i2s_set_pin((i2s_port_t)0, &pin_config);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0);
ESP_LOGI("OI", "MCLK output on CLK_OUT1");
// configure i2c
i2c_config = {
.mode = I2C_MODE_MASTER,

View File

@@ -0,0 +1,798 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <string.h>
#include "esp_log.h"
#include "es8311.h"
// #include "board.h"
/* ES8311 address
* 0x32:CE=1;0x30:CE=0
*/
#define ES8311_ADDR 0x32
#define ES7243_ADDR 0x26
/*
* to define the clock soure of MCLK
*/
#define FROM_MCLK_PIN 0
#define FROM_SCLK_PIN 1
/*
* to define work mode(master or slave)
*/
#define MASTER_MODE 0
#define SLAVE_MODE 1
/*
* to define serial digital audio format
*/
#define I2S_FMT 0
#define LEFT_JUSTIFIED_FMT 1
#define DPS_PCM_A_FMT 2
#define DPS_PCM_B_FMT 3
/*
* to define resolution of PCM interface
*/
#define LENGTH_16BIT 0
#define LENGTH_24BIT 1
#define LENGTH_32BIT 2
/*
* codec private data
*/
struct es8311_private {
bool dmic_enable;
bool mclkinv;
bool sclkinv;
uint8_t master_slave_mode;
uint8_t pcm_format;
uint8_t pcm_resolution;
uint8_t mclk_src;
};
static struct es8311_private *es8311_priv;
/*
* Clock coefficient structer
*/
struct _coeff_div {
uint32_t mclk; /* mclk frequency */
uint32_t rate; /* sample rate */
uint8_t prediv; /* the pre divider with range from 1 to 8 */
uint8_t premulti; /* the pre multiplier with x1, x2, x4 and x8 selection */
uint8_t adcdiv; /* adcclk divider */
uint8_t dacdiv; /* dacclk divider */
uint8_t fsmode; /* double speed or single speed, =0, ss, =1, ds */
uint8_t lrck_h; /* adclrck divider and daclrck divider */
uint8_t lrck_l;
uint8_t bclkdiv; /* sclk divider */
uint8_t adcosr; /* adc osr */
uint8_t dacosr; /* dac osr */
};
/* codec hifi mclk clock divider coefficients */
static const struct _coeff_div coeff_div[] = {
//mclk rate prediv mult adcdiv dacdiv fsmode lrch lrcl bckdiv osr
/* 8k */
{12288000, 8000 , 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 8000 , 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10},
{16384000, 8000 , 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000 , 8000 , 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 8000 , 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{4096000 , 8000 , 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2048000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 8000 , 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1024000 , 8000 , 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 11.025k */
{11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2822400 , 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1411200 , 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 12k */
{12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 16k */
{12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
{16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000 , 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{4096000 , 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2048000 , 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1024000 , 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 22.05k */
{11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2822400 , 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1411200 , 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 24k */
{12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 32k */
{12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
{16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000 , 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{4096000 , 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2048000 , 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
{1024000 , 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 44.1k */
{11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2822400 , 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1411200 , 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 48k */
{12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 64k */
{12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
{16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000 , 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
{4096000 , 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
{2048000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18},
{1024000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
/* 88.2k */
{11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2822400 , 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1411200 , 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
/* 96k */
{12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
};
static char *TAG = "DRV8311";
#define ES_ASSERT(a, format, b, ...) \
if ((a) != 0) { \
ESP_LOGE(TAG, format, ##__VA_ARGS__); \
return b;\
}
static int Es8311WriteReg(uint8_t regAdd, uint8_t data)
{
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES8311_ADDR, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_write_byte(cmd, data, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
ES_ASSERT(res, "Es8311 Write Reg error", -1);
return res;
}
int Es8311ReadReg(uint8_t regAdd)
{
uint8_t data;
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES8311_ADDR, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES8311_ADDR | 0x01, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_read_byte(cmd, &data, 0x01 /*NACK_VAL*/);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
ES_ASSERT(res, "Es8311 Read Reg error", -1);
return (int)data;
}
static int Es7243WriteReg(uint8_t regAdd, uint8_t data)
{
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES7243_ADDR, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_write_byte(cmd, data, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
ES_ASSERT(res, "Es7243 Write Reg error", -1);
return res;
}
int Es7243ReadReg(uint8_t regAdd)
{
uint8_t data;
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES7243_ADDR, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES7243_ADDR | 0x01, 1 /*ACK_CHECK_EN*/);
res |= i2c_master_read_byte(cmd, &data, 0x01 /*NACK_VAL*/);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
ES_ASSERT(res, "Es7243 Read Reg error", -1);
return (int)data;
}
esp_err_t Es7243Init(void)
{
esp_err_t ret = ESP_OK;
ret |= Es7243WriteReg(0x00, 0x01);
ret |= Es7243WriteReg(0x06, 0x00);
ret |= Es7243WriteReg(0x05, 0x1B);
ret |= Es7243WriteReg(0x01, 0x0C);
ret |= Es7243WriteReg(0x08, 0x43);
ret |= Es7243WriteReg(0x05, 0x13);
if (ret) {
ESP_LOGE(TAG, "Es7243 initialize failed!");
return ESP_FAIL;
}
return ret;
}
static int I2cInit(i2c_config_t *conf, int i2cMasterPort)
{
int res;
res = i2c_param_config(i2cMasterPort, conf);
res |= i2c_driver_install(i2cMasterPort, conf->mode, 0, 0, 0);
ES_ASSERT(res, "I2cInit error", -1);
return res;
}
/*
* look for the coefficient in coeff_div[] table
*/
static int get_coeff(uint32_t mclk, uint32_t rate)
{
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
return i;
}
return -1;
}
/*
* set es8311 clock parameter and PCM/I2S interface
*/
static void es8311_pcm_hw_params(uint32_t mclk, uint32_t lrck)
{
int coeff;
uint8_t regv, datmp;
ESP_LOGI(TAG, "Enter into es8311_pcm_hw_params()\n");
coeff = get_coeff(mclk, lrck);
if (coeff < 0) {
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK\n", lrck, mclk);
return;
}
/*
* set clock parammeters
*/
if (coeff >= 0) {
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG02) & 0x07;
regv |= (coeff_div[coeff].prediv - 1) << 5;
datmp = 0;
switch (coeff_div[coeff].premulti) {
case 1:
datmp = 0;
break;
case 2:
datmp = 1;
break;
case 4:
datmp = 2;
break;
case 8:
datmp = 3;
break;
default:
break;
}
#if CONFIG_ESP32_KORVO_V1_1_BOARD
datmp = 3;
#endif
regv |= (datmp) << 3;
Es8311WriteReg(ES8311_CLK_MANAGER_REG02, regv);
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG05) & 0x00;
regv |= (coeff_div[coeff].adcdiv - 1) << 4;
regv |= (coeff_div[coeff].dacdiv - 1) << 0;
Es8311WriteReg(ES8311_CLK_MANAGER_REG05, regv);
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG03) & 0x80;
regv |= coeff_div[coeff].fsmode << 6;
regv |= coeff_div[coeff].adcosr << 0;
Es8311WriteReg(ES8311_CLK_MANAGER_REG03, regv);
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG04) & 0x80;
regv |= coeff_div[coeff].dacosr << 0;
Es8311WriteReg(ES8311_CLK_MANAGER_REG04, regv);
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG07) & 0xC0;
regv |= coeff_div[coeff].lrck_h << 0;
Es8311WriteReg(ES8311_CLK_MANAGER_REG07, regv);
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG08) & 0x00;
regv |= coeff_div[coeff].lrck_l << 0;
Es8311WriteReg(ES8311_CLK_MANAGER_REG08, regv);
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG06) & 0xE0;
if (coeff_div[coeff].bclkdiv < 19) {
regv |= (coeff_div[coeff].bclkdiv - 1) << 0;
} else {
regv |= (coeff_div[coeff].bclkdiv) << 0;
}
Es8311WriteReg(ES8311_CLK_MANAGER_REG06, regv);
}
}
/*
* set data and clock in tri-state mode
* if tristate = 0, tri-state is disabled for normal mode
* if tristate = 1, tri-state is enabled
*/
// static void es8311_set_tristate(int tristate)
// {
// uint8_t regv;
// ESP_LOGI(TAG, "Enter into es8311_set_tristate(), tristate = %d\n", tristate);
// regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG07) & 0xcf;
// if (tristate) {
// Es8311WriteReg(ES8311_CLK_MANAGER_REG07, regv | 0x30);
// } else {
// Es8311WriteReg(ES8311_CLK_MANAGER_REG07, regv);
// }
// }
/*
* set es8311 dac mute or not
* if mute = 0, dac un-mute
* if mute = 1, dac mute
*/
static void es8311_mute(int mute)
{
uint8_t regv;
ESP_LOGI(TAG, "Enter into es8311_mute(), mute = %d\n", mute);
regv = Es8311ReadReg(ES8311_DAC_REG31) & 0x9f;
if (mute) {
Es8311WriteReg(ES8311_SYSTEM_REG12, 0x02);
Es8311WriteReg(ES8311_DAC_REG31, regv | 0x60);
Es8311WriteReg(ES8311_DAC_REG32, 0x00);
Es8311WriteReg(ES8311_DAC_REG37, 0x08);
} else {
Es8311WriteReg(ES8311_DAC_REG31, regv);
Es8311WriteReg(ES8311_SYSTEM_REG12, 0x00);
}
}
/*
* set es8311 into suspend mode
*/
// static void es8311_suspend(void)
// {
// ESP_LOGI(TAG, "Enter into es8311_suspend()\n");
// Es8311WriteReg(ES8311_DAC_REG32, 0x00);
// Es8311WriteReg(ES8311_ADC_REG17, 0x00);
// Es8311WriteReg(ES8311_SYSTEM_REG0E, 0xFF);
// Es8311WriteReg(ES8311_SYSTEM_REG12, 0x02);
// Es8311WriteReg(ES8311_SYSTEM_REG14, 0x00);
// Es8311WriteReg(ES8311_SYSTEM_REG0D, 0xFA);
// Es8311WriteReg(ES8311_ADC_REG15, 0x00);
// Es8311WriteReg(ES8311_DAC_REG37, 0x08);
// Es8311WriteReg(ES8311_RESET_REG00, 0x00);
// Es8311WriteReg(ES8311_RESET_REG00, 0x1F);
// Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x30);
// Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x00);
// Es8311WriteReg(ES8311_GP_REG45, 0x01);
// }
/*
* initialize es8311 codec
*/
static void es8311_init(uint32_t mclk_freq, uint32_t lrck_freq)
{
int regv;
Es8311WriteReg(ES8311_GP_REG45, 0x00);
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x30);
Es8311WriteReg(ES8311_CLK_MANAGER_REG02, 0x00);
Es8311WriteReg(ES8311_CLK_MANAGER_REG03, 0x10);
Es8311WriteReg(ES8311_ADC_REG16, 0x24);
Es8311WriteReg(ES8311_CLK_MANAGER_REG04, 0x10);
Es8311WriteReg(ES8311_CLK_MANAGER_REG05, 0x00);
Es8311WriteReg(ES8311_SYSTEM_REG0B, 0x00);
Es8311WriteReg(ES8311_SYSTEM_REG0C, 0x00);
Es8311WriteReg(ES8311_SYSTEM_REG10, 0x1F);
Es8311WriteReg(ES8311_SYSTEM_REG11, 0x7F);
Es8311WriteReg(ES8311_RESET_REG00, 0x80);
/*
* Set Codec into Master or Slave mode
*/
regv = Es8311ReadReg(ES8311_RESET_REG00);
/* set master/slave audio interface */
switch (es8311_priv->master_slave_mode) {
case MASTER_MODE: /* MASTER MODE */
ESP_LOGI(TAG, "ES8311 in Master mode\n");
regv |= 0x40;
break;
case SLAVE_MODE: /* SLAVE MODE */
ESP_LOGI(TAG, "ES8311 in Slave mode\n");
regv &= 0xBF;
break;
default:
regv &= 0xBF;
}
Es8311WriteReg(ES8311_RESET_REG00, regv);
Es8311WriteReg(ES8311_SYSTEM_REG0D, 0x01);
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x3F);
/*
* select clock source for internal mclk
*/
switch (es8311_priv->mclk_src) {
case FROM_MCLK_PIN:
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01);
regv &= 0x7F;
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv);
break;
case FROM_SCLK_PIN:
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01);
regv |= 0x80;
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv);
break;
default:
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01);
regv &= 0x7F;
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv);
break;
}
es8311_pcm_hw_params(mclk_freq, lrck_freq);
/*
* mclk inverted or not
*/
if (es8311_priv->mclkinv == true) {
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01);
regv |= 0x40;
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv);
} else {
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01);
regv &= ~(0x40);
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv);
}
/*
* sclk inverted or not
*/
if (es8311_priv->sclkinv == true) {
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG06);
regv |= 0x20;
Es8311WriteReg(ES8311_CLK_MANAGER_REG06, regv);
} else {
regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG06);
regv &= ~(0x20);
Es8311WriteReg(ES8311_CLK_MANAGER_REG06, regv);
}
Es8311WriteReg(ES8311_SYSTEM_REG14, 0x1A);
/*
* pdm dmic enable or disable
*/
if (es8311_priv->dmic_enable == true) {
regv = Es8311ReadReg(ES8311_SYSTEM_REG14);
regv |= 0x40;
Es8311WriteReg(ES8311_SYSTEM_REG14, regv);
} else {
regv = Es8311ReadReg(ES8311_SYSTEM_REG14);
regv &= ~(0x40);
Es8311WriteReg(ES8311_SYSTEM_REG14, regv);
}
Es8311WriteReg(ES8311_SYSTEM_REG13, 0x10);
Es8311WriteReg(ES8311_SYSTEM_REG0E, 0x02);
Es8311WriteReg(ES8311_ADC_REG15, 0x40);
Es8311WriteReg(ES8311_ADC_REG1B, 0x0A);
Es8311WriteReg(ES8311_ADC_REG1C, 0x6A);
Es8311WriteReg(ES8311_DAC_REG37, 0x48);
Es8311WriteReg(ES8311_GPIO_REG44, 0x08);
Es8311WriteReg(ES8311_DAC_REG32, 0xBF);
#ifdef CONFIG_USE_ES7243
Es7243Init();
#endif
}
/*
* set codec private data and initialize codec
*/
static void es8311_Codec_Startup(uint32_t mclk_freq, uint32_t lrck_freq)
{
ESP_LOGI(TAG, "Enter into es8311_Codec_Startup()\n");
es8311_priv->dmic_enable = false;
es8311_priv->mclkinv = false;
es8311_priv->sclkinv = false;
es8311_priv->pcm_format = I2S_FMT;
es8311_priv->pcm_resolution = LENGTH_16BIT;
es8311_priv->master_slave_mode = SLAVE_MODE;
#ifdef CONFIG_ESP32_KORVO_V1_1_BOARD
es8311_priv->mclk_src = FROM_SCLK_PIN;
#else
es8311_priv->mclk_src = FROM_MCLK_PIN;
#endif
es8311_init(mclk_freq, lrck_freq);
ESP_LOGI(TAG, "Exit es8311_Codec_Startup()\n");
}
// static int Es8311SetAdcDacVolume(int mode, int volume, int dot)
// {
// int res = 0;
// if ( volume < -96 || volume > 0 ) {
// ESP_LOGI(TAG, "Warning: volume < -96! or > 0!\n");
// if (volume < -96) {
// volume = -96;
// } else {
// volume = 0;
// }
// }
// dot = (dot >= 5 ? 1 : 0);
// return res;
// }
esp_err_t Es8311GetRef(bool flag)
{
esp_err_t ret = ESP_OK;
uint8_t regv = 0;
if (flag) {
regv = Es8311ReadReg(ES8311_GPIO_REG44);
regv |= 0x50;
ret |= Es8311WriteReg(ES8311_GPIO_REG44, regv);
} else {
ret |= Es8311WriteReg(ES8311_GPIO_REG44, 0x08);
}
return ret;
}
int Es8311Init(Es8311Config *cfg)
{
es8311_priv = calloc(1, sizeof(struct es8311_private));
I2cInit(&cfg->i2c_cfg, cfg->i2c_port_num); // ESP32 in master mode
es8311_Codec_Startup(11289600, 44100);
return 0;
}
void Es8311Uninit()
{
Es8311WriteReg(ES8311_RESET_REG00, 0x3f);
free(es8311_priv);
es8311_priv = NULL;
}
int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt)
{
int res = 0;
uint8_t regAdc = 0, regDac = 0;
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_ADC_REG17, 0xBF);
}
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_SYSTEM_REG12, 0x00);
}
regAdc = Es8311ReadReg(ES8311_SDPIN_REG09);
regDac = Es8311ReadReg(ES8311_SDPOUT_REG0A);
switch (fmt) {
case ES_I2S_NORMAL:
ESP_LOGI(TAG, "ES8311 in I2S Format");
regAdc &= ~0x03;
regDac &= ~0x03;
break;
case ES_I2S_LEFT:
case ES_I2S_RIGHT:
ESP_LOGI(TAG, "ES8311 in LJ Format");
regAdc &= ~0x03;
regAdc |= 0x01;
regDac &= ~0x03;
regDac |= 0x01;
break;
case ES_I2S_DSP:
ESP_LOGI(TAG, "ES8311 in DSP Format");
regAdc |= 0x03;
regDac |= 0x03;
break;
default:
ESP_LOGE(TAG, "Not Supported Format");
break;
}
res |= Es8311WriteReg(ES8311_SDPIN_REG09, regAdc);
res |= Es8311WriteReg(ES8311_SDPOUT_REG0A, regDac);
return res;
}
int Es8311I2sConfigClock(ESCodecI2sClock cfg)
{
int res = 0;
return res;
}
int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample)
{
int res = 0;
uint8_t reg = 0;
int bits = (int)bitPerSample;
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
reg = Es8311ReadReg(ES8311_SDPIN_REG09);
reg = reg & 0xe3;
res |= Es8311WriteReg(ES8311_SDPIN_REG09, reg | (bits << 2));
}
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
reg = Es8311ReadReg(ES8311_SDPOUT_REG0A);
reg = reg & 0xe3;
res |= Es8311WriteReg(ES8311_SDPOUT_REG0A, reg | (bits << 2));
}
return res;
}
int Es8311Start(ESCodecModule mode)
{
int res = 0;
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_ADC_REG17, 0xBF);
}
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_SYSTEM_REG12, Es8311ReadReg(ES8311_SYSTEM_REG12) & 0xfd);
}
return res;
}
int Es8311Stop(ESCodecModule mode)
{
int res = 0;
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_ADC_REG17, 0x00);
}
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_SYSTEM_REG12, Es8311ReadReg(ES8311_SYSTEM_REG12) | 0x02);
}
return res;
}
int Es8311SetVoiceVolume(int volume)
{
int res = 0;
if (volume < 0) {
volume = 0;
} else if (volume > 100) {
volume = 100;
}
int vol = (volume) * 2550 / 1000 + 0.5;
ESP_LOGI(TAG, "SET: volume:%d\n", vol);
Es8311WriteReg(ES8311_DAC_REG32, vol);
return res;
}
int Es8311GetVoiceVolume(int *volume)
{
int res = ESP_OK;
int regv = Es8311ReadReg(ES8311_DAC_REG32);
if (regv == ESP_FAIL) {
*volume = 0;
res = ESP_FAIL;
} else {
*volume = regv * 100 / 256;
}
ESP_LOGI(TAG, "GET: res:%d, volume:%d\n", regv, *volume);
return res;
}
int Es8311SetVoiceMute(int enable)
{
int res = 0;
ESP_LOGI(TAG, "Es8311SetVoiceMute volume:%d\n", enable);
es8311_mute(enable);
return res;
}
int Es8311GetVoiceMute(int *mute)
{
int res = -1;
uint8_t reg = 0;
res = Es8311ReadReg(ES8311_DAC_REG31);
if (res != ESP_FAIL) {
reg = (res & 0x20) >> 5;
}
*mute = reg;
return res;
}
int Es8311SetMicGain(MicGain gain)
{
int res = 0;
uint8_t gain_n = Es8311ReadReg(ES8311_ADC_REG16) & 0x07;
gain_n |= gain / 6;
res = Es8311WriteReg(ES8311_ADC_REG16, gain_n); // MIC gain scale
return res;
}
int Es8311ConfigAdcInput(AdcInput input)
{
int res = 0;
return res;
}
int Es8311SetAdcVolume(uint8_t adc_vol)
{
int res = 0;
res = Es8311WriteReg(ES8311_ADC_REG17, adc_vol); // MIC ADC Volume
return res;
}
int ES8311WriteReg(uint8_t regAdd, uint8_t data)
{
return Es8311WriteReg(regAdd, data);
}
void Es8311ReadAll()
{
for (int i = 0; i < 0x4A; i++) {
uint8_t reg = Es8311ReadReg(i);
ets_printf("REG:%02x, %02x\n", reg, i);
}
}

View File

@@ -0,0 +1,38 @@
#include "platform/MDNSService.h"
#include "dns_sd.h"
#include <arpa/inet.h>
/**
* MacOS implementation of MDNSService.
* @see https://developer.apple.com/documentation/dnssd/1804733-dnsserviceregister
**/
void MDNSService::registerService(
const std::string& serviceName,
const std::string& serviceType,
const std::string& serviceProto,
const std::string& serviceHost,
int servicePort,
const std::map<std::string, std::string> txtData
) {
DNSServiceRef ref = NULL;
TXTRecordRef txtRecord;
TXTRecordCreate(&txtRecord, 0, NULL);
for (auto& data : txtData) {
TXTRecordSetValue(&txtRecord, data.first.c_str(), data.second.size(), data.second.c_str());
}
DNSServiceRegister(
&ref, /* sdRef */
0, /* flags */
0, /* interfaceIndex */
serviceName.c_str(), /* name */
(serviceType + "." + serviceProto).c_str(), /* regType (_spotify-connect._tcp) */
NULL, /* domain */
NULL, /* host */
htons(servicePort), /* port */
TXTRecordGetLength(&txtRecord), /* txtLen */
TXTRecordGetBytesPtr(&txtRecord), /* txtRecord */
NULL, /* callBack */
NULL /* context */
);
TXTRecordDeallocate(&txtRecord);
}

View File

@@ -0,0 +1,35 @@
#include "platform/MDNSService.h"
#include <arpa/inet.h>
#include <vector>
#include "mdns.h"
/**
* ESP32 implementation of MDNSService
* @see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mdns.html
**/
void MDNSService::registerService(
const std::string& serviceName,
const std::string& serviceType,
const std::string& serviceProto,
const std::string& serviceHost,
int servicePort,
const std::map<std::string, std::string> txtData
) {
std::vector<mdns_txt_item_t> txtItems;
txtItems.reserve(txtData.size());
for (auto& data : txtData) {
mdns_txt_item_t item;
item.key = data.first.c_str();
item.value = data.second.c_str();
txtItems.push_back(item);
}
mdns_service_add(
serviceName.c_str(), /* instance_name */
serviceType.c_str(), /* service_type */
serviceProto.c_str(), /* proto */
servicePort, /* port */
txtItems.data(), /* txt */
txtItems.size() /* num_items */
);
}

View File

@@ -0,0 +1,16 @@
#include "platform/MDNSService.h"
/**
* Linux implementation of MDNSService using avahi.
* @see https://www.avahi.org/doxygen/html/
**/
void MDNSService::registerService(
const std::string& serviceName,
const std::string& serviceType,
const std::string& serviceProto,
const std::string& serviceHost,
int servicePort,
const std::map<std::string, std::string> txtData
) {
//TODO: add avahi stuff
}

View File

@@ -1,63 +0,0 @@
#include "platform/TLSSocket.h"
/**
* Platform TLSSocket implementation for the openssl
*/
bell::TLSSocket::TLSSocket() {
ERR_load_crypto_strings();
ERR_load_SSL_strings();
OpenSSL_add_all_algorithms();
ctx = SSL_CTX_new(SSLv23_client_method());
}
void bell::TLSSocket::open(std::string host, uint16_t port) {
/* We'd normally set some stuff like the verify paths and
* mode here because as things stand this will connect to
* any server whose certificate is signed by any CA.
*/
sbio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(sbio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BELL_LOG(info, "http_tls", "Connecting with %s", host.c_str());
BIO_set_conn_hostname(sbio, std::string(host + ":" + std::to_string(port)).c_str());
out = BIO_new_fp(stdout, BIO_NOCLOSE);
if (BIO_do_connect(sbio) <= 0) {
BELL_LOG(error, "http_tls", "Error connecting with server");
/* whatever ... */
}
if (BIO_do_handshake(sbio) <= 0) {
BELL_LOG(error, "http_tls", "Error TLS connection");
/* whatever ... */
} // remove https or http from url
// split by first "/" in url
}
size_t bell::TLSSocket::read(uint8_t *buf, size_t len) {
return BIO_read(sbio, buf, len);
}
size_t bell::TLSSocket::write(uint8_t *buf, size_t len) {
return BIO_write(sbio, buf, len);
}
size_t bell::TLSSocket::poll() {
return BIO_pending(sbio);
}
void bell::TLSSocket::close() {
if (!isClosed) {
BIO_free_all(sbio);
BIO_free(out);
isClosed = true;
}
}

View File

@@ -0,0 +1,27 @@
#include "platform/WrappedSemaphore.h"
WrappedSemaphore::WrappedSemaphore(int count)
{
this->semaphoreHandle = CreateSemaphore(NULL, 0, count, NULL);
}
WrappedSemaphore::~WrappedSemaphore()
{
CloseHandle(this->semaphoreHandle);
}
int WrappedSemaphore::wait()
{
WaitForSingleObject(this->semaphoreHandle, INFINITE);
return 0;
}
int WrappedSemaphore::twait(long milliseconds)
{
return WaitForSingleObject(this->semaphoreHandle, milliseconds) != WAIT_OBJECT_0;
}
void WrappedSemaphore::give()
{
ReleaseSemaphore(this->semaphoreHandle, 1, NULL);
}