neuray API Programmer's Manual

Example for the RTMP server

[Previous] [Next] [Up]

This example reads a flash video file (.flv) and serves the frames via an RTMP server.

New Topics

  • Running 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;
}

[Previous] [Next] [Up]