13.2. MPEG Renderer

  • SoMPEGRenderer

    • SoMPEGNavRenderer

    • SoMPEGFrameRenderer

Figure 13.1. MPEG output classes


Open Inventor allows you to generate an MPEG output file from a scene graph.

With the SoMPEGRenderer( C++ | Java | .NET ), SoMPEGNavRenderer( C++ | Java | .NET )and SoMPEGFrameRenderer( C++ | Java | .NET )classes, you have three ways to generate an MPEG-1 video file from your application. The class SoMPEGRenderer( C++ | Java | .NET ) simplifies the recording process with methods which automate it. The class SoNavMPEGRenderer registers all camera motions in your MPEG file. The class SoMPEGFrameRenderer( C++ | Java | .NET ) allows you to generate your video file frame by frame.

Generate MPEG video output from a scene graph.
Generate MPEG video output from a scene graph.
Generate MPEG video output from a scene graph.

Figure 13.2. Generate MPEG video output from a scene graph.


The class SoMPEGRenderer( C++ | Java | .NET )encapsulates several common parameters, described below.

You can specify a file name with the method SoMPEGRenderer::openFile() or a file pointer with the method SoMPEGRenderer::setFilePointer().

[Warning]

When using the method SoMPEGRenderer::openFile(), do not forget to close your MPEG file with the method SoMPEGRenderer::closeFile().

This class records all camera changes, then generates an MPEG output file. After specifying your MPEG file name, your scene graph, and other rendering options (see the section called “Common Parameters”), call SoMPEGNavRenderer::record() to record camera changes. Finally, call SoMPEGNavRenderer::stop() to stop recording and generate the MPEG output file.

[Important]

Generating the MPEG output (after the call to SoMPEGNavRenderer::stop()) may take several seconds or even minutes depending on the length of your video. To give you an idea of the time it may take, at least 3 minutes are necessary for obtaining 1 minute of video with 25 frames per second.

The example supplied in $OIVHOME/src/Inventor/examples/Features/MPEGRender/MPEGNavRenderer.cxx shows an example of use of SoMPEGNavRenderer( C++ | Java | .NET ). The following is a code fragment extract from this example.

Example 13.2. Extract of using SoMPEGNavRenderer


C++
void
myKeyPressCB(void *, SoEventCallback *eventCB)
  {
  const SoEvent *event = eventCB->getEvent();

  // check for the keys being pressed
    if (SO_KEY_PRESS_EVENT(event, R))
    {
    MPEGRenderer->record();
    }
  else if (SO_KEY_PRESS_EVENT(event, S))
    {
    MPEGRenderer->stop();
    }
  else if (SO_KEY_PRESS_EVENT(event, C))
    {
    MPEGRenderer->closeFile();
      delete MPEGRenderer;
      exit(0);
    }
  }


int
main(int , char **argv)
  {
  // Initialize Inventor and Xt
    Widget myWindow = SoXt::init("MPEGFrameRenderer");

  // Build the viewer in the applications main window
    SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow);

  // Read the geometry from a file and add to the scene
    SoInput myInput;
    if (!myInput.openFile("../../../data/jumpyMan.iv"))
    exit (1);

    SoSeparator *geomObject = SoDB::readAll(&myInput);
    if (geomObject == NULL)
    exit (1);

  // Build the scene graph
    SoSeparator *root = new SoSeparator;
    root->ref();

    SoPerspectiveCamera *camera = new SoPerspectiveCamera;
    root->addChild(camera);

    root->addChild(geomObject);

  // Track the keyboard events
    SoEventCallback *myEventCB = new SoEventCallback;
    myEventCB->addEventCallback(SoKeyboardEvent::getClassTypeId(),
    myKeyPressCB, myViewer);
    root->addChild(myEventCB);

  // Attach the viewer to the scene graph
    myViewer->setSceneGraph(root);

    camera->viewAll(geomObject, myViewer->getViewportRegion());

  // Create the MPEG renderer
    MPEGRenderer =
    new SoMPEGNavRenderer(myViewer->getSceneManager()->getSceneGraph());
    MPEGRenderer->setSize (SbVec2s (300, 300));
    MPEGRenderer->adjustNumFramesPerSecond(TRUE);

    int i = 0;
    char mpegFileName[20] = "MPEGOutput.mpg";
    while (!MPEGRenderer->openFile(mpegFileName))
    {
    sprintf(mpegFileName, "MPEGOutput%d.mpg", i);
      i++;
    }

  // Show the main window
    myViewer->show();
    SoXt::show(myWindow);

  // Loop forever
    SoXt::mainLoop();

    return 0;
  }
       

