github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/api/controllers/media.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package controllers 20 21 import ( 22 "time" 23 24 "github.com/deepch/vdk/format/mp4f" 25 "github.com/e154/smart-home/system/media" 26 "github.com/gobwas/ws" 27 "github.com/gobwas/ws/wsutil" 28 "github.com/labstack/echo/v4" 29 ) 30 31 // ControllerMedia ... 32 type ControllerMedia struct { 33 *ControllerCommon 34 } 35 36 // NewControllerMedia ... 37 func NewControllerMedia(common *ControllerCommon) *ControllerMedia { 38 return &ControllerMedia{ 39 ControllerCommon: common, 40 } 41 } 42 43 func (c ControllerMedia) StreamMSE(ctx echo.Context) error { 44 45 conn, _, _, err := ws.UpgradeHTTP(ctx.Request(), ctx.Response().Writer) 46 if err != nil { 47 log.Error(err.Error()) 48 return nil 49 } 50 51 defer func() { 52 err = conn.Close() 53 if err != nil { 54 log.Errorf(err.Error()) 55 } 56 }() 57 58 entityId := ctx.Param("entity_id") 59 token := ctx.Param("token") 60 clientIp := ctx.RealIP() 61 channel := ctx.Param("channel") 62 63 if !media.Storage.StreamChannelExist(entityId, channel) { 64 //log.Error(media.ErrorStreamNotFound.Error()) 65 return nil 66 } 67 68 if !media.RemoteAuthorization("WS", entityId, channel, token, clientIp) { 69 log.Error(media.ErrorStreamUnauthorized.Error()) 70 return nil 71 } 72 73 media.Storage.StreamChannelRun(entityId, channel) 74 err = conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) 75 if err != nil { 76 log.Error(err.Error()) 77 return nil 78 } 79 cid, ch, _, err := media.Storage.ClientAdd(entityId, channel, media.MSE) 80 if err != nil { 81 log.Error(err.Error()) 82 return nil 83 } 84 defer media.Storage.ClientDelete(entityId, cid, channel) 85 codecs, err := media.Storage.StreamChannelCodecs(entityId, channel) 86 if err != nil { 87 log.Error(err.Error()) 88 return nil 89 } 90 muxerMSE := mp4f.NewMuxer(nil) 91 err = muxerMSE.WriteHeader(codecs) 92 if err != nil { 93 log.Error(err.Error()) 94 return nil 95 } 96 meta, init := muxerMSE.GetInit(codecs) 97 err = wsutil.WriteServerMessage(conn, ws.OpBinary, append([]byte{9}, meta...)) 98 if err != nil { 99 log.Error(err.Error()) 100 return nil 101 } 102 err = wsutil.WriteServerMessage(conn, ws.OpBinary, init) 103 if err != nil { 104 log.Error(err.Error()) 105 return nil 106 } 107 var videoStart bool 108 controlExit := make(chan struct{}) 109 noClient := time.NewTimer(10 * time.Second) 110 go func() { 111 var header ws.Header 112 defer func() { 113 close(controlExit) 114 }() 115 for { 116 header, _, err = wsutil.NextReader(conn, ws.StateServerSide) 117 if err != nil { 118 return 119 } 120 switch header.OpCode { 121 case ws.OpPong: 122 noClient.Reset(10 * time.Second) 123 case ws.OpClose: 124 return 125 } 126 } 127 }() 128 noVideo := time.NewTimer(10 * time.Second) 129 pingTicker := time.NewTicker(500 * time.Millisecond) 130 defer pingTicker.Stop() 131 var buf []byte 132 for { 133 select { 134 135 case <-pingTicker.C: 136 if err = conn.SetWriteDeadline(time.Now().Add(3 * time.Second)); err != nil { 137 log.Error(err.Error()) 138 return nil 139 } 140 if buf, err = ws.CompileFrame(ws.NewPingFrame(nil)); err != nil { 141 log.Error(err.Error()) 142 return nil 143 } 144 if _, err = conn.Write(buf); err != nil { 145 log.Error(err.Error()) 146 return nil 147 } 148 case <-controlExit: 149 return nil 150 case <-noClient.C: 151 return nil 152 case <-noVideo.C: 153 return nil 154 case pck := <-ch: 155 if pck.IsKeyFrame { 156 noVideo.Reset(10 * time.Second) 157 videoStart = true 158 } 159 if !videoStart { 160 continue 161 } 162 var ready bool 163 if ready, buf, err = muxerMSE.WritePacket(*pck, false); err != nil { 164 log.Error(err.Error()) 165 return nil 166 } 167 if ready { 168 if err = conn.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil { 169 log.Error(err.Error()) 170 return nil 171 } 172 //err = websocket.Message.Send(ws, buf) 173 if err = wsutil.WriteServerMessage(conn, ws.OpBinary, buf); err != nil { 174 log.Error(err.Error()) 175 return nil 176 } 177 } 178 } 179 } 180 } 181 182 func (c ControllerMedia) StreamHLSLLInit(ctx echo.Context) error { 183 return nil 184 } 185 186 func (c ControllerMedia) StreamHLSLLM3U8(ctx echo.Context) error { 187 return nil 188 } 189 190 func (c ControllerMedia) StreamHLSLLM4Segment(ctx echo.Context) error { 191 return nil 192 } 193 194 func (c ControllerMedia) StreamHLSLLM4Fragment(ctx echo.Context) error { 195 return nil 196 }