dm_seg.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package model
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "encoding/binary"
  6. "encoding/json"
  7. "fmt"
  8. "io/ioutil"
  9. )
  10. var (
  11. _defaultSeg = &Segment{Start: 0, End: defaultVideoEnd, Cnt: 1, Num: 1, Duration: 0}
  12. // DefaultFlag default dm flag if bigdata downgrade.
  13. DefaultFlag = []byte(`{"rec_flag":2,"rec_text":"开启后,全站视频将按等级等优化弹幕","rec_switch":1,"dmflags":[]}`)
  14. )
  15. const (
  16. // segmentLength 分段长度,6分钟
  17. segmentLength = 60 * 6 * 1000
  18. // defaultVideoEnd 当视频时长不存在或者为0时的默认视频结尾时间点
  19. defaultVideoEnd = int64(10 * 60 * 60 * 1000)
  20. // DefaultVideoEnd 当视频时长不存在或者为0时的默认视频结尾时间点
  21. DefaultVideoEnd = int64(3 * 60 * 60 * 1000)
  22. // DefaultPageSize 默认分段长度
  23. DefaultPageSize = 60 * 6 * 1000
  24. // NotFound nothing found flag
  25. NotFound = int64(-1)
  26. // DefaultPage default page info
  27. // <d p="弹幕ID,弹幕属性,播放时间,弹幕模式,字体大小,颜色,发送时间,弹幕池,用户hash id">弹幕内容</d>
  28. _xmlSegFmt = `<d p="%d,%d,%d,%d,%d,%d,%d,%d,%s">%s</d>`
  29. // <d p="弹幕ID,弹幕属性,播放时间,弹幕模式,字体大小,颜色,发送时间,弹幕池,用户hash id,用户mid">弹幕内容</d>
  30. _xmlSegRealnameFmt = `<d p="%d,%d,%d,%d,%d,%d,%d,%d,%s,%d">%s</d>`
  31. _xmlSegHeader = `<?xml version="1.0" encoding="UTF-8"?><i><oid>%d</oid><ps>%d</ps><pe>%d</pe><pc>%d</pc><pn>%d</pn><state>%d</state><real_name>%d</real_name>`
  32. )
  33. // JudgeSlice sort for dm judgement
  34. type JudgeSlice []*DM
  35. func (d JudgeSlice) Len() int { return len(d) }
  36. func (d JudgeSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
  37. func (d JudgeSlice) Less(i, j int) bool { return d[i].Progress < d[j].Progress }
  38. // Segment dm segment struct
  39. type Segment struct {
  40. Start int64 `json:"ps"` // 分段起始时间
  41. End int64 `json:"pe"` // 分段结束时间
  42. Cnt int64 `json:"cnt"` // 总分段数
  43. Num int64 `json:"num"` // 当前第几段
  44. Duration int64 `json:"duration"` // 视频总时长
  45. }
  46. // DMSegResp segment dm list response
  47. type DMSegResp struct {
  48. Dms []*Elem `json:"dms"`
  49. Flag json.RawMessage `json:"flags,omitempty"`
  50. }
  51. // Page page info.
  52. type Page struct {
  53. Num int64 `json:"num"`
  54. Size int64 `json:"size"`
  55. Total int64 `json:"total"`
  56. }
  57. // ToXMLHeader convert segment to xml header format.
  58. func (s *Segment) ToXMLHeader(oid int64, state, realname int32) string {
  59. return fmt.Sprintf(_xmlSegHeader, oid, s.Start, s.End, s.Cnt, s.Num, state, realname)
  60. }
  61. // SegmentInfo get segment info by start time and video duration.
  62. func SegmentInfo(ps, duration int64) (s *Segment) {
  63. var cnt, num, pe int64
  64. if duration == 0 {
  65. s = _defaultSeg
  66. return
  67. }
  68. cnt = duration / segmentLength
  69. if duration%segmentLength > 0 {
  70. cnt++
  71. }
  72. for i := int64(0); i < cnt; i++ {
  73. if ps >= i*segmentLength && ps < (i+1)*segmentLength {
  74. ps = i * segmentLength
  75. pe = (i + 1) * segmentLength
  76. num = i + 1
  77. }
  78. }
  79. if pe > duration {
  80. pe = duration
  81. }
  82. s = &Segment{
  83. Start: ps,
  84. End: pe,
  85. Cnt: cnt,
  86. Num: num,
  87. Duration: duration,
  88. }
  89. return
  90. }
  91. // Encode dm ecode.
  92. func Encode(flag, xml []byte) (res []byte) {
  93. var (
  94. fl = uint32(len(flag))
  95. xl = uint32(len(xml))
  96. )
  97. res = make([]byte, 4+fl+xl)
  98. binary.BigEndian.PutUint32(res[0:4], fl)
  99. copy(res[4:], flag)
  100. copy(res[4+fl:], xml)
  101. return
  102. }
  103. // Decode decode dm proto.
  104. func Decode(buf []byte) (flag, xml []byte, err error) {
  105. var (
  106. zr *gzip.Reader
  107. )
  108. fl := binary.BigEndian.Uint32(buf[0:4])
  109. flag = buf[4 : 4+fl]
  110. if zr, err = gzip.NewReader(bytes.NewBuffer(buf[4+fl:])); err != nil {
  111. return
  112. }
  113. zr.Close()
  114. if xml, err = ioutil.ReadAll(zr); err != nil {
  115. return
  116. }
  117. return
  118. }
  119. // ToElem convert dm struct to element.
  120. func (d *DM) ToElem() (e *Elem) {
  121. if d.Content == nil {
  122. return
  123. }
  124. msg := d.Content.Msg
  125. if d.ContentSpe != nil {
  126. msg = d.ContentSpe.Msg
  127. }
  128. if len(msg) == 0 {
  129. return
  130. }
  131. if d.Pool == PoolSpecial {
  132. msg = ""
  133. }
  134. // "弹幕ID,弹幕属性,播放时间,弹幕模式,字体大小,颜色,发送时间,弹幕池,用户hash id
  135. e = &Elem{
  136. Attribute: fmt.Sprintf(`"%d,%d,%d,%d,%d,%d,%d,%d,%s"`, d.ID, d.Attr, d.Progress, d.Content.Mode, d.Content.FontSize, d.Content.Color, d.Ctime, d.Pool, Hash(d.Mid, uint32(d.Content.IP))),
  137. Content: msg,
  138. }
  139. return
  140. }