/// <summary> /// Creates a programmer-friendly object for using a device /// that happens implement a MediaServer. /// </summary> /// <param name="device"></param> public CpMediaServer(UPnPDevice device) { UPnPService sCM = device.GetServices(CpConnectionManager.SERVICE_NAME)[0]; UPnPService sCD = device.GetServices(CpContentDirectory.SERVICE_NAME)[0]; CpConnectionManager cpCM = new CpConnectionManager(sCM); CpContentDirectory cpCD = new CpContentDirectory(sCD); UDN = device.UniqueDeviceName; if ( (cpCD.HasAction_GetSearchCapabilities == false) || (cpCD.HasAction_GetSortCapabilities == false) || (cpCD.HasAction_GetSystemUpdateID == false) || (cpCD.HasAction_Browse == false) ) { throw new UPnPCustomException(0, "MediaServer does not implement minimum features."); } this.m_ConnectionManager = cpCM; this.m_ContentDirectory = cpCD; //create a virtualized root container with the desired settings m_Root = new CpRootContainer(this); m_Root.UDN = device.UniqueDeviceName; }
/// <summary> /// Allows the programmer to request that a transfer stop. /// </summary> /// <param name="TransferID"> /// The transfer to stop, identified by its ID, /// which can be obtained from the CDS TransferIDs comma-separated value list /// state variable. /// </param> /// <param name="Tag"> /// Miscellaneous, user-provided object for tracking this /// asynchronous call. Can be used as a means to pass a /// user-defined "state object" at invoke-time so that /// the executed callback during results-processing can be /// aware of the component's state at the time of the call. /// </param> /// <param name="callback">the callback to execute when results become available</param> public void RequestStopTransferResource(uint TransferID, object Tag, CpContentDirectory.Delegate_OnResult_StopTransferResource callback) { this.ContentDirectory.StopTransferResource(TransferID, Tag, callback); }
/// <summary> /// Processes the results of Browse requests. /// </summary> /// <param name="sender"></param> /// <param name="ObjectID"></param> /// <param name="BrowseFlag"></param> /// <param name="Filter"></param> /// <param name="StartingIndex"></param> /// <param name="RequestedCount"></param> /// <param name="SortCriteria"></param> /// <param name="Result"></param> /// <param name="NumberReturned"></param> /// <param name="TotalMatches"></param> /// <param name="UpdateID"></param> /// <param name="e"></param> /// <param name="_Tag"></param> private void OnBrowseDone(CpContentDirectory sender, System.String ObjectID, OpenSource.UPnP.AV.CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag BrowseFlag, System.String Filter, System.UInt32 StartingIndex, System.UInt32 RequestedCount, System.String SortCriteria, System.String Result, System.UInt32 NumberReturned, System.UInt32 TotalMatches, System.UInt32 UpdateID, UPnPInvokeException e, object _Tag) { _RequestState state = (_RequestState) _Tag; if (e != null) { if (state.Callback_Browse1 != null) { state.Callback_Browse1(this, ObjectID, BrowseFlag, Filter, StartingIndex, RequestedCount, SortCriteria, e, null, state.Tag, null, NumberReturned, TotalMatches, UpdateID); } else if (state.Callback_Browse2 != null) { state.Callback_Browse2(this, e, null, state.Tag, null, NumberReturned, TotalMatches, UpdateID); } } else { ArrayList al = null; try { al = CpMediaBuilder.BuildMediaBranches(Result); } catch (Exception parseError) { if (state.Callback_Browse1 != null) { state.Callback_Browse1(this, ObjectID, BrowseFlag, Filter, StartingIndex, RequestedCount, SortCriteria, e, parseError, state.Tag, null, NumberReturned, TotalMatches, UpdateID); } else if (state.Callback_Browse2 != null) { state.Callback_Browse2(this, e, parseError, state.Tag, null, NumberReturned, TotalMatches, UpdateID); } al = null; } if (al != null) { if (state.Callback_Browse1 != null) { state.Callback_Browse1(this, ObjectID, BrowseFlag, Filter, StartingIndex, RequestedCount, SortCriteria, e, null, state.Tag, (IUPnPMedia[]) al.ToArray(typeof(IUPnPMedia)), NumberReturned, TotalMatches, UpdateID); } else if (state.Callback_Browse2 != null) { state.Callback_Browse2(this, e, null, state.Tag, (IUPnPMedia[]) al.ToArray(typeof(IUPnPMedia)), NumberReturned, TotalMatches, UpdateID); } } } }
/// <summary> /// Requests the remote media server to provide its SystemUpdateID value. /// </summary> /// <param name="Tag"> /// Miscellaneous, user-provided object for tracking this /// asynchronous call. Can be used as a means to pass a /// user-defined "state object" at invoke-time so that /// the executed callback during results-processing can be /// aware of the component's state at the time of the call. /// </param> /// <param name="callback">the callback to execute when results become available</param> public void RequestGetSystemUpdateID(object Tag, CpContentDirectory.Delegate_OnResult_GetSystemUpdateID callback) { this.ContentDirectory.GetSystemUpdateID(Tag, callback); }
/// <summary> /// Allows the programmer to query the progress of this transfer object. /// </summary> /// <param name="TransferID"> /// The transfer to obtain information on, identified by its ID, /// which can be obtained from the CDS TransferIDs comma-separated value list /// state variable. /// </param> /// <param name="Tag"> /// Miscellaneous, user-provided object for tracking this /// asynchronous call. Can be used as a means to pass a /// user-defined "state object" at invoke-time so that /// the executed callback during results-processing can be /// aware of the component's state at the time of the call. /// </param> /// <param name="callback">the callback to execute when results become available</param> public void RequestGetTransferProgress(uint TransferID, object Tag, CpContentDirectory.Delegate_OnResult_GetTransferProgress callback) { this.ContentDirectory.GetTransferProgress(TransferID, Tag, callback); }
/// <summary> /// Executes when the ContentDirectory events for the first time. /// </summary> /// <param name="sender"></param> /// <param name="newVal"></param> private void Sink_OnCdEvented(CpContentDirectory sender, UInt32 newVal) { sender.OnStateVariable_SystemUpdateID -= new CpContentDirectory.StateVariableModifiedHandler_SystemUpdateID(this.Sink_OnCdEvented); string udn = sender.GetUPnPService().ParentDevice.UniqueDeviceName; lock (LockHashes) { InitStatus status = (InitStatus) UdnToInitStatus[udn]; if (status != null) { status.ZeroMeansDone--; status.EventedCD = true; this.ProcessInitStatusChange(udn); } } }
public static CdsBrowseSearchResults GetContainerMetadataAndValidate(string containerID, CpContentDirectory cds, CdsSubTest test, CdsSubTestArgument arg, CdsResult_BrowseAll details, out IUPnPMedia metadata) { BrowseInput input = new BrowseInput(); input.ObjectID = containerID; input.BrowseFlag = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA; input.Filter = "*"; input.StartingIndex = 0; input.RequestedCount = 0; input.SortCriteria = ""; metadata = null; test.SetExpectedTestingTime ((++details.ExpectedTotalBrowseRequests) * 30); arg.ActiveTests.UpdateTimeAndProgress( details.TotalBrowseRequests * 30); CdsBrowseSearchResults results = Browse(input, test, arg, cds, details); test.SetExpectedTestingTime ((details.ExpectedTotalBrowseRequests) * 30); arg.ActiveTests.UpdateTimeAndProgress( (details.TotalBrowseRequests) * 30); if (results.InvokeError == null) { if (results.NumberReturned != 1) { //results.SetError(UPnPTestStates.Warn); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned NumberReturned=" +results.NumberReturned+ " when it should logically be 1. This output parameter is not really useful for BrowseMetadata so this logic error does not prevent towards certification, but it should be fixed."; //arg._TestGroup.AddEvent(LogImportance.Low, test.Name, msg); results.ResultErrors.Add( new Exception(msg) ); } if (results.TotalMatches != 1) { //results.SetError(UPnPTestStates.Warn); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned TotalMatches=" +results.TotalMatches+ " when it should logically be 1. This output parameter is not really useful BrowseMetadata so this logic error does not prevent towards certification, but it should be fixed."; //arg._TestGroup.AddEvent(LogImportance.Low, test.Name, msg); results.ResultErrors.Add( new Exception(msg) ); } if (results.MediaObjects != null) { if (results.MediaObjects.Count == 1) { metadata = results.MediaObjects[0] as IUPnPMedia; if (metadata == null) { throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but the container's metadata is not stored in an IUPnPMedia object. The offending type is " + results.MediaObjects[0].GetType().ToString(), results.MediaObjects[0]); } IMediaContainer imc = metadata as IMediaContainer; // // check metadata // if (imc == null) { //results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned a DIDL-Lite media object but it was not declared with a \"container\" element."; //arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); results.ResultErrors.Add( new Exception(msg) ); } else { } if (metadata.ID != containerID) { //results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned a DIDL-Lite media object with ID=\"" +metadata.ID+ "\" when it should be \"" +containerID+"\" as indicated by the input parameter."; //arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); results.ResultErrors.Add( new Exception(msg) ); } if (containerID == "0") { if (metadata.ParentID != "-1") { //results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned a DIDL-Lite media object with parentID=\"" +metadata.ID+ "\" when it must be \"-1\" because the container is the root container."; //arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); results.ResultErrors.Add( new Exception(msg) ); } // no need to check parentID values for other containers because // they get checked when getting the children for this container. } if (results.WorstError < UPnPTestStates.Failed) { //string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " succeeded."; //arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, msg); } } else { //results.SetError(UPnPTestStates.Failed); //string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " did not yield exactly one CDS-compliant media object in its result. Instantiated a total of " +results.MediaObjects.Count+ " media objects."; //arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); } } else { //throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but no media objects were instantiated.", null); } } if ((results.InvokeError != null) || (results.ResultErrors.Count > 0)) { StringBuilder msg = new StringBuilder(); results.SetError(UPnPTestStates.Failed); msg.AppendFormat("\"{0}\": {1} did not yield exactly one CDS-compliant media object in its result. Instantiated a total of {2} media objects.", test.Name, input.PrintBrowseParams(), results.MediaObjects.Count); msg.AppendFormat("\r\nAdditional Information:"); if (results.InvokeError != null) { msg.AppendFormat("\r\n{0}", results.InvokeError.Message); } foreach (Exception e in results.ResultErrors) { msg.AppendFormat("\r\n{0}", e.Message); } arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg.ToString()); } return results; }
public override void RequestDestroyObject(DestroyObjectRequest request, CpContentDirectory.Delegate_OnResult_DestroyObject callback) { }
public override void RequestUpdateObject(UpdateObjectRequest request, CpContentDirectory.Delegate_OnResult_UpdateObject callback) { }
/// <summary> /// Tests filters for metadata. /// </summary> /// <param name="testThis"></param> /// <param name="test"></param> /// <param name="arg"></param> /// <param name="cds"></param> /// <param name="stats"></param> /// <returns></returns> public static CdsBrowseSearchResults TestFiltersBrowseMetadata(IUPnPMedia testThis, CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag browseFlag, Cds_BrowseFilter test, CdsSubTestArgument arg, CpContentDirectory cds, CdsResult_BrowseFilter stats) { CdsBrowseSearchResults r = new CdsBrowseSearchResults(); try { BrowseInput input = new BrowseInput(); input.BrowseFlag = browseFlag; if (input.BrowseFlag == CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA) { input.ObjectID = testThis.ID; } else { input.ObjectID = testThis.ParentID; } input.SortCriteria = ""; input.StartingIndex = 0; input.RequestedCount = 0; //assume the test will pass, but worsen the result when we find errors r.SetError(UPnPTestStates.Pass); ArrayList propNames = new ArrayList((ICollection)testThis.Properties.PropertyNames); // add property names related to resources propNames.Add(T[_DIDL.Res]); foreach (string str in MediaResource.GetPossibleAttributes()) { propNames.Add(T[_DIDL.Res] + "@" + str); propNames.Add("@" + str); } propNames.Add(T[_DIDL.Desc]); if (propNames.Count != stats.NumberOfProperties) { throw new TestException("Number of calculated metadata properties (" + propNames.Count + ") doesn't match number of actual metadata properties (" + propNames.Count + ") for testing.", stats); } test._Details.Filters = propNames; int inc = propNames.Count / 4; if (inc == 0) { inc = 1; } for (int numProps = 0; numProps < propNames.Count; numProps++) { for (int iProp = 0; iProp < propNames.Count; iProp += inc) { IList filterSettings = GetFilterSettings(propNames, numProps, iProp, iProp); input.Filter = GetCSVString(filterSettings); CdsBrowseSearchResults br; arg.ActiveTests.UpdateTimeAndProgress(90 * stats.TotalBrowseRequests); br = Cds_BrowseAll.Browse(input, test, arg, cds, stats); if (br.WorstError >= UPnPTestStates.Failed) { StringBuilder teMsg = new StringBuilder(); teMsg.AppendFormat("\"{0}\" is terminating early because {1} returned with an error or had problems with the DIDL-Lite.", test.Name, input.PrintBrowseParams()); if (br.Result != "") { teMsg.AppendFormat(" Returned DIDL=\"{0}\".", br.Result); } throw new TerminateEarly(teMsg.ToString()); } // if (br.InvokeError != null) // { // throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " +input.PrintBrowseParams()+ " returned with an error" + br.InvokeError.Message, br.InvokeError); // } if (input.BrowseFlag == CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA) { if (br.MediaObjects.Count != 1) { throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " + input.PrintBrowseParams() + " did not return with exactly one media object in its DIDL-Lite response. DIDL-Lite => " + br.Result); } IUPnPMedia mo = (IUPnPMedia)br.MediaObjects[0]; CheckReturnedMetadata(mo, testThis, input, filterSettings, br, test); arg.TestGroup.AddEvent(LogImportance.Remark, test.Name, "\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and encountered no errors in the results."); } else { IList childList = testThis.Parent.CompleteList; foreach (IUPnPMedia gotChild in br.MediaObjects) { IUPnPMedia testAgainstChild = null; foreach (IUPnPMedia child in childList) { if (child.ID == gotChild.ID) { testAgainstChild = child; break; } } if (testAgainstChild == null) { throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " + input.PrintBrowseParams() + " returned media object with ID=\"" + gotChild.ID + "\", which was not a child object of containerID=\"" + input.ObjectID + "\" during a prerequisite test."); } CheckReturnedMetadata(gotChild, testAgainstChild, input, filterSettings, br, test); arg.TestGroup.AddEvent(LogImportance.Remark, test.Name, "\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and encountered no errors in the results."); } } } } } catch (TerminateEarly te) { string reason = "\"" + test.Name + "\" terminating early. Reason => " + te.Message; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, reason); r.SetError(UPnPTestStates.Failed); return r; } if (r.WorstError > UPnPTestStates.Warn) { throw new TestException("\"" + test.Name + "\" should not reach this code if the result is worse than " + UPnPTestStates.Warn.ToString() + ".", null); } return r; }
/// <summary> /// No-op. /// </summary> /// <param name="br"></param> /// <param name="callback"></param> public override void RequestBrowse(BrowseRequest br, CpContentDirectory.Delegate_OnResult_Browse callback) { }
/// <summary> /// Tests the container with BrowseDirectChildren and different ranges. /// </summary> /// <param name="c"></param> /// <param name="test"></param> /// <param name="arg"></param> /// <param name="cds"></param> /// <param name="stats"></param> /// <returns></returns> public static CdsBrowseSearchResults TestContainerRanges(IMediaContainer c, CdsSubTest test, CdsSubTestArgument arg, CpContentDirectory cds, CdsResult_BrowseStats stats) { CdsBrowseSearchResults r = new CdsBrowseSearchResults(); try { BrowseInput input = new BrowseInput(); input.BrowseFlag = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN; input.Filter = "*"; input.ObjectID = c.ID; input.SortCriteria = ""; input.StartingIndex = 0; input.RequestedCount = 0; if (c.CompleteList.Count != c.ChildCount) { throw new TestException("\""+test.Name+"\" has c.CompleteList.Count=" +c.CompleteList.Count+ " but c.ChildCount=" +c.ChildCount+ ".", c); } //assume the test will pass, but worsen the result when we find errors r.SetError(UPnPTestStates.Pass); // test starting index = 0 to c.ChildCount+1 // test requested count = 0 to c.ChildCoun+1 for (uint start = 0; start < c.ChildCount+1; start++) { for (uint requested = 0; requested < c.ChildCount+1; requested++) { uint start_requested = start+requested; bool doBrowse = false; int tenthCount = c.ChildCount / 10; int quarterCount = c.ChildCount / 4; if (quarterCount == 0) { quarterCount = 1; } if (tenthCount == 0) { tenthCount = 1; } if (start < LIMIT) { if ( (requested == 0) || (start_requested > c.ChildCount - LIMIT) ) { doBrowse = true; } } else if (start >= c.ChildCount - LIMIT) { if (start_requested < c.ChildCount+LIMIT) { doBrowse = true; } } else if ((start % quarterCount == 0) && (requested % tenthCount == 0)) { doBrowse = true; } if (doBrowse == false) { //stats.TotalBrowseRequests++; //arg.ActiveTests.UpdateTimeAndProgress(stats.TotalBrowseRequests * 300); } else { arg.ActiveTests.UpdateTimeAndProgress(stats.TotalBrowseRequests * 300); input.StartingIndex = start; input.RequestedCount = requested; CdsBrowseSearchResults br; br = Cds_BrowseAll.Browse(input, test, arg, cds, stats); if (br.WorstError >= UPnPTestStates.Failed) { if ( (input.StartingIndex < c.ChildCount) ) { throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " +input.PrintBrowseParams()+ " returned with an error or had problems with the DIDL-Lite."); } else { arg._TestGroup.AddEvent(LogImportance.High, test.Name, "\"" + test.Name + "\": Warning: " +input.PrintBrowseParams()+ " returned an error."); r.SetError(UPnPTestStates.Warn); } } // check return values if (br.NumberReturned != br.MediaObjects.Count) { throw new TerminateEarly("\""+test.Name+"\" did a "+ input.PrintBrowseParams() + " and the number of media objects instantiated from the DIDL-Lite (instantiated=" +br.MediaObjects.Count+ ") does not match NumberReturned=" +br.NumberReturned+"."); } long expectedReturned; if (input.StartingIndex == 0) { if (input.RequestedCount == 0) { expectedReturned = c.ChildCount; } else if (input.RequestedCount > c.ChildCount) { expectedReturned = c.ChildCount; } else if (input.RequestedCount <= c.ChildCount) { expectedReturned = input.RequestedCount; } else { throw new TestException("\""+test.Name+"\" should not reach here.", null); } } else { if (input.RequestedCount == 0) { expectedReturned = c.ChildCount - input.StartingIndex; } else { expectedReturned = c.ChildCount - input.StartingIndex; if (expectedReturned > input.RequestedCount) { expectedReturned = input.RequestedCount; } } } if ((expectedReturned < 0) || (expectedReturned > c.ChildCount)) { throw new TestException("\""+test.Name+"\" did a " + input.PrintBrowseParams() + " and the expected number of media objects is invalid=" + expectedReturned + ".", br); } if (br.NumberReturned != expectedReturned) { throw new TerminateEarly("\""+test.Name+"\" did a "+ input.PrintBrowseParams() + " and NumberReturned=" +br.NumberReturned+ " but test expects " +expectedReturned+ " child objects according to results from a prerequisite test."); } if (br.TotalMatches != c.ChildCount) { throw new TerminateEarly("\""+test.Name+"\" did a "+ input.PrintBrowseParams() + " and TotalMatches=" +br.TotalMatches+ " but test found " +c.ChildCount+ " child objects in a prerequisite test."); } uint cUpdateID = 0; try { cUpdateID = (uint) c.Tag; } catch (Exception ce) { throw new TestException("\""+test.Name+"\" could not cast c.Tag into a uint value", null, ce); } if (br.UpdateID != cUpdateID) { throw new TerminateEarly("\""+test.Name+"\" did a "+ input.PrintBrowseParams() + " and UpdateID=" +br.UpdateID+ " but test expected=" +cUpdateID+ " as found in a prerequisite test."); } arg.TestGroup.AddEvent(LogImportance.Remark, test.Name, "\""+test.Name+"\" did a " +input.PrintBrowseParams()+ " and encountered no errors in the results."); arg.ActiveTests.UpdateTimeAndProgress(stats.TotalBrowseRequests * 300); } } } } catch (TerminateEarly te) { arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, test.Name + " is terminating early because of the following error: " + te.Message); r.SetError(UPnPTestStates.Failed); return r; } if (r.WorstError > UPnPTestStates.Warn) { throw new TestException("\"" + test.Name + "\" should not reach this code if the result is worse than " + UPnPTestStates.Warn.ToString() + ".", null); } return r; }
/// <summary> /// Performs a Browse invocation. /// </summary> /// <param name="input"></param> /// <param name="test"></param> /// <param name="arg"></param> /// <param name="cds"></param> /// <param name="stats"></param> /// <returns></returns> public static CdsBrowseSearchResults Browse(BrowseInput input, CdsSubTest test, CdsSubTestArgument arg, CpContentDirectory cds, CdsResult_BrowseStats stats) { CdsBrowseSearchResults results = new CdsBrowseSearchResults(); results.SetError(UPnPTestStates.Pass); results.ResultErrors = new ArrayList(); arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, "\""+test.Name+"\" about to do " + input.PrintBrowseParams()+ "."); try { cds.Sync_Browse(input.ObjectID, input.BrowseFlag, input.Filter, input.StartingIndex, input.RequestedCount, input.SortCriteria, out results.Result, out results.NumberReturned, out results.TotalMatches, out results.UpdateID); } catch (UPnPInvokeException error) { results.InvokeError = error; } if (results.InvokeError == null) { arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, "\""+test.Name+"\" completed " + input.PrintBrowseParams()+ " with no errors returned by the device."); } else { arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, "\""+test.Name+"\" completed " + input.PrintBrowseParams()+ " with the device returning an error."); } stats.TotalBrowseRequests++; ArrayList branches = null; if (results.InvokeError == null) { try { if (results.Result != null) { if (results.Result != "") { bool schemaOK = CheckDidlLiteSchema(results.Result); if (schemaOK) { results.MediaObjects = branches = MediaBuilder.BuildMediaBranches(results.Result, typeof (MediaItem), typeof(MediaContainer), true); if (branches.Count != results.NumberReturned) { results.ResultErrors.Add (new CdsException(input.PrintBrowseParams() + " has the \"Result\" argument indicating the presence of " +branches.Count+ " media objects but the request returned NumberReturned=" +results.NumberReturned+".")); } if (input.BrowseFlag == CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA) { if (branches.Count != 1) { results.ResultErrors.Add (new CdsException(input.PrintBrowseParams() + " has the \"Result\" argument indicating the presence of " +branches.Count+ " media objects but the request should have only returned 1 media object.")); } } foreach (IUPnPMedia mobj in branches) { IMediaContainer imc = mobj as IMediaContainer; if (imc != null) { if (imc.CompleteList.Count > 0) { StringBuilder offendingList = new StringBuilder(); int offenses = 0; foreach (IUPnPMedia offending in imc.CompleteList) { if (offenses > 0) { offendingList.Append(","); } offendingList.AppendFormat("\"{0}\"", offending.ID); offenses++; } results.ResultErrors.Add (new CdsException(input.PrintBrowseParams() + " has the \"Result\" argument with a declared container (ID=\""+imc.ID+"\") element that also includes its immediate children. Illegally declared media objects in the response are: "+offendingList.ToString())); } } } } } } } catch (Exception error2) { results.ResultErrors.Add (error2); if (results.MediaObjects == null) { results.MediaObjects = new ArrayList(); } } } // log any errors if ((results.InvokeError != null) || (results.ResultErrors.Count > 0)) { LogErrors(arg._TestGroup, test, input, "Browse", results.InvokeError, results.ResultErrors); results.SetError(UPnPTestStates.Failed); } return results; }
public static CdsBrowseSearchResults CheckMetadata(IUPnPMedia checkAgainstThis, CpContentDirectory cds, CdsSubTest test, CdsSubTestArgument arg, CdsResult_BrowseAll details) { // // Save a reference to the media object with the most filterable properties int numProperties = 0; if (details.MostMetadata != null) { numProperties = details.MostMetadata.Properties.Count; if (details.MostMetadata.DescNodes.Count > 0) { numProperties ++; } } int checkValue = 0; if (checkAgainstThis.ID != "0") { if (checkAgainstThis.Resources.Length > 0) { checkValue = checkAgainstThis.Properties.Count; if (checkAgainstThis.DescNodes.Count > 0) { checkValue++; } } } if (checkValue > numProperties) { details.MostMetadata = checkAgainstThis; } // do a browsemetadata on the media object and determine if the metadata matche. BrowseInput input = new BrowseInput(); input.ObjectID = checkAgainstThis.ID; input.BrowseFlag = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA; input.Filter = "*"; input.StartingIndex = 0; input.RequestedCount = 0; input.SortCriteria = ""; IUPnPMedia metadata = null; CdsBrowseSearchResults results = Browse(input, test, arg, cds, details); test.SetExpectedTestingTime ((details.ExpectedTotalBrowseRequests) * 30); arg.ActiveTests.UpdateTimeAndProgress( (details.TotalBrowseRequests) * 30); if (results.WorstError <= UPnPTestStates.Warn) { if (results.NumberReturned != 1) { results.SetError(UPnPTestStates.Warn); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned NumberReturned=" +results.NumberReturned+ " when it should logically be 1. This output parameter is not really useful for BrowseMetadata so this logic error does not prevent towards certification, but it should be fixed."; arg._TestGroup.AddEvent(LogImportance.Low, test.Name, msg); } if (results.TotalMatches != 1) { results.SetError(UPnPTestStates.Warn); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned TotalMatches=" +results.TotalMatches+ " when it should logically be 1. This output parameter is not really useful BrowseMetadata so this logic error does not prevent towards certification, but it should be fixed."; arg._TestGroup.AddEvent(LogImportance.Low, test.Name, msg); } if (results.MediaObjects != null) { if (results.MediaObjects.Count == 1) { metadata = results.MediaObjects[0] as IUPnPMedia; if (metadata == null) { throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but the object's metadata is not stored in an IUPnPMedia object. The offending type is " + results.MediaObjects[0].GetType().ToString(), results.MediaObjects[0]); } if (metadata.ID != input.ObjectID) { results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned a DIDL-Lite media object with ID=\"" +metadata.ID+ "\" when it should be \"" +input.ObjectID+"\" as indicated by the input parameter."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); } if (metadata.ParentID != checkAgainstThis.ParentID) { results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned a DIDL-Lite media object with parentID=\"" +metadata.ParentID+ "\" when it should be \"" +checkAgainstThis.ParentID+ "\"."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); } string original; string received; try { original = checkAgainstThis.ToDidl(); received = metadata.ToDidl(); } catch { results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " encountered errors with the DIDL-Lite."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); return results; } if (string.Compare(original, received) == 0) { //string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " successfully returned a DIDL-Lite media object that succesfully matches with previously seen metadata."; //arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, msg); } else { System.Xml.XmlDocument doc1 = new XmlDocument(); XmlDocument doc2 = new XmlDocument(); doc1.LoadXml(original); doc2.LoadXml(received); bool isMatch = true; foreach (XmlElement el1 in doc1.GetElementsByTagName("*")) { bool foundElement = false; foreach (XmlElement el2 in doc2.GetElementsByTagName("*")) { if ( (el1.Name != "item") && (el1.Name != "container") && (el1.OuterXml == el2.OuterXml) ) { foundElement = true; break; } else if ( ( (el1.Name == "DIDL-Lite") || (el1.Name == "item") || (el1.Name == "container") ) && (el1.Name == el2.Name) ) { foundElement = true; break; } } if (foundElement == false) { isMatch = false; break; } } if (isMatch==false) { results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned a DIDL-Lite media object that failed to match with previously seen metadata."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, test.Name + ": Original=\""+original+"\" Received=\""+received+"\""); } } } else { results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " did not yield exactly one CDS-compliant media object in its result. Instantiated a total of " +results.MediaObjects.Count+ " media objects."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); } } else { throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but no media objects were instantiated.", null); } } return results; }
/// <summary> /// Method executes when a contentdirectory events a change in a container. /// </summary> /// <param name="sender"></param> /// <param name="NewValue"></param> private void Sink_OnContainerUpdateIDsChanged(CpContentDirectory sender, System.String NewValue) { string csv_containers = NewValue; Hashtable cache = new Hashtable(); DText parser = new DText(); DText parser2 = new DText(); parser.ATTRMARK = ","; parser2.ATTRMARK = ","; if (csv_containers != "") { parser[0] = csv_containers; int dcnt = parser.DCOUNT(); for (int i=1; i <= dcnt; i++) { string id, update; if (Accumulator_ContainerUpdateIDs.Delimitor == ",") { id = parser[i++]; update = parser[i]; } else { string pair = parser[i]; parser2[0] = pair; id = parser2[1]; update = parser2[2]; } CpMediaContainer cpc = (CpMediaContainer) this.GetDescendent(id, cache); if (cpc !=null) { try { UInt32 updateId = UInt32.Parse(update); if (updateId != cpc.UpdateID) { cpc.ForceUpdate(false); } } catch { cpc.ForceUpdate(false); } } } } cache.Clear(); }
/// <summary> /// This method will invoke a CDS browse request and provide the results /// directly the application-caller. /// <para> /// Implementation simply calls the parent owner's implementation of /// <see cref="CpMediaContainer.RequestBrowse "/>(ICpMedia, CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag, string, uint, uint, string). /// </para> /// </summary> /// <param name="BrowseFlag">browse metadata or direct children</param> /// <param name="Filter"> /// Comma-separated value list of metadata names to include /// in the response. For all metadata, use * character. /// </param> /// <param name="StartingIndex"> /// If obtaining children, the start index of the results. /// Otherwise set to zero. /// </param> /// <param name="RequestedCount"> /// If obtaining children the max number of child objects /// to retrieve. Otherwise use zero. /// </param> /// <param name="SortCriteria"> /// Comma-separated value list of metadata names to use for /// sorting, such that preceding each metadata name (but after /// the comma) a + or - character is present to indicate /// ascending or descending sort order for that property. /// </param> /// <param name="Tag"> /// Miscellaneous, user-provided object for tracking this /// asynchronous call. Can be used as a means to pass a /// user-defined "state object" at invoke-time so that /// the executed callback during results-processing can be /// aware of the component's state at the time of the call. /// </param> /// <param name="callback">the callback to execute when results become available</param> /// <exception cref="ApplicationException"> /// Thrown if the BrowseFlag value is BrowseDirectChildren because only the /// object's metadata can be obtained use browse. /// </exception> public void RequestBrowse(CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag BrowseFlag, string Filter, uint StartingIndex, uint RequestedCount, string SortCriteria, object Tag, CpMediaDelegates.Delegate_ResultBrowse callback) { if (BrowseFlag == CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN) { throw new ApplicationException("BrowseFlag cannot be BROWSEDIRECTCHILDREN"); } CpMediaContainer parent = (CpMediaContainer) this.Parent; parent.RequestBrowse(this, BrowseFlag, Filter, StartingIndex, RequestedCount, SortCriteria, Tag, callback); }
/// <summary> /// Method executes when a media server indicates that the tree /// hierarchy has changed... only executes when the service /// doesn't implement the OnContainerUpdateIDs state variable /// </summary> private void Sink_OnSystemUpdateIDChanged(CpContentDirectory sender, System.UInt32 NewValue) { if (sender.HasStateVariable_ContainerUpdateIDs == false) { this.ForceUpdate(false); } }
/// <summary> /// Requests the remote media server to provide its sort capabilities. /// </summary> /// <param name="Tag"> /// Miscellaneous, user-provided object for tracking this /// asynchronous call. Can be used as a means to pass a /// user-defined "state object" at invoke-time so that /// the executed callback during results-processing can be /// aware of the component's state at the time of the call. /// </param> /// <param name="callback">the callback to execute when results become available</param> public void RequestGetSortCapabilities(object Tag, CpContentDirectory.Delegate_OnResult_GetSortCapabilities callback) { this.ContentDirectory.GetSortCapabilities(Tag, callback); }
/// <summary> /// Executes when the ContentDirectory service returns on the subscribe status. /// </summary> /// <param name="sender"></param> /// <param name="success"></param> private void Sink_OnCdServiceSubscribe(CpContentDirectory sender, bool success) { sender.OnSubscribe -= new CpContentDirectory.SubscribeHandler(this.Sink_OnCdServiceSubscribe); string udn = sender.GetUPnPService().ParentDevice.UniqueDeviceName; lock (LockHashes) { InitStatus status = (InitStatus) UdnToInitStatus[udn]; if (status != null) { status.ZeroMeansDone--; status.SubcribeCD = success; this.ProcessInitStatusChange(udn); } } }
/// <summary> /// /// </summary> /// <param name="parent"></param> /// <param name="cds"></param> /// <param name="test"></param> /// <param name="arg"></param> /// <param name="details"></param> /// <param name="C"></param> /// <returns></returns> /// <exception cref="TerminateEarly"> /// </exception> public static ArrayList GetContainerChildrenAndValidate(IMediaContainer parent, CpContentDirectory cds, CdsSubTest test, CdsSubTestArgument arg, CdsResult_BrowseAll details, Queue C) { uint totalExpected = uint.MaxValue; uint currentChild = 0; ArrayList children = new ArrayList(); while (currentChild < totalExpected) { BrowseInput input = new BrowseInput(); input.ObjectID = parent.ID; input.BrowseFlag = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN; input.Filter = "*"; input.StartingIndex = currentChild; input.RequestedCount = 1; input.SortCriteria = ""; string containerID = parent.ID; if (currentChild == 0) { test.SetExpectedTestingTime ((++details.ExpectedTotalBrowseRequests) * 30); arg.ActiveTests.UpdateTimeAndProgress( details.TotalBrowseRequests * 30); } CdsBrowseSearchResults results = Browse(input, test, arg, cds, details); test.SetExpectedTestingTime ((details.ExpectedTotalBrowseRequests) * 30); arg.ActiveTests.UpdateTimeAndProgress( (details.TotalBrowseRequests) * 30); if (results.WorstError >= UPnPTestStates.Failed) { throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " +input.PrintBrowseParams()+ " returned with an error or had problems with the DIDL-Lite."); } else { if (results.NumberReturned != 1) { if (currentChild != 0) { results.SetError(UPnPTestStates.Failed); arg._TestGroup.AddEvent(LogImportance.Low, test.Name, "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned NumberReturned=" +results.NumberReturned+ " when it should logically be 1."); } } if (results.TotalMatches != totalExpected) { if (currentChild != 0) { results.SetError(UPnPTestStates.Failed); arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned TotalMatches=" +results.TotalMatches+ " when it should logically be " +totalExpected+ " as reported in an earlier Browse request. This portion of the test requires that a MediaServer device not be in a state where its content hierarchy will change."); } else { totalExpected = results.TotalMatches; if (totalExpected > 0) { details.ExpectedTotalBrowseRequests += ((int)results.TotalMatches * 2) - 1; test.SetExpectedTestingTime ((details.ExpectedTotalBrowseRequests) * 30); arg.ActiveTests.UpdateTimeAndProgress( details.TotalBrowseRequests * 30); } } } if (results.MediaObjects != null) { if (results.MediaObjects.Count == 1) { IUPnPMedia child = results.MediaObjects[0] as IUPnPMedia; if (child == null) { throw new TestException("\"" + test.Name + "\"" + " has a TEST LOGIC ERROR. Browse returned without errors but the child object's metadata is not stored in an IUPnPMedia object. The offending type is " + results.MediaObjects[0].GetType().ToString(), results.MediaObjects[0]); } // ensure no duplicates in object ID foreach (IUPnPMedia previousChild in details.AllObjects) { if (previousChild.ID == child.ID) { string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned an object with ID=\"" +child.ID+ "\" which conflicts with a previously seen media object in ParentContainerID=\"" +previousChild.ParentID+ "\"."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); throw new TerminateEarly(msg); } } // ensure updateID is the same between BrowseDirectChildren and earlier BrowseMetadata. try { uint previousUpdateID = (uint) parent.Tag; if (results.UpdateID != previousUpdateID) { string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned an UpdateID=" +results.UpdateID+ " whilst an UpdateID=" +previousUpdateID+ " was obtained in a previous call for ContainerID=\"" +parent.ID+ "\" with BrowseMetadata."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); throw new TerminateEarly(msg); } } catch (TerminateEarly te) { throw te; } catch (Exception e) { throw new TestException(test.Name + " has a TEST LOGIC ERROR. Error comparing UpdateID values", parent, e); } // add the child to lists: C, parent's child list, and Allobjects try { parent.AddObject(child, false); } catch (Exception e) { AddObjectError aoe = new AddObjectError(); aoe.Parent = parent; aoe.Child = child; throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but the child object could not be added to its parent.", aoe, e); } details.AllObjects.Add(child); children.Add(child); if (child.IsContainer) { C.Enqueue(child); details.TotalContainers++; } else { details.TotalItems++; } // // Do a BrowseMetadata and check to see if the XML values are the same. // CdsBrowseSearchResults compareResults = CheckMetadata(child, cds, test, arg, details); if (compareResults.InvokeError != null) { arg._TestGroup.AddEvent(LogImportance.High, test.Name, test.Name + ": Browse(BrowseDirectChildren,StartingIndex="+currentChild+",RequestedCount=0) on ContainerID=["+containerID+"] succeeded with warnings because a BrowseMetadata request was rejected by the CDS."); } else if (compareResults.ResultErrors.Count > 0) { string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " failed because a BrowseMetadata request succeeded but the DIDL-Lite could not be represented in object form. Invalid DIDL-Lite is most likely the cause."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); throw new TerminateEarly(msg); } else if (compareResults.WorstError >= UPnPTestStates.Failed) { string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " failed because one or more child object's failed a comparison of results between BrowseDirectChildren and BrowseMetadata or encountered some other critical error in that process."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); throw new TerminateEarly(msg); } else { //string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " succeeded."; //arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, msg); } // // Track the metadata properties found so far // as we may use them in Browse SortCriteria // // standard top-level attributes Tags T = Tags.GetInstance(); AddTo(details.PropertyNames, "@"+T[_ATTRIB.id]); AddTo(details.PropertyNames, "@"+T[_ATTRIB.parentID]); AddTo(details.PropertyNames, "@"+T[_ATTRIB.restricted]); if (child.IsContainer) { if (child.IsSearchable) { AddTo(details.PropertyNames, "@"+T[_ATTRIB.searchable]); AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.searchable]); } AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.id]); AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.parentID]); AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.restricted]); } else if (child.IsItem) { if (child.IsReference) { AddTo(details.PropertyNames, "@"+T[_ATTRIB.refID]); AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.refID]); } AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.id]); AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.parentID]); AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.restricted]); } // standard metadata IMediaProperties properties = child.MergedProperties; IList propertyNames = properties.PropertyNames; foreach (string propertyName in propertyNames) { if (details.PropertyNames.Contains(propertyName) == false) { details.PropertyNames.Add(propertyName); // add attributes if they are not added IList propertyValues = properties[propertyName]; foreach (ICdsElement val in propertyValues) { ICollection attributes = val.ValidAttributes; foreach (string attribName in attributes) { StringBuilder sbpn = new StringBuilder(); sbpn.AppendFormat("{0}@{1}", propertyName, attribName); string fullAttribName = sbpn.ToString(); AddTo(details.PropertyNames, fullAttribName); } } } // resources IList resources = child.MergedResources; foreach (IMediaResource res in resources) { ICollection attributes = res.ValidAttributes; foreach (string attribName in attributes) { string name1 = "res@"+attribName; string name2 = "@"+attribName; AddTo(details.PropertyNames, name1); AddTo(details.PropertyNames, name2); } } if (resources.Count > 0) { AddTo(details.PropertyNames, T[_DIDL.Res]); } } } else { if (results.TotalMatches > 0) { results.SetError(UPnPTestStates.Failed); string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " did not yield exactly one CDS-compliant media object in its result. Instantiated a total of " +results.MediaObjects.Count+ " media objects."; arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg); throw new TerminateEarly(msg); } } } else { throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but no media objects were instantiated.", null); } } currentChild++; } return children; }