-- | -- Module : Xine -- Copyright : (c) Joachim Fasting 2010 -- License : LGPL (see COPYING) -- -- Maintainer : Joachim Fasting -- Stability : unstable -- Portability : not portable -- -- A simple binding to xine-lib. -- -- Example usage: -- -- > import qualified Xine -- -- > main = do -- -- > h <- Xine.open -- -- > sid <- Xine.openStream h "track.mp3" -- -- > Xine.play h sid module Xine ( -- * Configuration XineConf(..), VisualType(..), defaultConf, -- * Handle XineHandle, open, openWith, close, isClosed, -- * Streams -- $streams MRL, StreamId, openStream, closeStream, getCurrent, -- * Playback SeekArg(..), play, seek, stop, pause, -- * Information retrieval EngineStatus(..), MetaType(..), getStatus, getMetadata ) where import Xine.Foreign import Xine.Internal.Handle import Xine.Internal.Stream (StreamId) import qualified Xine.Internal.Stream as S import Control.Concurrent.MVar import Control.Monad (unless) import Data.Maybe (fromJust) ------------------------------------------------------------------------------ -- Configuration ------------------------------------------------------------------------------ -- | Xine configuration. data XineConf = XineConf { audioDriver :: !(Maybe String) -- ^ Audio driver. Use 'Nothing' for -- auto-detection. , videoDriver :: !(Maybe String) -- ^ Video driver. Use 'Nothing' for -- auto-detection. , visualType :: !VisualType -- ^ Video output type. Use 'None' to disable video output. } -- | Default configuration. Audio only. defaultConf :: XineConf defaultConf = XineConf { audioDriver = Nothing , videoDriver = Nothing , visualType = None } ------------------------------------------------------------------------------ -- Handle ------------------------------------------------------------------------------ -- | Open a new Xine handle using 'defaultConf'. open :: IO XineHandle open = openWith defaultConf -- | Open a new Xine handle using the supplied 'XineConf'. openWith :: XineConf -> IO XineHandle openWith conf = do engine <- xine_new xine_init engine ap <- maybe (fail "Failed to open the audio driver") return =<< xine_open_audio_driver engine (audioDriver conf) vp <- maybe (fail "Failed to open the video driver") return =<< xine_open_video_driver engine (videoDriver conf) (visualType conf) h_ <- newMVar $ XineHandle_ engine ap vp S.empty Nothing Open return $ XineHandle h_ -- | Close Xine handle. The handle is invalid after this. close :: XineHandle -> IO () close h@(XineHandle hv) = do withXineHandle h $ \h_ -> do mapM_ xine_close (S.streams $ hStreams h_) xine_close_audio_driver (hEngine h_) (hAudioPort h_) xine_close_video_driver (hEngine h_) (hVideoPort h_) xine_exit (hEngine h_) modifyMVar_ hv $ \x -> return x { hState = Closed } ------------------------------------------------------------------------------ -- $streams -- -- You may open multiple streams for playback. Beware, though, that new -- streams cannot be added once playback has started, so open your streams -- before using them. ------------------------------------------------------------------------------ -- | Open a new stream for the given MRL. openStream :: XineHandle -> MRL -> IO StreamId openStream h uri = do modifyXineHandle h $ \h_ -> do -- Create a new stream st <- maybe (fail "Failed to open a new stream") return =<< xine_stream_new (hEngine h_) (hAudioPort h_) (hVideoPort h_) -- Open the MRL ret <- xine_open st uri unless (ret == 1) (fail "Failed to open MRL") -- Add the stream to the handle let (s, i) = S.insert st (hStreams h_) return $ h_ { hStreams = s , hCurrent = Just i } fromJust `fmap` getCurrent h -- | Close the specified stream. closeStream :: XineHandle -> StreamId -> IO () closeStream h sid = modifyXineHandle h $ \h_ -> case S.lookup sid (hStreams h_) of Just st -> do xine_close st return $ h_ { hStreams = S.delete sid (hStreams h_) } Nothing -> return h_ -- | Get the current stream, if any. getCurrent :: XineHandle -> IO (Maybe StreamId) getCurrent h = withXineHandle h $ \hv -> return (hCurrent hv) ------------------------------------------------------------------------------ -- Playback ------------------------------------------------------------------------------ -- | Start playback. play :: XineHandle -> StreamId -> IO () play h sid = withStream h sid $ \st -> do ret <- xine_play st 0 0 unless (ret == 1) (fail "Failed to start playback") -- | Argument for 'seek'. data SeekArg = SeekTime Int | SeekPos Int deriving (Eq, Show) -- | Seek to a position or time in the stream. -- -- Warning: this will crash if 'xine_trick_mode' is not implemented. seek :: XineHandle -> StreamId -> SeekArg -> IO () seek h sid arg = withStream h sid $ \st -> do ret <- xine_trick_mode st (trick arg) (val arg) unless (ret == 1) (fail "Seek failed") where val (SeekTime x) = x val (SeekPos x) = x trick (SeekTime _) = TrickSeekToTime trick (SeekPos _) = TrickSeekToPosition -- | Stop playback. stop :: XineHandle -> StreamId -> IO () stop h sid = withStream h sid $ \st -> xine_stop st -- | Toggle pause. pause :: XineHandle -> StreamId -> IO () pause h sid = withStream h sid $ \st -> xine_get_param st Speed >>= xine_set_param st Speed . toggle where toggle Pause = Normal toggle _ = Pause ------------------------------------------------------------------------------ -- Information retrieval ------------------------------------------------------------------------------ -- | Get current engine status. getStatus :: XineHandle -> IO EngineStatus getStatus h = withXineHandle h $ \h_ -> case hCurrent h_ of Just sid -> xine_get_status (fromJust $ S.lookup sid (hStreams h_)) Nothing -> return Idle -- | Get meta data about the given stream. getMetadata :: XineHandle -> StreamId -> MetaType -> IO String getMetadata h sid m = withStream h sid $ \st -> xine_get_meta_info st m