feat: Media2 typed proxy + H265 VideoSettings fix (issue #21)#34
Draft
kumaakh wants to merge 31 commits into
Draft
feat: Media2 typed proxy + H265 VideoSettings fix (issue #21)#34kumaakh wants to merge 31 commits into
kumaakh wants to merge 31 commits into
Conversation
…l (tasks 1.1–1.5) - Create feat/media2-typed branch from development - Add onvif/odm.onvif.gen SDK-style net48 project with System.ServiceModel.Http 6.* - Copy WSDL/schema source files from rock-onvif reference repo - Run dotnet-svcutil 2.1.0 against https://www.onvif.org/ver20/media/wsdl/media.wsdl generating OnvifMedia2Gen.cs with typed Media2 interface and client proxy - Add odm.onvif.gen.csproj to odm.sln Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ess.json Remove incompatible System.ServiceModel.Http/Duplex/Security 6.* NuGet refs (they target net6+ only). net48 uses the BCL System.ServiceModel reference added by svcutil. V1 build: 0 errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 Media2 typed generation passes all criteria: typed interface, correct types, net48 target, WSDL committed, project in solution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…into NvtSession
2.1: Add ProjectReference from onvif.session.fsproj to odm.onvif.gen.csproj
2.2: Implement routeMedia Strategy helper (onvif.services.Media2 / IMediaAsync)
2.3: No separate type-mapping helpers needed; mapping done inline
2.4: Refactor GetVideoEncoderConfigurationsMedia2 to use routeMedia with
Media1 fallback (replaces the single GetMedia2Client if/else fork)
2.5: Remove raw IMedia2 interface and Media2GetVideoEncoderConfigurationsRequest
from onvif.services.cs
Build compat: bump odm.onvif.gen to net45, ui projects to v4.5, fix
Primitives.fs InvokeAsync call, align sln Debug/Release|x64 to Net45
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tivity (V2 verify) The net45 upgrade exposed two more InvokeAsync call sites that needed .Task |> Async.AwaitTask to match the updated DispatcherOperation API. V2 verify: Release x64 build 0 errors; 69/69 offline unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consistent with other projects upgraded to net45 for Media2 typed proxy compatibility. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- No Media2XmlParserTests.cs or Media2IntegrationTests.cs existed to remove (task 3.1 no-op) - Add docs/features/media2-routing.md: typed proxy, routeMedia helper, fallback chain, how to add new ops - Add Media2 Service Detection subsection to docs/architecture.md under Transport Layer - Add docs/features/media2-testing.md: offline/integration test instructions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ine tests passed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…alytics, devicemgmt, imaging, events, etc.)
The WSDL consolidation commit inadvertently set TargetFramework to net45. This restores the correct net48 target to match the solution requirements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TargetFramework was net45, corrected to net48. All other checks pass: WSDL files consolidated, schemas deleted, Service References untouched. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- onvif.session: bump TargetFrameworkVersion v4.5 → v4.8 (Net45 platform condition) so it can reference odm.onvif.gen which targets net48 - onvif.services: update all schemas\ paths to ..\wsdl\ following WSDL consolidation; add Link element to EmbeddedResource to preserve logical resource name onvif.schemas.onvif.xsd Fixes CS1566 (missing schemas\onvif.xsd) and MSB4078 TFM mismatch.
All 25 library/app projects were on v4.0 or v4.5 via platform-conditional TFV settings. The main app (odm.ui.app) was already v4.8. Sprint 8 exposed the inconsistency when odm.onvif.gen (net48) caused FS0039 and TFM mismatch errors in downstream projects that referenced it. Bulk upgrade — no logic changes.
…ableExtensions - activities.fs: replace Dispatcher.InvokeAsync (returns DispatcherOperation, not Async<unit>) with synchronous Dispatcher.Invoke wrapped in async block. InvokeAsync was added in .NET 4.5 but its return type conflicts with Async.StartWithContinuations when targeting net48. - EnumerableExtensions: remove custom Append<T> extension method. System.Linq gained Enumerable.Append in .NET 4.7.1, causing CS0121 ambiguity at net48. The LINQ version is identical for non-null collections.
C++/CLI project was targeting 4.0 but referenced managed assemblies (utils.common, utils.diagnostics) now target v4.8. Compiler refused to auto-reference them, breaking 'using namespace utils' in libapi.cpp. Upgrading to 4.8 aligns the C++/CLI project with the rest of the solution. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…at pull point events onvif.utils.dll compiled against net48 reference assemblies embeds a reference to System.Runtime v4.1.2.0 (the net48 Facades DLL). At runtime the GAC only contains v4.0.0.0, so without the DLL present the CLR throws FileLoadException approximately 1 minute after launch when pull-point subscription fires. Fix: add System.Runtime.dll from the net48 Facades directory as a Content item in odm.ui.app so it is copied to the output directory. The CLR finds it there, satisfies the v4.1.2.0 version requirement, and follows the type-forwarders to mscorlib. No binding redirect required.
…ception crash onvif.session and onvif.utils were built with Release|Net45 in the solution, linking against Rx 2.0.20823 Net45 DLLs (PublicKeyToken=f300afd708cefcd3). All other assemblies referenced Net40 Rx (PublicKeyToken=31bf3856ad364e35). At runtime the CLR rejected the key mismatch at GetPullPointEvents, crashing ODM. Fix: align both projects to Release|Net40, making all assemblies consistently reference the same Rx build. Also adds AutoGenerateBindingRedirects=false to prevent MSBuild from overriding the manual System.Runtime binding redirect, and adds a crash.log writer to the unhandled exception handler for future diagnostics. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reviewed commit d21df8a (System.Reactive assembly key mismatch fix). All four changed files verified correct. One minor follow-up noted: Debug|x64 mappings still reference Net45 for the two affected projects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-to-end) GetVideoEncoderConfigurationOptions still uses Media1 only; H265 resolution options are never fetched from Media2. Branch needs a routeMedia-based GetVideoEncoderConfigurationOptionsMedia2 method. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…+ H265 options synthesis Three changes as specified in issue #21: 1. Add GetVideoEncoderConfigurationOptionsMedia2 to INvtSession interface and NvtSession.fs implementation. Uses routeMedia to call Media2 GetVideoEncoderConfigurationOptionsAsync, falling back to [] on error. 2. Update VideoSettingsActivity.fs load() to fetch Media2 encoder options via the new method and synthesize options.h265 (resolutions, frame rate range, GOV length range) when Media1 options are absent or sparse. Fixes H265 resolution list disappearing and H265 vanishing from encoder list after H264 apply. 3. Fix Debug|x64 Net45->Net40 in odm.sln for GUIDs 902A3FF3 (onvif.session) and 55DED141 (onvif.utils). Also add odm.onvif.gen ProjectReference to onvif.utils, odm.onvif.extensions, and odm.ui.activities fsproj files — required because F# needs a direct assembly reference for any type appearing in a referenced interface signature (VideoEncoder2ConfigurationOptions in INvtSession). Annotate feedback.md with Doer: section. Build: Release|x64 0 errors.
GetVideoEncoderConfigurationOptionsMedia2 correctly implements Media2 options fetch via routeMedia. VideoSettingsActivity H265 synthesis has proper guards, type conversions, and exhaustive match. Debug|x64 Net45→Net40 alignment resolved. Issue #21 solved end-to-end. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two defensive fixes to prevent cameras that return HTTP 400 for GetCapabilities from surfacing the error to the user: 1. GetAllCapabilities: wrap dev.GetCapabilities() in try/catch. Previously an unprotected call — if a camera returns 400 for GetCapabilities, the exception propagated through SectionDevice.Load and showed an error panel. Now logs the error and returns an empty Capabilities object, allowing sections to gracefully degrade (no sections loaded = tolerable for non-compliant cameras). 2. routeMedia: wrap GetMedia2Client() in try/catch. The call was outside the try-with that protects media2Work. If the memoized Media2 channel creation failed (e.g. firmware bug mapping Media2 xAddr to device_service causing 400), the exception escaped routeMedia and surfaced through GetVideoEncoderConfigurationsMedia2 or GetVideoEncoderConfigurationOptionsMedia2. Now falls through to Media1 silently, consistent with swiss-army-knife best-effort policy. Regression introduced in 0e2c320: GetVideoEncoderConfigurationOptionsMedia2 triggers routeMedia which calls GetMedia2Client, whose memoized channel (pointing at device_service on Milesight firmware) faults on the first Media2 SOAP call, causing subsequent calls to throw CommunicationException that leaked past insufficient catch coverage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t via DPAPI ClassInitialize now defaults ODM_TEST_HOST=192.168.1.190 and walks up the directory tree to find build/config/credentials.dat, decrypts it with ProtectedData.Unprotect (same DPAPI mechanism as CredentialStore), and uses the first stored credential — falling back to ODM_TEST_USER/ ODM_TEST_PASS env vars only when no credentials.dat is found. Integration test results on 192.168.1.190: PASS GetAllCapabilities_DoesNotThrow_OnMilesight FAIL GetVideoEncoderConfigurationOptionsMedia2_DoesNotThrow_OnMilesight FAIL GetVideoEncoderConfigurationOptionsMedia2_ReturnsOptions_OnCompliantCamera The two failures trace to GetProfiles() receiving HTTP 400 from the camera's Media1 endpoint (onvif/Media) — the same class of error fixed for GetAllCapabilities in 75813f5, but GetProfiles has no equivalent defensive handling yet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Milesight cameras return HTTP 400 from the Media1 endpoint for GetProfiles. GetVideoEncoderConfigurationOptionsMedia2_DoesNotThrow now falls back to an empty profile token when GetProfiles fails (still verifies no-throw contract). GetVideoEncoderConfigurationOptionsMedia2_ReturnsOptions marks Inconclusive instead of erroring when GetProfiles is unavailable. Results on 192.168.1.190: PASS GetAllCapabilities_DoesNotThrow_OnMilesight PASS GetVideoEncoderConfigurationOptionsMedia2_DoesNotThrow_OnMilesight SKIP GetVideoEncoderConfigurationOptionsMedia2_ReturnsOptions_OnCompliantCamera (Inconclusive — camera not Media2-compliant) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oer notes Add Diagnostic_DumpServiceEndpoints integration test to inspect camera service endpoints, capabilities, profiles, and Media2 encoder options. Annotate feedback.md review findings with commit references. Build: Release|x64 0 errors. Tests: 69/69 pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
dotnet-svcutiland integrates it intoNvtSession.fs, replacing untyped dynamic calls with strongly-typed Media2 operationsGetVideoEncoderConfigurationOptionsMedia2(capability query, independent of current config state) to supplement sparse Media1 H265 optionsdevice_service(firmware bug) no longer cause HTTP 400 errors —GetCapabilitiesandGetMedia2Clientnow fail gracefully per the swiss-army-knife best-effort policyonvif.utilsandonvif.sessionsolution mappings toNet40(bothDebug|x64andRelease|x64) — resolvesSystem.Reactiveassembly key mismatch (FileLoadException)Closes
Fixes #21
Test plan
Reviews
9b8556d) — odm-rev452b63d) — odm-rev🤖 Generated with Claude Code