Skip to content

Commit

Permalink
[FEAT] Verbose logs for AudioPlayer + close to flac decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
nots1dd committed Feb 25, 2025
1 parent 2883fca commit 02d622b
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 49 deletions.
81 changes: 47 additions & 34 deletions include/playback.hpp
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
#define MINIAUDIO_IMPLEMENTATION
#include "logger.hpp"
#include "miniaudio.h"
#include <iomanip> // for std::fixed and std::setprecision
#include <stdexcept>
#include <vector>

#define BUFFER_SIZE 4096
#define SAMPLE_RATE 44100
#define CHANNELS 2

class AudioPlayer
{
private:
ma_device device;
ma_engine engine;
ma_decoder decoder;
std::vector<unsigned char> audioMemory; // Hold the Audio data to keep it valid during playback
std::vector<unsigned char> audioMemory;
bool isPlaying;
ma_device device;

static void dataCallback(ma_device* pDevice, void* pOutput, const void* pInput,
ma_uint32 frameCount)
{
auto* player = (AudioPlayer*)pDevice->pUserData;
auto* output = (float*)pOutput;

// Read directly from decoder
ma_uint64 framesRead;
ma_decoder_read_pcm_frames(&player->decoder, output, frameCount, &framesRead);

// Fill remaining with silence if we reached the end
if (framesRead < frameCount)
{
ma_silence_pcm_frames(output + (framesRead * CHANNELS), frameCount - framesRead,
Expand All @@ -34,18 +35,43 @@ class AudioPlayer
}

public:
// New constructor that initializes decoder from memory
AudioPlayer(const std::vector<unsigned char>& audioInput)
: audioMemory(audioInput), isPlaying(false)
{
ma_decoder_config decoderConfig = ma_decoder_config_init(ma_format_f32, CHANNELS, SAMPLE_RATE);
LOG_INFO << "Initializing AudioPlayer with " << audioMemory.size() << " bytes of audio data.";

if (ma_engine_init(nullptr, &engine) != MA_SUCCESS)
{
LOG_ERROR << "Failed to initialize audio engine.";
throw std::runtime_error("Failed to initialize audio engine");
}

ma_decoder_config decoderConfig = ma_decoder_config_init(ma_format_f32, CHANNELS, SAMPLE_RATE);
if (ma_decoder_init_memory(audioMemory.data(), audioMemory.size(), &decoderConfig, &decoder) !=
MA_SUCCESS)
{
LOG_ERROR << "Failed to initialize decoder from memory.";
ma_engine_uninit(&engine);
throw std::runtime_error("Failed to initialize decoder from memory");
}

LOG_INFO << "Decoder initialized: "
<< "Format: " << decoder.outputFormat << ", Channels: " << decoder.outputChannels
<< ", Sample Rate: " << decoder.outputSampleRate;

ma_uint64 totalFrames;
if (ma_decoder_get_length_in_pcm_frames(&decoder, &totalFrames) == MA_SUCCESS)
{
double duration = static_cast<double>(totalFrames) / decoder.outputSampleRate;
LOG_INFO << "Audio duration: " << std::fixed << std::setprecision(2) << duration
<< " seconds";
LOG_INFO << "Total frames: " << totalFrames;
}
else
{
LOG_WARNING << "Could not determine audio duration.";
}

ma_device_config config = ma_device_config_init(ma_device_type_playback);
config.playback.format = ma_format_f32;
config.playback.channels = CHANNELS;
Expand All @@ -55,56 +81,43 @@ class AudioPlayer

if (ma_device_init(nullptr, &config, &device) != MA_SUCCESS)
{
LOG_ERROR << "Failed to initialize audio device.";
ma_decoder_uninit(&decoder);
ma_engine_uninit(&engine);
throw std::runtime_error("Failed to initialize audio device");
}

LOG_INFO << "Audio device initialized: "
<< "Format: " << config.playback.format << ", Channels: " << config.playback.channels
<< ", Sample Rate: " << config.sampleRate;

LOG_INFO << "Audio engine and decoder initialized successfully.";
}

~AudioPlayer()
{
LOG_INFO << "Shutting down AudioPlayer.";
ma_device_uninit(&device);
ma_decoder_uninit(&decoder);
ma_engine_uninit(&engine);
}

void play()
{
LOG_INFO << "Starting playback.";
if (ma_device_start(&device) != MA_SUCCESS)
{
LOG_ERROR << "Failed to start audio device.";
throw std::runtime_error("Failed to start audio device");
}

isPlaying = true;

// Wait while playing
while (isPlaying)
{
ma_sleep(100);
}

ma_device_stop(&device);
LOG_INFO << "Playback completed.";
}
};

/*auto main() -> int*/
/*{*/
/* // Read Audio data from standard input*/
/* std::vector<unsigned char> audioData((std::istreambuf_iterator<char>(std::cin)),*/
/* std::istreambuf_iterator<char>());*/
/* if (audioData.empty())*/
/* {*/
/* std::cerr << "No Audio input received from STDIN" << std::endl;*/
/* return 1;*/
/* }*/
/**/
/* try*/
/* {*/
/* AudioPlayer player(audioData);*/
/* player.play();*/
/* }*/
/* catch (const std::exception& e)*/
/* {*/
/* std::cerr << "Error: " << e.what() << std::endl;*/
/* return 1;*/
/* }*/
/**/
/* return 0;*/
/*}*/
78 changes: 63 additions & 15 deletions src/client_receive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#endif

#include <cstdlib>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
Expand Down Expand Up @@ -71,8 +72,28 @@ auto perform_https_request(net::io_context& ioc, ssl::context& ctx, const std::s
}
}

bool write_transport_segments_to_file(const std::vector<std::string>& transport_segments,
const std::string& filename)
{
std::ofstream output_file(filename, std::ios::binary);
if (!output_file)
{
LOG_ERROR << "Failed to open output file: " << filename;
return false;
}

for (const auto& segment : transport_segments)
{
output_file.write(segment.data(), segment.size());
}

output_file.close();
LOG_INFO << "Successfully wrote transport streams to " << filename;
return true;
}

auto fetch_transport_segments(const std::string& ip_id, const std::string& audio_id,
GlobalState& gs, const std::string& server) -> bool
GlobalState& gs, const std::string& server, bool& flac_found) -> bool
{
net::io_context ioc;
ssl::context ctx(ssl::context::tlsv12_client);
Expand Down Expand Up @@ -133,17 +154,16 @@ auto fetch_transport_segments(const std::string& ip_id, const std::string& audio
playlist_content = perform_https_request(ioc, ctx, playlist_path, server);
}

std::istringstream segment_stream(playlist_content);
std::string line;
std::string init_mp4_data;
bool has_m4s_segments = false;
std::istringstream segment_stream(playlist_content);
std::string line;
std::string init_mp4_data;
bool has_m4s_segments = false;
std::vector<std::string> m4s_segments;

while (std::getline(segment_stream, line))
{
if (!line.empty() && line[0] != '#')
{
std::string segment_url = "/hls/" + ip_id + "/" + audio_id + "/" + line;

if (line.ends_with(macros::M4S_FILE_EXT))
{
has_m4s_segments = true;
Expand All @@ -163,6 +183,7 @@ auto fetch_transport_segments(const std::string& ip_id, const std::string& audio
}

LOG_INFO << "Fetched init.mp4, size: " << init_mp4_data.size() << " bytes.";
flac_found = true;
}

// Re-parse playlist for segments
Expand All @@ -182,8 +203,7 @@ auto fetch_transport_segments(const std::string& ip_id, const std::string& audio
{
if (line.ends_with(macros::M4S_FILE_EXT))
{
std::string full_segment = init_mp4_data + segment_data;
gs.transport_segments.push_back(std::move(full_segment));
m4s_segments.push_back(std::move(segment_data));
}
else
{
Expand All @@ -200,11 +220,38 @@ auto fetch_transport_segments(const std::string& ip_id, const std::string& audio
}
}

// Prepend init.mp4 ONCE before all .m4s segments
if (!m4s_segments.empty())
{
gs.transport_segments.push_back(std::move(init_mp4_data));
gs.transport_segments.insert(gs.transport_segments.end(),
std::make_move_iterator(m4s_segments.begin()),
std::make_move_iterator(m4s_segments.end()));
}
/************************************************************************************
*
* @NOTE
*
* To check the final file received from the server.
*
* if (!write_transport_segments_to_file(gs.transport_segments, "audio.raw"))
* {
* LOG_ERROR << "Error writing transport segments to file";
* return false;
* }
***********************************************************************************/

/*if (!write_transport_segments_to_file(gs.transport_segments, "audio.raw"))*/
/*{*/
/* LOG_ERROR << "Error writing transport segments to file";*/
/* return false;*/
/*}*/

LOG_INFO << "Stored " << gs.transport_segments.size() << " transport segments.";
return true;
}

auto decode_and_play(GlobalState& gs) -> bool
auto decode_and_play(GlobalState& gs, bool& flac_found) -> bool
{
if (gs.transport_segments.empty())
{
Expand Down Expand Up @@ -247,18 +294,19 @@ auto main(int argc, char* argv[]) -> int
return EXIT_FAILURE;
}

std::string ip_id = argv[1];
std::string audio_id = argv[2];
std::string server = argv[3];
std::string ip_id = argv[1];
std::string audio_id = argv[2];
std::string server = argv[3];
bool flac_found = false;

GlobalState gs;

if (!fetch_transport_segments(ip_id, audio_id, gs, server))
if (!fetch_transport_segments(ip_id, audio_id, gs, server, flac_found))
{
return EXIT_FAILURE;
}

if (!decode_and_play(gs))
if (!decode_and_play(gs, flac_found))
{
return EXIT_FAILURE;
}
Expand Down

0 comments on commit 02d622b

Please sign in to comment.