.NET
private SoSeparator			_root;
private SoWinExaminerViewer _viewer;
private SoSwitch			InfoSwitch;
private string				_currentPathData;
private SoMPEGNavRenderer	MPEGRenderer;

[STAThread]
static void Main() 
{
	MPEGNavRenderer demo = new MPEGNavRenderer();
	demo.Start();
	Application.Run(demo);
}


public void LaunchDemo()
{							
							
	try
	{
		_viewer = 
			new SoWinExaminerViewer(_parent, "Name", true, 
			SoWinFullViewer.BuildFlags.BUILD_ALL, 
			SoWinViewer.Types.BROWSER);
	}
	catch(Exception ex)
	{
		MessageBox.Show(ex.ToString());
	}
	
	
	_root = new SoSeparator();
	
	//			SoGradientBackground bg = new SoGradientBackground();
	//			_root.AddChild(bg);
	
	SoMaterial material = new SoMaterial();
	material.diffuseColor.SetValue(0.0f,0.0f,1.0f);
	_root.AddChild(material);
	
	SoSeparator geomObject = SoDB.ReadAll(_currentPathData + "/jumpyMan.iv");
	   
	// Build the scene graph
	InfoSwitch = DisplayInfo() ;
	_root.AddChild(InfoSwitch) ;

	SoPerspectiveCamera camera = new SoPerspectiveCamera() ;
	_root.AddChild(camera) ;
	_root.AddChild(geomObject) ;

	// Track the keyboard events
	
	SoEventCallback eventCB = new SoEventCallback();
	eventCB.AddEventCallback(typeof (SoKeyboardEvent), new SoEventCallback.EventCB(keyCB));
	
	_root.AddChild(eventCB);
	
	_viewer.SetSceneGraph(_root);
	_viewer.ViewAll();

	camera.ViewAll(geomObject, _viewer.GetViewportRegion());

	// Create the MPEG renderer
	MPEGRenderer = new SoMPEGNavRenderer(_viewer.GetSceneGraph());
	MPEGRenderer.SetSize (new SbVec2s (300, 300));
	MPEGRenderer.AdjustNumFramesPerSecond(true);
	MPEGRenderer.SetBitPerSec(-1);
	MPEGRenderer.SetCompressionRate(0.0f);

	int i = 1;
	while(File.Exists("MPEGOutput" + i + ".mpg"))
		i++;
	
	MPEGRenderer.OpenFile("MPEGOutput" + i + ".mpg");
}
			
public void Start()
{
	LaunchDemo();
	//Show();
}

public bool  Init(Panel parent)
{
	/* The demo Launcher always give a parent */
	/* If we don't want to put the application on demoLauncher, we just need to */
	/* Comment the setParent line and  uncomment the show method*/

	SetParent(parent);
	return true;
}
				
public void Stop()
{
	HideViewer();
	Close();
}

public void SetParent(Panel P)
{
	_parent = P;
}

public void HideViewer()
{
	_viewer.Hide();
}


private SoSwitch DisplayInfo() 
{
	// Informations
	SoSwitch infoSwitch = new SoSwitch() ;
	infoSwitch.whichChild.Value = SoSwitch.WhichChild.SO_SWITCH_ALL;

	SoSeparator infoSep = new SoSeparator() ;
	infoSwitch.AddChild(infoSep) ;

	SoLightModel lModel = new SoLightModel() ;
	lModel.model.Value = SoLightModel.Models.BASE_COLOR;
	infoSep.AddChild(lModel) ;

	SoFont fontInfo = new SoFont() ;
	fontInfo.size.SetValue(12.0f) ;
	infoSep.AddChild(fontInfo) ;

	SoBaseColor infoColor = new SoBaseColor() ;
	infoColor.rgb.SetValue(new SbColor(1, 1, 0)) ;
	infoSep.AddChild(infoColor) ;

	SoTranslation transInfo = new SoTranslation() ;
	transInfo.translation.SetValue(-0.95f, 0.95f, 0.0f) ;
	infoSep.AddChild(transInfo) ;

	SoText2 infoText = new SoText2() ;
	
	string[] display = new string[4]
	{
		"H : Toggle this display ",
		"R : Record camera movements to the MPEG file ",
		"S : Stop recording ",
		"C : Generate the file MPEGOutputx.mpg and Exit "
	};
	
	infoText.stringField.SetValues(0,display);
	infoSep.AddChild(infoText) ;

	return infoSwitch ;
}

