As of April 7, 2008 there is a new plugin mechanism for adding new types of media sources, like YouTube and Shoutcast. Previously, the media plugins, like xine plugin, mythtv plugin, slim server plugin, vlc plugin, etc. had the ability to register with Media_Plugin as being capable of playing the standard types of media already in the system (video files, mp3's, etc.). But all the content that shows up under Media, Video and Media, Audio is a catalog of the files on the hard drives that the UpdateMedia utility was able to catalog. The plugins could not add new content to appear in the media browser; they could only register as being able to media that was already there.
I created a plugin called "YouTube_Plugin" which code is in the svn src directory. This plugin is just a stub to show how the new system works. It's not functional. To use it, in the admin site go to Advanced, Configuration, Devices, choose the Core/DCERouter, and then create device, and pick the YouTube_Plugin device template. Then compile the you tube plugin with make clean so, and put the .so in the /usr/pluto/bin directory and reload the router.
Here is what changed in the code to make this work and what needs to be implemented in the youtube plugin.
The new plugin has a new device data called "Media Catalog". This needs to be set to a \n delimited list of media sources that the plugin will add to the catalog. For example, you tube now has: 5,you_tube_video,YouTube. The first digit is the PK_MediaType from the MediaType table (5=Stored Video). Next is a text string to describe the source. This must be globally unique and not contain any special characters and must start with a letter, not a number. Last is the description that the user should see in the media browser on the 'source' tab.
So if the field had this:
5,you_tube_video1,YouTube #1 5,you_tube_video2,YouTube #2 4,you_tube_audio,YouTube Tunes
then in the media browser under media, video would be two new sources: "YouTube #1 and YouTube #2", and under media, audio would be one new source "YouTube Tunes".
Changes Needed In Media Browser
The change to make this appear in the media browser is in src/OrbiterGen/DesignObj_Generator.cpp in DesignObj_Generator::GetArrayValues, in case ARRAY_Media_Filter_Source_CONST where there's a sql select for DEVICEDATA_Media_Catalog_CONST.
What Is Needed For The YouTube_Plugin
The YouTube_Plugin needs the same things as any other plugin that registers to play media. Create the DCE Device. Add the extra base class public MediaHandlerBase, implement the required functions CreateMediaStream, StartMedia, StopMedia. Now there are 2 more required functions you need to implement only if you are going to be adding media to the media browser dynamically: PopulateDataGrid and GetExtendedAttributes.
The Register function
As always the Register function needs to the media plugin and call RegisterMediaPlugin. The change to media plugin is that it now adds into m_mapMediaCatalog the token (you_tube_video) and pointer to the plugin that implements it. The function Media_Plugin::AttributesBrowser in Media_Plugin/Media_Plugin_Grids.cpp populates the media browser grid. Now, because OrbiterGen added the new sources to the orbiter's source group with the appropriate id's, sPK_Sources is no longer purely a string of numbers like "4,5" but can also include "5,4,you_tube_video". The line if( sPK_MediaSource>'9' ) handles the case where there's a non-numeric source and calls the plugins PopulateDataGrid function.
Implementation Note 1
You must implement that function to provide the content you want to appear in the media browser. This function must return quickly, less than 1 second. Otherwise the user experience will be sluggish and the framework will see the media plugin has stopped responding and automatically kill it. All incoming commands are expected to be processed in less than 15 seconds or else a device is considered "dead". So, in the example of YouTube video, it is expected the plugin will query youtube.com asynchronously to get a list of videos to populate with the list. Another option, which hasn't been really flushed out yet, is that the plugin can return an indication that it will get the media list asynchronously, and the user will see a 'please wait' on the GUI, and then when the plugin has finished getting the content it will show the grid.
Implementation Note 2
In the plugin you can add 3 types of entries to the media browser:
- a folder which the user can drill down into
- a file the user can play immediately, like youtube or shoutcast,
- a file that has to be downloaded asynchronously and you'll tell the user when he can start playing it.
To add a folder, which could be something like genres, do something like this:
FileBrowserInfo *pFileBrowserInfo = new FileBrowserInfo("B 1-sub folder","!e," + sToken + ",URL2 for sub folder",100,StringUtils::SQLDateTime("2008-02-10")); pFileBrowserInfo->m_PK_Picture = 100; pMediaListGrid->m_listFileBrowserInfo.push_back(pFileBrowserInfo);
The first parameter to FileBrowserInfo is the human readable text that will appear on the media browser. Next is the MRL for the media file, which should be in the format !e,token,url, where e means 'external folder', token is some unique alpha-numeric id for this type of media, and URL is where the media exists. The next field, 100 in this example, isn't used. Lastly pass in the date/time the media was last accessed by the user, which is used if the user choose sort by 'recently used'.
You can then assign a picture to this, where the ID is a record in the pluto_media.Picture table. To create a picture call the functions in src/Media_Plugin/MediaAttributes_LowLevel.h, which is a class available in YouTube_Plugin as m_pMedia_Plugin->m_pMediaAttributes->m_pMediaAttributes_LowLevel:
Row_Picture *AddPicture(char *pData,int iData_Size,string sFormat,string sURL);
Row_Picture *AddPicture(string sFile);
Either pass in a pointer to the JPEG file, where URL is a URL on the internet to retrieve the picture again, or pass in the fully qualified filename. You will get back a pointer to the new record in the Picture table, and Row_Picture::PK_Picture_get() will have the picture id to assign to m_PK_Picture.
When the user chooses the item you specified with !e, PopulateDataGrid will get called again and sPK_Attribute will contain the URL for the folder the user wants to view.
Instead of adding a folder, you can add a file the user can play by using !E instead of !e. Then the user will be able to select the media and choose 'play', and your plugin's StartMedia will get called. This part hasn't been done yet. However at this point, your plugin's startmedia should send a message to whatever device on the media director will play the media.
If the media needs to be downloaded offline and cannot be played immediate, use !o instead of !E. Then the 'play' button will be 'download' instead, and your plugin's DownloadMedia function will get called. This is not implemented yet.
When the user goes to selects a media file and wants to view it full screen, where the play and download buttons are, your plugin's GetExtendedAttributes function will get called. Here you return a tab delimited list of attributes that will appear next to the file, such as this:
*sValue_To_Assign = "FILE\tDummy you tube file" "\tTITLE\tTitle of you tube video" "\SYNOPSIS\tSynopsis" "\tPICTURE\t/home/mediapics/100.jpg";
SYNOPSIS will appear in the large text block. FILE and TITLE where the filename and title are. You can also add other attributes, like: ACTOR\tJohn Doe, etc., that appear in the attributes grid. the PICTURE should contain the path to the picture to display, which is normally just /home/mediapics/x.jpg, where x is the picture ID you assigned to m_PK_Picture.