verbetering tray implementatie en prefetching
This commit is contained in:
+112
-25
@@ -34,8 +34,7 @@
|
||||
[reported-state #:mutable]
|
||||
[commands #:mutable]
|
||||
[next-command-id #:mutable]
|
||||
[media-token #:mutable]
|
||||
[media-file #:mutable]
|
||||
media
|
||||
[ended-counter #:mutable])
|
||||
#:transparent)
|
||||
|
||||
@@ -106,6 +105,25 @@
|
||||
(define (fresh-media-token)
|
||||
(bytes->hex-string (crypto-random-bytes 32)))
|
||||
|
||||
(define (track-cache-key item)
|
||||
(sha1
|
||||
(open-input-string
|
||||
(path->string (track-file item)))))
|
||||
|
||||
(define (agent-track-data item index token)
|
||||
(hasheq 'playlistIndex index
|
||||
'trackNumber (+ index 1)
|
||||
'cacheKey (track-cache-key item)
|
||||
'mediaToken token
|
||||
'filename
|
||||
(path->string
|
||||
(or (file-name-from-path (track-file item))
|
||||
(track-file item)))
|
||||
'title (track-title item)
|
||||
'artist (track-artist item)
|
||||
'album (track-album item)
|
||||
'duration (or (track-duration item) 'null)))
|
||||
|
||||
(define (enqueue-agent-command! value agent action [data (hasheq)])
|
||||
(with-state-lock
|
||||
value
|
||||
@@ -353,25 +371,34 @@
|
||||
((eq? kind 'local)
|
||||
(audio-play! backend (track-file item)))
|
||||
((eq? kind 'agent)
|
||||
(let ((token (fresh-media-token)))
|
||||
(let* ((token (fresh-media-token))
|
||||
(data (agent-track-data item index token))
|
||||
(following-index (next-index value 1))
|
||||
(following-item
|
||||
(and following-index
|
||||
(not (= following-index index))
|
||||
(list-ref (player-tracks value) following-index)))
|
||||
(following-token
|
||||
(and following-item (fresh-media-token)))
|
||||
(following-data
|
||||
(and following-item
|
||||
(agent-track-data following-item
|
||||
following-index
|
||||
following-token))))
|
||||
(with-state-lock
|
||||
value
|
||||
(λ ()
|
||||
(set-playback-agent-media-token! backend token)
|
||||
(set-playback-agent-media-file! backend (track-file item))))
|
||||
(enqueue-agent-command!
|
||||
value
|
||||
backend
|
||||
"play"
|
||||
(hasheq 'mediaToken token
|
||||
'filename
|
||||
(path->string
|
||||
(or (file-name-from-path (track-file item))
|
||||
(track-file item)))
|
||||
'title (track-title item)
|
||||
'artist (track-artist item)
|
||||
'album (track-album item)
|
||||
'duration (or (track-duration item) 'null)))))
|
||||
(hash-set! (playback-agent-media backend)
|
||||
token
|
||||
(track-file item))
|
||||
(when following-item
|
||||
(hash-set! (playback-agent-media backend)
|
||||
following-token
|
||||
(track-file following-item)))))
|
||||
(enqueue-agent-command! value backend "play" data)
|
||||
(when following-data
|
||||
(enqueue-agent-command!
|
||||
value backend "prefetch" following-data))))
|
||||
(else
|
||||
(dlna-player-play! backend (track-file item))))
|
||||
(clear-error! value)))
|
||||
@@ -405,11 +432,15 @@
|
||||
(playback-agent-reported-state
|
||||
(player-backend value))))
|
||||
;; Keep the server's optimistic command state visible until the
|
||||
;; agent acknowledges all queued work. Its report in the poll that
|
||||
;; receives a command still describes the state before execution.
|
||||
;; agent acknowledges playback-changing work. Prefetching does not
|
||||
;; change playback state and may continue in the background.
|
||||
(when (and (hash? reported)
|
||||
(null? (playback-agent-commands
|
||||
(player-backend value))))
|
||||
(andmap
|
||||
(λ (command)
|
||||
(string=? (hash-ref command 'action "")
|
||||
"prefetch"))
|
||||
(playback-agent-commands
|
||||
(player-backend value))))
|
||||
(with-state-lock
|
||||
value
|
||||
(λ ()
|
||||
@@ -1171,7 +1202,7 @@
|
||||
(hasheq 'state "stopped"
|
||||
'position 0
|
||||
'volume 50)
|
||||
'() 1 #f #f 0)))
|
||||
'() 1 (make-hash) 0)))
|
||||
(set-player-agents!
|
||||
value
|
||||
(append (player-agents value) (list agent)))
|
||||
@@ -1228,6 +1259,15 @@
|
||||
(when (hash? reported)
|
||||
(set-playback-agent-reported-state! agent reported))
|
||||
(when (exact-nonnegative-integer? ack)
|
||||
(for ((command (in-list (playback-agent-commands agent)))
|
||||
#:when (<= (hash-ref command 'id) ack))
|
||||
(let* ((command-data
|
||||
(hash-ref command 'data (hasheq)))
|
||||
(media-token
|
||||
(hash-ref command-data 'mediaToken #f)))
|
||||
(when (string? media-token)
|
||||
(hash-remove! (playback-agent-media agent)
|
||||
media-token))))
|
||||
(set-playback-agent-commands!
|
||||
agent
|
||||
(filter
|
||||
@@ -1265,8 +1305,7 @@
|
||||
(agent-by-id value app-id))))
|
||||
(and agent
|
||||
(string? token)
|
||||
(equal? token (playback-agent-media-token agent))
|
||||
(playback-agent-media-file agent))))))
|
||||
(hash-ref (playback-agent-media agent) token #f))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Stop playback and release all player resources.
|
||||
@@ -1435,6 +1474,54 @@
|
||||
(player-renderers example-player)))
|
||||
"Office laptop")
|
||||
|
||||
(define first-file (build-path root "Album" "01.flac"))
|
||||
(define second-file (build-path root "Album" "02.flac"))
|
||||
(call-with-output-file first-file void #:exists 'truncate/replace)
|
||||
(call-with-output-file second-file void #:exists 'truncate/replace)
|
||||
(set-player-tracks!
|
||||
example-player
|
||||
(list (track first-file "First" "Artist" "Album" 60 "audio/flac")
|
||||
(track second-file "Second" "Artist" "Album" 60 "audio/flac")))
|
||||
|
||||
(player-command! example-player "play" (hasheq 'index 0))
|
||||
(define play-poll
|
||||
(player-agent-poll!
|
||||
example-player
|
||||
(hasheq 'appId test-agent-id
|
||||
'ack first-command-id
|
||||
'endedCounter 0
|
||||
'state (hasheq 'state "starting" 'position 0))))
|
||||
(define play-command (hash-ref play-poll 'command))
|
||||
(check-equal? (hash-ref play-command 'action) "play")
|
||||
(check-equal?
|
||||
(hash-ref (hash-ref play-command 'data) 'trackNumber)
|
||||
1)
|
||||
(check-equal?
|
||||
(player-agent-media
|
||||
example-player
|
||||
test-agent-id
|
||||
(hash-ref (hash-ref play-command 'data) 'mediaToken))
|
||||
first-file)
|
||||
|
||||
(define prefetch-poll
|
||||
(player-agent-poll!
|
||||
example-player
|
||||
(hasheq 'appId test-agent-id
|
||||
'ack (hash-ref play-command 'id)
|
||||
'endedCounter 0
|
||||
'state (hasheq 'state "playing" 'position 1))))
|
||||
(define prefetch-command (hash-ref prefetch-poll 'command))
|
||||
(check-equal? (hash-ref prefetch-command 'action) "prefetch")
|
||||
(check-equal?
|
||||
(hash-ref (hash-ref prefetch-command 'data) 'trackNumber)
|
||||
2)
|
||||
(check-equal?
|
||||
(player-agent-media
|
||||
example-player
|
||||
test-agent-id
|
||||
(hash-ref (hash-ref prefetch-command 'data) 'mediaToken))
|
||||
second-file)
|
||||
|
||||
(check-exn exn:fail?
|
||||
(λ ()
|
||||
(player-command!
|
||||
|
||||
Reference in New Issue
Block a user