github.com/cnotch/ipchub@v1.1.0/service/hls/hls.go (about)

     1  // Copyright (c) 2019,CAOHONGJU All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package hls
     6  
     7  import (
     8  	"io"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/cnotch/ipchub/config"
    15  	"github.com/cnotch/ipchub/media"
    16  	"github.com/cnotch/xlog"
    17  )
    18  
    19  // GetM3u8 .
    20  func GetM3u8(logger *xlog.Logger, path string, token string, addr string, w http.ResponseWriter) {
    21  	logger = logger.With(xlog.Fields(
    22  		xlog.F("path", path), xlog.F("ext", "m3u8"),
    23  		xlog.F("addr", addr)))
    24  
    25  	logger.Info("http-hls: access playlist")
    26  
    27  	// 需要手动启动,如果需要转换或拉流,很耗时
    28  	var c media.Hlsable
    29  	s := media.GetOrCreate(path)
    30  	if s != nil {
    31  		c = s.Hlsable()
    32  	}
    33  
    34  	if c == nil {
    35  		logger.Errorf("http-hls: not found stream '%s'", path)
    36  		http.Error(w, "404 page not found", http.StatusNotFound)
    37  		return
    38  	}
    39  
    40  	var err error
    41  	var cont []byte
    42  
    43  	// 最多等待完成 30 秒
    44  	waitSeconds := int(1.5 * float64(3*config.HlsFragment()))
    45  	for i := 0; i < waitSeconds; i++ {
    46  		cont, err = c.M3u8(token)
    47  		if err == nil {
    48  			break
    49  		}
    50  
    51  		<-time.After(time.Second)
    52  	}
    53  
    54  	if err != nil {
    55  		logger.Errorf("http-hls: request playlist error, %v.", err)
    56  		http.Error(w, err.Error(), http.StatusBadRequest)
    57  		return
    58  	}
    59  
    60  	w.Header().Set("Access-Control-Allow-Origin", "*")
    61  	w.Header().Set("Cache-Control", "no-cache")
    62  	w.Header().Set("Content-Type", "application/x-mpegURL")
    63  	w.Header().Set("Content-Length", strconv.Itoa(len(cont)))
    64  	w.Write(cont)
    65  
    66  	if logger.LevelEnabled(xlog.DebugLevel) {
    67  		logger.Debugf("m3u8 ===>>>\r\n%s", string(cont))
    68  	}
    69  }
    70  
    71  // GetTS .
    72  func GetTS(logger *xlog.Logger, path string, addr string, w http.ResponseWriter) {
    73  	logger = logger.With(xlog.Fields(
    74  		xlog.F("path", path), xlog.F("ext", "ts"),
    75  		xlog.F("addr", addr)))
    76  
    77  	logger.Info("http-hls: access segment file")
    78  
    79  	i := strings.LastIndex(path, "/")
    80  	if i < 0 {
    81  		logger.Errorf("http-hls: path illegal `%s`", path)
    82  		http.Error(w, "Path illegal", http.StatusBadRequest)
    83  		return
    84  	}
    85  
    86  	streamPath := path[:i]
    87  	seqStr := path[i+1:]
    88  	seq, err := strconv.Atoi(seqStr)
    89  	if err != nil {
    90  		logger.Errorf("http-hls: path illegal `%s`", path)
    91  		http.Error(w, "Path illegal", http.StatusBadRequest)
    92  		return
    93  	}
    94  
    95  	// 查找的消费者但不创建
    96  	var c media.Hlsable
    97  	s := media.GetOrCreate(streamPath)
    98  	if s != nil {
    99  		c = s.Hlsable()
   100  	}
   101  
   102  	if c == nil {
   103  		logger.Errorf("http-hls: not found `%s`", path)
   104  		http.Error(w, "404 page not found", http.StatusNotFound)
   105  		return
   106  	}
   107  
   108  	reader, size, err := c.Segment(seq)
   109  	if err != nil {
   110  		logger.Errorf("http-hls: not found `%s`", path)
   111  		http.Error(w, "404 page not found", http.StatusNotFound)
   112  		return
   113  	}
   114  	defer func() {
   115  		if closer, ok := reader.(io.Closer); ok {
   116  			closer.Close()
   117  		}
   118  	}()
   119  
   120  	w.Header().Set("Access-Control-Allow-Origin", "*")
   121  	w.Header().Set("Content-Type", "video/mp2ts")
   122  	w.Header().Set("Content-Length", strconv.Itoa(size))
   123  	io.Copy(w, reader)
   124  }