1use super::*;
2use player_plugin::PluginReference;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct DecoderPluginMatchRequest {
7 pub codec: String,
8 pub media_kind: DecoderMediaKind,
9}
10
11impl DecoderPluginMatchRequest {
12 pub fn video(codec: impl Into<String>) -> Self {
13 Self {
14 codec: codec.into(),
15 media_kind: DecoderMediaKind::Video,
16 }
17 }
18
19 pub fn audio(codec: impl Into<String>) -> Self {
20 Self {
21 codec: codec.into(),
22 media_kind: DecoderMediaKind::Audio,
23 }
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct DecoderPluginCodecSummary {
30 pub codec: String,
31 pub media_kind: DecoderMediaKind,
32}
33
34impl From<&DecoderCodecCapability> for DecoderPluginCodecSummary {
35 fn from(capability: &DecoderCodecCapability) -> Self {
36 Self {
37 codec: capability.codec.clone(),
38 media_kind: capability.media_kind,
39 }
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct DecoderPluginCapabilitySummary {
46 pub typed_codecs: Vec<DecoderPluginCodecSummary>,
47 pub codecs: Vec<String>,
48 pub supports_native_frame_output: bool,
49 pub native_requirements: Option<DecoderNativeRequirements>,
50 pub supports_hardware_decode: bool,
51 pub supports_cpu_video_frames: bool,
52 pub supports_audio_packets: bool,
53 pub supports_audio_frames: bool,
54 pub supports_pcm_frames: bool,
55 pub supports_gpu_handles: bool,
56 pub supports_presentation_release: bool,
57 pub supports_flush: bool,
58 pub supports_drain: bool,
59 pub max_sessions: Option<u32>,
60}
61
62impl From<&DecoderCapabilities> for DecoderPluginCapabilitySummary {
63 fn from(capabilities: &DecoderCapabilities) -> Self {
64 Self::from_capabilities(capabilities, false, None)
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct FrameProcessorPluginCapabilitySummary {
71 pub accepted_input_handle_kinds: Vec<NativeHandleKind>,
72 pub output_handle_kinds: Vec<NativeHandleKind>,
73 pub accepted_input_pipeline_profiles: Vec<NativeFramePipelineProfile>,
74 pub output_pipeline_profiles: Vec<NativeFramePipelineProfile>,
75 pub supports_video_frames: bool,
76 pub supports_in_place_passthrough: bool,
77 pub preserves_dimensions: bool,
78 pub may_change_dimensions: bool,
79 pub preserves_color_metadata: bool,
80 pub preserves_hdr_metadata: bool,
81 pub supports_flush: bool,
82 pub max_sessions: Option<u32>,
83 pub max_in_flight_frames: Option<u32>,
84}
85
86impl From<&FrameProcessorCapabilities> for FrameProcessorPluginCapabilitySummary {
87 fn from(capabilities: &FrameProcessorCapabilities) -> Self {
88 Self {
89 accepted_input_handle_kinds: capabilities.accepted_input_handle_kinds.clone(),
90 output_handle_kinds: capabilities.output_handle_kinds.clone(),
91 accepted_input_pipeline_profiles: capabilities.accepted_input_pipeline_profiles.clone(),
92 output_pipeline_profiles: capabilities.output_pipeline_profiles.clone(),
93 supports_video_frames: capabilities.supports_video_frames,
94 supports_in_place_passthrough: capabilities.supports_in_place_passthrough,
95 preserves_dimensions: capabilities.preserves_dimensions,
96 may_change_dimensions: capabilities.may_change_dimensions,
97 preserves_color_metadata: capabilities.preserves_color_metadata,
98 preserves_hdr_metadata: capabilities.preserves_hdr_metadata,
99 supports_flush: capabilities.supports_flush,
100 max_sessions: capabilities.max_sessions,
101 max_in_flight_frames: capabilities.max_in_flight_frames,
102 }
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct SourceNormalizerPacketPluginCapabilitySummary {
109 pub supported_runtime_profiles: Vec<String>,
110 pub max_level: player_plugin::SourceNormalizerNormalizeLevel,
111 pub media_kinds: Vec<player_plugin::SourceNormalizerPacketMediaKind>,
112 pub codecs: Vec<String>,
113 pub bitstream_formats: Vec<player_plugin::DecoderBitstreamFormat>,
114 pub supports_seek: bool,
115 pub supports_flush: bool,
116 pub required_capabilities: player_plugin::SourceNormalizerRequiredCapabilities,
117 pub max_sessions: Option<u32>,
118}
119
120impl From<&SourceNormalizerPacketCapabilities> for SourceNormalizerPacketPluginCapabilitySummary {
121 fn from(capabilities: &SourceNormalizerPacketCapabilities) -> Self {
122 Self {
123 supported_runtime_profiles: capabilities.supported_runtime_profiles.clone(),
124 max_level: capabilities.max_level,
125 media_kinds: capabilities.media_kinds.clone(),
126 codecs: capabilities.codecs.clone(),
127 bitstream_formats: capabilities.bitstream_formats.clone(),
128 supports_seek: capabilities.supports_seek,
129 supports_flush: capabilities.supports_flush,
130 required_capabilities: capabilities.required_capabilities.clone(),
131 max_sessions: capabilities.max_sessions,
132 }
133 }
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct SourceNormalizerResourcePluginCapabilitySummary {
139 pub supported_runtime_profiles: Vec<String>,
140 pub supported_output_routes: Vec<String>,
141 pub max_level: player_plugin::SourceNormalizerNormalizeLevel,
142 pub content_types: Vec<String>,
143 pub supports_growing_resources: bool,
144 pub supports_range_reads: bool,
145 pub supports_cancel: bool,
146 pub required_capabilities: player_plugin::SourceNormalizerRequiredCapabilities,
147 pub cache_policy: player_plugin::SourceNormalizerResourceCachePolicy,
148 pub max_sessions: Option<u32>,
149}
150
151impl From<&SourceNormalizerResourceCapabilities>
152 for SourceNormalizerResourcePluginCapabilitySummary
153{
154 fn from(capabilities: &SourceNormalizerResourceCapabilities) -> Self {
155 Self {
156 supported_runtime_profiles: capabilities.supported_runtime_profiles.clone(),
157 supported_output_routes: capabilities
158 .supported_output_routes
159 .iter()
160 .map(|route| route.wire_name().to_owned())
161 .collect(),
162 max_level: capabilities.max_level,
163 content_types: capabilities.content_types.clone(),
164 supports_growing_resources: capabilities.supports_growing_resources,
165 supports_range_reads: capabilities.supports_range_reads,
166 supports_cancel: capabilities.supports_cancel,
167 required_capabilities: capabilities.required_capabilities.clone(),
168 cache_policy: capabilities.cache_policy.clone(),
169 max_sessions: capabilities.max_sessions,
170 }
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
176pub enum PluginCapabilitySummary {
177 Decoder(DecoderPluginCapabilitySummary),
178 FrameProcessor(FrameProcessorPluginCapabilitySummary),
179 SourceNormalizerPacket(SourceNormalizerPacketPluginCapabilitySummary),
180 SourceNormalizerResource(SourceNormalizerResourcePluginCapabilitySummary),
181}
182
183impl DecoderPluginCapabilitySummary {
184 fn from_capabilities(
185 capabilities: &DecoderCapabilities,
186 supports_native_frame_output: bool,
187 native_requirements: Option<DecoderNativeRequirements>,
188 ) -> Self {
189 let typed_codecs = capabilities
190 .codecs
191 .iter()
192 .map(DecoderPluginCodecSummary::from)
193 .collect::<Vec<_>>();
194 let codecs = capabilities
195 .codecs
196 .iter()
197 .map(|codec| format!("{:?}:{}", codec.media_kind, codec.codec))
198 .collect();
199 Self {
200 typed_codecs,
201 codecs,
202 supports_native_frame_output,
203 native_requirements,
204 supports_hardware_decode: capabilities.supports_hardware_decode,
205 supports_cpu_video_frames: capabilities.supports_cpu_video_frames,
206 supports_audio_packets: capabilities
207 .codecs
208 .iter()
209 .any(|codec| codec.media_kind == DecoderMediaKind::Audio),
210 supports_audio_frames: capabilities.supports_audio_frames,
211 supports_pcm_frames: capabilities.supports_pcm_frames,
212 supports_gpu_handles: capabilities.supports_gpu_handles,
213 supports_presentation_release: capabilities.supports_presentation_release,
214 supports_flush: capabilities.supports_flush,
215 supports_drain: capabilities.supports_drain,
216 max_sessions: capabilities.max_sessions,
217 }
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
223pub enum PluginDiagnosticStatus {
224 Loaded,
225 LoadFailed,
226 UnsupportedKind,
227 DecoderSupported,
228 DecoderUnsupported,
229 FrameProcessorSupported,
230 FrameProcessorUnsupported,
231 SourceNormalizerSupported,
232 SourceNormalizerUnsupported,
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq)]
237pub enum PluginCapabilityAvailability {
238 Unknown,
240 Available,
242 Unavailable,
244}
245
246impl PluginCapabilityAvailability {
247 pub const fn wire_name(self) -> &'static str {
248 match self {
249 Self::Unknown => "unknown",
250 Self::Available => "available",
251 Self::Unavailable => "unavailable",
252 }
253 }
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258pub enum PluginCapabilityKind {
259 PostDownloadProcessor,
260 PipelineEventHook,
261 Decoder,
262 BenchmarkSink,
263 FrameProcessor,
264 SourceNormalizer,
265}
266
267impl PluginCapabilityKind {
268 pub const fn wire_name(self) -> &'static str {
269 match self {
270 Self::PostDownloadProcessor => "post_download_processor",
271 Self::PipelineEventHook => "pipeline_event_hook",
272 Self::Decoder => "decoder",
273 Self::BenchmarkSink => "benchmark_sink",
274 Self::FrameProcessor => "frame_processor",
275 Self::SourceNormalizer => "source_normalizer",
276 }
277 }
278}
279
280impl PluginDiagnosticStatus {
281 pub const fn wire_name(self) -> &'static str {
282 match self {
283 Self::Loaded => "loaded",
284 Self::LoadFailed => "loadFailed",
285 Self::UnsupportedKind => "unsupportedKind",
286 Self::DecoderSupported => "decoderSupported",
287 Self::DecoderUnsupported => "decoderUnsupported",
288 Self::FrameProcessorSupported => "frameProcessorSupported",
289 Self::FrameProcessorUnsupported => "frameProcessorUnsupported",
290 Self::SourceNormalizerSupported => "sourceNormalizerSupported",
291 Self::SourceNormalizerUnsupported => "sourceNormalizerUnsupported",
292 }
293 }
294
295 pub const fn capability_availability(self) -> PluginCapabilityAvailability {
299 match self {
300 Self::DecoderSupported
301 | Self::FrameProcessorSupported
302 | Self::SourceNormalizerSupported => PluginCapabilityAvailability::Available,
303 Self::Loaded => PluginCapabilityAvailability::Unknown,
304 Self::LoadFailed
305 | Self::UnsupportedKind
306 | Self::DecoderUnsupported
307 | Self::FrameProcessorUnsupported
308 | Self::SourceNormalizerUnsupported => PluginCapabilityAvailability::Unavailable,
309 }
310 }
311}
312
313#[derive(Debug, Clone, PartialEq, Eq)]
315pub struct PluginDiagnosticRecord {
316 pub path: PathBuf,
317 pub status: PluginDiagnosticStatus,
318 pub plugin_name: Option<String>,
319 pub plugin_kind: Option<PluginCapabilityKind>,
320 pub capability_summary: Option<PluginCapabilitySummary>,
321 pub message: Option<String>,
322}
323
324pub(crate) fn decoder_capability_summary(
325 record: &PluginDiagnosticRecord,
326) -> Option<&DecoderPluginCapabilitySummary> {
327 match record.capability_summary.as_ref() {
328 Some(PluginCapabilitySummary::Decoder(summary)) => Some(summary),
329 _ => None,
330 }
331}
332
333pub(crate) fn source_normalizer_packet_capability_summary(
334 record: &PluginDiagnosticRecord,
335) -> Option<&SourceNormalizerPacketPluginCapabilitySummary> {
336 match record.capability_summary.as_ref() {
337 Some(PluginCapabilitySummary::SourceNormalizerPacket(summary)) => Some(summary),
338 _ => None,
339 }
340}
341
342pub(crate) fn source_normalizer_resource_capability_summary(
343 record: &PluginDiagnosticRecord,
344) -> Option<&SourceNormalizerResourcePluginCapabilitySummary> {
345 match record.capability_summary.as_ref() {
346 Some(PluginCapabilitySummary::SourceNormalizerResource(summary)) => Some(summary),
347 _ => None,
348 }
349}
350
351impl PluginDiagnosticRecord {
352 pub const fn capability_availability(&self) -> PluginCapabilityAvailability {
354 self.status.capability_availability()
355 }
356
357 pub(crate) fn from_native_decoder_interface(
358 path: impl Into<PathBuf>,
359 plugin: &LoadedNativePlugin,
360 reference: &PluginReference,
361 decoder_match: &DecoderPluginMatchRequest,
362 ) -> Self {
363 let path = path.into();
364 match plugin.resolve_native_decoder(reference) {
365 Ok(factory) => {
366 let capabilities = factory.capabilities();
367 let native_requirements = factory.native_requirements();
368 let capability_summary = DecoderPluginCapabilitySummary::from_capabilities(
369 &capabilities,
370 true,
371 Some(native_requirements),
372 );
373 let supported =
374 capabilities.supports_codec(&decoder_match.codec, decoder_match.media_kind);
375 let status = if supported {
376 PluginDiagnosticStatus::DecoderSupported
377 } else {
378 PluginDiagnosticStatus::DecoderUnsupported
379 };
380 let message = if supported {
381 format!(
382 "{} instance `{}` advertises {:?} {} support with native-frame output",
383 factory.name(),
384 reference.capability_instance_id().unwrap_or("unknown"),
385 decoder_match.media_kind,
386 decoder_match.codec
387 )
388 } else {
389 format!(
390 "{} instance `{}` does not advertise {:?} {} support",
391 factory.name(),
392 reference.capability_instance_id().unwrap_or("unknown"),
393 decoder_match.media_kind,
394 decoder_match.codec
395 )
396 };
397 Self {
398 path,
399 status,
400 plugin_name: Some(factory.name().to_owned()),
401 plugin_kind: Some(PluginCapabilityKind::Decoder),
402 capability_summary: Some(PluginCapabilitySummary::Decoder(capability_summary)),
403 message: Some(message),
404 }
405 }
406 Err(error) => Self {
407 path,
408 status: PluginDiagnosticStatus::DecoderUnsupported,
409 plugin_name: Some(plugin.plugin_name().to_owned()),
410 plugin_kind: Some(PluginCapabilityKind::Decoder),
411 capability_summary: None,
412 message: Some(format!(
413 "instance `{}` is unavailable: {error}",
414 reference.capability_instance_id().unwrap_or("unknown")
415 )),
416 },
417 }
418 }
419
420 pub(crate) fn from_native_frame_processor_interface(
421 path: impl Into<PathBuf>,
422 plugin: &LoadedNativePlugin,
423 reference: &PluginReference,
424 ) -> Self {
425 let path = path.into();
426 match plugin.resolve_frame_processor(reference) {
427 Ok(factory) => {
428 let capabilities = factory.capabilities();
429 let capability_summary = FrameProcessorPluginCapabilitySummary::from(&capabilities);
430 let supported =
431 capabilities.supports_video_frames && !capabilities.may_change_dimensions;
432 let status = if supported {
433 PluginDiagnosticStatus::FrameProcessorSupported
434 } else {
435 PluginDiagnosticStatus::FrameProcessorUnsupported
436 };
437 let message = if supported {
438 format!(
439 "{} frame processor instance `{}` loaded",
440 factory.name(),
441 reference.capability_instance_id().unwrap_or("unknown")
442 )
443 } else if capabilities.may_change_dimensions {
444 format!(
445 "{} frame processor instance `{}` changes frame dimensions, which the current interface does not allow",
446 factory.name(),
447 reference.capability_instance_id().unwrap_or("unknown")
448 )
449 } else {
450 format!(
451 "{} frame processor instance `{}` does not advertise video frame processing support",
452 factory.name(),
453 reference.capability_instance_id().unwrap_or("unknown")
454 )
455 };
456 Self {
457 path,
458 status,
459 plugin_name: Some(factory.name().to_owned()),
460 plugin_kind: Some(PluginCapabilityKind::FrameProcessor),
461 capability_summary: Some(PluginCapabilitySummary::FrameProcessor(
462 capability_summary,
463 )),
464 message: Some(message),
465 }
466 }
467 Err(error) => Self {
468 path,
469 status: PluginDiagnosticStatus::FrameProcessorUnsupported,
470 plugin_name: Some(plugin.plugin_name().to_owned()),
471 plugin_kind: Some(PluginCapabilityKind::FrameProcessor),
472 capability_summary: None,
473 message: Some(format!(
474 "instance `{}` is unavailable: {error}",
475 reference.capability_instance_id().unwrap_or("unknown")
476 )),
477 },
478 }
479 }
480
481 pub(crate) fn from_native_source_packet_interface(
482 path: impl Into<PathBuf>,
483 plugin: &LoadedNativePlugin,
484 reference: &PluginReference,
485 ) -> Self {
486 let path = path.into();
487 match plugin.resolve_source_packet(reference) {
488 Ok(factory) => {
489 let capabilities = factory.packet_capabilities();
490 let capability_summary =
491 SourceNormalizerPacketPluginCapabilitySummary::from(&capabilities);
492 let supported = !capabilities.supported_runtime_profiles.is_empty()
493 && !capabilities.media_kinds.is_empty();
494 let status = if supported {
495 PluginDiagnosticStatus::SourceNormalizerSupported
496 } else {
497 PluginDiagnosticStatus::SourceNormalizerUnsupported
498 };
499 let message = if supported {
500 format!(
501 "{} source normalizer packet instance `{}` loaded",
502 factory.name(),
503 reference.capability_instance_id().unwrap_or("unknown")
504 )
505 } else if capabilities.supported_runtime_profiles.is_empty() {
506 format!(
507 "{} packet instance `{}` does not advertise source normalizer runtime profiles",
508 factory.name(),
509 reference.capability_instance_id().unwrap_or("unknown")
510 )
511 } else {
512 format!(
513 "{} packet instance `{}` does not advertise source normalizer media kinds",
514 factory.name(),
515 reference.capability_instance_id().unwrap_or("unknown")
516 )
517 };
518 Self {
519 path,
520 status,
521 plugin_name: Some(factory.name().to_owned()),
522 plugin_kind: Some(PluginCapabilityKind::SourceNormalizer),
523 capability_summary: Some(PluginCapabilitySummary::SourceNormalizerPacket(
524 capability_summary,
525 )),
526 message: Some(message),
527 }
528 }
529 Err(error) => Self {
530 path,
531 status: PluginDiagnosticStatus::SourceNormalizerUnsupported,
532 plugin_name: Some(plugin.plugin_name().to_owned()),
533 plugin_kind: Some(PluginCapabilityKind::SourceNormalizer),
534 capability_summary: None,
535 message: Some(format!(
536 "instance `{}` is unavailable: {error}",
537 reference.capability_instance_id().unwrap_or("unknown")
538 )),
539 },
540 }
541 }
542
543 pub(crate) fn from_native_source_resource_interface(
544 path: impl Into<PathBuf>,
545 plugin: &LoadedNativePlugin,
546 reference: &PluginReference,
547 ) -> Self {
548 let path = path.into();
549 match plugin.resolve_source_resource(reference) {
550 Ok(factory) => {
551 let capabilities = factory.resource_capabilities();
552 let capability_summary =
553 SourceNormalizerResourcePluginCapabilitySummary::from(&capabilities);
554 let supported = !capabilities.supported_runtime_profiles.is_empty()
555 && !capabilities.supported_output_routes.is_empty();
556 let status = if supported {
557 PluginDiagnosticStatus::SourceNormalizerSupported
558 } else {
559 PluginDiagnosticStatus::SourceNormalizerUnsupported
560 };
561 let message = if supported {
562 format!(
563 "{} source normalizer resource instance `{}` loaded",
564 factory.name(),
565 reference.capability_instance_id().unwrap_or("unknown")
566 )
567 } else if capabilities.supported_runtime_profiles.is_empty() {
568 format!(
569 "{} resource instance `{}` does not advertise source normalizer runtime profiles",
570 factory.name(),
571 reference.capability_instance_id().unwrap_or("unknown")
572 )
573 } else {
574 format!(
575 "{} resource instance `{}` does not advertise source normalizer output routes",
576 factory.name(),
577 reference.capability_instance_id().unwrap_or("unknown")
578 )
579 };
580 Self {
581 path,
582 status,
583 plugin_name: Some(factory.name().to_owned()),
584 plugin_kind: Some(PluginCapabilityKind::SourceNormalizer),
585 capability_summary: Some(PluginCapabilitySummary::SourceNormalizerResource(
586 capability_summary,
587 )),
588 message: Some(message),
589 }
590 }
591 Err(error) => Self {
592 path,
593 status: PluginDiagnosticStatus::SourceNormalizerUnsupported,
594 plugin_name: Some(plugin.plugin_name().to_owned()),
595 plugin_kind: Some(PluginCapabilityKind::SourceNormalizer),
596 capability_summary: None,
597 message: Some(format!(
598 "instance `{}` is unavailable: {error}",
599 reference.capability_instance_id().unwrap_or("unknown")
600 )),
601 },
602 }
603 }
604
605 pub(crate) fn unsupported_native_interface(
606 path: impl Into<PathBuf>,
607 plugin: &LoadedNativePlugin,
608 interface: &'static str,
609 ) -> Self {
610 Self {
611 path: path.into(),
612 status: PluginDiagnosticStatus::UnsupportedKind,
613 plugin_name: Some(plugin.plugin_name().to_owned()),
614 plugin_kind: None,
615 capability_summary: None,
616 message: Some(format!(
617 "{} does not expose interface {interface}",
618 plugin.plugin_name()
619 )),
620 }
621 }
622
623 pub fn load_failed(path: impl Into<PathBuf>, error: PluginLoadError) -> Self {
624 Self::load_failed_message(path, error.to_string())
625 }
626
627 pub(crate) fn load_failed_message(
628 path: impl Into<PathBuf>,
629 message: impl Into<String>,
630 ) -> Self {
631 Self {
632 path: path.into(),
633 status: PluginDiagnosticStatus::LoadFailed,
634 plugin_name: None,
635 plugin_kind: None,
636 capability_summary: None,
637 message: Some(message.into()),
638 }
639 }
640
641 pub fn summary(&self) -> String {
642 self.message
643 .clone()
644 .or_else(|| self.plugin_name.clone())
645 .unwrap_or_else(|| self.path.display().to_string())
646 }
647}