private void keyCB(SoEventCallback sender) 
{
	SoKeyboardEvent evt = (SoKeyboardEvent) sender.GetEvent();
	if (!SoKeyboardEvent.IsKeyReleaseEvent(evt, evt.GetKey())) return;		
	
	if (evt.GetKey() == SoKeyboardEvent.Keys.H) 
	{
		if(InfoSwitch.whichChild.Value == SoSwitch.WhichChild.SO_SWITCH_ALL)
			InfoSwitch.whichChild.Value = SoSwitch.WhichChild.SO_SWITCH_NONE;
		else
			InfoSwitch.whichChild.Value = SoSwitch.WhichChild.SO_SWITCH_ALL;

	}
        else if (evt.GetKey() == SoKeyboardEvent.Keys.R) 
	{	
		MPEGRenderer.Record();
	}
        else if (evt.GetKey() == SoKeyboardEvent.Keys.S) 
	{
		MPEGRenderer.Stop();
	}
        else if (evt.GetKey() == SoKeyboardEvent.Keys.C) 
	{
		MPEGRenderer.CloseFile();
		this.Close();
	}
}
     

Java
            
          

This class generates an MPEG movie, frame by frame. After specifying the MPEG file name, the scene graph, and other rendering options (see the section called “Common Parameters”), call SoMPEGFrameRenderer::recordFrame(float duration) to record a new frame in your MPEG video file. The duration field indicates how long to play the frame.

The example supplied in $OIVHOME/src/Inventor/examples/Features/MPEGRender/MPEGFrameRenderer.cxx shows an example of use of SoMPEGFrameRenderer( C++ | Java | .NET ). The following is a code fragment extract from this example.

Example 13.3. Generate an MPEG file, frame by frame


C++
void
myKeyPressCB (void *, SoEventCallback *eventCB)
{
    const SoEvent *event = eventCB->getEvent();

    // check for the keys being pressed
    if (SO_KEY_PRESS_EVENT(event, A))
    {
      MPEGRenderer->recordFrame(2);
    }
    else if (SO_KEY_PRESS_EVENT(event, C))
    {
      MPEGRenderer->closeFile();
      delete MPEGRenderer;
      exit(0);
    }
}


int
main(int , char **argv)
{
    // Initialize Inventor and Xt
    Widget myWindow = SoXt::init("MPEGFrameRenderer");

    // Build the viewer in the applications main window
    SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow);

    // Read the geometry from a file and add to the scene
    SoInput myInput;
    if (!myInput.openFile("../../../data/jumpyMan.iv"))
      exit (1);

    SoSeparator *geomObject = SoDB::readAll(&myInput);
    if (geomObject == NULL)
      exit (1);

    // Create the MPEG renderer
    MPEGRenderer = new SoMPEGFrameRenderer ();
    MPEGRenderer->setSize (SbVec2s (300, 300));

    int i = 0;
    char mpegFileName[20] = "MPEGOutput.mpg";
    while (!MPEGRenderer->openFile(mpegFileName))
    {
      sprintf(mpegFileName, "MPEGOutput%d.mpg", i);
      i++;
    }

    // Build the scene graph
    SoSeparator *root = new SoSeparator;
    root->ref();

    SoPerspectiveCamera *camera = new SoPerspectiveCamera;
    root->addChild(camera);

    root->addChild(geomObject);

    // Track the keyboard events
    SoEventCallback *myEventCB = new SoEventCallback;
    myEventCB->addEventCallback(SoKeyboardEvent::getClassTypeId(),
    myKeyPressCB, myViewer);
    root->addChild(myEventCB);

    // Attach the viewer to the scene graph
    myViewer->setSceneGraph(root);
    MPEGRenderer->setSceneGraph(myViewer->getSceneManager()->getSceneGraph());

    camera->viewAll(geomObject, myViewer->getViewportRegion());
    myViewer->show();
    SoXt::show(myWindow); // Show the main window
    SoXt::mainLoop(); // Loop forever
    return 0;
}
       

