|
1 | 1 | package srtgo
|
2 | 2 |
|
| 3 | +/* |
| 4 | +#cgo LDFLAGS: -lsrt |
| 5 | +#include <srt/srt.h> |
| 6 | +*/ |
| 7 | +import "C" |
| 8 | +import ( |
| 9 | + "strconv" |
| 10 | + "syscall" |
| 11 | +) |
| 12 | + |
3 | 13 | type SrtInvalidSock struct{}
|
4 | 14 | type SrtRendezvousUnbound struct{}
|
5 | 15 | type SrtSockConnected struct{}
|
@@ -43,3 +53,190 @@ func (m *SrtEpollTimeout) Timeout() bool {
|
43 | 53 | func (m *SrtEpollTimeout) Temporary() bool {
|
44 | 54 | return true
|
45 | 55 | }
|
| 56 | + |
| 57 | +//MUST be called from same OS thread that generated the error (i.e.: use runtime.LockOSThread()) |
| 58 | +func srtGetAndClearError() error { |
| 59 | + defer C.srt_clearlasterror() |
| 60 | + eSysErrno := C.int(0) |
| 61 | + errno := C.srt_getlasterror(&eSysErrno) |
| 62 | + srterr := SRTErrno(errno) |
| 63 | + if eSysErrno != 0 { |
| 64 | + return srterr.wrapSysErr(syscall.Errno(eSysErrno)) |
| 65 | + } |
| 66 | + return srterr |
| 67 | +} |
| 68 | + |
| 69 | +//Based of off golang errno handling: https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/syscall/syscall_unix.go;l=114 |
| 70 | +type SRTErrno int |
| 71 | + |
| 72 | +func (e SRTErrno) Error() string { |
| 73 | + //Workaround for unknown being -1 |
| 74 | + if e == Unknown { |
| 75 | + return "Internal error when setting the right error code" |
| 76 | + } |
| 77 | + if 0 <= int(e) && int(e) < len(srterrors) { |
| 78 | + s := srterrors[e] |
| 79 | + if s != "" { |
| 80 | + return s |
| 81 | + } |
| 82 | + } |
| 83 | + return "srterrno: " + strconv.Itoa(int(e)) |
| 84 | +} |
| 85 | + |
| 86 | +func (e SRTErrno) Is(target error) bool { |
| 87 | + //for backwards compat |
| 88 | + switch target.(type) { |
| 89 | + case *SrtInvalidSock: |
| 90 | + return e == EInvSock |
| 91 | + case *SrtRendezvousUnbound: |
| 92 | + return e == ERdvUnbound |
| 93 | + case *SrtSockConnected: |
| 94 | + return e == EConnSock |
| 95 | + case *SrtConnectionRejected: |
| 96 | + return e == EConnRej |
| 97 | + case *SrtConnectTimeout: |
| 98 | + return e == ETimeout |
| 99 | + case *SrtSocketClosed: |
| 100 | + return e == ESClosed |
| 101 | + } |
| 102 | + return false |
| 103 | +} |
| 104 | + |
| 105 | +func (e SRTErrno) Temporary() bool { |
| 106 | + return e == EAsyncFAIL || e == EAsyncRCV || e == EAsyncSND || e == ECongest || e == ETimeout |
| 107 | +} |
| 108 | + |
| 109 | +func (e SRTErrno) Timeout() bool { |
| 110 | + return e == ETimeout |
| 111 | +} |
| 112 | + |
| 113 | +func (e SRTErrno) wrapSysErr(errno syscall.Errno) error { |
| 114 | + return &srtErrnoSysErrnoWrapped{ |
| 115 | + e: e, |
| 116 | + eSys: errno, |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +type srtErrnoSysErrnoWrapped struct { |
| 121 | + e SRTErrno |
| 122 | + eSys syscall.Errno |
| 123 | +} |
| 124 | + |
| 125 | +func (e *srtErrnoSysErrnoWrapped) Error() string { |
| 126 | + return e.e.Error() |
| 127 | +} |
| 128 | + |
| 129 | +func (e *srtErrnoSysErrnoWrapped) Is(target error) bool { |
| 130 | + return e.e.Is(target) |
| 131 | +} |
| 132 | + |
| 133 | +func (e *srtErrnoSysErrnoWrapped) Temporary() bool { |
| 134 | + return e.e.Temporary() |
| 135 | +} |
| 136 | + |
| 137 | +func (e *srtErrnoSysErrnoWrapped) Timeout() bool { |
| 138 | + return e.e.Timeout() |
| 139 | +} |
| 140 | + |
| 141 | +func (e *srtErrnoSysErrnoWrapped) Unwrap() error { |
| 142 | + return error(e.eSys) |
| 143 | +} |
| 144 | + |
| 145 | +//Shadows SRT_ERRNO srtcore/srt.h line 490+ |
| 146 | +const ( |
| 147 | + Unknown = SRTErrno(C.SRT_EUNKNOWN) |
| 148 | + Success = SRTErrno(C.SRT_SUCCESS) |
| 149 | + //Major: SETUP |
| 150 | + EConnSetup = SRTErrno(C.SRT_ECONNSETUP) |
| 151 | + ENoServer = SRTErrno(C.SRT_ENOSERVER) |
| 152 | + EConnRej = SRTErrno(C.SRT_ECONNREJ) |
| 153 | + ESockFail = SRTErrno(C.SRT_ESOCKFAIL) |
| 154 | + ESecFail = SRTErrno(C.SRT_ESECFAIL) |
| 155 | + ESClosed = SRTErrno(C.SRT_ESCLOSED) |
| 156 | + //Major: CONNECTION |
| 157 | + EConnFail = SRTErrno(C.SRT_ECONNFAIL) |
| 158 | + EConnLost = SRTErrno(C.SRT_ECONNLOST) |
| 159 | + ENoConn = SRTErrno(C.SRT_ENOCONN) |
| 160 | + //Major: SYSTEMRES |
| 161 | + EResource = SRTErrno(C.SRT_ERESOURCE) |
| 162 | + EThread = SRTErrno(C.SRT_ETHREAD) |
| 163 | + EnoBuf = SRTErrno(C.SRT_ENOBUF) |
| 164 | + ESysObj = SRTErrno(C.SRT_ESYSOBJ) |
| 165 | + //Major: FILESYSTEM |
| 166 | + EFile = SRTErrno(C.SRT_EFILE) |
| 167 | + EInvRdOff = SRTErrno(C.SRT_EINVRDOFF) |
| 168 | + ERdPerm = SRTErrno(C.SRT_ERDPERM) |
| 169 | + EInvWrOff = SRTErrno(C.SRT_EINVWROFF) |
| 170 | + EWrPerm = SRTErrno(C.SRT_EWRPERM) |
| 171 | + //Major: NOTSUP |
| 172 | + EInvOp = SRTErrno(C.SRT_EINVOP) |
| 173 | + EBoundSock = SRTErrno(C.SRT_EBOUNDSOCK) |
| 174 | + EConnSock = SRTErrno(C.SRT_ECONNSOCK) |
| 175 | + EInvParam = SRTErrno(C.SRT_EINVPARAM) |
| 176 | + EInvSock = SRTErrno(C.SRT_EINVSOCK) |
| 177 | + EUnboundSock = SRTErrno(C.SRT_EUNBOUNDSOCK) |
| 178 | + ENoListen = SRTErrno(C.SRT_ENOLISTEN) |
| 179 | + ERdvNoServ = SRTErrno(C.SRT_ERDVNOSERV) |
| 180 | + ERdvUnbound = SRTErrno(C.SRT_ERDVUNBOUND) |
| 181 | + EInvalMsgAPI = SRTErrno(C.SRT_EINVALMSGAPI) |
| 182 | + EInvalBufferAPI = SRTErrno(C.SRT_EINVALBUFFERAPI) |
| 183 | + EDupListen = SRTErrno(C.SRT_EDUPLISTEN) |
| 184 | + ELargeMsg = SRTErrno(C.SRT_ELARGEMSG) |
| 185 | + EInvPollID = SRTErrno(C.SRT_EINVPOLLID) |
| 186 | + EPollEmpty = SRTErrno(C.SRT_EPOLLEMPTY) |
| 187 | + //EBindConflict = SRTErrno(C.SRT_EBINDCONFLICT) |
| 188 | + //Major: AGAIN |
| 189 | + EAsyncFAIL = SRTErrno(C.SRT_EASYNCFAIL) |
| 190 | + EAsyncSND = SRTErrno(C.SRT_EASYNCSND) |
| 191 | + EAsyncRCV = SRTErrno(C.SRT_EASYNCRCV) |
| 192 | + ETimeout = SRTErrno(C.SRT_ETIMEOUT) |
| 193 | + ECongest = SRTErrno(C.SRT_ECONGEST) |
| 194 | + //Major: PEERERROR |
| 195 | + EPeer = SRTErrno(C.SRT_EPEERERR) |
| 196 | +) |
| 197 | + |
| 198 | +//Unknown cannot be here since it would have a negative index! |
| 199 | +//Error strings taken from: https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md |
| 200 | +var srterrors = [...]string{ |
| 201 | + Success: "The value set when the last error was cleared and no error has occurred since then", |
| 202 | + EConnSetup: "General setup error resulting from internal system state", |
| 203 | + ENoServer: "Connection timed out while attempting to connect to the remote address", |
| 204 | + EConnRej: "Connection has been rejected", |
| 205 | + ESockFail: "An error occurred when trying to call a system function on an internally used UDP socket", |
| 206 | + ESecFail: "A possible tampering with the handshake packets was detected, or encryption request wasn't properly fulfilled.", |
| 207 | + ESClosed: "A socket that was vital for an operation called in blocking mode has been closed during the operation", |
| 208 | + EConnFail: "General connection failure of unknown details", |
| 209 | + EConnLost: "The socket was properly connected, but the connection has been broken", |
| 210 | + ENoConn: "The socket is not connected", |
| 211 | + EResource: "System or standard library error reported unexpectedly for unknown purpose", |
| 212 | + EThread: "System was unable to spawn a new thread when requried", |
| 213 | + EnoBuf: "System was unable to allocate memory for buffers", |
| 214 | + ESysObj: "System was unable to allocate system specific objects", |
| 215 | + EFile: "General filesystem error (for functions operating with file transmission)", |
| 216 | + EInvRdOff: "Failure when trying to read from a given position in the file", |
| 217 | + ERdPerm: "Read permission was denied when trying to read from file", |
| 218 | + EInvWrOff: "Failed to set position in the written file", |
| 219 | + EWrPerm: "Write permission was denied when trying to write to a file", |
| 220 | + EInvOp: "Invalid operation performed for the current state of a socket", |
| 221 | + EBoundSock: "The socket is currently bound and the required operation cannot be performed in this state", |
| 222 | + EConnSock: "The socket is currently connected and therefore performing the required operation is not possible", |
| 223 | + EInvParam: "Call parameters for API functions have some requirements that were not satisfied", |
| 224 | + EInvSock: "The API function required an ID of an entity (socket or group) and it was invalid", |
| 225 | + EUnboundSock: "The operation to be performed on a socket requires that it first be explicitly bound", |
| 226 | + ENoListen: "The socket passed for the operation is required to be in the listen state", |
| 227 | + ERdvNoServ: "The required operation cannot be performed when the socket is set to rendezvous mode", |
| 228 | + ERdvUnbound: "An attempt was made to connect to a socket set to rendezvous mode that was not first bound", |
| 229 | + EInvalMsgAPI: "The function was used incorrectly in the message API", |
| 230 | + EInvalBufferAPI: "The function was used incorrectly in the stream (buffer) API", |
| 231 | + EDupListen: "The port tried to be bound for listening is already busy", |
| 232 | + ELargeMsg: "Size exceeded", |
| 233 | + EInvPollID: "The epoll ID passed to an epoll function is invalid", |
| 234 | + EPollEmpty: "The epoll container currently has no subscribed sockets", |
| 235 | + //EBindConflict: "SRT_EBINDCONFLICT", |
| 236 | + EAsyncFAIL: "General asynchronous failure (not in use currently)", |
| 237 | + EAsyncSND: "Sending operation is not ready to perform", |
| 238 | + EAsyncRCV: "Receiving operation is not ready to perform", |
| 239 | + ETimeout: "The operation timed out", |
| 240 | + ECongest: "With SRTO_TSBPDMODE and SRTO_TLPKTDROP set to true, some packets were dropped by sender", |
| 241 | + EPeer: "Receiver peer is writing to a file that the agent is sending", |
| 242 | +} |
0 commit comments