From 0c8f2d6668947ddbd67f0c0bbe137fc25ce0e355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 20 Dec 2018 22:57:28 +0100 Subject: [PATCH] Refactor errors for consumption --- audio/src/context.rs | 12 +- audio/src/decoder.rs | 41 +++--- audio/src/lib.rs | 2 +- audio/src/offline_sink.rs | 15 +-- audio/src/render_thread.rs | 21 ++- audio/src/sink.rs | 29 +++-- backends/gstreamer/src/audio_decoder.rs | 162 +++++++++++++++--------- backends/gstreamer/src/audio_sink.rs | 52 ++++---- backends/gstreamer/src/lib.rs | 30 +---- backends/gstreamer/src/player.rs | 66 +++++----- examples/player/main.rs | 8 +- player/src/lib.rs | 66 ++++++---- servo-media/src/lib.rs | 26 ++-- 13 files changed, 282 insertions(+), 248 deletions(-) diff --git a/audio/src/context.rs b/audio/src/context.rs index 797a1e81..ad7df3ab 100644 --- a/audio/src/context.rs +++ b/audio/src/context.rs @@ -142,7 +142,8 @@ impl AudioContext { graph, options, ); - }).unwrap(); + }) + .unwrap(); Self { sender, state: Cell::new(ProcessingState::Suspended), @@ -251,11 +252,7 @@ impl AudioContext { /// Asynchronously decodes the audio file data contained in the given /// buffer. - pub fn decode_audio_data( - &self, - data: Vec, - callbacks: AudioDecoderCallbacks<::Error>, - ) { + pub fn decode_audio_data(&self, data: Vec, callbacks: AudioDecoderCallbacks) { let mut options = AudioDecoderOptions::default(); options.sample_rate = self.sample_rate; Builder::new() @@ -264,7 +261,8 @@ impl AudioContext { let audio_decoder = B::make_decoder(); audio_decoder.decode(data, callbacks, Some(options)); - }).unwrap(); + }) + .unwrap(); } pub fn set_eos_callback(&self, callback: Box>) + Send + Sync + 'static>) { diff --git a/audio/src/decoder.rs b/audio/src/decoder.rs index 8d99999c..f6d54396 100644 --- a/audio/src/decoder.rs +++ b/audio/src/decoder.rs @@ -1,16 +1,29 @@ use boxfnonce::SendBoxFnOnce; -use std::fmt::Debug; use std::sync::Mutex; -pub struct AudioDecoderCallbacks { +#[derive(Debug, PartialEq)] +pub enum AudioDecoderError { + /// Backend specific error. + Backend(String), + /// Could not read the audio buffer content. + BufferReadFailed, + /// The media trying to be decoded has an invalid format. + InvalidMediaFormat, + /// An invalid sample was found while decoding the audio. + InvalidSample, + /// Could not move to a different state. + StateChangeFailed, +} + +pub struct AudioDecoderCallbacks { pub eos: Mutex>>, - pub error: Mutex>>, + pub error: Mutex>>, pub progress: Option>, u32) + Send + Sync + 'static>>, pub ready: Mutex>>, } -impl AudioDecoderCallbacks { - pub fn new() -> AudioDecoderCallbacksBuilder { +impl AudioDecoderCallbacks { + pub fn new() -> AudioDecoderCallbacksBuilder { AudioDecoderCallbacksBuilder { eos: None, error: None, @@ -27,7 +40,7 @@ impl AudioDecoderCallbacks { }; } - pub fn error(&self, error: E) { + pub fn error(&self, error: AudioDecoderError) { let callback = self.error.lock().unwrap().take(); match callback { None => return, @@ -51,14 +64,14 @@ impl AudioDecoderCallbacks { } } -pub struct AudioDecoderCallbacksBuilder { +pub struct AudioDecoderCallbacksBuilder { eos: Option>, - error: Option>, + error: Option>, progress: Option>, u32) + Send + Sync + 'static>>, ready: Option>, } -impl AudioDecoderCallbacksBuilder { +impl AudioDecoderCallbacksBuilder { pub fn eos(self, eos: F) -> Self { Self { eos: Some(SendBoxFnOnce::new(eos)), @@ -66,7 +79,7 @@ impl AudioDecoderCallbacksBuilder { } } - pub fn error(self, error: F) -> Self { + pub fn error(self, error: F) -> Self { Self { error: Some(SendBoxFnOnce::new(error)), ..self @@ -90,7 +103,7 @@ impl AudioDecoderCallbacksBuilder { } } - pub fn build(self) -> AudioDecoderCallbacks { + pub fn build(self) -> AudioDecoderCallbacks { AudioDecoderCallbacks { eos: Mutex::new(self.eos), error: Mutex::new(self.error), @@ -113,11 +126,10 @@ impl Default for AudioDecoderOptions { } pub trait AudioDecoder { - type Error: Debug; fn decode( &self, data: Vec, - callbacks: AudioDecoderCallbacks, + callbacks: AudioDecoderCallbacks, options: Option, ); } @@ -125,6 +137,5 @@ pub trait AudioDecoder { pub struct DummyAudioDecoder; impl AudioDecoder for DummyAudioDecoder { - type Error = (); - fn decode(&self, _: Vec, _: AudioDecoderCallbacks<()>, _: Option) {} + fn decode(&self, _: Vec, _: AudioDecoderCallbacks, _: Option) {} } diff --git a/audio/src/lib.rs b/audio/src/lib.rs index fccfc9f0..bccd5af2 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -39,5 +39,5 @@ pub trait AudioBackend { type Decoder: decoder::AudioDecoder; type Sink: sink::AudioSink; fn make_decoder() -> Self::Decoder; - fn make_sink() -> Result::Error>; + fn make_sink() -> Result; } diff --git a/audio/src/offline_sink.rs b/audio/src/offline_sink.rs index 0781552e..38a291eb 100644 --- a/audio/src/offline_sink.rs +++ b/audio/src/offline_sink.rs @@ -1,6 +1,6 @@ use block::{Chunk, FRAMES_PER_BLOCK_USIZE}; use render_thread::AudioRenderThreadMsg; -use sink::AudioSink; +use sink::{AudioSink, AudioSinkError}; use std::cell::{Cell, RefCell}; use std::sync::mpsc::Sender; @@ -34,22 +34,17 @@ impl OfflineAudioSink { } } -// replace with ! when it stabilizes -#[derive(Debug)] -pub enum OfflineError {} - impl AudioSink for OfflineAudioSink { - type Error = OfflineError; - fn init(&self, _: f32, _: Sender) -> Result<(), OfflineError> { + fn init(&self, _: f32, _: Sender) -> Result<(), AudioSinkError> { Ok(()) } - fn play(&self) -> Result<(), OfflineError> { + fn play(&self) -> Result<(), AudioSinkError> { self.has_enough_data.set(false); Ok(()) } - fn stop(&self) -> Result<(), OfflineError> { + fn stop(&self) -> Result<(), AudioSinkError> { self.has_enough_data.set(true); Ok(()) } @@ -59,7 +54,7 @@ impl AudioSink for OfflineAudioSink { || (self.rendered_blocks.get() * FRAMES_PER_BLOCK_USIZE >= self.length) } - fn push_data(&self, mut chunk: Chunk) -> Result<(), OfflineError> { + fn push_data(&self, mut chunk: Chunk) -> Result<(), AudioSinkError> { let offset = self.rendered_blocks.get() * FRAMES_PER_BLOCK_USIZE; let (last, copy_len) = if self.length - offset <= FRAMES_PER_BLOCK_USIZE { (true, self.length - offset) diff --git a/audio/src/render_thread.rs b/audio/src/render_thread.rs index ee3281c6..a06645ce 100644 --- a/audio/src/render_thread.rs +++ b/audio/src/render_thread.rs @@ -12,7 +12,7 @@ use node::{BlockInfo, ChannelInfo}; use offline_sink::OfflineAudioSink; use oscillator_node::OscillatorNode; use panner_node::PannerNode; -use sink::{AudioSink, DummyAudioSink}; +use sink::{AudioSink, AudioSinkError, DummyAudioSink}; use std::sync::mpsc::{Receiver, Sender}; pub enum AudioRenderThreadMsg { @@ -41,27 +41,25 @@ pub enum Sink { } impl AudioSink for Sink { - type Error = S::Error; - fn init( &self, sample_rate: f32, sender: Sender, - ) -> Result<(), Self::Error> { + ) -> Result<(), AudioSinkError> { match *self { Sink::RealTime(ref sink) => sink.init(sample_rate, sender), Sink::Offline(ref sink) => Ok(sink.init(sample_rate, sender).unwrap()), } } - fn play(&self) -> Result<(), Self::Error> { + fn play(&self) -> Result<(), AudioSinkError> { match *self { Sink::RealTime(ref sink) => sink.play(), Sink::Offline(ref sink) => Ok(sink.play().unwrap()), } } - fn stop(&self) -> Result<(), Self::Error> { + fn stop(&self) -> Result<(), AudioSinkError> { match *self { Sink::RealTime(ref sink) => sink.stop(), Sink::Offline(ref sink) => Ok(sink.stop().unwrap()), @@ -75,7 +73,7 @@ impl AudioSink for Sink { } } - fn push_data(&self, chunk: Chunk) -> Result<(), Self::Error> { + fn push_data(&self, chunk: Chunk) -> Result<(), AudioSinkError> { match *self { Sink::RealTime(ref sink) => sink.push_data(chunk), Sink::Offline(ref sink) => Ok(sink.push_data(chunk).unwrap()), @@ -109,9 +107,9 @@ impl AudioRenderThread { sample_rate: f32, graph: AudioGraph, options: AudioContextOptions, - ) -> Result + ) -> Result where - F: FnOnce() -> Result, + F: FnOnce() -> Result, { let sink = match options { AudioContextOptions::RealTimeAudioContext(_) => { @@ -152,7 +150,7 @@ impl AudioRenderThread { graph: AudioGraph, options: AudioContextOptions, ) where - F: FnOnce() -> Result, + F: FnOnce() -> Result, { let thread = Self::prepare_thread(make_sink, sender.clone(), sample_rate, graph, options); match thread { @@ -169,7 +167,8 @@ impl AudioRenderThread { sample_rate, graph, options, - ).map_err(|_| ()) + ) + .map_err(|_| ()) .unwrap(); thread.event_loop(event_queue) } diff --git a/audio/src/sink.rs b/audio/src/sink.rs index 87dbebec..297a95d0 100644 --- a/audio/src/sink.rs +++ b/audio/src/sink.rs @@ -1,39 +1,46 @@ use block::Chunk; use render_thread::AudioRenderThreadMsg; -use std::fmt::Debug; use std::sync::mpsc::Sender; +#[derive(Debug, PartialEq)] +pub enum AudioSinkError { + /// Backend specific error. + Backend(String), + /// Could not push buffer into the audio sink. + BufferPushFailed, + /// Could not move to a different state. + StateChangeFailed, +} + pub trait AudioSink { - type Error: Debug; fn init( &self, sample_rate: f32, render_thread_channel: Sender, - ) -> Result<(), Self::Error>; - fn play(&self) -> Result<(), Self::Error>; - fn stop(&self) -> Result<(), Self::Error>; + ) -> Result<(), AudioSinkError>; + fn play(&self) -> Result<(), AudioSinkError>; + fn stop(&self) -> Result<(), AudioSinkError>; fn has_enough_data(&self) -> bool; - fn push_data(&self, chunk: Chunk) -> Result<(), Self::Error>; + fn push_data(&self, chunk: Chunk) -> Result<(), AudioSinkError>; fn set_eos_callback(&self, callback: Box>) + Send + Sync + 'static>); } pub struct DummyAudioSink; impl AudioSink for DummyAudioSink { - type Error = (); - fn init(&self, _: f32, _: Sender) -> Result<(), ()> { + fn init(&self, _: f32, _: Sender) -> Result<(), AudioSinkError> { Ok(()) } - fn play(&self) -> Result<(), ()> { + fn play(&self) -> Result<(), AudioSinkError> { Ok(()) } - fn stop(&self) -> Result<(), ()> { + fn stop(&self) -> Result<(), AudioSinkError> { Ok(()) } fn has_enough_data(&self) -> bool { true } - fn push_data(&self, _: Chunk) -> Result<(), ()> { + fn push_data(&self, _: Chunk) -> Result<(), AudioSinkError> { Ok(()) } fn set_eos_callback(&self, _: Box>) + Send + Sync + 'static>) {} diff --git a/backends/gstreamer/src/audio_decoder.rs b/backends/gstreamer/src/audio_decoder.rs index c68656ae..3eca3494 100644 --- a/backends/gstreamer/src/audio_decoder.rs +++ b/backends/gstreamer/src/audio_decoder.rs @@ -1,11 +1,11 @@ use super::gst_app::{AppSink, AppSinkCallbacks, AppSrc}; use super::gst_audio; -use super::BackendError; use byte_slice_cast::*; use gst::buffer::{MappedBuffer, Readable}; use gst::prelude::*; use gst::{self, MessageView}; -use servo_media_audio::decoder::{AudioDecoder, AudioDecoderCallbacks, AudioDecoderOptions}; +use servo_media_audio::decoder::{AudioDecoder, AudioDecoderCallbacks}; +use servo_media_audio::decoder::{AudioDecoderError, AudioDecoderOptions}; use std::io::Cursor; use std::io::Read; use std::sync::{mpsc, Arc, Mutex}; @@ -27,11 +27,10 @@ impl GStreamerAudioDecoder { } impl AudioDecoder for GStreamerAudioDecoder { - type Error = BackendError; fn decode( &self, data: Vec, - callbacks: AudioDecoderCallbacks, + callbacks: AudioDecoderCallbacks, options: Option, ) { let pipeline = gst::Pipeline::new(None); @@ -39,23 +38,31 @@ impl AudioDecoder for GStreamerAudioDecoder { let appsrc = match gst::ElementFactory::make("appsrc", None) { Some(appsrc) => appsrc, - None => return callbacks.error(BackendError::ElementCreationFailed("appsrc")), + None => { + return callbacks.error(AudioDecoderError::Backend( + "appsrc creation failed".to_owned(), + )) + } }; let decodebin = match gst::ElementFactory::make("decodebin", None) { Some(decodebin) => decodebin, - None => return callbacks.error(BackendError::ElementCreationFailed("decodebin")), + None => { + return callbacks.error(AudioDecoderError::Backend( + "decodebin creation failed".to_owned(), + )) + } }; // decodebin uses something called a "sometimes-pad", which is basically // a pad that will show up when a certain condition is met, // in decodebins case that is media being decoded if let Err(e) = pipeline.add_many(&[&appsrc, &decodebin]) { - return callbacks.error(BackendError::PipelineFailed(e.0)); + return callbacks.error(AudioDecoderError::Backend(e.0.to_owned())); } if let Err(e) = gst::Element::link_many(&[&appsrc, &decodebin]) { - return callbacks.error(BackendError::PipelineFailed(e.0)); + return callbacks.error(AudioDecoderError::Backend(e.0.to_owned())); } let appsrc = appsrc.downcast::().unwrap(); @@ -92,7 +99,9 @@ impl AudioDecoder for GStreamerAudioDecoder { let pipeline = match pipeline_.upgrade() { Some(pipeline) => pipeline, None => { - callbacks.error(BackendError::PipelineFailed("upgrade")); + callbacks.error(AudioDecoderError::Backend( + "Pipeline failed upgrade".to_owned(), + )); let _ = sender.lock().unwrap().send(()); return; } @@ -108,7 +117,9 @@ impl AudioDecoder for GStreamerAudioDecoder { match media_type { None => { - callbacks.error(BackendError::Caps("Failed to get media type from pad")); + callbacks.error(AudioDecoderError::Backend( + "Failed to get media type from pad".to_owned(), + )); let _ = sender.lock().unwrap().send(()); return; } @@ -117,7 +128,7 @@ impl AudioDecoder for GStreamerAudioDecoder { }; if !is_audio { - callbacks.error(BackendError::InvalidMediaFormat); + callbacks.error(AudioDecoderError::InvalidMediaFormat); let _ = sender.lock().unwrap().send(()); return; } @@ -125,7 +136,7 @@ impl AudioDecoder for GStreamerAudioDecoder { let sample_audio_info = match gst_audio::AudioInfo::from_caps(&caps) { Some(sample_audio_info) => sample_audio_info, None => { - callbacks.error(BackendError::AudioInfoFailed); + callbacks.error(AudioDecoderError::Backend("AudioInfo failed".to_owned())); let _ = sender.lock().unwrap().send(()); return; } @@ -133,19 +144,24 @@ impl AudioDecoder for GStreamerAudioDecoder { let channels = sample_audio_info.channels(); callbacks.ready(channels); - let insert_deinterleave = || -> Result<(), BackendError> { - let convert = gst::ElementFactory::make("audioconvert", None) - .ok_or(BackendError::ElementCreationFailed("audioconvert"))?; - let resample = gst::ElementFactory::make("audioresample", None) - .ok_or(BackendError::ElementCreationFailed("audioresample"))?; - let filter = gst::ElementFactory::make("capsfilter", None) - .ok_or(BackendError::ElementCreationFailed("capsfilter"))?; + let insert_deinterleave = || -> Result<(), AudioDecoderError> { + let convert = gst::ElementFactory::make("audioconvert", None).ok_or( + AudioDecoderError::Backend("audioconvert creation failed".to_owned()), + )?; + let resample = gst::ElementFactory::make("audioresample", None).ok_or( + AudioDecoderError::Backend("audioresample creation failed".to_owned()), + )?; + let filter = gst::ElementFactory::make("capsfilter", None).ok_or( + AudioDecoderError::Backend("capsfilter creation failed".to_owned()), + )?; let deinterleave = gst::ElementFactory::make("deinterleave", Some("deinterleave")) - .ok_or(BackendError::ElementCreationFailed("deinterleave"))?; + .ok_or(AudioDecoderError::Backend( + "deinterleave creation failed".to_owned(), + ))?; deinterleave .set_property("keep-positions", &true.to_value()) - .map_err(|e| BackendError::SetPropertyFailed(e.0))?; + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; let pipeline_ = pipeline.downgrade(); let callbacks_ = callbacks.clone(); deinterleave.connect_pad_added(move |_, src_pad| { @@ -158,16 +174,22 @@ impl AudioDecoder for GStreamerAudioDecoder { let callbacks = &callbacks_; let pipeline = match pipeline_.upgrade() { Some(pipeline) => pipeline, - None => return callbacks.error(BackendError::PipelineFailed("upgrade")), + None => { + return callbacks.error(AudioDecoderError::Backend( + "Pipeline failedupgrade".to_owned(), + )) + } }; - let insert_sink = || -> Result<(), BackendError> { - let queue = gst::ElementFactory::make("queue", None) - .ok_or(BackendError::ElementCreationFailed("queue"))?; - let sink = gst::ElementFactory::make("appsink", None) - .ok_or(BackendError::ElementCreationFailed("appsink"))?; + let insert_sink = || -> Result<(), AudioDecoderError> { + let queue = gst::ElementFactory::make("queue", None).ok_or( + AudioDecoderError::Backend("queue creation failed".to_owned()), + )?; + let sink = gst::ElementFactory::make("appsink", None).ok_or( + AudioDecoderError::Backend("appsink creation failed".to_owned()), + )?; let appsink = sink.clone().dynamic_cast::().unwrap(); sink.set_property("sync", &false.to_value()) - .map_err(|e| BackendError::SetPropertyFailed(e.0))?; + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; let callbacks_ = callbacks.clone(); appsink.set_callbacks( @@ -183,15 +205,15 @@ impl AudioDecoder for GStreamerAudioDecoder { let buffer = if let Some(buffer) = sample.get_buffer() { buffer } else { - callbacks_.error(BackendError::InvalidSample); + callbacks_.error(AudioDecoderError::InvalidSample); return gst::FlowReturn::Error; }; let caps = if let Some(caps) = sample.get_caps() { caps } else { - callbacks_.error(BackendError::Caps( - "Could not get caps from sample", + callbacks_.error(AudioDecoderError::Backend( + "Could not get caps from sample".to_owned(), )); return gst::FlowReturn::Error; }; @@ -201,7 +223,9 @@ impl AudioDecoder for GStreamerAudioDecoder { { audio_info } else { - callbacks_.error(BackendError::AudioInfoFailed); + callbacks_.error(AudioDecoderError::Backend( + "AudioInfo failed".to_owned(), + )); return gst::FlowReturn::Error; }; assert_eq!(audio_info.channels(), 1); @@ -209,19 +233,22 @@ impl AudioDecoder for GStreamerAudioDecoder { { positions } else { - callbacks_.error(BackendError::AudioInfoFailed); + callbacks_.error(AudioDecoderError::Backend( + "AudioInfo failed".to_owned(), + )); return gst::FlowReturn::Error; }; for position in positions.iter() { let buffer = buffer.clone(); - let map = - if let Ok(map) = buffer.into_mapped_buffer_readable() { - map - } else { - callbacks_.error(BackendError::BufferReadError); - return gst::FlowReturn::Error; - }; + let map = if let Ok(map) = + buffer.into_mapped_buffer_readable() + { + map + } else { + callbacks_.error(AudioDecoderError::BufferReadFailed); + return gst::FlowReturn::Error; + }; let progress = Box::new(GStreamerAudioDecoderProgress(map)); let channel = position.to_mask() as u32; callbacks_.progress(progress, channel); @@ -235,23 +262,28 @@ impl AudioDecoder for GStreamerAudioDecoder { let elements = &[&queue, &sink]; pipeline .add_many(elements) - .map_err(|e| BackendError::PipelineFailed(e.0))?; + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; gst::Element::link_many(elements) - .map_err(|e| BackendError::PipelineFailed(e.0))?; + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; for e in elements { e.sync_state_with_parent() - .map_err(|e| BackendError::PipelineFailed(e.0))?; + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; } - let sink_pad = queue - .get_static_pad("sink") - .ok_or(BackendError::GetStaticPadFailed("sink"))?; + let sink_pad = + queue + .get_static_pad("sink") + .ok_or(AudioDecoderError::Backend( + "Could not get static pad sink".to_owned(), + ))?; src_pad .link(&sink_pad) .into_result() .map(|_| ()) - .map_err(|_| BackendError::PadLinkFailed) + .map_err(|_| { + AudioDecoderError::Backend("Sink pad link failed".to_owned()) + }) }; if let Err(e) = insert_sink() { @@ -263,32 +295,38 @@ impl AudioDecoder for GStreamerAudioDecoder { gst_audio::AUDIO_FORMAT_F32, options.sample_rate as u32, channels, - ).build() - .ok_or(BackendError::AudioInfoFailed)?; - let caps = audio_info.to_caps().ok_or(BackendError::AudioInfoFailed)?; - filter - .set_property("caps", &caps.to_value()) - .map_err(|_| BackendError::SetPropertyFailed("caps"))?; + ) + .build() + .ok_or(AudioDecoderError::Backend("AudioInfo failed".to_owned()))?; + let caps = audio_info + .to_caps() + .ok_or(AudioDecoderError::Backend("AudioInfo failed".to_owned()))?; + filter.set_property("caps", &caps.to_value()).map_err(|_| { + AudioDecoderError::Backend("Setting caps property failed".to_owned()) + })?; let elements = &[&convert, &resample, &filter, &deinterleave]; pipeline .add_many(elements) - .map_err(|e| BackendError::PipelineFailed(e.0))?; - gst::Element::link_many(elements).map_err(|e| BackendError::PipelineFailed(e.0))?; + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; + gst::Element::link_many(elements) + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; for e in elements { e.sync_state_with_parent() - .map_err(|e| BackendError::PipelineFailed(e.0))?; + .map_err(|e| AudioDecoderError::Backend(e.0.to_owned()))?; } let sink_pad = convert .get_static_pad("sink") - .ok_or(BackendError::GetStaticPadFailed("sink"))?; + .ok_or(AudioDecoderError::Backend( + "Get static pad sink failed".to_owned(), + ))?; src_pad .link(&sink_pad) .into_result() .map(|_| ()) - .map_err(|_| BackendError::PadLinkFailed) + .map_err(|_| AudioDecoderError::Backend("Sink pad link failed".to_owned())) }; if let Err(e) = insert_deinterleave() { @@ -303,8 +341,8 @@ impl AudioDecoder for GStreamerAudioDecoder { let bus = match pipeline.get_bus() { Some(bus) => bus, None => { - callbacks.error(BackendError::PipelineFailed( - "Pipeline without bus. Shouldn't happen!", + callbacks.error(AudioDecoderError::Backend( + "Pipeline without bus. Shouldn't happen!".to_owned(), )); let _ = sender.lock().unwrap().send(()); return; @@ -315,7 +353,7 @@ impl AudioDecoder for GStreamerAudioDecoder { bus.set_sync_handler(move |_, msg| { match msg.view() { MessageView::Error(e) => { - callbacks_.error(BackendError::PipelineBusError( + callbacks_.error(AudioDecoderError::Backend( e.get_debug().unwrap_or("Unknown".to_owned()), )); let _ = sender.lock().unwrap().send(()); @@ -334,7 +372,7 @@ impl AudioDecoder for GStreamerAudioDecoder { .into_result() .is_err() { - callbacks.error(BackendError::StateChangeFailed); + callbacks.error(AudioDecoderError::StateChangeFailed); return; } diff --git a/backends/gstreamer/src/audio_sink.rs b/backends/gstreamer/src/audio_sink.rs index ad847d0a..0999d7ab 100644 --- a/backends/gstreamer/src/audio_sink.rs +++ b/backends/gstreamer/src/audio_sink.rs @@ -1,4 +1,3 @@ -use super::BackendError; use byte_slice_cast::*; use gst; use gst::prelude::*; @@ -6,7 +5,7 @@ use gst_app::{AppSrc, AppSrcCallbacks}; use gst_audio; use servo_media_audio::block::{Chunk, FRAMES_PER_BLOCK}; use servo_media_audio::render_thread::AudioRenderThreadMsg; -use servo_media_audio::sink::AudioSink; +use servo_media_audio::sink::{AudioSink, AudioSinkError}; use std::cell::{Cell, RefCell}; use std::sync::mpsc::Sender; use std::sync::Arc; @@ -23,14 +22,14 @@ pub struct GStreamerAudioSink { } impl GStreamerAudioSink { - pub fn new() -> Result { + pub fn new() -> Result { if let Some(category) = gst::DebugCategory::get("openslessink") { category.set_threshold(gst::DebugLevel::Trace); } - gst::init().map_err(BackendError::Gstreamer)?; + gst::init().map_err(|_| AudioSinkError::Backend("GStreamer init failed".to_owned()))?; let appsrc = gst::ElementFactory::make("appsrc", None) - .ok_or(BackendError::ElementCreationFailed("appsrc"))?; + .ok_or(AudioSinkError::Backend("appsrc creation failed".to_owned()))?; let appsrc = appsrc.downcast::().unwrap(); Ok(Self { pipeline: gst::Pipeline::new(None), @@ -43,19 +42,20 @@ impl GStreamerAudioSink { } impl GStreamerAudioSink { - fn set_audio_info(&self, sample_rate: f32, channels: u8) -> Result<(), BackendError> { + fn set_audio_info(&self, sample_rate: f32, channels: u8) -> Result<(), AudioSinkError> { let audio_info = gst_audio::AudioInfo::new( gst_audio::AUDIO_FORMAT_F32, sample_rate as u32, channels.into(), - ).build() - .ok_or(BackendError::AudioInfoFailed)?; + ) + .build() + .ok_or(AudioSinkError::Backend("AudioInfo failed".to_owned()))?; self.appsrc.set_caps(&audio_info.to_caps().unwrap()); *self.audio_info.borrow_mut() = Some(audio_info); Ok(()) } - fn set_channels_if_changed(&self, channels: u8) -> Result<(), BackendError> { + fn set_channels_if_changed(&self, channels: u8) -> Result<(), AudioSinkError> { let curr_channels = if let Some(ch) = self.audio_info.borrow().as_ref() { ch.channels() } else { @@ -69,12 +69,11 @@ impl GStreamerAudioSink { } impl AudioSink for GStreamerAudioSink { - type Error = BackendError; fn init( &self, sample_rate: f32, graph_thread_channel: Sender, - ) -> Result<(), BackendError> { + ) -> Result<(), AudioSinkError> { self.sample_rate.set(sample_rate); self.set_audio_info(sample_rate, 2)?; self.appsrc.set_property_format(gst::Format::Time); @@ -96,42 +95,45 @@ impl AudioSink for GStreamerAudioSink { .unwrap(); let appsrc = self.appsrc.as_ref().clone().upcast(); - let resample = gst::ElementFactory::make("audioresample", None) - .ok_or(BackendError::ElementCreationFailed("audioresample"))?; - let convert = gst::ElementFactory::make("audioconvert", None) - .ok_or(BackendError::ElementCreationFailed("audioconvert"))?; - let sink = gst::ElementFactory::make("autoaudiosink", None) - .ok_or(BackendError::ElementCreationFailed("autoaudiosink"))?; + let resample = gst::ElementFactory::make("audioresample", None).ok_or( + AudioSinkError::Backend("audioresample creation failed".to_owned()), + )?; + let convert = gst::ElementFactory::make("audioconvert", None).ok_or( + AudioSinkError::Backend("audioconvert creation failed".to_owned()), + )?; + let sink = gst::ElementFactory::make("autoaudiosink", None).ok_or( + AudioSinkError::Backend("autoaudiosink creation failed".to_owned()), + )?; self.pipeline .add_many(&[&appsrc, &resample, &convert, &sink]) - .map_err(|e| BackendError::PipelineFailed(e.0))?; + .map_err(|e| AudioSinkError::Backend(e.0.to_owned()))?; gst::Element::link_many(&[&appsrc, &resample, &convert, &sink]) - .map_err(|e| BackendError::PipelineFailed(e.0))?; + .map_err(|e| AudioSinkError::Backend(e.0.to_owned()))?; Ok(()) } - fn play(&self) -> Result<(), BackendError> { + fn play(&self) -> Result<(), AudioSinkError> { self.pipeline .set_state(gst::State::Playing) .into_result() .map(|_| ()) - .map_err(|_| BackendError::StateChangeFailed) + .map_err(|_| AudioSinkError::StateChangeFailed) } - fn stop(&self) -> Result<(), BackendError> { + fn stop(&self) -> Result<(), AudioSinkError> { self.pipeline .set_state(gst::State::Paused) .into_result() .map(|_| ()) - .map_err(|_| BackendError::StateChangeFailed) + .map_err(|_| AudioSinkError::StateChangeFailed) } fn has_enough_data(&self) -> bool { self.appsrc.get_current_level_bytes() >= self.appsrc.get_max_bytes() } - fn push_data(&self, mut chunk: Chunk) -> Result<(), BackendError> { + fn push_data(&self, mut chunk: Chunk) -> Result<(), AudioSinkError> { if let Some(block) = chunk.blocks.get(0) { self.set_channels_if_changed(block.chan_count())?; } @@ -184,7 +186,7 @@ impl AudioSink for GStreamerAudioSink { .push_buffer(buffer) .into_result() .map(|_| ()) - .map_err(BackendError::Flow) + .map_err(|_| AudioSinkError::BufferPushFailed) } fn set_eos_callback(&self, _: Box>) + Send + Sync + 'static>) {} diff --git a/backends/gstreamer/src/lib.rs b/backends/gstreamer/src/lib.rs index e4c02124..59662bdb 100644 --- a/backends/gstreamer/src/lib.rs +++ b/backends/gstreamer/src/lib.rs @@ -11,7 +11,7 @@ extern crate ipc_channel; extern crate servo_media_audio; extern crate servo_media_player; -use servo_media_audio::sink::AudioSink; +use servo_media_audio::sink::AudioSinkError; use servo_media_audio::AudioBackend; use servo_media_player::PlayerBackend; @@ -19,32 +19,6 @@ pub mod audio_decoder; pub mod audio_sink; pub mod player; -#[derive(Debug, PartialEq)] -pub enum BackendError { - AudioInfoFailed, - BufferReadError, - Caps(&'static str), - ElementCreationFailed(&'static str), - EnoughData, - Flow(gst::FlowError), - GetStaticPadFailed(&'static str), - Gstreamer(gst::Error), - InvalidMediaFormat, - InvalidSample, - MissingElement(&'static str), - PadLinkFailed, - PipelineBusError(String), - PipelineFailed(&'static str), - PlayerError(String), - PlayerPushDataFailed, - PlayerEOSFailed, - PlayerNonSeekable, - PlayerSeekOutOfRange, - PlayerSourceSetupFailed, - SetPropertyFailed(&'static str), - StateChangeFailed, -} - pub struct GStreamerBackend; impl AudioBackend for GStreamerBackend { @@ -53,7 +27,7 @@ impl AudioBackend for GStreamerBackend { fn make_decoder() -> Self::Decoder { audio_decoder::GStreamerAudioDecoder::new() } - fn make_sink() -> Result::Error> { + fn make_sink() -> Result { audio_sink::GStreamerAudioSink::new() } } diff --git a/backends/gstreamer/src/player.rs b/backends/gstreamer/src/player.rs index 4664eb15..0b2f7fa4 100644 --- a/backends/gstreamer/src/player.rs +++ b/backends/gstreamer/src/player.rs @@ -1,4 +1,3 @@ -use super::BackendError; use glib; use glib::*; use gst; @@ -9,7 +8,7 @@ use gst_video::{VideoFrame, VideoInfo}; use ipc_channel::ipc::IpcSender; use servo_media_player::frame::{Frame, FrameRenderer}; use servo_media_player::metadata::Metadata; -use servo_media_player::{PlaybackState, Player, PlayerEvent, StreamType}; +use servo_media_player::{PlaybackState, Player, PlayerError, PlayerEvent, StreamType}; use std::cell::RefCell; use std::error::Error; use std::sync::atomic::{AtomicBool, Ordering}; @@ -102,7 +101,7 @@ struct PlayerInner { } impl PlayerInner { - pub fn set_input_size(&mut self, size: u64) -> Result<(), BackendError> { + pub fn set_input_size(&mut self, size: u64) -> Result<(), PlayerError> { // Set input_size to proxy its value, since it // could be set by the user before calling .setup(). self.input_size = size; @@ -116,21 +115,21 @@ impl PlayerInner { Ok(()) } - pub fn set_rate(&mut self, rate: f64) -> Result<(), BackendError> { + pub fn set_rate(&mut self, rate: f64) -> Result<(), PlayerError> { // This method may be called before the player setup is done, so we safe the rate value // and set it once the player is ready and after getting the media info self.rate = rate; if let Some(ref metadata) = self.last_metadata { if !metadata.is_seekable { eprintln!("Player must be seekable in order to set the playback rate"); - return Err(BackendError::PlayerNonSeekable); + return Err(PlayerError::NonSeekableStream); } self.player.set_rate(rate); } Ok(()) } - pub fn set_stream_type(&mut self, type_: StreamType) -> Result<(), BackendError> { + pub fn set_stream_type(&mut self, type_: StreamType) -> Result<(), PlayerError> { let type_ = match type_ { StreamType::Stream => AppStreamType::Stream, StreamType::Seekable => AppStreamType::Seekable, @@ -145,45 +144,45 @@ impl PlayerInner { Ok(()) } - pub fn play(&mut self) -> Result<(), BackendError> { + pub fn play(&mut self) -> Result<(), PlayerError> { self.player.play(); Ok(()) } - pub fn stop(&mut self) -> Result<(), BackendError> { + pub fn stop(&mut self) -> Result<(), PlayerError> { self.player.stop(); self.last_metadata = None; self.appsrc = None; Ok(()) } - pub fn pause(&mut self) -> Result<(), BackendError> { + pub fn pause(&mut self) -> Result<(), PlayerError> { self.player.pause(); Ok(()) } - pub fn end_of_stream(&mut self) -> Result<(), BackendError> { + pub fn end_of_stream(&mut self) -> Result<(), PlayerError> { if let Some(ref mut appsrc) = self.appsrc { if appsrc.end_of_stream() == gst::FlowReturn::Ok { return Ok(()); } } - Err(BackendError::PlayerEOSFailed) + Err(PlayerError::EOSFailed) } - pub fn seek(&mut self, time: f64) -> Result<(), BackendError> { + pub fn seek(&mut self, time: f64) -> Result<(), PlayerError> { // XXX Support AppStreamType::RandomAccess. The callback model changes // if the stream type is set to RandomAccess (i.e. the seek-data // callback is received right after pushing the first chunk of data, // even if player.seek() is not called). if self.stream_type.is_none() || self.stream_type.unwrap() != AppStreamType::Seekable { - return Err(BackendError::PlayerNonSeekable); + return Err(PlayerError::NonSeekableStream); } if let Some(ref metadata) = self.last_metadata { if let Some(ref duration) = metadata.duration { if duration < &time::Duration::new(time as u64, 0) { eprintln!("Trying to seek out of range"); - return Err(BackendError::PlayerSeekOutOfRange); + return Err(PlayerError::SeekOutOfRange); } } } @@ -193,23 +192,23 @@ impl PlayerInner { Ok(()) } - pub fn set_volume(&mut self, value: f64) -> Result<(), BackendError> { + pub fn set_volume(&mut self, value: f64) -> Result<(), PlayerError> { self.player.set_volume(value); Ok(()) } - pub fn push_data(&mut self, data: Vec) -> Result<(), BackendError> { + pub fn push_data(&mut self, data: Vec) -> Result<(), PlayerError> { if let Some(ref mut appsrc) = self.appsrc { if appsrc.get_current_level_bytes() + data.len() as u64 >= appsrc.get_max_bytes() { - return Err(BackendError::EnoughData); + return Err(PlayerError::EnoughData); } let buffer = - gst::Buffer::from_slice(data).ok_or_else(|| BackendError::PlayerPushDataFailed)?; + gst::Buffer::from_slice(data).ok_or_else(|| PlayerError::BufferPushFailed)?; if appsrc.push_buffer(buffer) == gst::FlowReturn::Ok { return Ok(()); } } - Err(BackendError::PlayerPushDataFailed) + Err(PlayerError::BufferPushFailed) } pub fn set_app_src(&mut self, appsrc: gst_app::AppSrc) { @@ -284,7 +283,7 @@ impl GStreamerPlayer { } } - fn setup(&self) -> Result<(), BackendError> { + fn setup(&self) -> Result<(), PlayerError> { if self.inner.borrow().is_some() { return Ok(()); } @@ -293,7 +292,10 @@ impl GStreamerPlayer { // need to make this work. for element in vec!["playbin", "queue"].iter() { if gst::ElementFactory::find(element).is_none() { - return Err(BackendError::MissingElement(element)); + return Err(PlayerError::Backend(format!( + "Missing dependency: {}", + element + ))); } } @@ -306,14 +308,14 @@ impl GStreamerPlayer { config.set_position_update_interval(500u32); player .set_config(config) - .map_err(|e| BackendError::SetPropertyFailed(e.0))?; + .map_err(|e| PlayerError::Backend(e.0.to_owned()))?; let video_sink = gst::ElementFactory::make("appsink", None) - .ok_or(BackendError::ElementCreationFailed("appsink"))?; + .ok_or(PlayerError::Backend("appsink creation failed".to_owned()))?; let pipeline = player.get_pipeline(); pipeline .set_property("video-sink", &video_sink.to_value()) - .map_err(|e| BackendError::SetPropertyFailed(e.0))?; + .map_err(|e| PlayerError::Backend(e.0.to_owned()))?; let video_sink = video_sink.dynamic_cast::().unwrap(); video_sink.set_caps(&gst::Caps::new_simple( "video/x-raw", @@ -332,7 +334,7 @@ impl GStreamerPlayer { // https://github.com/servo/servo/issues/22010#issuecomment-432599657 player .set_property("uri", &Value::from("appsrc://")) - .map_err(|e| BackendError::SetPropertyFailed(e.0))?; + .map_err(|e| PlayerError::Backend(e.0.to_owned()))?; *self.inner.borrow_mut() = Some(Arc::new(Mutex::new(PlayerInner { player, @@ -508,7 +510,7 @@ impl GStreamerPlayer { let _ = sender .lock() .unwrap() - .send(Err(BackendError::PlayerSourceSetupFailed)); + .send(Err(PlayerError::Backend("Source setup failed".to_owned()))); return None; } let source = source.unwrap(); @@ -573,16 +575,14 @@ impl GStreamerPlayer { let _ = sender_clone .lock() .unwrap() - .send(Err(BackendError::PlayerSourceSetupFailed)); + .send(Err(PlayerError::Backend("Source setup failed".to_owned()))); } let error_handler_id = inner.player.connect_error(move |player, error| { let _ = sender_clone .lock() .unwrap() - .send(Err(BackendError::PlayerError( - error.description().to_string(), - ))); + .send(Err(PlayerError::Backend(error.description().to_string()))); player.stop(); }); @@ -599,7 +599,7 @@ impl GStreamerPlayer { macro_rules! inner_player_proxy { ($fn_name:ident) => ( - fn $fn_name(&self) -> Result<(), BackendError> { + fn $fn_name(&self) -> Result<(), PlayerError> { self.setup()?; let inner = self.inner.borrow(); let mut inner = inner.as_ref().unwrap().lock().unwrap(); @@ -608,7 +608,7 @@ macro_rules! inner_player_proxy { ); ($fn_name:ident, $arg1:ident, $arg1_type:ty) => ( - fn $fn_name(&self, $arg1: $arg1_type) -> Result<(), BackendError> { + fn $fn_name(&self, $arg1: $arg1_type) -> Result<(), PlayerError> { self.setup()?; let inner = self.inner.borrow(); let mut inner = inner.as_ref().unwrap().lock().unwrap(); @@ -618,8 +618,6 @@ macro_rules! inner_player_proxy { } impl Player for GStreamerPlayer { - type Error = BackendError; - inner_player_proxy!(play); inner_player_proxy!(pause); inner_player_proxy!(stop); diff --git a/examples/player/main.rs b/examples/player/main.rs index 604f1ffd..ce79f914 100644 --- a/examples/player/main.rs +++ b/examples/player/main.rs @@ -19,7 +19,6 @@ use gleam::gl; use ipc_channel::ipc; use servo_media::player::frame::{Frame, FrameRenderer}; use servo_media::player::{Player, PlayerEvent}; -use servo_media::Error as ServoMediaError; use servo_media::ServoMedia; use std::env; use std::fs::File; @@ -38,7 +37,7 @@ use webrender::api::*; mod ui; struct PlayerWrapper { - player: Arc>>>, + player: Arc>>, shutdown: Arc, } @@ -54,10 +53,7 @@ impl PlayerWrapper { .set_input_size(metadata.len()) .unwrap(); let (sender, receiver) = ipc::channel().unwrap(); - player - .lock() - .unwrap() - .register_event_handler(sender); + player.lock().unwrap().register_event_handler(sender); let player_ = player.clone(); let player__ = player.clone(); let shutdown = Arc::new(AtomicBool::new(false)); diff --git a/player/src/lib.rs b/player/src/lib.rs index 65059be9..3f798b90 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -6,7 +6,6 @@ pub mod frame; pub mod metadata; use ipc_channel::ipc::IpcSender; -use std::fmt::Debug; use std::sync::{Arc, Mutex}; #[derive(Clone, Debug, Deserialize, Serialize)] @@ -17,6 +16,22 @@ pub enum PlaybackState { Playing, } +#[derive(Debug, PartialEq)] +pub enum PlayerError { + /// Backend specific error. + Backend(String), + /// Could not push buffer contents to the player. + BufferPushFailed, + /// The player cannot consume more data. + EnoughData, + /// Setting End Of Stream failed. + EOSFailed, + /// The media stream is not seekable. + NonSeekableStream, + /// Tried to seek out of range. + SeekOutOfRange, +} + #[derive(Clone, Debug, Deserialize, Serialize)] pub enum PlayerEvent { EndOfStream, @@ -49,60 +64,57 @@ pub enum StreamType { } pub trait Player: Send { - type Error: Debug; fn register_event_handler(&self, sender: IpcSender); fn register_frame_renderer(&self, renderer: Arc>); - fn play(&self) -> Result<(), Self::Error>; - fn pause(&self) -> Result<(), Self::Error>; - fn stop(&self) -> Result<(), Self::Error>; - fn seek(&self, time: f64) -> Result<(), Self::Error>; - fn set_volume(&self, value: f64) -> Result<(), Self::Error>; - fn set_input_size(&self, size: u64) -> Result<(), Self::Error>; - fn set_rate(&self, rate: f64) -> Result<(), Self::Error>; - fn set_stream_type(&self, type_: StreamType) -> Result<(), Self::Error>; - fn push_data(&self, data: Vec) -> Result<(), Self::Error>; - fn end_of_stream(&self) -> Result<(), Self::Error>; + fn play(&self) -> Result<(), PlayerError>; + fn pause(&self) -> Result<(), PlayerError>; + fn stop(&self) -> Result<(), PlayerError>; + fn seek(&self, time: f64) -> Result<(), PlayerError>; + fn set_volume(&self, value: f64) -> Result<(), PlayerError>; + fn set_input_size(&self, size: u64) -> Result<(), PlayerError>; + fn set_rate(&self, rate: f64) -> Result<(), PlayerError>; + fn set_stream_type(&self, type_: StreamType) -> Result<(), PlayerError>; + fn push_data(&self, data: Vec) -> Result<(), PlayerError>; + fn end_of_stream(&self) -> Result<(), PlayerError>; } pub struct DummyPlayer {} impl Player for DummyPlayer { - type Error = (); - fn register_event_handler(&self, _: IpcSender) { - } + fn register_event_handler(&self, _: IpcSender) {} fn register_frame_renderer(&self, _: Arc>) {} - fn play(&self) -> Result<(), ()> { + fn play(&self) -> Result<(), PlayerError> { Ok(()) } - fn pause(&self) -> Result<(), ()> { + fn pause(&self) -> Result<(), PlayerError> { Ok(()) } - fn stop(&self) -> Result<(), ()> { + fn stop(&self) -> Result<(), PlayerError> { Ok(()) } - fn seek(&self, _: f64) -> Result<(), ()> { + fn seek(&self, _: f64) -> Result<(), PlayerError> { Ok(()) } - fn set_volume(&self, _: f64) -> Result<(), ()> { + fn set_volume(&self, _: f64) -> Result<(), PlayerError> { Ok(()) } - fn set_input_size(&self, _: u64) -> Result<(), ()> { + fn set_input_size(&self, _: u64) -> Result<(), PlayerError> { Ok(()) } - fn set_rate(&self, _: f64) -> Result<(), ()> { + fn set_rate(&self, _: f64) -> Result<(), PlayerError> { Ok(()) } - fn set_stream_type(&self, _: StreamType) -> Result<(), ()> { + fn set_stream_type(&self, _: StreamType) -> Result<(), PlayerError> { Ok(()) } - fn push_data(&self, _: Vec) -> Result<(), ()> { - Err(()) + fn push_data(&self, _: Vec) -> Result<(), PlayerError> { + Ok(()) } - fn end_of_stream(&self) -> Result<(), ()> { - Err(()) + fn end_of_stream(&self) -> Result<(), PlayerError> { + Ok(()) } } diff --git a/servo-media/src/lib.rs b/servo-media/src/lib.rs index 4bdf0e19..13938c1c 100644 --- a/servo-media/src/lib.rs +++ b/servo-media/src/lib.rs @@ -1,12 +1,15 @@ pub extern crate servo_media_audio as audio; -#[cfg(any(all(target_os = "android", target_arch = "arm"), target_arch = "x86_64"))] +#[cfg(any( + all(target_os = "android", target_arch = "arm"), + target_arch = "x86_64" +))] extern crate servo_media_gstreamer; pub extern crate servo_media_player as player; use std::sync::{self, Arc, Mutex, Once}; use audio::context::{AudioContext, AudioContextOptions}; use audio::decoder::DummyAudioDecoder; -use audio::sink::DummyAudioSink; +use audio::sink::{AudioSinkError, DummyAudioSink}; use audio::AudioBackend; use player::{DummyPlayer, Player, PlayerBackend}; @@ -24,7 +27,7 @@ impl AudioBackend for DummyBackend { DummyAudioDecoder } - fn make_sink() -> Result { + fn make_sink() -> Result { Ok(DummyAudioSink) } } @@ -40,16 +43,17 @@ impl DummyBackend { pub fn init() {} } -#[cfg(any(all(target_os = "android", target_arch = "arm"), target_arch = "x86_64"))] +#[cfg(any( + all(target_os = "android", target_arch = "arm"), + target_arch = "x86_64" +))] pub type Backend = servo_media_gstreamer::GStreamerBackend; -#[cfg(not(any(all(target_os = "android", target_arch = "arm"), target_arch = "x86_64")))] +#[cfg(not(any( + all(target_os = "android", target_arch = "arm"), + target_arch = "x86_64" +)))] pub type Backend = DummyBackend; -#[cfg(any(all(target_os = "android", target_arch = "arm"), target_arch = "x86_64"))] -pub type Error = servo_media_gstreamer::BackendError; -#[cfg(not(any(all(target_os = "android", target_arch = "arm"), target_arch = "x86_64")))] -pub type Error = (); - impl ServoMedia { pub fn new() -> Self { Backend::init(); @@ -72,7 +76,7 @@ impl ServoMedia { AudioContext::new(options) } - pub fn create_player(&self) -> Box> { + pub fn create_player(&self) -> Box { Box::new(Backend::make_player()) } }