.NET
private SoSwitch			InfoSwitch;
private SoMPEGFrameRenderer MPEGRenderer;
private SoSeparator			_root;
private string				_currentPathData;
private SoWinExaminerViewer _viewer;

[STAThread]
static void Main() 
{
	MPEGFrameRenderer demo = new MPEGFrameRenderer();
	demo.Start();
	Application.Run(demo);
}

public void LaunchDemo()
{							
	try
	{
		_viewer = 
			new SoWinExaminerViewer(_parent, "Name", true, 
			SoWinFullViewer.BuildFlags.BUILD_ALL, 
			SoWinViewer.Types.BROWSER);
	}
	catch(Exception ex)
	{
		MessageBox.Show(ex.ToString());
	}
	
	// Create the MPEG renderer
	MPEGRenderer = new SoMPEGFrameRenderer();
	MPEGRenderer.SetSize (new SbVec2s (300, 300));
	MPEGRenderer.SetBitPerSec(-1);
	MPEGRenderer.SetCompressionRate(0.0f);

	int i = 1;
	while(File.Exists("MPEGOutput" + i + ".mpg"))
		i++;
	
	MPEGRenderer.OpenFile("MPEGOutput" + i + ".mpg");

	_root = new SoSeparator();
	
	//			SoGradientBackground bg = new SoGradientBackground();
	//			_root.AddChild(bg);
	
	SoMaterial material = new SoMaterial();
	material.diffuseColor.SetValue(0.0f,0.0f,1.0f);
	_root.AddChild(material);
	
	SoSeparator geomObject = SoDB.ReadAll(_currentPathData + "/jumpyMan.iv");
	   
	// Build the scene graph
	InfoSwitch = DisplayInfo() ;
	_root.AddChild(InfoSwitch) ;

	SoPerspectiveCamera camera = new SoPerspectiveCamera() ;
	_root.AddChild(camera) ;
	_root.AddChild(geomObject) ;

	// Track the keyboard events
	
	SoEventCallback eventCB = new SoEventCallback();
	eventCB.AddEventCallback(typeof (SoKeyboardEvent), new SoEventCallback.EventCB(keyCB));
	
	_root.AddChild(eventCB);
	
	_viewer.SetSceneGraph(_root);
	_viewer.ViewAll();
	
	MPEGRenderer.SetSceneGraph(_viewer.GetSceneGraph());
	camera.ViewAll(geomObject, _viewer.GetViewportRegion());
}
			
public void Start()
{
	LaunchDemo();
	//Show();
}

public bool  Init(Panel parent)
{
	/* The demo Launcher always give a parent */
	/* If we don't want to put the application on demoLauncher, we just need to */
	/* Comment the setParent line and  uncomment the show method*/

	SetParent(parent);
	return true;
}
				
public void Stop()
{
	HideViewer();
	Close();
}

public void SetParent(Panel P)
{
	_parent = P;
}

public void HideViewer()
{
	_viewer.Hide();
}


private SoSwitch DisplayInfo() 
{
	// Informations
	SoSwitch infoSwitch = new SoSwitch() ;
	infoSwitch.whichChild.Value = SoSwitch.WhichChild.SO_SWITCH_ALL;

	SoSeparator infoSep = new SoSeparator() ;
	infoSwitch.AddChild(infoSep) ;

	SoLightModel lModel = new SoLightModel() ;
	lModel.model.Value = SoLightModel.Models.BASE_COLOR;
	infoSep.AddChild(lModel) ;

	SoFont fontInfo = new SoFont() ;
	fontInfo.size.Value = 12.0f ;
	infoSep.AddChild(fontInfo) ;

	SoBaseColor infoColor = new SoBaseColor() ;
	infoColor.rgb.SetValue(new SbColor(1, 1, 0)) ;
	infoSep.AddChild(infoColor) ;

	SoTranslation transInfo = new SoTranslation() ;
	transInfo.translation.SetValue(-0.95f, 0.95f, 0.0f) ;
	infoSep.AddChild(transInfo) ;

	SoText2 infoText = new SoText2() ;
	string[] display = new string[3]
	{
		"H : Toggle this display ",
		"A : Add a new frame to the MPEG file",
		"C : Generate the file MPEGOutputx.mpg and Exit "						
	};
	
	infoText.stringField.SetValues(0,display);
	infoSep.AddChild(infoText) ;

	return infoSwitch ;
}

