videocapture.videocapture - Videocapture
Table of Contents
1 Namespace: videocapture.videocapture
This namespace manages the GStreamer pipeline separated from the GUI.
2 Types
Name | Format | Description | Generate? |
---|---|---|---|
::state | any? | x | |
::imagecontainer | (partial instance? ImageContainer) | x | |
:gstreamer/pipeline | |||
:videocapture.pipeparser/appcaps |
3 Helper functions
(defn- connect-source "Connects the source configured in settings.edn to the provided GStreamer pad." [[pad index]] {:pre [(s/valid? :gstreamer/element pad) (number? index)]} (let [camera (nth (settings/get-setting :camera) index) src-type (:type camera) ;;(settings/get-setting :camera :type) src-address (:address camera) ;;(settings/get-setting :camera :address) src-username (:username camera) ;;(settings/get-setting :camera :username) src-password (:password camera) ;;(settings/get-setting :camera :password) ] (debug "Connecting to device" src-address) (case src-type :rtsp (do (debug "Connecting to" (str "rtsp://" src-username ":" src-password "@" src-address)) (.set ^Element pad "location" (str "rtsp://" src-address)) (.set ^Element pad "user-id" src-username) (.set ^Element pad "user-pw" src-password)) :device (do (debug "Connecting to" src-address) (.set ^Element pad "device" src-address)))))
4 Recording
(defn record [filename] (let [pipe (:pipeline @vcapture) appcaps (:appcaps @vcapture) endpoint (second (first (:endpoint appcaps))) fileout (ElementFactory/make "filesink" "fileout") valves (map second (:storage-valve appcaps)) extension (settings/get-setting :C :file-extension)] (.set fileout "location" (str filename extension)) (.addMany pipe (into-array Element [fileout])) (Element/linkMany (into-array Element [endpoint fileout])) (doall (map #(.set % "drop" false) valves)) (async/thread (try (.play pipe) (catch Exception e (error e)))) (reset! (:state @vcapture) :recording) (reset! (:target-file @vcapture) (str filename extension))))
(defn fpath [filename] (.toPath (io/file filename))) (defn recording-stop [filename] (let [tempfile (str (settings/get-setting :xdg :home) "tmp" (settings/get-setting :C :file-extension))] (debug "Moving" filename "to" tempfile "...") (Files/move (fpath filename) (fpath tempfile) (into-array java.nio.file.StandardCopyOption [java.nio.file.StandardCopyOption/ATOMIC_MOVE])) (debug "Setting correcting timestamps...") (sh/sh "ffmpeg" "-i" tempfile "-c" "copy" "-fflags" "+genpts" filename) (debug "Cleaning up.") (Files/deleteIfExists (fpath tempfile)) ))
5 Pipeline state
(defn- start-pipeline [] (let [gst-listener (new AppSinkListener) image-container (.getImageContainer gst-listener) [^Pipeline pipe appcaps] (pipeparser/build-pipeline :recording) sourcepad (s/conform (s/coll-of :gstreamer/element) (map second (:source appcaps))) videosink (s/conform :gstreamer/element (.getElementByName pipe "displaysink"))] (debug "Camera:" (settings/get-setting :camera)) (debug "Appcaps:" appcaps) (debug "Sourcepad:" (pr-str sourcepad)) (doall (map connect-source (apply assoc {} (interleave sourcepad (range))))) (.connect videosink gst-listener) (async/thread (.play pipe)) {:pipeline pipe :appcaps appcaps :image-container image-container :target-file (atom "") :state (atom :preview)})) (defn- stop-pipeline [vcapture] (let [pipe (:pipeline @vcapture) state @(:state @vcapture) filename @(:target-file @vcapture) eosreceiver (map second (:storage-valve (:appcaps @vcapture))) ] (debug "Stopping pipeline" pipe) (debug "Recording state was" state) (.set pipe "message-forward" true) (.connect (.getBus pipe) (fx/fi org.freedesktop.gstreamer.Bus$EOS [src] (debug "Stopping preview player...") (.stop pipe) (try (recording-stop filename) (info "Preview player stopped") (catch Exception e (error e))) )) (debug "Sending EOS to" (pr-str eosreceiver)) (async/thread (doall (map #(.sendEvent % (new org.freedesktop.gstreamer.event.EOSEvent)) eosreceiver))) (.sendEvent pipe (new org.freedesktop.gstreamer.event.EOSEvent)) )) (defstate vcapture :start (start-pipeline) :stop (stop-pipeline vcapture))
6 Complete namespace definition
(ns videocapture.videocapture "Manages the GStreamer pipeline separated from the GUI." (:require [mount.core :as mount :refer [defstate]] [clojurefx.clojurefx :as fx] [videocapture.pipeparser :as pipeparser] [videocapture.settings :as settings] [clojure.java.shell :as sh] [clojure.java.io :as io] [clojure.core.async :as async :refer [chan]] [clojure.spec.alpha :as s] [taoensso.timbre :as timbre :refer [log trace debug info warn error fatal report logf tracef debugf infof warnf errorf fatalf reportf spy get-env]]) (:import (org.freedesktop.gstreamer ElementFactory Element Pipeline) (videocapture AppSinkListener ImageContainer) (clojure.lang Atom) (java.nio.file Files))) (declare vcapture) <<types>> <<helper-fns>> <<recording>> <<recording-stop>> <<pipeline-state>>