fllcc.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. package fllcc
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "log"
  10. "net/http"
  11. "strings"
  12. "sync"
  13. "time"
  14. "gogs.daxia.dev/huanan/pkg.daxia.dev.git/fllcc/cache"
  15. "github.com/beevik/guid"
  16. "github.com/grafov/m3u8"
  17. )
  18. const (
  19. TYPE_VIDEO = "video"
  20. TYPE_KEY = "key"
  21. TYPE_TS = "ts"
  22. VERSION = "0.1"
  23. )
  24. var port = 4000
  25. var host = "127.0.0.1"
  26. var playUrlMap = map[string]string{}
  27. var playUrlMapLock = sync.Mutex{}
  28. var activeUrlMap = map[string]int64{}
  29. var activeMasterUrlMap = map[string]string{}
  30. var activeUrlMapLock = sync.Mutex{}
  31. var envMode = "prod"
  32. var srv *http.Server = nil
  33. var srvLock = sync.Mutex{}
  34. var fileCache = &cache.FileCache{}
  35. var client = http.Client{
  36. Timeout: 15 * time.Second,
  37. }
  38. var clientShortTimeout = http.Client{
  39. Timeout: 3 * time.Second,
  40. }
  41. //启动内部服务器
  42. func startup(dir string, cacheDir string, size int) {
  43. if cacheDir == "" || cacheDir == "/" {
  44. outputLog("启动失败")
  45. return
  46. }
  47. http.HandleFunc("/video/", videoHandler)
  48. http.HandleFunc("/key/", keyHandler)
  49. http.HandleFunc("/ts/", tsHandler)
  50. http.HandleFunc("/ping", pingHandler)
  51. http.HandleFunc("/version", versionHandler)
  52. fileCache = cache.NewFileCache(cacheDir, size)
  53. //这里有权限控制,没事
  54. _ = fileCache.ClearCache()
  55. if dir != "" {
  56. http.Handle("/v/",
  57. http.StripPrefix("/v/", http.FileServer(http.Dir(dir))))
  58. //打印一下目录内容
  59. //获取文件或目录相关信息
  60. fileInfoList, err := ioutil.ReadDir(dir)
  61. if err != nil {
  62. log.Println(err)
  63. } else {
  64. fmt.Println(len(fileInfoList))
  65. for i := range fileInfoList {
  66. log.Println(fileInfoList[i].Name()) //打印当前文件或目录下的文件或目录名
  67. }
  68. }
  69. }
  70. log.Println("run mode:" + envMode)
  71. //选取4000 - 5000的端口
  72. freePort, err := getFreePort()
  73. if err != nil {
  74. outputLog(err)
  75. return
  76. }
  77. port = freePort
  78. startThread()
  79. d := time.Second * 5
  80. t := time.NewTicker(d)
  81. defer t.Stop()
  82. for {
  83. <-t.C
  84. //检测一下是否真的停止了
  85. pingUrl := fmt.Sprintf("http://%s:%d/ping", host, port)
  86. resp, err := client.Get(pingUrl)
  87. if err == nil {
  88. resp.Body.Close()
  89. continue
  90. }
  91. outputLog("重启服务器")
  92. startThread()
  93. }
  94. }
  95. func serverLoop(srv *http.Server) {
  96. defer func() {
  97. if p := recover(); p != nil {
  98. outputLog("pinic:", p)
  99. }
  100. }()
  101. srvLock.Lock()
  102. srvTmp := &http.Server{Addr: fmt.Sprintf("%s:%d", host, port)}
  103. srv = srvTmp
  104. srvLock.Unlock()
  105. err := srvTmp.ListenAndServe()
  106. if err != nil {
  107. outputLog(err)
  108. }
  109. }
  110. func startThread() {
  111. srvLock.Lock()
  112. defer srvLock.Unlock()
  113. if srv != nil {
  114. //出错的时候才停止
  115. _ = srv.Shutdown(context.Background())
  116. }
  117. //启动服务器,但重启的时候,会退出
  118. go serverLoop(srv)
  119. }
  120. func getPlayUrl(oriPlayUrl string) string {
  121. if strings.HasPrefix(oriPlayUrl, "file://") {
  122. oriPlayUrl = strings.TrimPrefix(oriPlayUrl, "file://")
  123. oriPlayUrl = fmt.Sprintf("http://%s:%d/v/%s", host, port, oriPlayUrl)
  124. }
  125. playUrlMapLock.Lock()
  126. defer playUrlMapLock.Unlock()
  127. randUrl := getRandomUrl(TYPE_VIDEO)
  128. playUrlMap[randUrl] = oriPlayUrl
  129. activeUrlMapLock.Lock()
  130. defer activeUrlMapLock.Unlock()
  131. md5 := getMD5Hash(oriPlayUrl)
  132. activeUrlMap[md5] = time.Now().Unix()
  133. return randUrl
  134. }
  135. //处理ts,有缓存使用缓存,没有就获取数据,写入缓存,返回
  136. func tsHandler(w http.ResponseWriter, r *http.Request) {
  137. defer func() {
  138. _ = recover()
  139. }()
  140. curPath := r.URL.String()
  141. //提取ts的key
  142. pathList := strings.Split(curPath, "/")
  143. key := pathList[len(pathList)-1]
  144. data, err := fileCache.GetData(key)
  145. if err == nil {
  146. _, _ = w.Write(data)
  147. return
  148. }
  149. oriPath := ""
  150. //获取原路径
  151. func() {
  152. playUrlMapLock.Lock()
  153. defer playUrlMapLock.Unlock()
  154. oriPath = playUrlMap[curPath]
  155. }()
  156. if oriPath == "" {
  157. outputLog("获取原路径失败:" + curPath)
  158. w.WriteHeader(http.StatusInternalServerError)
  159. return
  160. }
  161. resp, err := client.Get(oriPath)
  162. if err != nil {
  163. w.WriteHeader(http.StatusInternalServerError)
  164. return
  165. }
  166. defer resp.Body.Close()
  167. bodyBuf := make([]byte, 0)
  168. const bufLen = 4096
  169. for {
  170. buf := make([]byte, bufLen)
  171. n, err := resp.Body.Read(buf)
  172. if n > 0 {
  173. //调整缓冲去大小
  174. buf = buf[:n]
  175. bodyBuf = append(bodyBuf, buf...)
  176. _, _ = w.Write(buf)
  177. continue
  178. }
  179. if err == io.EOF {
  180. break
  181. }
  182. if err != nil {
  183. w.WriteHeader(http.StatusInternalServerError)
  184. return
  185. }
  186. }
  187. _ = fileCache.SetData(key, bodyBuf)
  188. }
  189. //获取原来的key,解密内容后,返回正常的内容
  190. func keyHandler(w http.ResponseWriter, r *http.Request) {
  191. curPath := r.URL.String()
  192. oriPath := ""
  193. //校验key的有效性
  194. pathList := strings.Split(curPath, "_")
  195. if len(pathList) != 2 {
  196. outputLog("路径格式不对")
  197. w.WriteHeader(http.StatusInternalServerError)
  198. return
  199. }
  200. token := r.Header.Get("token")
  201. if token != "C203561DD73AEDC699358DFA92217E57" && envMode != "dev" {
  202. outputLog("token不对")
  203. w.WriteHeader(http.StatusInternalServerError)
  204. return
  205. }
  206. err := func() error {
  207. activeUrlMapLock.Lock()
  208. defer activeUrlMapLock.Unlock()
  209. md5Url := pathList[1]
  210. md5Map := activeMasterUrlMap[md5Url]
  211. if md5Map != "" {
  212. md5Url = md5Map
  213. }
  214. //urlActiveTime := activeUrlMap[md5Url]
  215. //超过5秒,没有心跳,停止播放
  216. //if time.Now().Unix()-urlActiveTime > 5 && envMode != "dev" {
  217. // return errors.New("心跳错误")
  218. //}
  219. return nil
  220. }()
  221. if err != nil {
  222. w.WriteHeader(http.StatusInternalServerError)
  223. return
  224. }
  225. func() {
  226. playUrlMapLock.Lock()
  227. defer playUrlMapLock.Unlock()
  228. oriPath = playUrlMap[curPath]
  229. }()
  230. if oriPath == "" {
  231. outputLog("获取原路径失败:" + curPath)
  232. w.WriteHeader(http.StatusInternalServerError)
  233. return
  234. }
  235. resp, err := client.Get(oriPath)
  236. if err != nil {
  237. w.WriteHeader(http.StatusInternalServerError)
  238. return
  239. }
  240. defer resp.Body.Close()
  241. body, err := ioutil.ReadAll(resp.Body)
  242. if err != nil {
  243. w.WriteHeader(http.StatusInternalServerError)
  244. return
  245. }
  246. bodyStr := string(body)
  247. //解密key数据
  248. bodyStr = strings.TrimSpace(bodyStr)
  249. outputLog("key数据:" + bodyStr)
  250. //判断是不是试播地址,尝试解密
  251. tryKey := func() string {
  252. data, err := base64.StdEncoding.DecodeString(bodyStr)
  253. dataStr := string(data)
  254. outputLog("解码的key:" + dataStr)
  255. suffix := "123456"
  256. if err == nil && strings.HasSuffix(dataStr, suffix) {
  257. //这个就是试播
  258. dataStr = strings.TrimSuffix(dataStr, suffix)
  259. outputLog("试播key:" + dataStr)
  260. //_, _ = w.Write([]byte(dataStr))
  261. return dataStr
  262. }
  263. outputLog("不是试播key:" + dataStr)
  264. return ""
  265. }()
  266. outputLog("try key:" + tryKey)
  267. if tryKey != "" {
  268. outputLog("返回key" + tryKey)
  269. w.WriteHeader(http.StatusOK)
  270. _, _ = w.Write([]byte(tryKey))
  271. return
  272. }
  273. outputLog("进入非试播路径")
  274. descData, err := descData(bodyStr, "24a1d6ff39d8")
  275. if err != nil {
  276. w.WriteHeader(http.StatusInternalServerError)
  277. return
  278. }
  279. data, err := base64.StdEncoding.DecodeString(descData)
  280. if err != nil {
  281. outputLog(err.Error())
  282. w.WriteHeader(http.StatusInternalServerError)
  283. return
  284. }
  285. _, _ = w.Write(data)
  286. }
  287. func videoHandler(w http.ResponseWriter, r *http.Request) {
  288. path := r.URL.String()
  289. pathList := strings.Split(path, "/")
  290. id := pathList[len(pathList)-1]
  291. //获取完整请求地址
  292. randPath := getUrlByID(TYPE_VIDEO, id)
  293. oriPath := ""
  294. err := func() error {
  295. playUrlMapLock.Lock()
  296. defer playUrlMapLock.Unlock()
  297. oriPath = playUrlMap[randPath]
  298. if oriPath == "" {
  299. return errors.New("not exists")
  300. }
  301. return nil
  302. }()
  303. if err != nil {
  304. w.WriteHeader(http.StatusNotFound)
  305. return
  306. }
  307. resp, err := client.Get(oriPath)
  308. if err != nil {
  309. outputLog("get err:" + err.Error())
  310. w.WriteHeader(http.StatusInternalServerError)
  311. return
  312. }
  313. defer resp.Body.Close()
  314. body, err := ioutil.ReadAll(resp.Body)
  315. if err != nil {
  316. w.WriteHeader(http.StatusInternalServerError)
  317. return
  318. }
  319. bodyStr := string(body)
  320. newM3U8Str, err := replaceUrl(oriPath, bodyStr)
  321. if err != nil {
  322. outputLog("解析内容失败:" + bodyStr)
  323. w.WriteHeader(http.StatusInternalServerError)
  324. return
  325. }
  326. _, _ = w.Write([]byte(newM3U8Str))
  327. }
  328. //ping 测试
  329. func pingHandler(w http.ResponseWriter, r *http.Request) {
  330. _, _ = w.Write([]byte("pong"))
  331. }
  332. //version 版本
  333. func versionHandler(w http.ResponseWriter, r *http.Request) {
  334. _, _ = w.Write([]byte(VERSION))
  335. }
  336. func getRandomUrl(typeName string) string {
  337. guid := guid.New().String()
  338. randUrl := fmt.Sprintf("http://%s:%d/%s/%s", host, port, typeName, guid)
  339. if typeName == TYPE_VIDEO {
  340. randUrl += ".m3u8"
  341. }
  342. return randUrl
  343. }
  344. func replaceUrl(baseUrl string, m3u8Str string) (string, error) {
  345. playUrlMapLock.Lock()
  346. defer playUrlMapLock.Unlock()
  347. urlMd5 := getMD5Hash(baseUrl)
  348. //解析m3u8内容,替换一下地址,加密key,用内部的,ts文件,用绝对地址
  349. p, listType, err := m3u8.DecodeFrom(strings.NewReader(m3u8Str), true)
  350. if err != nil {
  351. outputLog(err.Error())
  352. return "", err
  353. }
  354. if listType == m3u8.MASTER {
  355. masterPlayList := p.(*m3u8.MasterPlaylist)
  356. for _, item := range masterPlayList.Variants {
  357. masterUrl := item.URI
  358. oriUrl, _ := urlToABS(baseUrl, masterUrl)
  359. randUrl := getRandomUrl(TYPE_VIDEO)
  360. item.URI = randUrl
  361. playUrlMap[randUrl] = oriUrl
  362. func() {
  363. activeUrlMapLock.Lock()
  364. defer activeUrlMapLock.Unlock()
  365. activeMasterUrlMap[getMD5Hash(oriUrl)] = urlMd5
  366. }()
  367. }
  368. return masterPlayList.String(), nil
  369. }
  370. if listType != m3u8.MEDIA {
  371. return "", errors.New("解析失败")
  372. }
  373. mediaPlayList := p.(*m3u8.MediaPlaylist)
  374. if mediaPlayList.Key != nil {
  375. oriUrl, _ := urlToABS(baseUrl, mediaPlayList.Key.URI)
  376. randUrl := getRandomUrlRelative(TYPE_KEY) + "_" + urlMd5
  377. mediaPlayList.Key.URI = fmt.Sprintf("http://%s:%d%s", host, port, randUrl)
  378. playUrlMap[randUrl] = oriUrl
  379. }
  380. for _, item := range mediaPlayList.Segments {
  381. if item == nil {
  382. continue
  383. }
  384. //ts路径,用全路径
  385. oriUrl, _ := urlToABS(baseUrl, item.URI)
  386. randUrl := fmt.Sprintf("/ts/%s", getMD5Hash(oriUrl))
  387. playUrlMap[randUrl] = oriUrl
  388. item.URI = fmt.Sprintf("http://%s:%d/%s", host, port, randUrl)
  389. if item.Key == nil {
  390. continue
  391. }
  392. {
  393. oriUrl, _ := urlToABS(baseUrl, item.Key.URI)
  394. randUrl := getRandomUrlRelative(TYPE_KEY) + "_" + urlMd5
  395. item.Key.URI = fmt.Sprintf("http://%s:%d%s", host, port, randUrl)
  396. playUrlMap[randUrl] = oriUrl
  397. }
  398. }
  399. return mediaPlayList.String(), nil
  400. }
  401. func getRandomUrlRelative(typeName string) string {
  402. guid := guid.New().String()
  403. return fmt.Sprintf("/%s/%s", typeName, guid)
  404. }
  405. func geRandomUrlAbs(typeName string) string {
  406. guid := guid.New().String()
  407. return fmt.Sprintf("http://%s:%d/%s/%s", host, port, typeName, guid)
  408. }
  409. func getUrlByID(typeName string, id string) string {
  410. return fmt.Sprintf("http://%s:%d/%s/%s", host, port, typeName, id)
  411. }