NVIDIA Iray API — software examples nvidia_logo_transpbg.gif Up
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
example_rtmp_server.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2020 NVIDIA Corporation. All rights reserved.
3  *****************************************************************************/
4 
5 // examples/example_rtmp_server.cpp
6 //
7 // Serves a flash player (.swf file) over HTTP to a client browser which then connects to the RTMP
8 // server which produces a video stream from the rendering of a scene.
9 //
10 // The example expects the following command line arguments:
11 //
12 // example_rtmp_server <swf_file> <scene_file> <mdl_path> <port>
13 //
14 // swf_file the flash player, the .swf-file included in the examples directory
15 // scene_file some scene file, e.g., main.mi
16 // mdl_path path to the MDL modules, e.g., iray-<version>/mdl
17 // port port for the HTTP server
18 
19 #include <mi/neuraylib.h>
20 
21 // Include code shared by all examples.
22 #include "example_shared.h"
23 // Include an implementation of IRender_target.
25 
26 #include <fstream>
27 #include <iostream>
28 #include <vector>
29 
30 // HTTP server implementation
31 //
32 // The HTTP servers just serves the .swf file.
33 
34 // A simple implementation of the IBuffer interface.
35 class Buffer : public mi::base::Interface_implement<mi::neuraylib::IBuffer>
36 {
37 public:
38  const mi::Uint8* get_data() const { return &m_buffer[0]; }
39 
40  mi::Size get_data_size() const { return m_buffer.size(); }
41 
42  Buffer( const std::vector<mi::Uint8>& content) { m_buffer = content; }
43 
44 private:
45  std::vector<mi::Uint8> m_buffer;
46 };
47 
48 // An HTTP response handler which always sets the content type for flash.
49 class Response_handler : public mi::base::Interface_implement<mi::http::IResponse_handler>
50 {
51 public:
52  void handle( mi::http::IConnection* connection)
53  {
54  mi::http::IResponse* iresponse( connection->get_response());
55  iresponse->set_header( "Content-Type", "application/x-shockwave-flash");
56  }
57 };
58 
59 // An HTTP request handler which always sends the .swf file.
60 class Request_handler : public mi::base::Interface_implement<mi::http::IRequest_handler>
61 {
62 public:
63  Request_handler( const char* swf_file) : m_swf_file( swf_file) { }
64 
65  bool handle( mi::http::IConnection* connection)
66  {
67  std::ifstream file( m_swf_file, std::ios::in|std::ios::binary|std::ios::ate);
68  check_success( file);
69 
70  std::ifstream::pos_type size = file.tellg();
71  std::vector<mi::Uint8> data( static_cast<mi::Size>( size));
72  file.seekg( 0, std::ios::beg);
73  file.read( reinterpret_cast<char*>( &data[0]), size);
74  file.close();
75 
77  connection->enqueue( buffer.get());
78  return true;
79  }
80 
81 private:
82  const char* m_swf_file;
83 };
84 
85 // RTMP server implementation
86 //
87 // The RTMP renders a given scene and interprets mouse movements as camera movements.
88 
89 // An RTMP play event handler that chooses the screen video codec and initializes it with a
90 // predefined window size.
91 class Play_event_handler : public mi::base::Interface_implement<mi::rtmp::IPlay_event_handler>
92 {
93 public:
94  bool handle( bool is_start, mi::rtmp::IStream* stream, mi::neuraylib::IVideo_data** out)
95  {
96  if( is_start) {
97  check_success( stream->use_codec( "screen video"));
99  check_success( codec->init( 512, 384, out) == 0);
100  }
101  else {
103  check_success( codec->close( out) == 0);
104  }
105  return true;
106  }
107 };
108 
109 // An RTMP frame event handler that encodes a frame and gives it to the RTMP server for
110 // sending. Note that this event runs in another thread than the other event handlers, most
111 // importantly the render handler, so care needs to be taken to avoid synchronization issues.
112 class Frame_event_handler : public mi::base::Interface_implement<mi::rtmp::IFrame_event_handler>
113 {
114 public:
115  bool handle(
116  mi::rtmp::IStream* stream, mi::neuraylib::IVideo_data** out, bool send_queue_is_full)
117  {
118  if (send_queue_is_full) // we do not want to increase buffering
119  return true;
121  mi::neuraylib::ICanvas* canvas = 0;
122  {
123  mi::base::Lock::Block block( &m_cached_canvas_lock);
124  canvas = m_cached_canvas.get();
125  if ( !canvas)
126  return true;
127  canvas->retain();
128  }
129  bool result = (codec->encode_canvas( canvas, out) == 0);
130  canvas->release();
131  return result;
132  }
134  {
135  mi::base::Lock::Block block( &m_cached_canvas_lock);
136  m_cached_canvas = make_handle_dup( new_canvas);
137  }
138 private:
139  mi::base::Lock m_cached_canvas_lock;
141 };
142 
143 
144 // An RTMP render event handler that renders a given scene into a canvas and saves it for the
145 // frame event handler to encode.
146 class Render_event_handler : public mi::base::Interface_implement<mi::rtmp::IRender_event_handler>
147 {
148 public:
151  : m_neuray( neuray, mi::base::DUP_INTERFACE),
152  m_scope( scope, mi::base::DUP_INTERFACE),
153  m_frame_handler( handler, mi::base::DUP_INTERFACE)
154  {
156  {
158  transaction->edit<mi::neuraylib::IScene>( "the_scene"));
159  m_render_context = scene->create_render_context( transaction.get(), "iray");
160  check_success( m_render_context.is_valid_interface());
161  mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
162  scheduler_mode->set_c_str( "interactive");
163  m_render_context->set_option( "scheduler_mode", scheduler_mode.get());
164  mi::base::Handle<mi::IFloat32> interval( transaction->create<mi::IFloat32>());
165  interval->set_value( 0.1f);
166  m_render_context->set_option( "interactive_update_interval", interval.get());
167  }
168  transaction->commit();
169  }
170 
171  bool handle( mi::rtmp::IStream* /*stream*/)
172  {
174  {
178  new Render_target( image_api.get(), "Color", 512, 384));
180  m_render_context->render( transaction.get(), render_target.get(), 0) >= 0);
181 
182  mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
183  m_frame_handler->update_canvas( canvas.get());
184  }
185  transaction->commit();
186  return true;
187  }
188 
189 private:
194 };
195 
196 // An RTMP stream event handler that registers the play and render event handlers above.
197 class Stream_event_handler : public mi::base::Interface_implement<mi::rtmp::IStream_event_handler>
198 {
199 public:
201  : m_neuray( neuray, mi::base::DUP_INTERFACE), m_scope( scope, mi::base::DUP_INTERFACE) { }
202 
203  bool handle(
204  bool is_create, mi::rtmp::IStream* stream,
205  const mi::IData* /*command_arguments*/)
206  {
207  if( is_create) {
209  new Play_event_handler());
210  stream->register_play_event_handler( play_event_handler.get());
213  new Render_event_handler( m_neuray.get(), m_scope.get(),frame_event_handler.get()));
214  stream->register_render_event_handler( render_event_handler.get());
215  stream->register_frame_event_handler( frame_event_handler.get());
216  }
217  return true;
218  }
219 
220 private:
223 };
224 
225 // An RTMP call event handler that moves the camera according to the arguments 'pan_x' and 'pan_y'.
226 class Call_event_handler : public mi::base::Interface_implement<mi::rtmp::ICall_event_handler>
227 {
228 public:
229  Call_event_handler( mi::neuraylib::IScope* scope) : m_scope( scope, mi::base::DUP_INTERFACE) { }
230 
231  bool handle(
232  mi::rtmp::IConnection* /*connection*/,
233  const char* /*procedure_name*/,
234  const mi::IData* /*command_arguments*/,
235  const mi::IData* user_arguments,
236  mi::IData** /*response_arguments*/)
237  {
239  {
240  // The "camera" name matches the camera in main.mi in the examples directory.
242  transaction->edit<mi::neuraylib::ICamera>( "camera"));
244  mi::base::Handle<const mi::IMap> imap( user_arguments->get_interface<const mi::IMap>());
245  check_success( imap.is_valid_interface());
246  mi::base::Handle<const mi::ISint32> pan_x( imap->get_value<mi::ISint32>( "pan_x"));
247  if ( pan_x) {
248  mi::Float64 x = camera->get_offset_x();
249  camera->set_offset_x( x - pan_x->get_value<mi::Sint32>());
250  // The example client also demonstrates how to send/parse a double.
252  imap->get_value<mi::IFloat64>( "pan_xd"));
253  if( pan_xd) {
254  mi::Float64 xd = pan_xd->get_value<mi::Float64>();
255  check_success( mi::Sint32(xd) == pan_x->get_value<mi::Sint32>());
256  }
257  }
258  mi::base::Handle<const mi::ISint32> pan_y( imap->get_value<mi::ISint32>( "pan_y"));
259  if( pan_y) {
260  mi::Float64 y = camera->get_offset_y();
261  camera->set_offset_y( y - pan_y->get_value<mi::Sint32>());
262  }
263  // Demonstrate getting a bool from the example client
265  imap->get_value<mi::IBoolean>( "going_right"));
266  if ( dir) {
267  bool going_right = dir->get_value<bool>();
268  going_right = !going_right; // avoid compiler warning
269  }
270  }
271  transaction->commit();
272  return true;
273  }
274 
275 private:
277 };
278 
279 // An RTMP connect event handler that registers the stream and call event handlers above.
280 class Connect_event_handler : public mi::base::Interface_implement<mi::rtmp::IConnect_event_handler>
281 {
282 public:
284  : m_neuray( neuray, mi::base::DUP_INTERFACE), m_scope( scope, mi::base::DUP_INTERFACE) { }
285 
286  bool handle(
287  bool is_create, mi::rtmp::IConnection* connection,
288  const mi::IData* /*command_arguments*/,
289  const mi::IData* /*user_arguments*/)
290  {
291  if( is_create) {
293  new Stream_event_handler( m_neuray.get(), m_scope.get()));
294  connection->register_stream_event_handler( stream_event_handler.get());
296  new Call_event_handler( m_scope.get()));
297  connection->register_remote_call_handler( call_event_handler.get(), "moveCamera");
298  }
299  return true;
300  }
301 
302 private:
305 };
306 
307 void configuration( mi::neuraylib::INeuray* neuray, const char* mdl_path)
308 {
309  // Configure the neuray library. Here we set the search path for .mdl files.
312  check_success( rc->add_mdl_path( mdl_path) == 0);
313  check_success( rc->add_mdl_path( ".") == 0);
314 
315  // Load the FreeImage, Iray Photoreal, and .mi importer plugins.
316  // Also load the default video codec plugin which will be used to encode the rendered frames.
319  check_success( pc->load_plugin_library( "nv_freeimage" MI_BASE_DLL_FILE_EXT) == 0);
320  check_success( pc->load_plugin_library( "libiray" MI_BASE_DLL_FILE_EXT) == 0);
321  check_success( pc->load_plugin_library( "mi_importer" MI_BASE_DLL_FILE_EXT) == 0);
322  check_success( pc->load_plugin_library( "screen_video" MI_BASE_DLL_FILE_EXT) == 0);
323 }
324 
326  mi::neuraylib::INeuray* neuray, const char* scene_file)
327 {
328  // Get the database, the global scope of the database, and create a transaction in the global
329  // scope for importing the scene file and storing the scene.
332  check_success( database.is_valid_interface());
334  database->get_global_scope());
336  scope->create_transaction());
337  check_success( transaction.is_valid_interface());
338 
339  // Import the scene file
342  check_success( import_api.is_valid_interface());
343  mi::base::Handle<const mi::IString> uri( import_api->convert_filename_to_uri( scene_file));
345  import_api->import_elements( transaction.get(), uri->get_c_str()));
346  check_success( import_result->get_error_number() == 0);
347 
348  // Create the scene object
350  transaction->create<mi::neuraylib::IScene>( "Scene"));
351  scene->set_rootgroup( import_result->get_rootgroup());
352  scene->set_options( import_result->get_options());
353  scene->set_camera_instance( import_result->get_camera_inst());
354 
355  // And store it in the database such that the render loop can later access it
356  transaction->store( scene.get(), "the_scene");
357  transaction->commit();
358 }
359 
361  mi::neuraylib::INeuray* neuray, const char* port, const char* swf_file)
362 {
363  // Create an HTTP server instance
367  http_factory->create_server());
368 
369  // Install our HTTP request and response handlers
371  new Request_handler( swf_file));
372  http_server->install( request_handler.get());
374  new Response_handler());
375  http_server->install( response_handler.get());
376 
377  // Assemble HTTP server address
378  const char* ip = "0.0.0.0:";
379  char address[255];
380  address[0] = '\0';
381  strncat( address, ip, sizeof(address) - 1);
382  strncat( address, port, sizeof(address) - 1 - strlen(address));
383 
384  // Start HTTP server
385  http_server->start( address);
386 
387  // Create an RTMP server instance
390  mi::base::Handle<mi::rtmp::IServer> rtmp_server( rtmp_factory->create_server());
391 
392  // Install our HTTP connect handler
396  database->get_global_scope());
398  new Connect_event_handler( neuray, scope.get()));
399  rtmp_server->install( connect_handler.get());
400 
401  // Start RTMP server
402  rtmp_server->start( "0.0.0.0:1935");
403 
404  // Run both servers for fixed time interval
405  sleep_seconds( 30);
406  http_server->shutdown();
407  rtmp_server->shutdown();
408 }
409 
410 int main( int argc, char* argv[])
411 {
412  // Collect command line parameters
413  if( argc != 5) {
414  std::cerr << "Usage: example_rtmp_server <swf_file> <scene_file> <mdl_path> <port>"
415  << std::endl;
417  return EXIT_FAILURE;
418  }
419  const char* swf_file = argv[1];
420  const char* scene_file = argv[2];
421  const char* mdl_path = argv[3];
422  const char* port = argv[4];
423 
424  // Access the neuray library
427 
428  // Configure the neuray library
429  configuration( neuray.get(), mdl_path);
430 
431  // Start the neuray library
432  mi::Sint32 result = neuray->start();
433  check_start_success( result);
434 
435  // Set up the scene
436  prepare_rendering( neuray.get(), scene_file);
437 
438  // Serve video stream via RTMP server
439  run_http_and_rtmp_server( neuray.get(), port, swf_file);
440 
441  // Shut down the neuray library
442  check_success( neuray->shutdown() == 0);
443  neuray = 0;
444 
445  // Unload the neuray library
446  check_success( unload());
447 
449  return EXIT_SUCCESS;
450 }