-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi_format.go
More file actions
138 lines (121 loc) · 3.79 KB
/
api_format.go
File metadata and controls
138 lines (121 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package control
import (
"encoding/json"
"net/http"
"sort"
"github.com/zsiec/switchframe/server/control/httperr"
"github.com/zsiec/switchframe/server/switcher"
)
// formatInfo describes the current pipeline format.
type formatInfo struct {
Width int `json:"width"`
Height int `json:"height"`
FPSNum int `json:"fpsNum"`
FPSDen int `json:"fpsDen"`
Name string `json:"name"`
}
// formatResponse is the JSON response for GET /api/format.
type formatResponse struct {
Format formatInfo `json:"format"`
Presets []string `json:"presets"`
}
// formatRequest is the JSON body for PUT /api/format.
type formatRequest struct {
Format string `json:"format,omitempty"` // preset name like "1080p25"
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
FPSNum int `json:"fpsNum,omitempty"`
FPSDen int `json:"fpsDen,omitempty"`
}
// registerFormatRoutes registers format-related API routes on the given mux.
func (a *API) registerFormatRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /api/format", a.handleGetFormat)
mux.HandleFunc("PUT /api/format", timedHandler(a.cmdQueue, "format_set", a.seqTracker, a.handleSetFormatInner))
}
// handleGetFormat returns the current pipeline format and available presets.
func (a *API) handleGetFormat(w http.ResponseWriter, r *http.Request) {
f := a.switcher.PipelineFormat()
presets := make([]string, 0, len(switcher.FormatPresets))
for name := range switcher.FormatPresets {
presets = append(presets, name)
}
sort.Strings(presets)
resp := formatResponse{
Format: formatInfo{
Width: f.Width,
Height: f.Height,
FPSNum: f.FPSNum,
FPSDen: f.FPSDen,
Name: f.Name,
},
Presets: presets,
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(resp)
}
// handleSetFormatInner changes the pipeline format using a preset name or custom dimensions.
// body is the pre-read request body provided by timedHandler.
func (a *API) handleSetFormatInner(w http.ResponseWriter, r *http.Request, body []byte) {
a.setLastOperator(r)
var req formatRequest
if err := json.Unmarshal(body, &req); err != nil {
httperr.Write(w, http.StatusBadRequest, "invalid json")
return
}
var f switcher.PipelineFormat
switch {
case req.Format != "":
// Preset name provided.
preset, ok := switcher.FormatPresets[req.Format]
if !ok {
httperr.Write(w, http.StatusBadRequest, "unknown format preset: "+req.Format)
return
}
f = preset
case req.Width > 0 || req.Height > 0 || req.FPSNum > 0 || req.FPSDen > 0:
// Custom dimensions provided.
if req.Width < 320 || req.Width > 7680 {
httperr.Write(w, http.StatusBadRequest, "width must be 320-7680")
return
}
if req.Height < 180 || req.Height > 4320 {
httperr.Write(w, http.StatusBadRequest, "height must be 180-4320")
return
}
if req.Width%2 != 0 {
httperr.Write(w, http.StatusBadRequest, "width must be even")
return
}
if req.Height%2 != 0 {
httperr.Write(w, http.StatusBadRequest, "height must be even")
return
}
if req.FPSNum <= 0 {
httperr.Write(w, http.StatusBadRequest, "fpsNum must be positive")
return
}
if req.FPSDen <= 0 {
httperr.Write(w, http.StatusBadRequest, "fpsDen must be positive")
return
}
f = switcher.PipelineFormat{
Width: req.Width,
Height: req.Height,
FPSNum: req.FPSNum,
FPSDen: req.FPSDen,
}
default:
httperr.Write(w, http.StatusBadRequest, "provide format preset or width/height/fpsNum/fpsDen")
return
}
if err := a.switcher.SetPipelineFormat(f); err != nil {
httperr.WriteErr(w, errorStatus(err), err)
return
}
// Update compositor FPS for frame-locked animations (tickers).
if a.compositor != nil {
a.compositor.SetPipelineFPS(f.FPSNum, f.FPSDen)
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(a.enrichedState())
}