From: qydysky Date: Tue, 13 Apr 2021 16:35:04 +0000 (+0800) Subject: 快速响应 flv播放支持 限定文件才进行流处理 X-Git-Tag: v0.5.9~1^2~27 X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=eeb60048da1e9cac874fae14c7ae88e91d889e91;p=bili_danmu%2F.git 快速响应 flv播放支持 限定文件才进行流处理 --- diff --git a/README.md b/README.md index 3f4baed..8ac81e4 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ golang go version go1.15 linux/amd64 ### 说明 本项目使用github action自动构建,构建过程详见[yml](https://github.com/qydysky/bili_danmu/blob/master/.github/workflows/go.yml) -#### 局域网转直播流服务 +#### 直播流Web服务 启动Web流服务,为下载的直播流提供局域网内的流服务。 在`demo/config/config_K_v.json`中可找到配置项,默认开启。 @@ -108,7 +108,13 @@ golang go version go1.15 linux/amd64 "直播保存位置Web服务":true, ``` -开启之后,启动会显示服务地址,在局域网内打开网址可以取得当前直播流(`dtmp`结尾)的flv流地址。可以在其他设备进行网络串流观看(如安卓mx player)。通过对获取flv直播流进行修改,使得无论何时打开局域网串流,播放的进度与当前直播时刻相同。服务地址也可通过命令行` room`查看。 +开启之后,启动会显示服务地址,在局域网内打开网址可以取得所有直播流的串流地址。 + +- dtmp结尾:当前正在获取的流,播放此链接时进度将保持当前流进度 +- flv结尾:保存完毕的直播流,播放此链接时将从头开始播放 +- ass结尾:保存完毕的直播流字幕,有些播放器会在播放flv串流时获取此文件 + +服务地址也可通过命令行` room`查看。 ``` I: 2021/04/13 20:07:45 命令行操作 [直播Web服务: http://192.168.31.245:38259] diff --git a/Reply/F.go b/Reply/F.go index 5e5e50a..3e8592e 100644 --- a/Reply/F.go +++ b/Reply/F.go @@ -958,7 +958,7 @@ func init() { s.Handle(map[string]func(http.ResponseWriter,*http.Request){ `/`:func(w http.ResponseWriter,r *http.Request){ var path string = r.URL.Path[1:] - if path == `` { + if ext := filepath.Ext(path);ext == `` || ext != `.dtmp` || ext != `.flv` { http.FileServer(http.Dir(base_dir)).ServeHTTP(w,r) } else { path = base_dir+path @@ -968,11 +968,6 @@ func init() { return } - w.WriteHeader(200) - if f, ok := w.(http.Flusher); ok { - f.Flush() - } - byteC := make(chan []byte,1024*1024) cancel := make(chan struct{}) defer close(cancel) @@ -983,6 +978,8 @@ func init() { } }() + + var fastRespon = true for { buf := <- byteC if len(buf) == 0 {break} @@ -990,6 +987,9 @@ func init() { if _,err := w.Write(buf);err != nil { flog.Base_add(`直播Web服务`).L(`T: `,`E: `,err); break + } else if fastRespon { + fastRespon = false + if flusher, flushSupport := w.(http.Flusher);flushSupport {flusher.Flush()} } } } diff --git a/Reply/flvDecode.go b/Reply/flvDecode.go index 4a2ed33..c4b5417 100644 --- a/Reply/flvDecode.go +++ b/Reply/flvDecode.go @@ -5,6 +5,7 @@ import ( "bytes" "time" "errors" + "path/filepath" c "github.com/qydysky/bili_danmu/CV" F "github.com/qydysky/bili_danmu/F" @@ -21,7 +22,7 @@ const ( //custom define eof_tag = byte(0x00) - copy_buf_size = 1024*1024 + copy_buf_size = 1024*1024*10 ) var ( @@ -40,67 +41,73 @@ func Stream(path string,streamChan chan []byte,cancel chan struct{}) (error) { defer f.Close() defer close(streamChan) - //get flv header(9byte) + FirstTagSize(4byte) - { - f.Seek(0,0) - buf := make([]byte, flv_header_size+previou_tag_size) - if _,err := f.Read(buf);err != nil {return err} - if !bytes.Contains(buf,flv_header_sign) {return errors.New(`no flv`)} - streamChan <- buf - } + living := filepath.Ext(path) == `.dtmp` + //living stream ,seed to least + if living { + //get flv header(9byte) + FirstTagSize(4byte) + { + f.Seek(0,0) + buf := make([]byte, flv_header_size+previou_tag_size) + if _,err := f.Read(buf);err != nil {return err} + if !bytes.Contains(buf,flv_header_sign) {return errors.New(`no flv`)} + streamChan <- buf + } - //get tag func - var getTag = func(f *os.File)(tag byte,offset int64,buf_p *[]byte,data_p *[]byte){ - buf := make([]byte, tag_header_size) - if _,err := f.Read(buf);err != nil {tag = eof_tag;return} - tag = buf[0] - size := F.Btoi32(append([]byte{0x00},buf[1:4]...),0) - - data := make([]byte, size+previou_tag_size) - if _,err := f.Read(data);err != nil {tag = eof_tag;return} - - offset,_ = f.Seek(0,1) - offset -= tag_header_size+int64(size)+previou_tag_size - return tag,offset,&buf,&data - } + //get tag func + var getTag = func(f *os.File)(tag byte,offset int64,buf_p *[]byte,data_p *[]byte){ + buf := make([]byte, tag_header_size) + buf_p = &buf + if _,err := f.Read(buf);err != nil {tag = eof_tag;return} + tag = buf[0] + size := F.Btoi32(append([]byte{0x00},buf[1:4]...),0) + + data := make([]byte, size+previou_tag_size) + data_p = &data + if _,err := f.Read(data);err != nil {tag = eof_tag;return} + + offset,_ = f.Seek(0,1) + offset -= tag_header_size+int64(size)+previou_tag_size + return + } - //get first video and audio tag - //find last_keyframe_video_offset - var last_keyframe_video_offset int64 - first_video_tag,first_audio_tag := false,false - for { - tag,offset,buf_p,data_p := getTag(f) - if tag == script_tag { - streamChan <- *buf_p - streamChan <- *data_p - } else if tag == video_tag { - if !first_video_tag { - first_video_tag = true + //get first video and audio tag + //find last_keyframe_video_offset + var last_keyframe_video_offset int64 + first_video_tag,first_audio_tag := false,false + for { + tag,offset,buf_p,data_p := getTag(f) + if tag == script_tag { streamChan <- *buf_p streamChan <- *data_p - } + } else if tag == video_tag { + if !first_video_tag { + first_video_tag = true + streamChan <- *buf_p + streamChan <- *data_p + } - if (*data_p)[0] & 0xf0 == 0x10 { - last_keyframe_video_offset = offset - } - } else if tag == audio_tag { - if !first_audio_tag { - first_audio_tag = true - streamChan <- *buf_p - streamChan <- *data_p + if (*data_p)[0] & 0xf0 == 0x10 { + last_keyframe_video_offset = offset + } + } else if tag == audio_tag { + if !first_audio_tag { + first_audio_tag = true + streamChan <- *buf_p + streamChan <- *data_p + } + } else {//eof_tag + break; } - } else {//eof_tag - break; } - } - //seed to last tag - f.Seek(last_keyframe_video_offset,0) + //seed to last tag + f.Seek(last_keyframe_video_offset,0) + } //copy { buf := make([]byte, copy_buf_size) - eof_wait_turn := 3 + preOffset,_ := f.Seek(0,1) for { //退出 @@ -113,16 +120,20 @@ func Stream(path string,streamChan chan []byte,cancel chan struct{}) (error) { if err != nil { if err.Error() != `EOF` { return err + } else if offset,_ := f.Seek(0,1);offset == preOffset { + break } - if eof_wait_turn < 0 {break} - eof_wait_turn -= 1 } if size > 0 { streamChan <- buf[:size] } - if eof_wait_turn > 0 {time.Sleep(time.Second*3)} + if err != nil { + preOffset,_ = f.Seek(0,1) + time.Sleep(time.Duration(3) * time.Second) + } + } }