diff --git a/cmd/CMakeLists.txt b/cmd/CMakeLists.txt index 8b9a6e0..c58bb6f 100644 --- a/cmd/CMakeLists.txt +++ b/cmd/CMakeLists.txt @@ -1,27 +1,2 @@ -add_executable( echoSFU echoSFU.cc) -target_link_libraries( echoSFU PUBLIC neoMedia) - -add_executable( bcastSFU bcastSFU.cc) -target_link_libraries( bcastSFU PUBLIC neoMedia) - add_executable(forty forty-bytes.cc) target_link_libraries( forty PUBLIC neoMedia) - -find_package(portaudio QUIET) -if(portaudio_FOUND) - add_executable( sound sound.cc) - target_link_libraries( sound PUBLIC neoMedia ) - - if (WIN32) - target_link_libraries( sound PUBLIC portaudio ) - else(WIN32) - target_link_libraries( sound PUBLIC portaudio_static ) - endif(WIN32) -else(portaudio_FOUND) - message(STATUS "Skipping building sound as missing portaudio") -endif(portaudio_FOUND) - -if (BUILD_SEND_VIDEO_FRAME) - add_executable(sendVideoFrame sendVideoFrame.cc) - target_link_libraries(sendVideoFrame PUBLIC neoMedia) -endif (BUILD_SEND_VIDEO_FRAME) diff --git a/cmd/bcastSFU.cc b/cmd/bcastSFU.cc deleted file mode 100644 index 16e0187..0000000 --- a/cmd/bcastSFU.cc +++ /dev/null @@ -1,118 +0,0 @@ - -#include -#include -#include - -#include "transport_manager.hh" - -using namespace neo_media; - -bool use_quic = true; - -static std::string to_hex(const std::vector &data) -{ - std::stringstream hex(std::ios_base::out); - hex.flags(std::ios::hex); - for (const auto &byte : data) - { - hex << std::setw(2) << std::setfill('0') << int(byte); - } - return hex.str(); -} - -struct SFU -{ - struct Association - { - uint64_t clientID; - neo_media::NetTransport::PeerConnectionInfo peer_info; - }; - - std::map subscribers; -}; - -void print(const Packet *packet) -{ - std::cout << "Type " << packet->packetType << std::endl; - std::cout << "Client " << packet->clientID << std::endl; - std::cout << "ConfID " << packet->conferenceID << std::endl; - std::cout << "XportID " << packet->peer_info.transport_connection_id - << std::endl; - std::cout << "Size " << packet->data.size() << std::endl; -} - -int main(int argc, char *argv[]) -{ - auto sfu = SFU{}; - std::string transport_str; - if (argc < 2) - { - std::cerr << "Must provide transport" << std::endl; - std::cerr << "Usage: bcastSFU " << std::endl; - std::cerr << "Transport: q (for quic), r (udp)" << std::endl; - return -1; - } - - transport_str.assign(argv[1]); - - auto transport_type = NetTransport::Type::UDP; - if (transport_str == "q") - { - std::cout << "Using Quic Transport\n"; - transport_type = NetTransport::Type::PICO_QUIC; - } - - ServerTransportManager transport(transport_type); - - while (1) - { - if (transport.empty()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - PacketPointer packet = transport.recv(); - if (!sfu.subscribers.count(packet->clientID)) - { - std::cout << packet->packetType - << ": adding subscriber: " << packet->clientID - << std::endl; - sfu.subscribers.insert( - {packet->clientID, - SFU::Association{packet->clientID, packet->peer_info}}); - } - - // std::cout << "bsfu: cnxId " - // << to_hex(packet->peer_info.transport_connection_id); - // std::cout <<", size " << packet->data.size() << std::endl; - - // broadcast - for (auto const &[client, assoc] : sfu.subscribers) - { - if (packet->clientID == client) - { - // std::cout << "not forwarding; reason(sender)\n"; - continue; - } - - if (packet->packetType != neo_media::Packet::Type::StreamContent) - { - // std::cout << "ignoring non content packet: " << - // packet->packetType << std::endl; - continue; - } - - std::cout << "forwarding to cnx " - << to_hex(assoc.peer_info.transport_connection_id) - << std::endl; - auto copy = std::make_unique(*packet); - copy->peer_info = assoc.peer_info; - // std::clog << packet->clientID <<".[" << assoc.clientID <<"]"; - transport.send(std::move(copy)); - } - // std::clog << ".["<< packet->sequenceNumber<<"]"; - } - - return 0; -} diff --git a/cmd/echoSFU.cc b/cmd/echoSFU.cc deleted file mode 100644 index 5eaad64..0000000 --- a/cmd/echoSFU.cc +++ /dev/null @@ -1,90 +0,0 @@ - -#include -#include -#include - -#include "transport_manager.hh" - -using namespace neo_media; - -static std::string to_hex(const std::vector &data) -{ - std::stringstream hex(std::ios_base::out); - hex.flags(std::ios::hex); - for (const auto &byte : data) - { - hex << std::setw(2) << std::setfill('0') << int(byte); - } - return hex.str(); -} - -int main(int argc, char *argv[]) -{ - std::string transport_str; - if (argc < 2) - { - std::cerr << "Must provide transport" << std::endl; - std::cerr << "Usage: echoSFU " << std::endl; - std::cerr << "Transport: q (for quic), r (udp)" << std::endl; - std::cerr << "(Optional: debug )" << std::endl; - return -1; - } - - transport_str.assign(argv[1]); - - // Logger. - auto logger = std::make_shared("ECHO"); - bool debug = false; - if (argc == 3) - { - bool echo = false; - std::stringstream ss(argv[2]); - ss >> std::boolalpha >> debug; - } - logger->SetLogLevel(debug ? LogLevel::DEBUG : LogLevel::INFO); - - auto transport_type = NetTransport::Type::UDP; - if (transport_str == "q") - { - std::cout << "Using Quic Transport\n"; - transport_type = NetTransport::Type::PICO_QUIC; - } - - ServerTransportManager transport(transport_type, 5004, nullptr, logger); - while (1) - { - if (transport.empty()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - // transport.waitForPacket(); - PacketPointer packet = transport.recv(); - if (Packet::Type::StreamContent == packet->packetType) - { - // send ack immediately ( real sfu shall not be that reactive) - PacketPointer content_ack = std::make_unique(); - content_ack->packetType = Packet::Type::StreamContentAck; - content_ack->transportSequenceNumber = - packet->transportSequenceNumber; - content_ack->peer_info = packet->peer_info; - transport.send(std::move(content_ack)); - // echo packet - std::clog << to_hex(packet->data) << std::endl; - transport.send(std::move(packet)); - } - else if (Packet::Type::StreamContentAck == packet->packetType) - { - // std::clog << "got content ack for: " << - // packet->transportSequenceNumber << std::endl; - } - else if (Packet::Type::IdrRequest == packet->packetType) - { - // echo packet - transport.send(std::move(packet)); - } - } - - return 0; -} diff --git a/cmd/sendVideoFrame.cc b/cmd/sendVideoFrame.cc deleted file mode 100644 index 675e9db..0000000 --- a/cmd/sendVideoFrame.cc +++ /dev/null @@ -1,180 +0,0 @@ -#include -#include -#include -#include -#include - -#include "neo_media_client.hh" -#include "neo.hh" - -using namespace std::chrono_literals; - -std::uint64_t source_id = 0; // wait to receive something non-zero -std::uint64_t send_source_id = 37; // random choice in send direction - -void source_callback(std::uint64_t cid, - std::uint64_t sid, - std::uint64_t ts, - int source_type) -{ - source_id = sid; - std::cout << std::endl - << "Got New Source:" << sid << " Client:" << cid << " Time:" << ts - << " Type:" << source_type << std::endl; -} - -int main(int argc, char **argv) -{ - std::uint64_t client_id = 5; // made it up - std::uint64_t conference_id = 1; // made it up - - std::uint32_t enc_format = 0; // encode format: 0=NV12 - std::uint32_t dec_format = 1; // decode format: 1=I420 - - std::size_t image_width = 1280; - std::size_t image_height = 720; - std::size_t image_y_size = image_width * image_height; - std::size_t image_uv_size = image_y_size >> 2; // YUV420 - std::size_t image_size = image_y_size + image_uv_size * 2; - char *image = static_cast(malloc(image_size)); - assert(image); - - // Fill image with color gradients. - // 16M YUV combos mapped to 1M pixels. - // 8 bits of Y in MSBs + 6 bits of U/V in LSBs = 20 bits total = 1M. - - for (int i = 0; i < image_y_size; i++) - { - image[i] = (i >> 12 & 0xff) + 16; // Y in bits 12-19 - } - for (int i = 0; i < image_uv_size; i++) - { - image[image_y_size + i * 2 + 0] = (i >> 6 & 0x3f) << 2; // U in - // bits - // 6-11 - image[image_y_size + i * 2 + 1] = (i >> 0 & 0x3f) << 2; // V in - // bits - // 0-5 - } - - NeoMediaInstance neo = Init( - "127.0.0.1", // server name/IP - 5004, // UDP port - 48000, // 48kHz audio sample rate - 2, // audio channels - 0, // 0=Neo::PCMfloat32=0, 1=Neo::PCMint16 - image_width, - image_height, - 30, // frame rate - 200000, // bitrate - enc_format, // encode format: 0=NV12 - dec_format, // decode format: 1=I420 - client_id, // my (sender) client id - conference_id, // my (sender) conference id - source_callback, // callback when received a new source - NetTransport::Type::UDP, // UDP or QUIC - [](const char *message) { // Logs - std::cout << message << std::endl; - }, - false); // echo - - setLoopbackMode(neo, 3); - - // Send frames. - std::thread sendThread( - [neo, image, image_size, image_width, image_height, enc_format]() - { - while (true) - { - std::this_thread::sleep_for(std::chrono::milliseconds(33)); - std::uint64_t timestamp = - std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - std::cerr << " S "; // << sourceRecordTime << "ns " << - // image_size << "bytes" << - // std::endl; - sendVideoFrame(neo, - image, - image_size, - image_width, - image_height, - image_width, - image_width, - image_width * image_height, - 0, - enc_format, - timestamp, - send_source_id); - image[0] = image[0] + 1; // Embed a frame counter to - // check on receive - } - }); - - // Get frames. - std::thread recvThread( - [neo, - client_id, - image, - image_size, - image_width, - image_height, - dec_format]() - { - while (true) - { - std::cerr << " R "; // << ts << " " << - // received_image_size << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(33)); - if (source_id == 0) continue; - unsigned char *buffer = nullptr; - std::uint64_t ts = 0; - std::uint32_t width = 0; - std::uint32_t height = 0; - std::uint32_t format = dec_format; // I420 - auto received_image_size = getVideoFrame(neo, - client_id, - source_id, - ts, - width, - height, - format, - &buffer); - - // std::cerr << " w:" << width << " iw:" << image_width - // << " h:" << height << " ih:" << image_height - // << " f:" << format << " df:" << dec_format << - // std::endl; - assert(received_image_size == image_size); - assert(width == image_width); - assert(height == image_height); - assert(format == dec_format); - assert(buffer); - - // Enable assert below if testing Raw video with echoSFU and no - // concealment. Must explicitly disable AV1 and decoder - // concealment for this assert to pass. assert(buffer[0] == 0x80 - // || memcmp(buffer, image, image_size) == 0); - std::cerr << " " << (int) (buffer[0]) << " "; // log - // frame - // counter - - /* PSNR calc to test sanity (30-50 is believable) and quality - (>40=great) std::int64_t sq_err = 1; for (int i = 0; i < - image_size; - ++i) { int diff = image[i] - buffer[i]; sq_err += diff * diff; - } - double psnr = 10.0 * log10(255.0*255.0*image_size*8/12/sq_err); - if (psnr>100.0) psnr=100.0; - std::cerr << " PSNR=" << psnr << " "; - */ - std::cerr << " r "; // << ts << " " << - // received_image_size << std::endl; - } - }); - - // Main thread. - while (true) - { - } -} diff --git a/cmd/sound.cc b/cmd/sound.cc deleted file mode 100644 index 1498edc..0000000 --- a/cmd/sound.cc +++ /dev/null @@ -1,486 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "neo.hh" -#include "resampler.hh" -#include "logger.hh" - -using namespace neo_media; - -static bool shutDown; - -static PaStream *audioStream; -static std::mutex audioReadMutex; -static std::mutex audioWriteMutex; - -static bool recvMedia = false; -static uint64_t recvClientID; -static uint64_t recvSourceID; - -static const unsigned int remote_port = 5004; -static const unsigned int sample_rate = 48000; -static const unsigned int frames_per_buffer = 10 * 48; // 10 ms -static const unsigned int audio_channels = 1; -static const unsigned int bytesPerSample = 4; -static const Neo::audio_sample_type sample_type_neo = - Neo::audio_sample_type::Float32; -static const double resample_ratio = 1.0; -static Resampler resampler; - -static std::ofstream outg_sound_file; -static LoggerPointer logger = std::make_shared("Sound", true); - -void recordThreadFunc(Neo *neo) -{ - assert(audioStream); - - int buff_size = frames_per_buffer * bytesPerSample * audio_channels; - char *zerobuff = (char *) malloc(buff_size); - if (zerobuff == nullptr) - { - logger->error << "Couldn't allocate zero buffer" << std::flush; - return; - } - memset(zerobuff, 0, buff_size); - uint64_t sourceID = 2; - uint64_t timestamp = 0; - - while (!shutDown) - { - char *audioBuff = (char *) malloc(buff_size); - if (audioBuff == nullptr) - { - logger->error << "Failed to allocate audio buffer" << std::flush; - continue; - } - - PaError err; - { - std::lock_guard lock(audioReadMutex); - err = Pa_ReadStream(audioStream, audioBuff, frames_per_buffer); - } - if (err) - { - logger->error << "Failed to read PA stream: " - << Pa_GetErrorText(err) << std::flush; - continue; - } - - if (memcmp(audioBuff, zerobuff, buff_size) == 0) - { - logger->debug << "0" << std::flush; - } - - timestamp = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - - neo->sendAudio(audioBuff, buff_size, timestamp, sourceID); - logger->debug << "-" << std::flush; - - if (outg_sound_file.is_open()) - { - switch (sample_type_neo) - { - case Neo::audio_sample_type::Float32: - { - float *fvalue = nullptr; - for (int i = 0; i < (buff_size / sizeof(float)); - i += sizeof(float)) - { - fvalue = (float *) &audioBuff[i]; - outg_sound_file << *fvalue << ","; - } - break; - } - case Neo::audio_sample_type::PCMint16: - { - uint16_t *ivalue = nullptr; - for (int i = 0; i < (buff_size / sizeof(uint16_t)); - i += sizeof(uint16_t)) - { - ivalue = (uint16_t *) audioBuff[i]; - outg_sound_file << *ivalue << ","; - } - break; - } - } - } - free(audioBuff); - } - free(zerobuff); -} - -void playThreadFunc(Neo *neo) -{ - assert(audioStream); - - std::chrono::steady_clock::time_point loop_time = - std::chrono::steady_clock::now(); - - while (!shutDown) - { - auto delta = std::chrono::duration_cast( - std::chrono::steady_clock::now() - loop_time); - logger->debug << "{" << delta.count() << "}" << std::flush; - loop_time = std::chrono::steady_clock::now(); - - if (!recvMedia) - { - logger->debug << "Z" << std::flush; - std::chrono::steady_clock::time_point sleep_time = - std::chrono::steady_clock::now(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - auto sleep_delta = - std::chrono::duration_cast( - std::chrono::steady_clock::now() - sleep_time); - logger->debug << "{S: " << sleep_delta.count() << "}" << std::flush; - continue; - } - - long playoutSpaceAvailable; - { - std::lock_guard lock(audioWriteMutex); - playoutSpaceAvailable = Pa_GetStreamWriteAvailable(audioStream); - logger->debug << "[" << playoutSpaceAvailable << "]" << std::flush; - } - - if (playoutSpaceAvailable < 1000 /* 21 ms */) - { - // not enought space to play, wait till later - logger->debug << "$" << std::flush; - std::chrono::steady_clock::time_point sleep_time = - std::chrono::steady_clock::now(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - auto sleep_delta = - std::chrono::duration_cast( - std::chrono::steady_clock::now() - sleep_time); - logger->debug << "{S: " << sleep_delta.count() << "}" << std::flush; - continue; - } - - int buff_size = frames_per_buffer * bytesPerSample * audio_channels; - unsigned char *raw_data = nullptr; - uint64_t timestamp; - Packet *freePacket = nullptr; - - std::chrono::steady_clock::time_point get_audio = - std::chrono::steady_clock::now(); - int recv_actual = neo->getAudio(recvClientID, - recvSourceID, - timestamp, - &raw_data, - buff_size, - &freePacket); - auto audio_delta = std::chrono::duration_cast( - std::chrono::steady_clock::now() - get_audio); - logger->debug << "{A:" << audio_delta.count() << "}" << std::flush; - - PaError err; - if (!recv_actual) - { - // write silence to the stream as not packet available - assert(sizeof(float) == bytesPerSample); - float plc[frames_per_buffer * audio_channels]; - std::fill(plc, plc + frames_per_buffer * audio_channels, 0.0); - - logger->debug << "*" << std::flush; - { - std::lock_guard lock(audioWriteMutex); - err = Pa_WriteStream(audioStream, plc, frames_per_buffer); - } - if (err) - { - logger->error - << "Pa_WriteStream (plc) failed: " << Pa_GetErrorText(err) - << std::flush; - assert(0); // TODO - } - // std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - logger->debug << "+" << std::flush; - { - std::chrono::steady_clock::time_point start_write = - std::chrono::steady_clock::now(); - std::lock_guard lock(audioWriteMutex); - - if (resample_ratio != 1.0) - { - float *src = (float *) raw_data; - long input_frames = recv_actual / (bytesPerSample); - long resampled_output_frames = input_frames * resample_ratio; - float *resampled = new float[resampled_output_frames]; - int ret = resampler.simple_resample(src, - input_frames, - resampled, - resampled_output_frames, - audio_channels, - resample_ratio); - - if (ret) - { - logger->error << "Resample failed: " << src_strerror(ret) - << std::flush; - } - else - { - err = Pa_WriteStream( - audioStream, - (const void *) resampled, - resampled_output_frames / audio_channels); - } - - delete[] resampled; - } - else - { - err = Pa_WriteStream( - audioStream, - raw_data, - recv_actual / (audio_channels * bytesPerSample)); - auto write_delta = - std::chrono::duration_cast( - std::chrono::steady_clock::now() - start_write); - logger->debug << "{W: " << write_delta.count() << "}" - << std::flush; - } - } - if (err) - { - logger->error << "Pa_WriteStream failed: " << Pa_GetErrorText(err) - << std::flush; - } - - // hack until Packet is split into meta and data - std::chrono::steady_clock::time_point free_time = - std::chrono::steady_clock::now(); - if (freePacket != nullptr) - { - delete (Packet *) freePacket; - } - auto free_delta = std::chrono::duration_cast( - std::chrono::steady_clock::now() - free_time); - logger->debug << "{F: " << free_delta.count() << "}" << std::flush; - } -} - -void streamCallBack(uint64_t clientID, - uint64_t sourceID, - uint64_t startTime, - Packet::MediaType type) -{ - // TODO: This will change to post decode only stream type at some point. - if (type == Packet::MediaType::Opus) - { - recvMedia = true; - recvClientID = clientID; - recvSourceID = sourceID; - } -} - -int main(int argc, char *argv[]) -{ -#if defined(_WIN32) - timeBeginPeriod(1); // timerstonk - push minimum resolution down to 1 - // ms -#endif - if (argc < 4) - { - std::cout << "Must provide hostname of SFU on command line" - << std::endl; - std::cout << "Usage: sound sfuHostname transport echo[true/false] " - " " - << std::endl; - std::cout << "transport -> q for quic, r for raw-udp" << std::endl; - return 64; // EX_USAGE in BSD for usage/parameter errors - } - - std::string remote_address; - remote_address.assign(argv[1]); - - std::string transport; - transport.assign(argv[2]); - - // default to udp - auto transport_type = NetTransport::Type::UDP; - if (transport == "q") - { - logger->info << "Using Quic Transport" << std::flush; - transport_type = NetTransport::Type::PICO_QUIC; - } - - // SFU echo flag. - bool echo = false; - std::stringstream ss(argv[3]); - ss >> std::boolalpha >> echo; - - std::ostringstream oss; - std::chrono::steady_clock::time_point timePoint = - std::chrono::steady_clock::now(); - auto min = std::chrono::duration_cast( - timePoint.time_since_epoch()); - - // Debug mode / log level. - bool debug = false; - if (argc == 5) - { - bool echo = false; - std::stringstream ss(argv[4]); - ss >> std::boolalpha >> debug; - } - logger->SetLogLevel(debug ? LogLevel::DEBUG : LogLevel::INFO); - - if (argc == 6) - { - switch (sample_type_neo) - { - case Neo::audio_sample_type::Float32: - oss << argv[5] << "-float32-" << audio_channels << "-" - << min.count() << ".csv"; - break; - case Neo::audio_sample_type::PCMint16: - oss << argv[5] << "-pcm16-" << audio_channels << "-" - << min.count() << ".csv"; - break; - } - - outg_sound_file.open(oss.str()); - if (outg_sound_file.fail()) - { - logger->error << "Unable to open sound file: " << oss.str() - << std::flush; - return 73; // EX_CANTCREATE in BSD for can't create output - // file - } - } - - std::default_random_engine engine(time(0)); - std::uniform_int_distribution distribution(1, 9); - auto clientID = distribution(engine); - logger->info << "ClientID: " << clientID << std::flush; - - Neo neo(logger); - neo.init(remote_address, - remote_port, // network settings - sample_rate, - audio_channels, - sample_type_neo, // audio settings - 0, - 0, - 0, - 0, // no video settings - (Neo::video_pixel_format) 0, // no video settings - (Neo::video_pixel_format) 0, // no video settings - clientID, - 123456, // conferenceID - streamCallBack, - transport_type, // QUIC or UDP - echo); - - shutDown = false; - - // setup port audio - PaError err = Pa_Initialize(); - if (err != paNoError) - { - assert(0); // TODO - } - - PaStreamParameters inputParameters; - PaStreamParameters outputParameters; - - inputParameters.device = Pa_GetDefaultInputDevice(); - outputParameters.device = Pa_GetDefaultOutputDevice(); - - assert(inputParameters.device != paNoDevice); - assert(outputParameters.device != paNoDevice); - - assert(bytesPerSample == 4); - assert(sample_type_neo == Neo::audio_sample_type::Float32); - - inputParameters.channelCount = audio_channels; - inputParameters.sampleFormat = paFloat32; - inputParameters.suggestedLatency = - Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - - outputParameters.channelCount = audio_channels; - outputParameters.sampleFormat = paFloat32; - outputParameters.suggestedLatency = - Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - - err = Pa_OpenStream(&audioStream, - &inputParameters, - &outputParameters, - sample_rate, - frames_per_buffer, - paClipOff, - NULL, - NULL); - if (err != paNoError) - { - logger->error << Pa_GetErrorText(err) << std::flush; - assert(0); // TODO - } - else - { - logger->info << "Audio device is open" << std::flush; - } - - err = Pa_StartStream(audioStream); - if (err != paNoError) - { - assert(0); // TODO - } - - std::thread recordThread(recordThreadFunc, &neo); - std::thread playThread(playThreadFunc, &neo); - playThread.detach(); - - logger->info << "Starting" << std::flush; - int count = 0; - while (!shutDown) - { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - - count++; - if (count > 50 * 1) - { - shutDown = true; - } - } - logger->info << "Shutting down" << std::flush; - - recordThread.join(); - playThread.join(); - - err = Pa_StopStream(audioStream); - if (err != paNoError) - { - logger->error << "Failure to stop stream: " << Pa_GetErrorText(err) - << std::flush; - } - - if (outg_sound_file.is_open()) outg_sound_file.close(); - - err = Pa_Terminate(); - if (err != paNoError) - { - logger->error << "Failure to shutdown portaudio: " - << Pa_GetErrorText(err) << std::flush; - } - - return err; -} diff --git a/test/fullFill.cc b/test/fullFill.cc deleted file mode 100644 index 2f52aa9..0000000 --- a/test/fullFill.cc +++ /dev/null @@ -1,159 +0,0 @@ -#include -#include "audio_encoder.hh" - -using namespace neo_media; - -void callback_dummy(PacketPointer packet) -{ -} - -TEST_CASE("fullFillMatch") -{ - fullFill buffers; - const int length = 480 * 1 * sizeof(uint16_t); - uint8_t input_buffer[length]; - std::vector fill_buff; - uint64_t time = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - uint64_t time_inc = 10000; // 10ms - - uint64_t fill_time; - - fill_buff.resize(0); - buffers.addBuffer(input_buffer, length, time); - CHECK(buffers.fill(fill_buff, length, fill_time)); - fill_buff.resize(0); - CHECK_FALSE(buffers.fill(fill_buff, length, fill_time)); // too early - // to pull - // again - fill_buff.resize(0); - buffers.addBuffer(input_buffer, length, time + time_inc); - CHECK(buffers.fill(fill_buff, length, fill_time)); - fill_buff.resize(0); -} - -TEST_CASE("fullFillAppleSizeSend") -{ - fullFill buffers; - - const int input_length = 512 * 1 * sizeof(uint16_t); - uint8_t input_buffer[input_length]; - - const int fill_length = 480 * 1 * sizeof(uint16_t); - std::vector fill_buffer; - - uint64_t time = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - uint64_t add_time = 10667; - - uint64_t fill_time; - - buffers.addBuffer(input_buffer, input_length, time); - CHECK(buffers.fill(fill_buffer, fill_length, fill_time)); - fill_buffer.resize(0); - buffers.addBuffer(input_buffer, input_length, time + (1 * add_time)); - CHECK(buffers.fill(fill_buffer, fill_length, fill_time)); - fill_buffer.resize(0); - buffers.addBuffer(input_buffer, input_length, time + (2 * add_time)); - CHECK(buffers.fill(fill_buffer, fill_length, fill_time)); - fill_buffer.resize(0); - buffers.addBuffer(input_buffer, input_length, time + (3 * add_time)); - CHECK(buffers.fill(fill_buffer, fill_length, fill_time)); - fill_buffer.resize(0); -} - -TEST_CASE("fullFillAppleRace") -{ - fullFill buffers; - const int input_length = 512 * 1 * sizeof(uint16_t); - uint8_t input_buffer[input_length]; - const int fill_length = 480 * 1 * sizeof(uint16_t); - std::vector fill_buffer; - - uint64_t time = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - uint64_t add_time = 10667; - uint64_t fill_time; - - buffers.addBuffer(input_buffer, input_length, time); - - const unsigned int read_samples = 480 * 1; - const unsigned int write_samples = 512 * 1; - bool success = true; - - for (uint64_t samples = 0; samples < 48000; samples++) - { - if ((samples % read_samples) == 0) - { - time += add_time; - fill_buffer.resize(0); - bool ret = buffers.fill(fill_buffer, fill_length, fill_time); - if (success) success = ret; - } - - if ((samples % write_samples) == 0) - { - time += add_time; - buffers.addBuffer(input_buffer, input_length, time); - } - } - CHECK_EQ(success, true); -} - -TEST_CASE("fullFillAppleRace2Channels") -{ - fullFill buffers; - const int input_length = 512 * 2 * sizeof(uint16_t); - uint8_t input_buffer[input_length]; - const int fill_length = 480 * 2 * sizeof(uint16_t); - std::vector fill_buffer; - - uint64_t time = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - uint64_t add_time = 10667; - uint64_t fill_time; - - buffers.addBuffer(input_buffer, input_length, time); - - const unsigned int read_samples = 480 * 2; - const unsigned int write_samples = 512 * 2; - bool success = true; - - for (uint64_t samples = 0; samples < 48000; samples++) - { - if ((samples % read_samples) == 0) - { - fill_buffer.resize(0); - bool ret = buffers.fill(fill_buffer, fill_length, fill_time); - if (success) success = ret; - } - - if ((samples % write_samples) == 0) - { - time += add_time; - buffers.addBuffer(input_buffer, input_length, time); - } - } - CHECK_EQ(success, true); -} - -TEST_CASE("fullFillSamplesToMicroseconds") -{ - fullFill buffers; - int channels = 2; - buffers.sample_divisor = channels * sizeof(float); - - uint64_t time = 1000000; - unsigned int read_front = 0; - CHECK_EQ(time, buffers.calculate_timestamp(read_front, time)); - - read_front = channels * sizeof(float); // single sample - CHECK_EQ(time + 20, buffers.calculate_timestamp(read_front, time)); - - read_front = 10 * channels * sizeof(float); - CHECK_EQ(time + 208, buffers.calculate_timestamp(read_front, time)); -} \ No newline at end of file diff --git a/test/jitter.cc b/test/jitter.cc deleted file mode 100644 index 05a5ed0..0000000 --- a/test/jitter.cc +++ /dev/null @@ -1,589 +0,0 @@ -#include -#include -#include -#include "jitter.hh" -#include "jitter_queues.hh" -#include "opus_assembler.hh" - -using namespace neo_media; - -void pushPacket(Jitter &jitter, - uint64_t seq, - uint64_t sourceID, - std::chrono::steady_clock::time_point now, - bool silence) -{ - PacketPointer packet = std::make_unique(); - packet->mediaType = Packet::MediaType::F32; - packet->encodedSequenceNum = seq; - packet->sourceID = sourceID; - packet->clientID = 10; - - int audio_buff_size = 480 * 2 * sizeof(float); - auto *buffer = new uint8_t[audio_buff_size]; - if (silence) - memset(buffer, 0, audio_buff_size); - else - memset(buffer, 35, audio_buff_size); - - packet->data.resize(0); - packet->data.reserve(audio_buff_size); - std::copy( - &buffer[0], &buffer[audio_buff_size], back_inserter(packet->data)); - jitter.push(std::move(packet), now); -} - -void pushPacket(Jitter &jitter, - uint64_t seq, - uint64_t sourceID, - std::chrono::steady_clock::time_point now) -{ - pushPacket(jitter, seq, sourceID, now, false); -} - -void pushAndWork(Jitter &jitter, - uint64_t seq, - uint64_t sourceID, - std::chrono::steady_clock::time_point &now) -{ - pushPacket(jitter, seq, sourceID, now); - now += std::chrono::milliseconds(5); - jitter.QueueMonitor(now); - now += std::chrono::milliseconds(5); - jitter.QueueMonitor(now); -} - -void pushAndWorkVariableDelay(Jitter &jitter, - uint64_t seq, - uint64_t sourceID, - std::chrono::steady_clock::time_point &now) -{ - pushPacket(jitter, seq, sourceID, now); - now += std::chrono::milliseconds(5); - jitter.QueueMonitor(now); - now += std::chrono::milliseconds(5); - jitter.QueueMonitor(now); -} - -TEST_CASE("ordered_queue_inserts") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - // indicate client is active by popping - PacketPointer packet = jitter.popAudio(sourceID, 3840, clock); - - for (int i = 0; i < 10; i++) - { - pushPacket(jitter, i, sourceID, clock); - clock += std::chrono::milliseconds(10); - } - - for (int i = 0; i < 10; i++) - { - packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(packet->encodedSequenceNum, i); - packet.reset(); - clock += std::chrono::milliseconds(10); - } -} - -TEST_CASE("unordered_queue_inserts") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - - int sourceID = 5; - std::vector seqs = {1, 3, 2, 7, 5, 6, 8, 10, 9, 4}; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - PacketPointer packet = jitter.popAudio(sourceID, 3840, clock); - - for (auto seq_num : seqs) - { - pushPacket(jitter, seq_num, sourceID, clock); - clock += std::chrono::milliseconds(10); - } - - int seq_num = 0; - for (auto it : seqs) - { - ++seq_num; - packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(packet->encodedSequenceNum, seq_num); - packet.reset(); - clock += std::chrono::milliseconds(10); - } -} - -TEST_CASE("insert_opus_plc") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - PacketPointer packet = jitter.popAudio(sourceID, 3840, clock); - - std::vector seq = {1, 3, 6, 11}; - for (auto seq_num : seq) - { - pushPacket(jitter, seq_num, sourceID, clock); - clock += std::chrono::milliseconds(10); - } - - int seq_num = 0; - for (int i = 0; i < 11; i++) - { - ++seq_num; - - std::shared_ptr frame = jitter.audio.mq.front(); - if (std::find(std::begin(seq), std::end(seq), seq_num) != std::end(seq)) - CHECK_EQ(frame->type, MetaFrame::Type::media); - else - CHECK_EQ(frame->type, MetaFrame::Type::plc_gen); - - packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(packet->encodedSequenceNum, seq_num); - packet.reset(); - clock += std::chrono::milliseconds(10); - } -} - -TEST_CASE("dont_jitter_old_packet") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushPacket(jitter, 1, sourceID, clock); - clock += std::chrono::milliseconds(10); - - pushPacket(jitter, 2, sourceID, clock); - clock += std::chrono::milliseconds(10); - - PacketPointer packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(1, packet->encodedSequenceNum); - - pushPacket(jitter, 1, sourceID, clock); - clock += std::chrono::milliseconds(10); - - packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(2, packet->encodedSequenceNum); -} - -TEST_CASE("inital_hold_back") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushAndWork(jitter, 1, sourceID, clock); - PacketPointer packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(packet->encodedSequenceNum, 0); - - pushAndWork(jitter, 2, sourceID, clock); - packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(packet->encodedSequenceNum, 1); - - pushAndWork(jitter, 3, sourceID, clock); - packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_EQ(packet->encodedSequenceNum, 2); -} - -TEST_CASE("adjust_jitter_no_pop") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - for (int i = 0; i < 10; i++) - { - pushAndWork(jitter, i, sourceID, clock); - } - CHECK_EQ(jitter.audio.mq.size(), 2); - - // jitter.setDelayMode(Jitter::DelayMode::listen); - for (int i = 10; i < 30; i++) - { - pushAndWork(jitter, i, sourceID, clock); - } - CHECK_EQ(jitter.audio.mq.size(), 15); -} - -TEST_CASE("update_jitter_values") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - uint64_t sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - for (int i = 1; i < 20; i++) - { - pushPacket(jitter, i, sourceID, clock); - clock += std::chrono::milliseconds(10 + i); - } - jitter.audio_jitter.updateJitterValues(jitter.audio.mq, - jitter.audio.getMsPerAudioPacket()); - - auto pop_items = jitter.audio_jitter.jitter_values.size(); - for (size_t i = 0; i < pop_items; i++) - { - long value = jitter.audio_jitter.jitter_values.front(); - CHECK_EQ(i + 1, value); - jitter.audio_jitter.jitter_values.pop_front(); - } -} - -TEST_CASE("correlation_coefficient_flat") -{ - LeakyBucket bucket; - LeakyBucket::Tracker tracker; - - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - for (int i = 0; i < 100; i++) - { - tracker.emplace_back(1, clock); - clock += std::chrono::milliseconds(10); - } -} - -TEST_CASE("correlation_coefficient_increasing") -{ - LeakyBucket bucket; - LeakyBucket::Tracker tracker; - - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - for (int i = 0; i < 100; i++) - { - tracker.emplace_back(i, clock); - clock += std::chrono::milliseconds(10); - } -} - -TEST_CASE("correlation_coefficient_decrasing") -{ - LeakyBucket bucket; - LeakyBucket::Tracker tracker; - - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - for (int i = 100; i > 0; i--) - { - tracker.emplace_back(i, clock); - clock += std::chrono::milliseconds(10); - } -} - -TEST_CASE("correlation_prefect_flow_active_speaker") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - for (int i = 1; i < 100; i++) - { - pushAndWork(jitter, i, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - } -} - -TEST_CASE("correlation_prefect_flow_listener") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - // jitter.setDelayMode(Jitter::DelayMode::listen); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushAndWork(jitter, 1, sourceID, clock); - pushAndWork(jitter, 2, sourceID, clock); - - for (int i = 3; i < 300; i++) - { - pushAndWork(jitter, i, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - } -} - -TEST_CASE("sensible_src_ratio_for_pefect_stream") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - // jitter.setDelayMode(Jitter::DelayMode::listen); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushAndWork(jitter, 1, sourceID, clock); - pushAndWork(jitter, 2, sourceID, clock); - - for (int i = 3; i < 30; i++) - { - pushAndWork(jitter, i, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - } - pushAndWork(jitter, 30, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - - double src_ratio = jitter.bucket.getSrcRatio(); - REQUIRE(src_ratio == doctest::Approx(1.0)); -} - -TEST_CASE("normal_src_ratio_small_burst") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushAndWork(jitter, 1, sourceID, clock); - pushAndWork(jitter, 2, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - - for (int i = 3; i < 6; i++) - { - pushPacket(jitter, i, sourceID, clock); - } - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - - double src_ratio = jitter.bucket.getSrcRatio(); - REQUIRE(src_ratio == doctest::Approx(1.0)); -} - -TEST_CASE("normal_src_ratio_larger_burst_but_not_allowed_to_change_yet") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushAndWork(jitter, 1, sourceID, clock); - pushAndWork(jitter, 2, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - - for (int i = 3; i < 8; i++) - { - pushPacket(jitter, i, sourceID, clock); - } - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - - double src_ratio = jitter.bucket.getSrcRatio(); - REQUIRE(src_ratio == doctest::Approx(1.0)); -} - -TEST_CASE("burst_80_ms_increase_buffer_size") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushAndWork(jitter, 1, sourceID, clock); - pushAndWork(jitter, 2, sourceID, clock); - - for (int i = 3; i < 100; i++) - { - pushAndWork(jitter, i, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - } - clock += std::chrono::milliseconds(7 * 10); - for (int i = 100; i < 108; i++) - { - pushPacket(jitter, i, sourceID, clock); - } - clock += std::chrono::milliseconds(5); - jitter.QueueMonitor(clock); - clock += std::chrono::milliseconds(5); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - jitter.QueueMonitor(clock); - - unsigned int queue_size = jitter.audio.mq.size(); - CHECK_EQ(queue_size, 8); -} - -TEST_CASE("empty_pop_test_splash_80ms_resample_20ms") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - pushAndWork(jitter, 1, sourceID, clock); - pushAndWork(jitter, 2, sourceID, clock); - - // normal flow - for (int i = 3; i < 100; i++) - { - pushAndWork(jitter, i, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - } - - // huge wifi jitter - no packets coming in - for (int i = 100; i < 108; i++) - { - clock += std::chrono::milliseconds(10); - auto packet = jitter.popAudio(sourceID, 3840, clock); // empty - // pops - - // not good - // stuff - packet.reset(); - } - - // burst of packets coming in - for (int i = 100; i < 108; i++) - { - pushPacket(jitter, i, sourceID, clock); - } - - CHECK_EQ(jitter.audio.mq.size(), 8); - - // normal flow - for (int i = 108; i < 500; i++) - { - pushAndWork(jitter, i, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - - // simulate 10% incrased jitter speed by poping extra every 10th packet - if (jitter.bucket.getSrcRatio() < 1.0) - { - if ((i % 10) == 0) - { - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - } - } - } - pushAndWork(jitter, 300, sourceID, clock); - auto packet = jitter.popAudio(sourceID, 3840, clock); - packet.reset(); - - CHECK_EQ(jitter.audio.mq.size(), 0); -} - -TEST_CASE("audio_frame_sizes") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - CHECK_EQ(jitter.audio.getFrameSize(), 3840); - - Jitter jitter2( - 48000, 1, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - CHECK_EQ(jitter2.audio.getFrameSize(), 1920); - - Jitter jitter3( - 41000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - CHECK_EQ(jitter3.audio.getFrameSize(), 3280); -} - -TEST_CASE("remove_200ms_silence_from_1000ms") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - // indicate client is active by popping - PacketPointer packet = jitter.popAudio(sourceID, 3840, clock); - - for (int i = 0; i < 21; i++) - { - pushPacket(jitter, i, sourceID, clock, true); - clock += std::chrono::milliseconds(10); - } - - int queue_size = 21; - - for (int i = 0; i < 100; i++) - { - packet = jitter.popAudio(sourceID, 3840, clock); - CHECK_LE(jitter.audio.mq.size(), queue_size); - queue_size = jitter.audio.mq.size(); - packet.reset(); - pushPacket(jitter, i + 21, sourceID, clock, true); - clock += std::chrono::milliseconds(10); - } - CHECK_EQ(jitter.audio.mq.size(), 2); -} - -TEST_CASE("inject_silence_after_listen_mode") -{ - Jitter jitter( - 48000, 2, Packet::MediaType::F32, 1920, 1080, 1, nullptr, nullptr); - int sourceID = 5; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - - // indicate client is active by popping - PacketPointer packet = jitter.popAudio(sourceID, 3840, clock); - - int queue_size = jitter.audio.mq.size(); - - for (int i = 0; i < 21; i++) - { - CHECK_GE(jitter.audio.mq.size(), queue_size); - packet = jitter.popAudio(sourceID, 3840, clock); - pushPacket(jitter, i, sourceID, clock, true); - clock += std::chrono::milliseconds(10); - queue_size = jitter.audio.mq.size(); - } - - // jitter.setDelayMode(JitterInterface::DelayMode::listen); - for (int i = 21; i < 42; i++) - { - CHECK_GE(jitter.audio.mq.size(), queue_size); - packet = jitter.popAudio(sourceID, 3840, clock); - pushPacket(jitter, i, sourceID, clock, true); - clock += std::chrono::milliseconds(10); - queue_size = jitter.audio.mq.size(); - } -} \ No newline at end of file diff --git a/test/playout.cc b/test/playout.cc deleted file mode 100644 index ff4b0db..0000000 --- a/test/playout.cc +++ /dev/null @@ -1,417 +0,0 @@ -#include -#include "playout_sync.hh" -#include "jitter_queues.hh" - -using namespace neo_media; - -void pushVideoPacket(MetaQueue &queue, - uint64_t seq, - uint64_t sourceID, - Packet::VideoFrameType frame_type, - uint64_t source_time, - std::chrono::steady_clock::time_point now) -{ - auto frame = std::make_shared(MetaFrame::Type::media, now); - frame->packet = std::make_unique(); - frame->packet->mediaType = Packet::MediaType::AV1; - frame->packet->encodedSequenceNum = seq; - frame->packet->sourceID = sourceID; - frame->packet->clientID = 10; - frame->packet->videoFrameType = frame_type; - frame->packet->sourceRecordTime = source_time; - - int video_frame_size = 1000; - auto *buffer = new uint8_t[video_frame_size]; - memset(buffer, 0, video_frame_size); - frame->packet->data.resize(0); - frame->packet->data.reserve(video_frame_size); - std::copy(&buffer[0], - &buffer[video_frame_size], - back_inserter(frame->packet->data)); - queue.Q.push_back(frame); -} - -TEST_CASE("sync_initial_pop_IDR_video_only") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - uint64_t source_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - pushVideoPacket(mq, 1, 5, Packet::VideoFrameType::Idr, source_time, clock); - Sync::sync_action action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop); -} - -TEST_CASE("sync_discard_until_IDR_video_only") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - uint64_t source_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - for (int i = 1; i < 5; i++) - { - pushVideoPacket( - mq, i, 5, Packet::VideoFrameType::None, source_time, clock); - clock += std::chrono::milliseconds(33); - } - pushVideoPacket(mq, 5, 5, Packet::VideoFrameType::Idr, source_time, clock); - Sync::sync_action action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop_discard); - CHECK_EQ(num_pop, 4); - - for (int i = 1; i < 5; i++) - { - mq.Q.pop_front(); - } - num_pop = 0; - action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop); -} - -TEST_CASE("sync_pop_in_order_video_only") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - Sync::sync_action action; - - uint64_t source_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - pushVideoPacket(mq, 1, 5, Packet::VideoFrameType::Idr, source_time, clock); - action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop); - CHECK_EQ(num_pop, 1); - sync.video_popped(source_time, mq.getNextSeq(), clock); - mq.Q.pop_front(); - - for (int i = 2; i < 10; i++) - { - clock += std::chrono::milliseconds(33); - source_time += 33000; - pushVideoPacket( - mq, i, 5, Packet::VideoFrameType::None, source_time, clock); - action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop_video_only); - CHECK_EQ(num_pop, 1); - sync.video_popped(source_time, mq.getNextSeq(), clock); - mq.Q.pop_front(); - } -} - -TEST_CASE("sync_freeze_until_IDR_video_only") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - Sync::sync_action action; - - uint64_t source_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - sync.video_popped(source_time, 1, clock); // avoid initial pop - and - // initial IDR - - unsigned int to_pop = 0; - for (int seq = 3; seq < 10; seq++) - { - pushVideoPacket( - mq, seq, 5, Packet::VideoFrameType::None, source_time, clock); - action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop_discard); - CHECK_EQ(num_pop, ++to_pop); - } - pushVideoPacket(mq, 10, 5, Packet::VideoFrameType::Idr, source_time, clock); - action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop_discard); - CHECK_EQ(num_pop, to_pop); - - for (unsigned int pops = 0; pops < to_pop; pops++) - { - sync.video_popped(mq.getNextSourceTime(), mq.getNextSeq(), clock); - mq.Q.pop_front(); - } - action = sync.getVideoAction(10, 33, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop_video_only); - CHECK_EQ(num_pop, 1); -} - -TEST_CASE("sync_follow_audio_exact_match") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - Sync::sync_action action; - - uint64_t source_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - sync.video_popped(source_time, 1, clock); - pushVideoPacket( - mq, 2, 5, Packet::VideoFrameType::Idr, source_time + 33000, clock); - pushVideoPacket( - mq, 3, 5, Packet::VideoFrameType::Idr, source_time + 66000, clock); - - int seq = 1; - sync.audio_popped(source_time, seq++, clock); - action = sync.getVideoAction(10000, 33000, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::hold); - CHECK_EQ(num_pop, 0); - - source_time += 10000; - sync.audio_popped(source_time, seq++, clock); // 10000 microseconds - action = sync.getVideoAction(10000, 33000, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::hold); - CHECK_EQ(num_pop, 0); - - source_time += 10000; - sync.audio_popped(source_time, seq++, clock); // 10000 microseconds - action = sync.getVideoAction(10000, 33000, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::hold); - CHECK_EQ(num_pop, 0); - - source_time += 10000; - sync.audio_popped(source_time, seq++, clock); // 10000 microseconds - action = sync.getVideoAction(10000, 33000, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::hold); - CHECK_EQ(num_pop, 0); - - source_time += 10000; - sync.audio_popped(source_time, seq++, clock); // 10000 microseconds - action = sync.getVideoAction(10000, 33000, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop); - CHECK_EQ(num_pop, 1); - sync.video_popped(mq.getNextSourceTime(), mq.getNextSeq(), clock); - mq.Q.pop_front(); -} - -TEST_CASE("sync_follow_audio_10ms_30fps") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - Sync::sync_action action; - - uint64_t source_start_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - int audio_seq = 1; - int video_seq = 1; - uint64_t audio_interval = 10000; - uint64_t video_interval = 33333; - sync.video_popped(source_start_time, video_seq++, clock); - uint64_t video_time = source_start_time + video_interval; - uint64_t three_seconds = source_start_time + 3000000; - for (uint64_t time = source_start_time; time < three_seconds; - time += audio_interval) - { - sync.audio_popped(time, audio_seq++, clock); - - if (video_time < time) - { - pushVideoPacket(mq, - video_seq, - 5, - Packet::VideoFrameType::None, - video_time, - clock); - action = sync.getVideoAction( - audio_interval, video_interval, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::pop); - CHECK_EQ(num_pop, 1); - sync.video_popped(video_time, video_seq, clock); - ++video_seq; - video_time += video_interval; - mq.Q.pop_front(); - } - else - { - action = sync.getVideoAction( - audio_interval, video_interval, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::hold); - CHECK_FALSE(num_pop); - } - } -} - -TEST_CASE("sync_follow_audio_IDR_freeze") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point clock = - std::chrono::steady_clock::now(); - Sync::sync_action action; - - uint64_t source_start_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - int audio_seq = 1; - int video_seq = 1; - uint64_t audio_interval = 10000; - uint64_t video_interval = 33333; - uint64_t video_time = source_start_time + video_interval; - uint64_t video_pop_time = video_time; - uint64_t three_seconds = source_start_time + 3000000; - sync.video_popped(source_start_time, video_seq++, clock); - Packet::VideoFrameType frame = Packet::VideoFrameType::None; - bool errorInStream = false; - - for (uint64_t time = (source_start_time + audio_interval); - time < three_seconds; - time += audio_interval) - { - sync.audio_popped(time, audio_seq++, clock); - - // every 20th frame is an IDR - if (video_seq % 20 == 0) - { - frame = Packet::VideoFrameType::Idr; - errorInStream = false; - } - else - frame = Packet::VideoFrameType::None; - - // loose every 8th video frame - if (video_seq % 8 == 0) - { - ++video_seq; - video_time += video_interval; - errorInStream = true; - } - - if (video_pop_time < time) - { - pushVideoPacket(mq, video_seq, 5, frame, video_time, clock); - - action = sync.getVideoAction( - audio_interval, video_interval, mq, num_pop, clock); - if (errorInStream) - { - if (num_pop > 0) - { - CHECK_EQ(action, Sync::sync_action::pop_discard); - for (unsigned int i = 0; i < num_pop; i++) - { - mq.Q.pop_front(); - } - } - } - else - { - CHECK_EQ(action, Sync::sync_action::pop); - sync.video_popped( - mq.getNextSourceTime(), mq.getNextSeq(), clock); - mq.Q.pop_front(); - ++video_seq; - video_time += video_interval; - } - video_pop_time += video_interval; - } - else - { - if (errorInStream) - { - action = sync.getVideoAction( - audio_interval, video_interval, mq, num_pop, clock); - if (num_pop > 0) - { - CHECK_EQ(action, Sync::sync_action::pop_discard); - for (unsigned int i = 0; i < num_pop; i++) - { - mq.Q.pop_front(); - } - } - else - CHECK_EQ(action, Sync::sync_action::hold); - } - else - { - action = sync.getVideoAction( - audio_interval, video_interval, mq, num_pop, clock); - CHECK_EQ(action, Sync::sync_action::hold); - } - } - } -} - -TEST_CASE("video only switch after audio stopping") -{ - Sync sync; - MetaQueue mq; - unsigned int num_pop; - std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - Sync::sync_action action; - - uint64_t source_start_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - source_start_time -= 200 * 1000; // source time happens before pop - // time - - uint64_t audio_seq = 1; - uint64_t video_seq = 1; - - sync.audio_popped(source_start_time, audio_seq++, now); - source_start_time += 23 * 1000; - now += std::chrono::milliseconds(13); - pushVideoPacket(mq, - video_seq, - 1, - Packet::VideoFrameType::Idr, - source_start_time, - now); // initial packet to be popped - mq.pop(now); - sync.video_popped(source_start_time, video_seq, now); // popping IDR - pushVideoPacket(mq, - ++video_seq, - 1, - Packet::VideoFrameType::None, - source_start_time, - now); // no new audio - this packet will be popped - // after 400ms - - // no more popping audio - let's start popping video - for (uint64_t new_source = source_start_time; - new_source < (source_start_time + (33000 * 11)); - new_source += 33000) - { - now += std::chrono::milliseconds(33); - action = sync.getVideoAction(0, 0, mq, num_pop, now); - CHECK_EQ(action, Sync::sync_action::hold); - ++video_seq; - } - - now += std::chrono::milliseconds(33); - action = sync.getVideoAction(0, 0, mq, num_pop, now); - CHECK_EQ(action, Sync::sync_action::pop_video_only); -} \ No newline at end of file diff --git a/test/resampler.cc b/test/resampler.cc deleted file mode 100644 index 1446320..0000000 --- a/test/resampler.cc +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include -#include "resampler.hh" - -using namespace neo_media; - -void runResampleOnFile(const int frame_size, double ratio) -{ - std::ifstream sound_file; - - sound_file.open(DATA_PATH + std::string("test_sound-float32-1-2633.csv")); - CHECK_FALSE(sound_file.fail()); - - std::vector values; - float value; - char delim; - if (sound_file.is_open()) - { - while (!sound_file.eof()) - { - sound_file >> value; - values.push_back(value); - sound_file >> delim; - } - - Resampler resampler; - for (unsigned int i = 0; i < values.size(); i += frame_size) - { - PacketPointer packet = std::make_unique(); - packet->mediaType = Packet::MediaType::F32; - packet->encodedSequenceNum = 1; - packet->data.resize(0); - int size_bytes = frame_size * sizeof(float); - packet->data.reserve(size_bytes); - uint8_t *bytes = (uint8_t *) &values[i]; - std::copy(bytes, bytes + size_bytes, back_inserter(packet->data)); - - int pre_size = packet->data.size(); - resampler.resample(packet.get(), 1, ratio); - int post_size = packet->data.size(); - CHECK_EQ(ratio * pre_size, post_size); - packet.reset(); - } - sound_file.close(); - } -} - -TEST_CASE("upsample_shorten_2ms_on_10ms_frame") -{ - runResampleOnFile(480, 0.8); -} - -TEST_CASE("upsample_shorten_10ms_on_20ms_frame") -{ - runResampleOnFile(960, 0.5); -} - -TEST_CASE("downsample_extend_2ms_on_10ms_frame") -{ - runResampleOnFile(480, 1.2); -} -TEST_CASE("downsample_extend_10ms_on_20ms_frame") -{ - runResampleOnFile(960, 1.5); -}