From c3319ba895150639c0cee980332209e41a095d37 Mon Sep 17 00:00:00 2001 From: Nick Ogden Date: Fri, 6 Nov 2015 15:38:02 +0000 Subject: [PATCH] Add documentation for client namespace --- build.boot | 4 +- src/multimedia/streaming/rtsp/client.clj | 224 +++++++++++++++++++++-- 2 files changed, 206 insertions(+), 22 deletions(-) diff --git a/build.boot b/build.boot index a3b354f..14abf88 100644 --- a/build.boot +++ b/build.boot @@ -13,8 +13,8 @@ pom {:project 'multimedia.streaming/rtsp :version "0.1.0-SNAPSHOT" - :description "A client and server for the Real Time Streaming Protocol (RTSP) - as described by RFC 2326." + :description "An asynchronous client and server for the Real Time + Streaming Protocol (RTSP) as described by RFC 2326." :url "http://nogden.github.io/rtsp/" :scm {:url "https://github.com/nogden/rtsp"} :license {"Eclipse Public License" diff --git a/src/multimedia/streaming/rtsp/client.clj b/src/multimedia/streaming/rtsp/client.clj index 1ed0941..6c9762f 100644 --- a/src/multimedia/streaming/rtsp/client.clj +++ b/src/multimedia/streaming/rtsp/client.clj @@ -1,14 +1,41 @@ (ns multimedia.streaming.rtsp.client "The `multimedia.streaming.rtsp.client` namespace defines the published - interface for the client API." + interface for the client." (:require [clojure.string :as string] [multimedia.streaming.rtsp.protocol :as rtsp] - [multimedia.streaming.rtsp.transport :refer [request! connect-to IRequest]])) + [multimedia.streaming.rtsp.transport :refer [request! + connect-to + IRequest]])) -(def ^:private default-session - {:c-seq (atom 1) - :version "RTSP/1.0"}) +;; # What is RTSP? +;; +;; The Real Time Streaming Protocol, or RTSP, is an application-level +;; protocol for control over the delivery of data with real-time +;; properties. RTSP provides an extensible framework to enable +;; controlled, on-demand delivery of real-time data, such as audio and +;; video. Sources of data can include both live data feeds and stored +;; clips. This protocol is intended to control multiple data delivery +;; sessions, provide a means for choosing delivery channels such as UDP, +;; multicast UDP and TCP, and provide a means for choosing delivery +;; mechanisms based upon RTP (RFC 1889). +;; # The RTSP Session +;; +;; A `Session` represents a single communication session between a +;; client and server. It identifies the peer to connect to and the +;; protocol version to use. It also holds the protocol state required +;; for RTSP communication to take place. +;; +;; The lifecycle of a session is as follows. +;; +;; 1. A `Session` is created with the `session` construction function. +;; +;; 1. Requests are made within the session via the `request!` function +;; of the `multimedia.streaming.rtsp.transport.IRequest` protocol. +;; +;; 1. The `Session` is closed via the `.close` method of the +;; `java.io.Closeable` interface. +;; (defrecord Session [url version connection c-seq] IRequest (request! [this {:keys [path method headers body timeout] @@ -25,30 +52,156 @@ (.close connection) (reset! c-seq 0))) -(defn- print-session [session writer] - (.write writer (str "#Session" (select-keys session [:url :version])))) +;; ## Session Construction -(defmethod print-method Session [session ^java.io.Writer writer] - (print-session session writer)) +(def ^:private default-session + "`default-session` provides the default values for a `Session`. + These may be overridden with options to the `session` construction + function." + {:c-seq (atom 1) + :version "RTSP/1.0"}) -(defmethod print-dup Session [session ^java.io.Writer writer] - (print-session session writer)) +(defn session + "`session` starts a new RTSP session with the peer at `url`. If + `options` is provided, it must be a map with one or more of the + following keys. -(.addMethod clojure.pprint/simple-dispatch Session - #(print-session % *out*)) + `:version` is optional and specifies the version of the RTSP + protocol to use for the session. Its value must be a string. The + default is `\"RTSP/1.0\"`. -(defn session - "`session` creates a new RTSP session object, optionally with the - specified `options`, that may be used to commnicate with the RTSP - server at `url`." + `:c-seq` is optional and sepcifies the starting CSeq sequence + number. Its value must be an intager. The default value is `1` + + Note that `session` is a pure function that simply returns a data + structure representation of the session, the actual connection is + established upon first request. + + (def peer (session \"rtsp://192.168.10.26\")) + + The transport and port are determined by the scheme and port + segements of the provided URL respectively. If no port is provided, + the default RTSP port of `554` is used." ([url] (session url {})) ([url options] (let [url (rtsp/split-url url) options (assoc options :url url - :connection (connect-to url))] + :connection (connect-to url)) + options (if-let [c-seq (:c-seq options)] + (assoc options :c-seq (atom c-seq)) + options)] (map->Session (merge default-session options))))) -(defmacro ^:private def-rtsp-method [method-name] +;; # Making Requests +;; +;; Once a session has been obtained, RTSP requests can be made by +;; calling the `request!` function, with a ring-like map +;; describing the request. +;; +;; (request! peer {:method "DESCRIBE" :path "media.mkv"}) +;; +;; Alternatively, there are convenience functions defined for each of +;; the RTSP methods. See [Convenience Functions][]. + +;; ## The Request Map +;; +;; The RTSP request map may contain the following fields. +;; +;; `:method` is required and specifies the RTSP method (verb) to use +;; in the request. Its value is a case-sensitive string. For example +;; `"PLAY"`. +;; +;; `:path` is required and specifies the path section of the RTSP URI +;; to which the request is addressed. Its value must be one of the +;; followi1ng. +;; +;; - The path component of a URI. E.g. "media/stream-1". The leading +;; `/` is optional, so `"/media/stream-1"` is semantically equiverlent +;; in this case. +;; - The value `"*"` if the request is for the peer itself and +;; addresses no particular presentation or stream. +;; +;; `:headers` is optional and specifies the RTSP headers to be sent +;; with the request. Its value must be a map of header names to +;; header values. For example: +;; +;; {:accept "application/sdp" :cache-control "none"} +;; +;; Header names may be either strings or keywords, but should be +;; lowercase. Case conversion as per the RTSP standard is performed +;; automatically. Values are case sensitive strings. +;; +;; `:body` is optional and specifies the body for the request. Its +;; value must be a string. +;; +;; `:timeout` is optional and specifies the maximum number of +;; milliseconds that that library will wait for a response. The +;; default value is `30000` (30 seconds). + +;; # Response Handling +;; +;; The RTSP library implements all communication asynchronously. +;; Calls to `request!` and to the convenience functions return a +;; [manifold](https://github.com/ztellman/manifold) deferred that will +;; yeild the response once it has been received. To retrieve the +;; response, simply call `clojure.core/deref` or use the reader +;; literal `@`. If the response is available it will be returned +;; immediately, otherise the call to `deref` will block until the +;; response has been received, or until the request times out. This +;; allows very easy switching between synchronous and asynchronous +;; programming. +;; +;; ;; Asynchronous +;; (request! peer {:method "DESCRIBE" :path "media.mkv"}) +;; +;; ;; Synchronous +;; @(request! peer {:method "DESCRIBE" :path "media.mkv"}) +;; +;; This also allows all of manifold's asynchronous programming +;; constructs to be used for response handling. +;; +;; ## The Response Map +;; +;; Like requests, responses take the form of a simple map structure +;; with the following keys. +;; +;; `:version` is the version of the RTSP protocol used to encode the +;; message. +;; +;; `:status` is the integer status code of the response. +;; +;; `:reason` is the textual description associated with the status +;; code. +;; +;; `:headers` is a map of header names to header values representing +;; the headers sent by the peer. +;; +;; `:body` is the body of the message. +;; +;; For example: +;; +;; @(request! peer {:method "OPTIONS" :path "*"}) +;; ;=> {:version "RTSP/1.0", +;; :status 200, +;; :reason "OK", +;; :headers {:c-seq "2", +;; :date "Fri, Nov 06 2015 14:52:22 GMT", +;; :public "OPTIONS, DESCRIBE, SETUP, +;; TEARDOWN, PLAY, PAUSE, +;; GET_PARAMETER, SET_PARAMETER"} +;; :body ""} +;; +;; + +;; # Convenience Functions + +(defmacro ^:private def-rtsp-method + "`def-rtsp-method` defines a function that will call `request!`, + with an RTSP request map whose method is set to `method-name`, and + return a deferred representing the response. Note that the defined + function has an exlamation mark appended to the name to reflect its + stateful effects." + [method-name] (let [verb# (-> method-name (string/replace \- \_) string/upper-case) @@ -62,6 +215,14 @@ (request! ~s (assoc ~o :method ~verb# :path ~p)))))) +;; Convenience functions are provided for each of the methods defined +;; in the RTSP specification and may be used instead of the lower +;; level `request!` function. +;; +;; (def response (describe! peer "path/to/media")) +;; +;; Note that each function has an exlamation mark appeneded to its +;; name. (def-rtsp-method options) (def-rtsp-method describe) (def-rtsp-method announce) @@ -74,4 +235,27 @@ (def-rtsp-method set-parameter) (def-rtsp-method teardown) -(defn close! [session] (.close session)) +(defn close! + "`close!` closes the specified session and terminates its connection + to the peer. A closed session can be reused by issuing another + request, all state will be as if `session` has just been called with + the original parameters." + [session] + (.close session)) + +(defn- print-session + "`print-session` defines the textual representation of a `Session`." + [session writer] + (.write writer (str "#Session" (select-keys session [:url :version])))) + +;; Printing support for the `Session` record. +(defmethod print-method Session [session ^java.io.Writer writer] + (print-session session writer)) + +;; Serialisation support for the `Session` record. +(defmethod print-dup Session [session ^java.io.Writer writer] + (print-session session writer)) + +;; Pretty printing support for the `Session` record. +(.addMethod clojure.pprint/simple-dispatch Session + #(print-session % *out*))