//------------------------------------------------------------------------------ // // Microsoft Windows Media // Copyright (C) Microsoft Corporation. All rights reserved. // // Class1.cs // //------------------------------------------------------------------------------ using System; using System.Runtime.InteropServices; using Microsoft.Win32; using System.Threading; using System.Data; using System.Xml; using System.Reflection; using System.Diagnostics; using System.Collections; using System.IO; using Microsoft.WindowsMediaServices.Interop; using System.Xml.Serialization; namespace CacheProxySamplePlugin { class PlaylistItem { public string CacheUrl; public string CDLData; public int CacheID; }; public class ContentInfo { public ContentInfo(string o, string c) { Debug.WriteLine("ContentInfo::ContentInfo entered"); OriginUrl = o; CacheUrl = c; CacheFlags=0; ContentSize = 0; SubscriptionFlag=0; ContentType=0; EntityTags = new ArrayList(); LastModified = System.DateTime.Now; ExpirationTime = System.DateTime.Now; CacheProxyCallback = null; varContext = null; lExpiration = 0; CDL = new ArrayList(); } public string OriginUrl, CacheUrl; public int SubscriptionFlag; public int ContentType; public DateTime LastModified; public DateTime ExpirationTime; public int CacheFlags; public System.Int64 ContentSize; public ArrayList EntityTags; public string CDLData; public IWMSCacheProxyCallback CacheProxyCallback; public object varContext; public int lExpiration; // needed for prestuffing public ArrayList CDL; //array of strings for playlist }; public class INSSBufferImpl : INSSBuffer { public byte[] Buffer; public uint Length; public INSSBufferImpl() { Debug.WriteLine("INSSBufferImpl::INSSBufferImpl entered"); Length = 0; } void INSSBuffer.GetBufferAndLength( IntPtr ppbBuffer, out uint pdwLength) { Debug.WriteLine("INSSBufferImpl::GetBufferAndLength entered"); pdwLength=Length; INSSBuffer pBuf = this as INSSBuffer; pBuf.GetBuffer( ppbBuffer); } void INSSBuffer.GetBuffer( IntPtr ppbBuffer) { Debug.WriteLine("INSSBufferImpl::GetBuffer entered"); ppbBuffer = Marshal.AllocCoTaskMem((int)Length); try { Marshal.Copy(Buffer,0,ppbBuffer,(int)Length); } catch(Exception e) { Debug.WriteLine(e); } } void INSSBuffer.GetLength(out uint pdwLength) { Debug.WriteLine("INSSBufferImpl::GetLength entered"); pdwLength=Length; } void INSSBuffer.GetMaxLength(out uint pdwLength) { Debug.WriteLine("INSSBufferImpl::GetMaxLength entered"); pdwLength=Length; } void INSSBuffer.SetLength(uint dwLength) { Debug.WriteLine("INSSBufferImpl::SetLength entered"); Length = dwLength; } public void SetBuffer(string Buf) { Debug.WriteLine("INSSBufferImpl::SetBuffer entered"); if(Buf!=null) { try { Buffer = Convert.FromBase64String(Buf); Length = (uint)Buffer.Length; } catch(Exception e) { Debug.WriteLine(e); } } Debug.WriteLine("INSSBufferImpl::SetBuffer ended"); } } class IWMSCacheItemDescriptorImpl : IWMSCacheItemDescriptor { public IWMSCacheItemDescriptorImpl(IWMSCacheItemCollectionImpl collection,DataRow row) { Row = row; Collection = collection; } void IWMSCacheItemDescriptor.GetCacheUrl(out string url) { url = null; try { url = Convert.ToString(Row["CacheUrl"]); } catch(Exception e) { Debug.WriteLine(e); throw new COMException("NO ERROR",1); } } void IWMSCacheItemDescriptor.GetContentInformation(out IWMSContext contentinfo) { contentinfo=null; try { ContentInfo ci; Collection.Plugin.GetContentInfo(Convert.ToString(Row["OriginUrl"]),out ci); Collection.Plugin.GetContentInfoContext(ci,out contentinfo); } catch(Exception e) { Debug.WriteLine(e); throw new COMException("NO ERROR",1); } } void IWMSCacheItemDescriptor.GetContentSize(out int sizeLow, out int sizeHigh) { sizeLow = sizeHigh = 0; try { sizeLow = sizeHigh = 0; } catch(Exception e) { Debug.WriteLine(e); throw new COMException("NO ERROR",1); } } void IWMSCacheItemDescriptor.GetOriginUrl(out string url) { url = null; try { url = Convert.ToString(Row["OriginUrl"]); } catch(Exception e) { Debug.WriteLine(e); throw new COMException("NO ERROR",1); } } DataRow Row; IWMSCacheItemCollectionImpl Collection; } class IWMSCacheItemCollectionImpl : IWMSCacheItemCollection { static string strSel = "ContentType%2 = 0"; public IWMSCacheItemCollectionImpl(CacheProxyPlugin plugin,DataTable dt) { Plugin = plugin; DT = dt; } void IWMSCacheItemCollection.GetCount(out int count) { // return the count of the Cache Table entries // we have to return the count of only the Cache downloads and have to exclude the // Broadcast specific cache information try { DataRow[] rows = DT.Select(strSel); count = rows.Length; } catch(Exception e) { count = 0; Debug.WriteLine(e); throw new COMException("NO ERROR",1); } } void IWMSCacheItemCollection.GetItem(int index, out IWMSCacheItemDescriptor cacheItem) { cacheItem = null; try { DataRow[] rows = DT.Select(strSel); if((index <0)||(index >= rows.Length)) { throw new COMException("NO ERROR",1); } cacheItem = new IWMSCacheItemDescriptorImpl(this,rows[index]) as IWMSCacheItemDescriptor; } catch(Exception e) { Debug.WriteLine(e); throw new COMException("NO ERROR",1); } } DataTable DT; public CacheProxyPlugin Plugin; } [Guid("1976C73A-4B3A-4738-8B84-1C5545CE80EC")] /// /// Summary description for Class1. /// public class CacheProxyPlugin : IWMSBasicPlugin, IWMSCacheProxy, IWMSCacheProxyServerCallback, IWMSProxyContext { static readonly string strGuid = "{1976C73A-4B3A-4738-8B84-1C5545CE80EC}"; static readonly string strSubKey1 = "SOFTWARE\\Microsoft\\Windows Media\\Server\\RegisteredPlugins\\Cache Proxy\\" + strGuid; static readonly string strSubKey2 = "CLSID\\" + strGuid + "\\Properties"; static readonly string strPluginName = "WMS SDK Sample C# Cache Proxy Plug-in"; IWMSClassObject ClassObject; IWMSCacheProxyServer CacheProxyServer; // the xml that stores all the configuration details as well as the cache database string ConfigPath; string DatabasePath; // xml file that saves the cache database CachePluginAdmin AdminSettings; DataSet DS; static string GetStringFromNSSBuffer( INSSBuffer NsBuffer) { Debug.WriteLine("CacheProxyPlugin::GetStringFromNSSBuffer entered"); uint bufSize; IntPtr pBuf = IntPtr.Zero; NsBuffer.GetBufferAndLength( pBuf, out bufSize ); byte[] Buf = new byte[bufSize]; Marshal.Copy(pBuf,Buf,0,(int)bufSize); /* string s = Marshal.PtrToStringUni( pBuf, (int) bufSize / 2 ); return s; */ return Convert.ToBase64String(Buf,0,(int)bufSize); } void CreateCacheTable() { Debug.WriteLine("CacheProxyPlugin::CreateCacheTable entered"); // create a table and add columns for it under CacheDatabase Node DataTable dt = new DataTable("CachedItems"); DataColumn col = dt.Columns.Add("OriginUrl",typeof(string)); dt.Columns.Add("CacheUrl",typeof(string)); dt.Columns.Add("ContentSize",typeof(System.Int64)); dt.Columns.Add("ContentType",typeof(int)); dt.Columns.Add("CacheFlags",typeof(int)); dt.Columns.Add("ExpirationTime",typeof(System.DateTime)); dt.Columns.Add("LastModified",typeof(System.DateTime)); dt.Columns.Add("SubscriptionFlag",typeof(int)); dt.Columns.Add("EntityTags",typeof(string)); dt.Columns.Add("CDLData",typeof(string)); col.Unique = true; dt.PrimaryKey = new DataColumn[]{col}; DS = new DataSet("SampleCachePlugin"); DS.Tables.Add(dt); // we add a dummy row to this file so that // next inserts won't get confused by mismatched columns etc object[] obj = {"test","test",0,0,0,DateTime.Now,DateTime.Now,0,"test","test"}; DataRow dr = dt.NewRow(); dr.ItemArray = obj; } void CreateReverseProxyTable() { Debug.WriteLine("CacheProxyPlugin::CreateReverseProxyTable entered"); // create a table and add columns for it under CacheDatabase Node DataTable dt = new DataTable("ReverseProxyEntries"); DataColumn col = dt.Columns.Add("OriginUrl",typeof(string)); dt.Columns.Add("CacheUrl",typeof(string)); dt.Columns.Add("ContentSize",typeof(System.Int64)); dt.Columns.Add("ContentType",typeof(int)); dt.Columns.Add("CacheFlags",typeof(int)); dt.Columns.Add("ExpirationTime",typeof(System.DateTime)); dt.Columns.Add("LastModified",typeof(System.DateTime)); dt.Columns.Add("SubscriptionFlag",typeof(int)); dt.Columns.Add("EntityTags",typeof(string)); dt.Columns.Add("CDLData",typeof(string)); col.Unique = true; dt.PrimaryKey = new DataColumn[]{col}; DS = new DataSet("SampleCachePlugin"); DS.Tables.Add(dt); // we add a dummy row to this file so that // next inserts won't get confused by mismatched columns etc object[] obj = {"test","test",0,0,0,DateTime.Now,DateTime.Now,0,"test","test"}; DataRow dr = dt.NewRow(); dr.ItemArray = obj; dt.Rows.Add(dr); } void CreatePlaylistTable() { Debug.WriteLine("CacheProxyPlugin::CreatePlaylistTable entered"); DataTable dt2 = new DataTable("PlaylistEntries"); DataColumn col2 = dt2.Columns.Add("OriginUrl",typeof(string)); DataColumn col3 = dt2.Columns.Add("CacheID",typeof(string)); dt2.Columns.Add("CDLData",typeof(string)); dt2.Columns.Add("CacheUrl",typeof(string)); dt2.PrimaryKey = new DataColumn[] {col2,col3}; if(DS==null) { DS = new DataSet("SampleCachePlugin"); } DS.Tables.Add(dt2); object[] obj = {"test","test","test","test"}; DataRow dr = dt2.NewRow(); dr.ItemArray = obj; } // creates the config document if it doesn't exist // otherwise loads it from disk void LoadConfig() { Debug.WriteLine("CacheProxyPlugin::LoadConfig entered"); Console.WriteLine( "LoadConfig entered" ); // get the module path and replace extension with xml string Path = Assembly.GetExecutingAssembly().Location; string Dir = Path.Substring(0,Path.LastIndexOf('.')); ConfigPath = Dir + ".xml"; DatabasePath = Dir+"database"+".xml"; try { Console.WriteLine( "Attempting to load config file {0}", ConfigPath ); // Load configuration from file AdminSettings = new CachePluginAdmin(); // Choose one or the other means of persistence if( ! AdminSettings.LoadSettingsFromXmlFile( ConfigPath ) ) { Console.WriteLine( "Failed to read XML file. Reading from server namespace." ); AdminSettings.LoadSettingsFromServerNamespace(); } IWMSCacheAdmin cacheAdmin = ( IWMSCacheAdmin ) AdminSettings; Console.WriteLine( "AdminSettings loaded: diskquota: " + cacheAdmin.DiskQuota ); MyChangeEventHandler = new CachePluginAdmin.ModifySettingsEventHandler( PersistSettings ); AdminSettings.RegisterChangeHandler( MyChangeEventHandler ); } catch(Exception e) { Console.WriteLine( "ASSERT1: " + e.Message + e.StackTrace ); //load failed, we will treat this as a new file and save it } try { DS = new DataSet("SampleCachePlugin"); DS.ReadXml(DatabasePath, XmlReadMode.ReadSchema); } catch(Exception e) { Console.WriteLine( "ASSERT2: " + e.Message + e.StackTrace ); CreateCacheTable(); CreatePlaylistTable(); } File.Delete(DatabasePath); } public CacheProxyPlugin() { Debug.WriteLine("CacheProxyPlugin::CacheProxyPlugin entered"); } [ComRegisterFunctionAttribute] public static void RegisterFunction(Type t) { Debug.WriteLine("CacheProxyPlugin::RegisterFunction entered"); try { RegistryKey regHKLM = Registry.LocalMachine; regHKLM = regHKLM.CreateSubKey( strSubKey1 ); regHKLM.SetValue(null, strPluginName); RegistryKey regHKCR = Registry.ClassesRoot; regHKCR = regHKCR.CreateSubKey( strSubKey2 ); regHKCR.SetValue("Name", strPluginName); regHKCR.SetValue("Author", "Microsoft Corporation"); regHKCR.SetValue("CopyRight", "Copyright (C) Microsoft Corporation. All rights reserved."); regHKCR.SetValue("Description", "Enables you to configure Cache/Proxy"); regHKCR.SetValue("UnsupportedLoadTypes", 0x02); } catch(Exception error) { Console.WriteLine( "Error Registering DLL. Error " + error.Message ); } } [ComUnregisterFunctionAttribute] public static void UnRegisterFunction(Type t) { Debug.WriteLine("CacheProxyPlugin::UnRegisterFunction entered"); try { RegistryKey regHKLM = Registry.LocalMachine; regHKLM.DeleteSubKey( strSubKey1 ); } catch( Exception ) { // ignore error for deleting sub key } try { RegistryKey regHKCR = Registry.ClassesRoot; regHKCR.DeleteSubKeyTree( strSubKey2 ); } catch( Exception ) { // ignore error for deleting sub key } } void IWMSBasicPlugin.InitializePlugin(IWMSContext pServerContext, WMSNamedValues pNamedValues, IWMSClassObject pClassFactory) { Debug.WriteLine("CacheProxyPlugin::InitializePlugin entered"); Console.WriteLine( "IWMSBasicPlugin.InitializePlugin entered" ); ClassObject = pClassFactory; Type t = typeof(IWMSCacheProxyServer); Guid guid = t.GUID; Object obj; pServerContext.GetAndQueryIUnknownValue(WMSDefines.WMS_SERVER_CACHE_MANAGER,WMSDefines.WMS_SERVER_CACHE_MANAGER_ID, ref guid,out obj ,0); CacheProxyServer = (IWMSCacheProxyServer)obj; //load config file LoadConfig(); if( null != AdminSettings ) { AdminSettings.NamedValues = pNamedValues; } } void IWMSBasicPlugin.EnablePlugin(ref int flags,ref int heartbeat ) { Debug.WriteLine("WMSBasicPlugin.EnablePlugin entered"); } void IWMSBasicPlugin.DisablePlugin() { Debug.WriteLine("WMSBasicPlugin.DisablePlugin entered"); } CachePluginAdmin.ModifySettingsEventHandler MyChangeEventHandler; public void PersistSettings( string strName, object objValue ) { if( null != AdminSettings ) { AdminSettings.PersistNameValToServerNamespace( strName, objValue ); } } object IWMSBasicPlugin.GetCustomAdminInterface() { Debug.WriteLine("IWMSBasicPlugin.GetCustomAdminInterface entered"); IWMSCacheAdmin CacheAdmin = null; try { CacheAdmin = ( IWMSCacheAdmin ) AdminSettings; } catch( Exception e ) { Console.WriteLine( "EXCEPTION: " + e.Message + e.StackTrace ); AdminSettings = null; } finally { CacheAdmin = null; } return( ( object ) AdminSettings ); } void IWMSBasicPlugin.OnHeartbeat() { Debug.WriteLine("IWMSBasicPlugin.OnHeartbeat entered"); } void IWMSBasicPlugin.ShutdownPlugin() { Debug.WriteLine("IWMSBasicPlugin.ShutdownPlugin entered"); File.Delete(DatabasePath); DS.WriteXml(DatabasePath,XmlWriteMode.WriteSchema); } // // IWMSCacheProxy // void IWMSCacheProxy.QueryCache(string bstrOriginUrl , IWMSContext pUserContext , IWMSCommandContext pCommandContext , IWMSContext pPresentationContext , int lQueryType , IWMSCacheProxyCallback pCallback , object varContext ) { Debug.WriteLine("IWMSCacheProxy.QueryCache entered"); // we simply return a hard coded URL for the request WMS_CACHE_QUERY_OPEN for now int nFlag = (int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_OPEN; int nOpen = lQueryType & nFlag; int nGCI = lQueryType & ((int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_GET_CONTENT_INFO); int nReverseProxy = lQueryType & ((int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_REVERSE_PROXY); // either open or GCI is called // for GCI we don't care about CompareContentInformation // if not upto date, we treat as a miss if((nOpen!=0)||(nGCI!=0)) { // allocate ContentInfoContext and DataContainerObject // stuff it with the information // and call back IWMSContext Context; WMS_CACHE_QUERY_RESPONSE Response = WMS_CACHE_QUERY_RESPONSE.WMS_CACHE_QUERY_MISS; ContentInfo ci; //gets allocated here GetContentInfo(bstrOriginUrl,out ci); ci.CacheProxyCallback = pCallback; ci.varContext = varContext; GetContentInfoContext(ci,out Context); bool bQueryCache = true; bool bOnDemand = true; if((ci.CacheUrl!=null) && (nReverseProxy==0)) // there is something in the cache { // if content is not expired, this is a hit DateTime now = DateTime.Now; Debug.WriteLine(string.Format("Current local time={0}",now)); //covert to UTC time now = now.ToUniversalTime(); Debug.WriteLine(string.Format("Current UTC time={0}",now)); Debug.WriteLine(string.Format("Expiration time={0}",ci.ExpirationTime)); if(ci.ExpirationTime > now) { if((ci.ContentType & 1 )!=0) // it's a brodcast content { Response = WMS_CACHE_QUERY_RESPONSE.WMS_CACHE_QUERY_HIT_PLAY_BROADCAST; bOnDemand = false; } else { Response = WMS_CACHE_QUERY_RESPONSE.WMS_CACHE_QUERY_HIT_PLAY_ON_DEMAND; bOnDemand = true; } } else // content appears expired, we will have to call CompareContentInformation { if(nOpen!=0) // only for open queries { bQueryCache = false; } } } if(bQueryCache) { string CacheUrl = ci.CacheUrl; if(bOnDemand) { CacheUrl = string.Format("file://{0}",ci.CacheUrl); } pCallback.OnQueryCache( 0, Response, CacheUrl, Context, null, varContext); } else { CacheProxyServer.CompareContentInformation(bstrOriginUrl,Context,pPresentationContext, this,null,this,(object)ci); } } else { // see of this is for event propagation int nCacheEvent = lQueryType & ((int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_CACHE_EVENT); int nLocalEvent = lQueryType & ((int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_LOCAL_EVENT); if((nCacheEvent | nLocalEvent)!=0) { // we declare it as a miss // and on QCP, we ask the cachemanager to forward it WMS_CACHE_QUERY_RESPONSE Response = WMS_CACHE_QUERY_RESPONSE.WMS_CACHE_QUERY_MISS; pCallback.OnQueryCache( 0, Response, bstrOriginUrl, null, null, varContext); } } return; } void IWMSCacheProxy.QueryCacheMissPolicy ( string bstrOriginUrl , IWMSContext pUserContext , IWMSCommandContext pCommandContext , IWMSContext pPresentationContext , object pCachePluginContext , int lQueryType , IWMSCacheProxyCallback pCallback , object varContext ) { Debug.WriteLine("IWMSCacheProxy.QueryCacheMissPolicy entered"); int nOpenFlag = (int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_OPEN; int nGCI = lQueryType & ((int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_GET_CONTENT_INFO); int nReverseProxy = lQueryType & ((int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_REVERSE_PROXY); ContentInfo ci = new ContentInfo(bstrOriginUrl,null); if((nOpenFlag & lQueryType)!=0) // open query { if(nReverseProxy==0) //normal mode { // get content information ci.CacheProxyCallback = pCallback; ci.varContext = varContext; CacheProxyServer.GetContentInformation(bstrOriginUrl,pPresentationContext,null, null,this,ci); } else // it's a reverse proxy mode { // we simply look up our table to see if there is a mapping between the requested //Url and the RP url and return it // we distinguish the table entry if it's a reverse proxy or not by // checking the CDL data // we store a special string "ReverseProxy" there for the distinction // one can simply add another entry in the table to be safer // as there might be some CDL data written as "ReverseProxy" !! ContentInfo ciRP = null; GetContentInfo(bstrOriginUrl,out ciRP); WMS_CACHE_QUERY_MISS_RESPONSE Response = WMS_CACHE_QUERY_MISS_RESPONSE.WMS_CACHE_QUERY_MISS_PLAY_ON_DEMAND; if((ciRP.ContentType & 1 )!=0) // it's a brodcast content { Response = WMS_CACHE_QUERY_MISS_RESPONSE.WMS_CACHE_QUERY_MISS_PLAY_BROADCAST; } else { Response = WMS_CACHE_QUERY_MISS_RESPONSE.WMS_CACHE_QUERY_MISS_PLAY_ON_DEMAND; } IWMSContext ContentInfoContext = null; GetContentInfoContext(ci,out ContentInfoContext); pCallback.OnQueryCacheMissPolicy(0,Response,ciRP.CacheUrl,null,ContentInfoContext,varContext); } } if((nGCI & lQueryType)!=0) // GCI query from downstream server { WMS_CACHE_QUERY_MISS_RESPONSE Response = WMS_CACHE_QUERY_MISS_RESPONSE.WMS_CACHE_QUERY_MISS_FORWARD_REQUEST; IWMSContext ContentInfoContext = null; GetContentInfoContext(ci,out ContentInfoContext); pCallback.OnQueryCacheMissPolicy(0,Response,bstrOriginUrl,null,ContentInfoContext,varContext); } if((lQueryType & (int)WMS_CACHE_QUERY_TYPE_FLAGS.WMS_CACHE_QUERY_CACHE_EVENT)!=0) { pCallback.OnQueryCacheMissPolicy(0,WMS_CACHE_QUERY_MISS_RESPONSE.WMS_CACHE_QUERY_MISS_FORWARD_REQUEST, null,this,null,varContext); } return; } void IWMSCacheProxy.RemoveCacheItem( string stringOriginUrl, IWMSCacheProxyCallback pCallback, object varContext ) { Debug.WriteLine("IWMSCacheProxy.RemoveCacheItem entered"); RemoveEntryFromDatabase(stringOriginUrl,true); pCallback.OnRemoveCacheItem(0,varContext); return; } void IWMSCacheProxy.RemoveAllCacheItems( IWMSCacheProxyCallback pCallback, object varContext ) { Debug.WriteLine("IWMSCacheProxy.RemoveAllCacheItems entered"); DataTable dt = DS.Tables["CachedItems"]; DataRow[] drows = dt.Select(); foreach(DataRow row in drows) { string File = Convert.ToString(row["OriginUrl"]); RemoveEntryFromDatabase(File,true); } pCallback.OnRemoveAllCacheItems(0,varContext); return; } void IWMSCacheProxy.AddCacheItem( string bstrOriginUrl , string bstrPrestuffUrl , int lExpiration , int lBandwidth , int lRemoteEventFlags , IWMSCacheProxyCallback pCallback , object varContext ) { Debug.WriteLine("IWMSCacheProxy.AddCacheItem entered"); //first remove the entry from database ContentInfo ci = new ContentInfo(bstrOriginUrl,bstrPrestuffUrl); ci.lExpiration = lExpiration; IWMSContext pPresentationContext=null; Type t = typeof(IWMSContext); Guid guid = t.GUID; System.IntPtr punk; ClassObject.AllocIWMSContext(ref guid,WMS_CONTEXT_TYPE.WMS_PRESENTATION_CONTEXT_TYPE,null, out punk); pPresentationContext = (IWMSContext)Marshal.GetObjectForIUnknown(punk); CacheProxyServer.GetContentInformation(bstrOriginUrl,pPresentationContext,null, null,this,ci); return; } void IWMSCacheProxy.QuerySpaceForCacheItem( int lContentSizeLow , int lContentSizeHigh , out System.Boolean pvarfSpaceAvail ) { Debug.WriteLine("IWMSCacheProxy.QuerySpaceForCacheItem entered"); pvarfSpaceAvail = true; return; } void IWMSCacheProxy.FindCacheItem( string bstrOriginUrl , out IWMSCacheItemDescriptor ppCacheItemDescriptor ) { Debug.WriteLine("IWMSCacheProxy.FindCacheItem entered"); ppCacheItemDescriptor = null; return; } void IWMSCacheProxy.CreateCacheItemCollection( out IWMSCacheItemCollection ppCacheItemCollection ) { Debug.WriteLine("IWMSCacheProxy.CreateCacheItemCollection entered"); IWMSCacheItemCollectionImpl obj = new IWMSCacheItemCollectionImpl(this,DS.Tables["CachedItems"]); ppCacheItemCollection = obj as IWMSCacheItemCollection; return; } void IWMSCacheProxy.OnCacheClientClose( int resultHr , IWMSContext pUserContext , IWMSContext pPresentationContext ) { Debug.WriteLine("IWMSCacheProxy.OnCacheClientClose entered"); return; } void GetContentInfoFromContext(IWMSContext pContentInfo, ref ContentInfo ci) { Debug.WriteLine("CacheProxyPlugin::GetContentInfoFromContext entered"); int nFlag=0; int lCacheFlags=0; IWMSDataContainerVersion DCV = null; Type t = typeof(IWMSDataContainerVersion); Guid guid = t.GUID; object odcv = (object)DCV; pContentInfo.GetAndQueryIUnknownValue(WMSDefines.WMS_CACHE_CONTENT_INFORMATION_DATA_CONTAINER_VERSION, WMSDefines.WMS_CACHE_CONTENT_INFORMATION_DATA_CONTAINER_VERSION_ID,ref guid,out odcv, 0); DCV = (IWMSDataContainerVersion)odcv; DCV.GetCacheFlags(out lCacheFlags); nFlag = lCacheFlags & (int)WMS_DATA_CONTAINER_VERSION_CACHE_FLAGS.WMS_DATA_CONTAINER_VERSION_ALLOW_PROXY_CACHING; ci.CacheFlags = lCacheFlags; //content size int nLowVal=0, nHighVal=0; DCV.GetContentSize(out nLowVal,out nHighVal); ci.ContentSize = nLowVal; //content type int nContentType = 0; pContentInfo.GetLongValue(WMSDefines.WMS_CACHE_CONTENT_INFORMATION_CONTENT_TYPE, WMSDefines.WMS_CACHE_CONTENT_INFORMATION_CONTENT_TYPE_ID,out nContentType,0); ci.ContentType = nContentType; //expiration time DateTime time = new DateTime(0); //last modified DCV.GetLastModifiedTime(out time); ci.LastModified = time; //following call throws exception // when this is a bpp sourcing from encoder // so we don't call it based on the lastmodified time if(((ci.ContentType &1)!=0) && time.Year==1899) { } else { DCV.GetExpirationTime(out time); try { ci.ExpirationTime = time; } catch(Exception e) { //if this call fails, we just ignore Debug.WriteLine(e); } } //Subscription flags int nSubFlag=0; pContentInfo.GetLongValue(WMSDefines.WMS_CACHE_CONTENT_INFORMATION_EVENT_SUBSCRIPTIONS ,WMSDefines.WMS_CACHE_CONTENT_INFORMATION_EVENT_SUBSCRIPTIONS_ID,out nSubFlag,0); ci.SubscriptionFlag = nSubFlag; //clear previous entries ci.EntityTags.Clear(); int nTagCount = 0; DCV.GetEntityTagCount(out nTagCount); for(int i=0;i // // // // if(ci==null) { return; } if(ci.CDL.Count==0) { return; } string filename=string.Format("{0}.wsx",((PlaylistItem)ci.CDL[0]).CacheUrl); XmlDocument dom = new XmlDocument(); //create an empty playlist dom.LoadXml(""); //get the smil node XmlElement smilnode = dom.DocumentElement; int i=0; foreach(Object obj in ci.CDL) { ((PlaylistItem)obj).CacheID = i; string file = ((PlaylistItem)obj).CacheUrl; XmlElement elem = dom.CreateElement("media"); XmlAttribute attr = dom.CreateAttribute("src"); attr.Value = file; elem.Attributes.Append(attr); attr = dom.CreateAttribute("CacheID"); attr.Value = i.ToString(); elem.Attributes.Append(attr); smilnode.AppendChild(elem); i++; } // now save the file string filesaved = string.Format("{0}.wsx",((PlaylistItem)ci.CDL[0]).CacheUrl); dom.Save(filesaved); // set the cache URL to this file for cache hit ci.CacheUrl = filesaved; } // based on the response // we reply as a hit or miss // and update database accordingly void IWMSCacheProxyServerCallback.OnCompareContentInformation( int lHr, WMS_CACHE_VERSION_COMPARE_RESPONSE CompareResponse, IWMSContext pNewContentInfo, object varContext ) { Debug.WriteLine("CacheProxyPlugin::IWMSCacheProxyServerCallback.OnCompareContentInformation entered"); WMS_CACHE_QUERY_RESPONSE Response = WMS_CACHE_QUERY_RESPONSE.WMS_CACHE_QUERY_MISS; ContentInfo ci = (ContentInfo)varContext; // we use this content info to call back by default // it changes if the CI in our database is valid IWMSContext pContenInfo = pNewContentInfo; // if the call failed, we have to go for protocol rollover etc if(lHr==0) { switch (CompareResponse) { case WMS_CACHE_VERSION_COMPARE_RESPONSE.WMS_CACHE_VERSION_CACHE_STALE: { RemoveEntryFromDatabase(ci,true); } break; case WMS_CACHE_VERSION_COMPARE_RESPONSE.WMS_CACHE_VERSION_CACHE_UP_TO_DATE: { if((ci.ContentType & 1 )!=0) // it's a brodcast content { Response = WMS_CACHE_QUERY_RESPONSE.WMS_CACHE_QUERY_HIT_PLAY_BROADCAST; } else { Response = WMS_CACHE_QUERY_RESPONSE.WMS_CACHE_QUERY_HIT_PLAY_ON_DEMAND; } // update the database with the new information RemoveEntryFromDatabase(ci,false); GetContentInfoFromContext(pNewContentInfo,ref ci); UpdateTable(ci); GetContentInfoContext(ci,out pContenInfo); } break; case WMS_CACHE_VERSION_COMPARE_RESPONSE.WMS_CACHE_VERSION_FAIL_TO_CHECK_VERSION: { } break; default: break; } Debug.WriteLine("CacheProxyPlugin::IWMSCacheProxyServerCallback.OnCompareContentInformation -> calling OnQueryCache"); // crude test...look up if we have :// embedded in the URL, if not prefix it with file:// // otherwise just let it go int nIndex = ci.CacheUrl.IndexOf("://"); string strCacheUrl = ci.CacheUrl; if(nIndex==-1) { strCacheUrl = string.Format("file://{0}",ci.CacheUrl); } ci.CacheProxyCallback.OnQueryCache(0,Response,strCacheUrl,pContenInfo, null,ci.varContext); } return; } void AddCDLToContext(IWMSContext pContentInfo,ContentInfo ci) { IWMSDataContainerVersion DCV = null; Type t = typeof(IWMSDataContainerVersion); Guid guid = t.GUID; object odcv = (object)DCV; /* pContentInfo.GetAndQueryIUnknownValue("WMS_CACHE_CONTENT_INFORMATION_DATA_CONTAINER_VERSION", 3,ref guid,out odcv, 0); */ pContentInfo.GetAndQueryIUnknownValue(WMSDefines.WMS_CACHE_CONTENT_INFORMATION_DATA_CONTAINER_VERSION, WMSDefines.WMS_CACHE_CONTENT_INFORMATION_DATA_CONTAINER_VERSION_ID,ref guid,out odcv, 0); DCV = (IWMSDataContainerVersion)odcv; if(ci.EntityTags!=null) { foreach(string str in ci.EntityTags) { DCV.SetEntityTag(str); } } } void IWMSCacheProxyServerCallback.OnDownloadContentProgress( int lHr, WMS_RECORD_PROGRESS_OPCODE opCode, IWMSContext pArchiveContext, object varContext ) { Debug.WriteLine("CacheProxyPlugin::IWMSCacheProxyServerCallback.OnDownloadContentProgress entered"); if(lHr==0) { if(opCode==WMS_RECORD_PROGRESS_OPCODE.WMS_RECORD_PROGRESS_ARCHIVE_STARTED) { INSSBuffer NSBuffer=null; Guid guid = typeof(INSSBuffer).GUID; Object obj; pArchiveContext.GetAndQueryIUnknownValue(WMSDefines.WMS_ARCHIVE_CONTENT_DESCRIPTION_LIST_BUFFER, WMSDefines.WMS_ARCHIVE_CONTENT_DESCRIPTION_LIST_BUFFER_ID,ref guid,out obj ,0); NSBuffer = (INSSBuffer)obj; // get the file name string CacheFile; pArchiveContext.GetStringValue(WMSDefines.WMS_ARCHIVE_FILENAME, WMSDefines.WMS_ARCHIVE_FILENAME_ID,out CacheFile,0); ContentInfo ci = (ContentInfo)varContext; string s = GetStringFromNSSBuffer(NSBuffer); if((ci.ContentType & 2)==0) // not a playlist { ci.CDLData = s; ci.CacheUrl = CacheFile; } else { PlaylistItem Item = new PlaylistItem(); Item.CacheUrl = CacheFile; Item.CDLData = s; ci.CDL.Add((object)Item); } } } return; } void IWMSCacheProxyServerCallback.OnDownloadContentFinished( int lHr, object[] psaArchiveContexts, object varContext ) { Debug.WriteLine("CacheProxyPlugin::IWMSCacheProxyServerCallback.OnDownloadContentFinished entered"); if(lHr==0) // download succeeded { ContentInfo ci = (ContentInfo)varContext; // server might have renamed the specified file if there is a name collision // so we get the actual name of the file from archive context that we get if(psaArchiveContexts.Length!=0) { if((ci.ContentType & 2)==0) // it's not a playlist { IWMSContext ArchiveContext = (IWMSContext)psaArchiveContexts.GetValue(0); // get the file name string CacheFile; ArchiveContext.GetStringValue(WMSDefines.WMS_ARCHIVE_FILENAME, WMSDefines.WMS_ARCHIVE_FILENAME_ID,out CacheFile,0); ci.CacheUrl = CacheFile; } else { SavePlaylist(ci); } UpdateTable(ci); } } else { Debug.WriteLine(string.Format("HRESULT for CacheProxyPlugin::IWMSCacheProxyServerCallback.OnDownloadContentFinished = {0}", lHr.ToString("X"))); } return; } void UpdateTable(ContentInfo ci) { Debug.WriteLine("CacheProxyPlugin::UpdateTable entered"); string strTags=null; for(int i=0;(ci.EntityTags!=null)&&(i