Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blackmagic hyperdeck recording #190

Merged
60 changes: 60 additions & 0 deletions field/arena.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"log"
"math"
"net"
"reflect"
"time"

Expand Down Expand Up @@ -459,6 +460,7 @@ func (arena *Arena) AbortMatch() error {
arena.AudienceDisplayModeNotifier.Notify()
arena.AllianceStationDisplayMode = "logo"
arena.AllianceStationDisplayModeNotifier.Notify()
arena.stopRecording()
return nil
}

Expand Down Expand Up @@ -548,6 +550,7 @@ func (arena *Arena) Update() {
arena.AudienceDisplayModeNotifier.Notify()
arena.AllianceStationDisplayMode = "match"
arena.AllianceStationDisplayModeNotifier.Notify()
arena.startRecording()
if game.MatchTiming.WarmupDurationSec > 0 {
arena.MatchState = WarmupPeriod
enabled = false
Expand Down Expand Up @@ -599,6 +602,7 @@ func (arena *Arena) Update() {
auto = false
enabled = false
sendDsPacket = true
arena.stopRecording()
go func() {
// Leave the scores on the screen briefly at the end of the match.
time.Sleep(time.Second * matchEndScoreDwellSec)
Expand Down Expand Up @@ -1087,3 +1091,59 @@ func (arena *Arena) runPeriodicTasks() {
arena.updateEarlyLateMessage()
arena.purgeDisconnectedDisplays()
}

// Connect to Blackmagic device. Returns connection object.
func connectToRecorder(connString string) (net.Conn, error) {
const MAX_CONN_ATTEMPTS = 3
const CONN_TIMEOUT = time.Millisecond * 100

var err error
var conn net.Conn

for i := 0; i < MAX_CONN_ATTEMPTS; i++ {
conn, err = net.DialTimeout("tcp", connString, CONN_TIMEOUT)
if err != nil {
log.Println("CONNECTION ERROR*******", "connString:***", connString, "***error: ", err)
} else {
return conn, nil
}
}

return nil, err
}

// Starts recording using configured, on-network Blackmagic Device
func (arena *Arena) startRecording() {
var connections []net.Conn

for i := 0; i < len(arena.EventSettings.RecorderAddresses); i++ {
recCon, err := connectToRecorder(arena.EventSettings.RecorderAddresses[i])
if recCon == nil {
log.Println("recorder connection error: ", err)
} else {
connections = append(connections, recCon)
}
}

for i := 0; i < len(connections); i++ {
fmt.Fprint(connections[i], "record\n")
}
}

// Stops recording using configured, on-network Blackmagic Device
func (arena *Arena) stopRecording() {
var connections []net.Conn

for i := 0; i < len(arena.EventSettings.RecorderAddresses); i++ {
recCon, err := connectToRecorder(arena.EventSettings.RecorderAddresses[i])
if recCon == nil {
log.Println("recorder connection error: ", err)
} else {
connections = append(connections, recCon)
}
}

for i := 0; i < len(connections); i++ {
fmt.Fprint(connections[i], "stop\n")
}
}
2 changes: 2 additions & 0 deletions model/event_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type EventSettings struct {
TeamSignBlue2Address string
TeamSignBlue3Address string
TeamSignBlueTimerAddress string
RecorderAddressesRaw string
RecorderAddresses []string
WarmupDurationSec int
AutoDurationSec int
PauseDurationSec int
Expand Down
13 changes: 13 additions & 0 deletions templates/setup_settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,19 @@
</div>
</div>
</fieldset>
<fieldset class="mb-4">
<legend>Instant Replay Match Recording</legend>
<p>
If you are using a Blackmagic HyperDeck device to record matches, enter device information here.
Format entries as ip_address:port (e.g. 192.168.0.123:9993). One entry per line.
</p>
<div class="row mb-3">
<label class="col-lg-6 control-label">IP Address</label>
<div class="col-lg-6">
<textarea class="form-control" name="recorderAddresses">{{.RecorderAddressesRaw}}</textarea>
</div>
</div>
</fieldset>
<fieldset class="mb-4">
<legend>Game-Specific</legend>
<div class="row mb-3">
Expand Down
12 changes: 12 additions & 0 deletions web/setup_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io/ioutil"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -94,6 +95,17 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) {
eventSettings.TeamSignBlue2Address = r.PostFormValue("teamSignBlue2Address")
eventSettings.TeamSignBlue3Address = r.PostFormValue("teamSignBlue3Address")
eventSettings.TeamSignBlueTimerAddress = r.PostFormValue("teamSignBlueTimerAddress")

eventSettings.RecorderAddressesRaw = r.PostFormValue("recorderAddresses")
const recAddrEntryPattern = "^(?:\\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|[0-9])\\b(?:\r?\n|$))+$"
match, _ := regexp.MatchString(recAddrEntryPattern, eventSettings.RecorderAddressesRaw)
if match {
eventSettings.RecorderAddresses = strings.Split(strings.ReplaceAll(eventSettings.RecorderAddressesRaw, "\r", ""), "\n")
} else {
web.renderSettings(w, r, "Recorder address entry error. Please ensure all addresses entered correctly.")
return
}

eventSettings.WarmupDurationSec, _ = strconv.Atoi(r.PostFormValue("warmupDurationSec"))
eventSettings.AutoDurationSec, _ = strconv.Atoi(r.PostFormValue("autoDurationSec"))
eventSettings.PauseDurationSec, _ = strconv.Atoi(r.PostFormValue("pauseDurationSec"))
Expand Down
Loading