Picture in Picture (PiP)
For smooth streaming, the player framework includes a plugin to help you balance heuristics between multiple instances of the SmoothStreamingMediaElement. This is useful for applications that need picture in picture where two players need to be in sync with one another and yet allow priority to be given to the main video so resources aren’t stolen from the main video track. This ensures the user always sees the best quality picture in the main player.
Getting Started
1) Create a new application that uses the player framework. Note: This feature only works with smooth streaming videos. You should also make sure you are using a smooth streaming manifest that has at least two video tracks.
2) Add a project reference to the Heuristics plugin shipped with the player framework.
3) Add a file called heuristics.xml to your project root with Build Action = Content.
<?xml version="1.0" encoding="utf-8" ?>
<heuristics>
<!--Master/Slave Settings-->
<pollingfrequencymillis>5000</pollingfrequencymillis>
<primaryminbitrate>6000</primaryminbitrate>
<primaryminframerate>10</primaryminframerate>
<secondaryminbitrate>5000</secondaryminbitrate>
<secondaryminframerate>10</secondaryminframerate>
<synctolerancemillis>2000</synctolerancemillis>
<!--Balanced Settings-->
<pollingfrequencymillis>5000</pollingfrequencymillis>
<tolerableproblematicplugins>0</tolerableproblematicplugins>
<tolerablesuboptimaltrackdifferences>1</tolerablesuboptimaltrackdifferences>
<tolerabledroppedframespercentage>.2</tolerabledroppedframespercentage>
<stablebuffersize>20000</stablebuffersize>
<bitrateblacklistduration>10000</bitrateblacklistduration>
</heuristics>
4) Start by adding two instances of the SMFPlayer object to your page and set them both to the same media source. Be sure to position the secondary/picture in picture player off to the side so you can see both.
<Core:SMFPlayer x:Name="main">
<Core:SMFPlayer.Playlist>
<Media:PlaylistItem DeliveryMethod="AdaptiveStreaming" MediaSource="http://streams.smooth.vertigo.com/...ism/manifest" />
</Core:SMFPlayer.Playlist>
</Core:SMFPlayer>
<Core:SMFPlayer x:Name="pip" HorizontalAlignment="Right" VerticalAlignment="Center" Width="200" Height="113">
<Core:SMFPlayer.Playlist>
<Media:PlaylistItem DeliveryMethod="AdaptiveStreaming" MediaSource="http://streams.smooth.vertigo.com/...ism/manifest" />
</Core:SMFPlayer.Playlist>
</Core:SMFPlayer>
5) Turn off the control strip on the secondary player:
<Core:SMFPlayer x:Name="pip" IsControlStripVisible="False" HorizontalAlignment="Right" VerticalAlignment="Center" Width="200" Height="113">
6) Force the two players to be in sync with one another by setting EnableSync=”True” on both players:
<Core:SMFPlayer x:Name="main" EnableSync="True"…>
<Core:SMFPlayer x:Name="pip" EnableSync="True"…>
7) Configure the behavior of the heuristics plugin by setting the HeuristicsPluginRequiredMetadata property on both instances of SMFPlayer.
<Core:SMFPlayer.HeuristicsPluginRequiredMetadata>
<Utilities:MetadataItem Key="SupportsMasterSlaveMode" Value="True"/>
</Core:SMFPlayer.HeuristicsPluginRequiredMetadata>
8) Select the appropriate video tracks on each player instance by handling the MediaOpened events for the two instances:
public partial class MainPage : UserControl
{
const string trackKeyName = "cameraAngle";
const string trackValueFormat = "camera{0}";
public MainPage()
{
InitializeComponent();
main.HeuristicsPriority = 0;
pip.HeuristicsPriority = -1;
main.MediaOpened += main_MediaOpened;
pip.MediaOpened += pip_MediaOpened;
}
void main_MediaOpened(object sender, EventArgs e)
{
var player = (IPlayer)sender;
SelectVideoTracks(2, player.ActiveMediaPlugin as IAdaptiveMediaPlugin);
}
void pip_MediaOpened(object sender, EventArgs e)
{
var player = (IPlayer)sender;
SelectVideoTracks(4, player.ActiveMediaPlugin as IAdaptiveMediaPlugin);
}
void SelectVideoTracks(int trackNumber, IAdaptiveMediaPlugin ActiveAdaptiveMediaPlugin)
{
if (ActiveAdaptiveMediaPlugin == null)
return;
ISegment currentSegment = ActiveAdaptiveMediaPlugin.CurrentSegment;
if (currentSegment == null || currentSegment.AvailableStreams == null)
return;
IMediaStream videoStream = currentSegment.AvailableStreams.Where(s => s.Type == StreamType.Video).FirstOrDefault();
if (videoStream == null)
return;
var trackName = string.Format(trackValueFormat, trackNumber);
var pair = new KeyValuePair<string, string>(trackKeyName, trackName);
var tracks = videoStream.AvailableTracks.Where(t => t.CustomAttributes.Contains(pair));
if (tracks.Any())
{
videoStream.SetSelectedTracks(tracks);
}
else
{
// Select all tracks and let the SSME choose the default track.
videoStream.SetSelectedTracks(videoStream.AvailableTracks);
}
}
}