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  }