/examples/voip/Server.cpp
C++ | 200 lines | 109 code | 30 blank | 61 comment | 21 complexity | 607bd9dd5a8409165a6a3134e427c52d MD5 | raw file
- ////////////////////////////////////////////////////////////
- // Headers
- ////////////////////////////////////////////////////////////
- #include <SFML/Audio.hpp>
- #include <SFML/Network.hpp>
- #include <iomanip>
- #include <iostream>
- #include <iterator>
- const sf::Uint8 audioData = 1;
- const sf::Uint8 endOfStream = 2;
- ////////////////////////////////////////////////////////////
- /// Customized sound stream for acquiring audio data
- /// from the network
- ////////////////////////////////////////////////////////////
- class NetworkAudioStream : public sf::SoundStream
- {
- public:
- ////////////////////////////////////////////////////////////
- /// Default constructor
- ///
- ////////////////////////////////////////////////////////////
- NetworkAudioStream() :
- m_offset (0),
- m_hasFinished(false)
- {
- // Set the sound parameters
- initialize(1, 44100);
- }
- ////////////////////////////////////////////////////////////
- /// Run the server, stream audio data from the client
- ///
- ////////////////////////////////////////////////////////////
- void start(unsigned short port)
- {
- if (!m_hasFinished)
- {
- // Listen to the given port for incoming connections
- if (m_listener.listen(port) != sf::Socket::Done)
- return;
- std::cout << "Server is listening to port " << port << ", waiting for connections... " << std::endl;
- // Wait for a connection
- if (m_listener.accept(m_client) != sf::Socket::Done)
- return;
- std::cout << "Client connected: " << m_client.getRemoteAddress() << std::endl;
- // Start playback
- play();
- // Start receiving audio data
- receiveLoop();
- }
- else
- {
- // Start playback
- play();
- }
- }
- private:
- ////////////////////////////////////////////////////////////
- /// /see SoundStream::OnGetData
- ///
- ////////////////////////////////////////////////////////////
- virtual bool onGetData(sf::SoundStream::Chunk& data)
- {
- // We have reached the end of the buffer and all audio data have been played: we can stop playback
- if ((m_offset >= m_samples.size()) && m_hasFinished)
- return false;
- // No new data has arrived since last update: wait until we get some
- while ((m_offset >= m_samples.size()) && !m_hasFinished)
- sf::sleep(sf::milliseconds(10));
- // Copy samples into a local buffer to avoid synchronization problems
- // (don't forget that we run in two separate threads)
- {
- sf::Lock lock(m_mutex);
- m_tempBuffer.assign(m_samples.begin() + m_offset, m_samples.end());
- }
- // Fill audio data to pass to the stream
- data.samples = &m_tempBuffer[0];
- data.sampleCount = m_tempBuffer.size();
- // Update the playing offset
- m_offset += m_tempBuffer.size();
- return true;
- }
- ////////////////////////////////////////////////////////////
- /// /see SoundStream::OnSeek
- ///
- ////////////////////////////////////////////////////////////
- virtual void onSeek(sf::Time timeOffset)
- {
- m_offset = timeOffset.asMilliseconds() * getSampleRate() * getChannelCount() / 1000;
- }
- ////////////////////////////////////////////////////////////
- /// Get audio data from the client until playback is stopped
- ///
- ////////////////////////////////////////////////////////////
- void receiveLoop()
- {
- while (!m_hasFinished)
- {
- // Get waiting audio data from the network
- sf::Packet packet;
- if (m_client.receive(packet) != sf::Socket::Done)
- break;
- // Extract the message ID
- sf::Uint8 id;
- packet >> id;
- if (id == audioData)
- {
- // Extract audio samples from the packet, and append it to our samples buffer
- const sf::Int16* samples = reinterpret_cast<const sf::Int16*>(static_cast<const char*>(packet.getData()) + 1);
- std::size_t sampleCount = (packet.getDataSize() - 1) / sizeof(sf::Int16);
- // Don't forget that the other thread can access the sample array at any time
- // (so we protect any operation on it with the mutex)
- {
- sf::Lock lock(m_mutex);
- std::copy(samples, samples + sampleCount, std::back_inserter(m_samples));
- }
- }
- else if (id == endOfStream)
- {
- // End of stream reached: we stop receiving audio data
- std::cout << "Audio data has been 100% received!" << std::endl;
- m_hasFinished = true;
- }
- else
- {
- // Something's wrong...
- std::cout << "Invalid packet received..." << std::endl;
- m_hasFinished = true;
- }
- }
- }
- ////////////////////////////////////////////////////////////
- // Member data
- ////////////////////////////////////////////////////////////
- sf::TcpListener m_listener;
- sf::TcpSocket m_client;
- sf::Mutex m_mutex;
- std::vector<sf::Int16> m_samples;
- std::vector<sf::Int16> m_tempBuffer;
- std::size_t m_offset;
- bool m_hasFinished;
- };
- ////////////////////////////////////////////////////////////
- /// Launch a server and wait for incoming audio data from
- /// a connected client
- ///
- ////////////////////////////////////////////////////////////
- void doServer(unsigned short port)
- {
- // Build an audio stream to play sound data as it is received through the network
- NetworkAudioStream audioStream;
- audioStream.start(port);
- // Loop until the sound playback is finished
- while (audioStream.getStatus() != sf::SoundStream::Stopped)
- {
- // Leave some CPU time for other threads
- sf::sleep(sf::milliseconds(100));
- }
- std::cin.ignore(10000, '\n');
- // Wait until the user presses 'enter' key
- std::cout << "Press enter to replay the sound..." << std::endl;
- std::cin.ignore(10000, '\n');
- // Replay the sound (just to make sure replaying the received data is OK)
- audioStream.play();
- // Loop until the sound playback is finished
- while (audioStream.getStatus() != sf::SoundStream::Stopped)
- {
- // Leave some CPU time for other threads
- sf::sleep(sf::milliseconds(100));
- }
- }