Example for the RTMP server
This example reads a flash video file (.flv) and serves the frames via an RTMP server.
Detailed Description
- Running an RTMP server
-
This example provides a very simple RTMP server which just serves one particular video stream. The frames are being read from a flash video file (.flv) on disk and then sent out to a video client. For example VLC or a flash client.
This examples uses a simple connect handler which always accepts the connection and serves the same video stream. No video plugin is needed since no codec is needed; the video frames in the file are already encoded.
Example Source
Source Code Location: examples/example_rtmp_server_flv.cpp
/****************************************************************************** * Copyright 1986, 2011 NVIDIA Corporation. All rights reserved. *****************************************************************************/ // examples/example_rtmp_server_flv.cpp // // Serves a flash video file (.flv) over the built-in RTMP server #include <mi/neuraylib.h> #include <fstream> #include <iostream> #ifdef MI_PLATFORM_WINDOWS #include <winsock2.h> #else #include <unistd.h> #include <arpa/inet.h> #endif // Include code shared by all examples. #include "example_shared.h" // A simple implementation of the IVideo_data interface. class Video_data : public mi::base::Interface_implement<mi::IVideo_data> { public: // Constructor. // // Takes over ownership of \p data. Video_data( mi::Size size, mi::Uint8* data, bool is_key_frame) : m_data( data), m_data_size( size), m_is_key_frame( is_key_frame) { } // Destructor. // // Releases m_data. ~Video_data() { if( m_data) delete[] m_data; } mi::Uint8* get_data() { return m_data; } mi::Size get_data_size() const { return m_data_size; } bool is_key_frame() const { return m_is_key_frame; } private: mi::Uint8* m_data; mi::Size m_data_size; bool m_is_key_frame; }; // A frame handler which sends out frames from a FLV file. It is called repeatedly with a frequency // of 25 times per second (or as often as indicated by IStream::set_max_render_rate()). class Flv_frame_event_handler : public mi::base::Interface_implement<mi::rtmp::IFrame_event_handler> { public: Flv_frame_event_handler( const char* flv_path) : m_flv_path( flv_path) { } bool handle( mi::rtmp::IStream* stream, INVALID_DOXYREFmi::IVideo_data** out, bool send_queue_is_full) { // Open the file if necessary. if( !m_file.is_open()) { m_file.open( m_flv_path); if( !m_file) return false; // Check file header char header[13]; m_file.read( header, sizeof( header)); if( memcmp( header, "FLV", 3) != 0) return false; // not a proper FLV file m_last_frame = 0; m_last_timestamp = 0; } // Find next video frame while( true) { // Read frame header char bytes[11]; m_file.read( bytes, sizeof( bytes)); if( m_file.eof()) { m_file.close(); return true; // EOF but we return true so it will be played again } char type = bytes[0]; int length = ntohl( *(int*)(&bytes[1])) >> 8; // Check frame type if( type == 0x09) { // video frame // Sleep until it is time to show this frame int timestamp = ntohl( *(int*)(&bytes[4])) >> 8; sleep_until_frame_display_time( timestamp); // Read frame data from file into an instance of Video_data and send it over the // stream. The last argument indicates whether the frame is a key frame or not. char* buffer = new char[length]; m_file.read( buffer, length); *out = new Video_data( length, (mi::Uint8*) buffer, (buffer[0] & 0xf0) == 0x10); } else // not a video frame (probably an audio frame) m_file.seekg( length, std::ios_base::cur); // Consistency check m_file.read( bytes, 4); int tag_length = ntohl( *(int*)(&bytes[0])); if( tag_length != length+11) return false; // corrupt FLV file // If the frame was a video frame we are done. Otherwise proceed to the next frame. if( type == 0x09) return true; } } private: double get_time() { #ifndef MI_PLATFORM_WINDOWS struct timeval tv; gettimeofday(&tv, NULL); return static_cast<double>( tv.tv_sec) + static_cast<double>(tv.tv_usec*1.0e-6); #else LARGE_INTEGER freq,counter; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&counter); return static_cast<double>(counter.QuadPart) / static_cast<double>(freq.QuadPart); #endif } void sleep_until_frame_display_time( int timestamp) { double now = get_time(); double delay = (timestamp - m_last_timestamp) / 1e3; if( delay > (now - m_last_frame)) { #ifndef MI_PLATFORM_WINDOWS ::usleep( (delay - (now - m_last_frame)) * 1e6); #else ::Sleep( static_cast<DWORD>((delay - (now - m_last_frame)) * 1e3)); #endif } m_last_frame = get_time(); m_last_timestamp = timestamp; } const char* m_flv_path; std::ifstream m_file; double m_last_frame; int m_last_timestamp; }; // A stream handler that registers the play and frame handlers above on new streams. class Stream_event_handler : public mi::base::Interface_implement<mi::rtmp::IStream_event_handler> { public: Stream_event_handler( const char* flv_path) : m_flv_path( flv_path) { } bool handle( bool is_create, mi::rtmp::IStream* stream, const mi::IData* command_arguments) { if( is_create) { mi::base::Handle< mi::rtmp::IFrame_event_handler> frame_event_handler( new Flv_frame_event_handler( m_flv_path)); stream->register_frame_event_handler( frame_event_handler.get()); } return true; } private: const char* m_flv_path; }; // A connect handler which accepts all connection requests. A real application might want to check // the URL passed in the arguments. class Connect_event_handler : public mi::base::Interface_implement<mi::rtmp::IConnect_event_handler> { public: Connect_event_handler( const char* flv_path) : m_flv_path( flv_path) { } bool handle( bool is_create, mi::rtmp::IConnection* connection, const mi::IData* command_arguments, const mi::IData* user_arguments) { if( is_create) { mi::base::Handle< mi::rtmp::IStream_event_handler> stream_event_handler( new Stream_event_handler( m_flv_path)); connection->register_stream_event_handler( stream_event_handler.get()); } return true; } private: const char* m_flv_path; }; void run_rtmp_server( mi::base::Handle< mi::neuraylib::INeuray> neuray, const char* flv_path) { // Create a server instance mi::base::Handle< mi::rtmp::IFactory> rtmp_factory( neuray->get_api_component<mi::rtmp::IFactory>()); mi::base::Handle< mi::rtmp::IServer> rtmp_server( rtmp_factory->create_server()); // Install our connect handlers mi::base::Handle< mi::rtmp::IConnect_event_handler> connect_event_handler( new Connect_event_handler( flv_path)); rtmp_server->install( connect_event_handler.get()); // Run server for fixed time interval rtmp_server->start( "0.0.0.0:1935"); sleep_seconds( 300); rtmp_server->shutdown(); } // The example takes the following command line argument: // // example_rtmp_server_flv <path_to_flv_file> // // path_to_flv_file path to the flv file to stream out // int main( int argc, char* argv[]) { // Collect command line parameters if( argc != 2) { std::cerr << "Usage: example_rtmp_server_flv <path_to_flv_file>" << std::endl; return EXIT_FAILURE; } const char* flv_path = argv[1]; // Access the neuray library mi::base::Handle< mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); check_success( neuray.is_valid_interface()); // Start the neuray library check_success( neuray->start( true) == 0); // Serve video flash file via RTMP server run_rtmp_server( neuray, flv_path); // Shut down the neuray library check_success( neuray->shutdown() == 0); neuray = 0; // Unload the neuray library check_success( unload()); return EXIT_SUCCESS; }