diff --git a/include/playback.hpp b/include/playback.hpp index 8b28417..35dd19a 100644 --- a/include/playback.hpp +++ b/include/playback.hpp @@ -1,18 +1,21 @@ #define MINIAUDIO_IMPLEMENTATION +#include "logger.hpp" #include "miniaudio.h" +#include // for std::fixed and std::setprecision +#include #include -#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 audioMemory; // Hold the Audio data to keep it valid during playback + std::vector audioMemory; bool isPlaying; + ma_device device; static void dataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) @@ -20,11 +23,9 @@ class AudioPlayer 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, @@ -34,18 +35,43 @@ class AudioPlayer } public: - // New constructor that initializes decoder from memory AudioPlayer(const std::vector& 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(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; @@ -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 audioData((std::istreambuf_iterator(std::cin)),*/ -/* std::istreambuf_iterator());*/ -/* 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;*/ -/*}*/ diff --git a/src/client_receive.cpp b/src/client_receive.cpp index 19d965c..d0bdc6d 100644 --- a/src/client_receive.cpp +++ b/src/client_receive.cpp @@ -3,6 +3,7 @@ #endif #include +#include #include #include #include @@ -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& 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); @@ -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 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; @@ -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 @@ -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 { @@ -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()) { @@ -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; }