private void keyCB(SoEventCallback sender) 
{
	SoKeyboardEvent evt = (SoKeyboardEvent) sender.GetEvent();
	if (!SoKeyboardEvent.IsKeyReleaseEvent(evt, evt.GetKey())) return;		
	
	if (evt.GetKey() == SoKeyboardEvent.Keys.H) 
	{
		if(InfoSwitch.whichChild.Value == SoSwitch.WhichChild.SO_SWITCH_ALL)
			InfoSwitch.whichChild.Value = SoSwitch.WhichChild.SO_SWITCH_NONE;
		else
			InfoSwitch.whichChild.Value = SoSwitch.WhichChild.SO_SWITCH_ALL;
	}
	else if (evt.GetKey() == SoKeyboardEvent.Keys.A) 
	{	
		int savedSwitchValue = InfoSwitch.whichChild.Value ;
		InfoSwitch.whichChild.Value = SoSwitch.WhichChild.SO_SWITCH_NONE;
		MPEGRenderer.RecordFrame(2);
		InfoSwitch.whichChild.Value = savedSwitchValue;
	}
	else if (evt.GetKey() == SoKeyboardEvent.Keys.C) 
	{
		MPEGRenderer.CloseFile();
		this.Close();
	}
}
     

Java
class ProcessKeyEvents extends SoEventCallbackCB
{
  public void invoke(SoEventCallback event)
  {
    // check for the keys being pressed
    if ( SoKeyboardEvent.isKeyPressEvent(event.getEvent(), SoKeyboardEvent.Keys.P) )
    {
      MPEGRenderer.recordFrame(2);
      event.setHandled();
    }
    else if ( SoKeyboardEvent.isKeyPressEvent(event.getEvent(), SoKeyboardEvent.Keys.C) )
    {
      MPEGRenderer.closeFile();
      event.setHandled();
    }
  }
}

@Override
public void start()
{
  super.start();
  setLayout(new BorderLayout());
  Panel panel = new Panel(new BorderLayout());

  myViewer = new SwSimpleViewer(SwSimpleViewer.EXAMINER);

  // Build the viewer in the applications main window
  myViewer = new SwSimpleViewer(SwSceneViewer.EXAMINER);

  // Read the geometry from a file and add to the scene
  SoInput myInput = new SoInput();
  if ( !myInput.openFile("../../../../data/jumpyMan.iv") )
    return;

  SoSeparator geomObject = SoDB.readAll(myInput);
  if ( geomObject == null )
    return;

  // Create the MPEG renderer
  MPEGRenderer = new SoMPEGFrameRenderer();
  MPEGRenderer.setSize(new SbVec2s((short) 300, (short) 300));

  int i = 0;
  String mpegFileName = "MPEGOutput.mpg";
  while ( !MPEGRenderer.openFile(mpegFileName) )
  {
    mpegFileName = "MPEGOutput" + i + ".mpg";
    i++;
  }

  // Build the scene graph
  SoSeparator root = new SoSeparator();

  SoPerspectiveCamera camera = new SoPerspectiveCamera();
  root.addChild(camera);

  root.addChild(geomObject);

  // Track the keyboard events
  SoEventCallback myEventCB = new SoEventCallback();
  myEventCB.addEventCallback(SoKeyboardEvent.class, new ProcessKeyEvents(), myViewer);
  root.addChild(myEventCB);

  // Create a viewer
  myViewer = new SwSimpleViewer(SwSimpleViewer.EXAMINER);
  // attach and show viewer
  myViewer.setSceneGraph(root);
  MPEGRenderer.setSceneGraph(myViewer.getScene().getSceneGraph());

  panel.add(myViewer);
  add(panel);
}