func (btp *BTPlayer) playerLoop() { defer btp.Close() start := time.Now() btp.log.Info("Buffer loop") buffered, bufferDone := btp.bufferEvents.Listen() defer close(bufferDone) go btp.bufferDialog() if err := <-buffered; err != nil { return } ga.TrackTiming("player", "buffer_time_real", int(time.Now().Sub(start).Seconds()*1000), "") btp.log.Info("Waiting for playback...") oneSecond := time.NewTicker(1 * time.Second) defer oneSecond.Stop() playbackTimeout := time.After(playbackMaxWait) playbackWaitLoop: for { if xbmc.PlayerIsPlaying() { break playbackWaitLoop } select { case <-playbackTimeout: btp.log.Info("Playback was unable to start after %d seconds. Aborting...", playbackMaxWait) btp.bufferEvents.Broadcast(errors.New("Playback was unable to start before timeout.")) return case <-oneSecond.C: ga.TrackEvent("player", "waiting_playback", btp.torrentName, -1) } } ga.TrackTiming("player", "buffer_time_perceived", int(time.Now().Sub(start).Seconds()*1000), "") btp.log.Info("Playback loop") playingTicker := time.NewTicker(60 * time.Second) defer playingTicker.Stop() playbackLoop: for { if xbmc.PlayerIsPlaying() == false { break playbackLoop } select { case <-playingTicker.C: ga.TrackEvent("player", "playing", btp.torrentName, -1) case <-oneSecond.C: } } ga.TrackEvent("player", "stop", btp.torrentName, -1) ga.TrackTiming("player", "watched_time", int(time.Now().Sub(start).Seconds()*1000), "") }
func (btp *BTPlayer) bufferDialog() { halfSecond := time.NewTicker(500 * time.Millisecond) defer halfSecond.Stop() oneSecond := time.NewTicker(1 * time.Second) defer oneSecond.Stop() for { select { case <-halfSecond.C: if btp.dialogProgress.IsCanceled() { btp.log.Info("User cancelled the buffering") go ga.TrackEvent("player", "buffer_canceled", btp.torrentName, -1) btp.bufferEvents.Broadcast(errors.New("user canceled the buffering")) return } case <-oneSecond.C: bufferProgress := float64(0) btp.bufferPiecesProgressLock.Lock() if len(btp.bufferPiecesProgress) > 0 { totalProgress := float64(0) btp.piecesProgress(btp.bufferPiecesProgress) for _, v := range btp.bufferPiecesProgress { totalProgress += v } bufferProgress = totalProgress / float64(len(btp.bufferPiecesProgress)) } btp.bufferPiecesProgressLock.Unlock() status := btp.torrentHandle.Status(uint(libtorrent.Torrent_handleQuery_name)) line1, line2, line3 := btp.statusStrings(bufferProgress, status) btp.dialogProgress.Update(int(bufferProgress*100.0), line1, line2, line3) if bufferProgress >= 1 { btp.bufferEvents.Signal() return } } } }
func (btp *BTPlayer) onMetadataReceived() { btp.log.Info("Metadata received.") btp.torrentHandle.Pause() defer btp.torrentHandle.Resume() btp.torrentName = btp.torrentHandle.Status(uint(0)).GetName() go ga.TrackEvent("player", "metadata_received", btp.torrentName, -1) btp.torrentInfo = btp.torrentHandle.Torrent_file() if btp.diskStatus != nil { btp.log.Info("Checking for sufficient space on %s...", btp.bts.config.DownloadPath) torrentSize := btp.torrentInfo.Total_size() if btp.diskStatus.Free < torrentSize { btp.log.Info("Unsufficient free space on %s. Has %d, needs %d.", btp.bts.config.DownloadPath, btp.diskStatus.Free, torrentSize) xbmc.Notify("Pulsar", "Not enough space available on the download path.", config.AddonIcon()) btp.bufferEvents.Broadcast(errors.New("Not enough space on download destination.")) return } } btp.biggestFile = btp.findBiggestFile() btp.log.Info("Biggest file: %s", btp.biggestFile.GetPath()) btp.log.Info("Setting piece priorities") pieceLength := float64(btp.torrentInfo.Piece_length()) startPiece, endPiece, _ := btp.getFilePiecesAndOffset(btp.biggestFile) startLength := float64(endPiece-startPiece) * float64(pieceLength) * startBufferPercent if startLength < startBufferMinSize { startLength = startBufferMinSize } startBufferPieces := int(math.Ceil(startLength / pieceLength)) // Prefer a fixed size, since metadata are very rarely over endPiecesSize=10MB // anyway. endBufferPieces := int(math.Ceil(float64(endBufferSize) / pieceLength)) piecesPriorities := libtorrent.NewStd_vector_int() defer libtorrent.DeleteStd_vector_int(piecesPriorities) btp.bufferPiecesProgressLock.Lock() defer btp.bufferPiecesProgressLock.Unlock() // Properly set the pieces priority vector curPiece := 0 for _ = 0; curPiece < startPiece; curPiece++ { piecesPriorities.Add(0) } for _ = 0; curPiece < startPiece+startBufferPieces; curPiece++ { // get this part piecesPriorities.Add(1) btp.bufferPiecesProgress[curPiece] = 0 btp.torrentHandle.Set_piece_deadline(curPiece, 0, 0) } for _ = 0; curPiece < endPiece-endBufferPieces; curPiece++ { piecesPriorities.Add(1) } for _ = 0; curPiece <= endPiece; curPiece++ { // get this part piecesPriorities.Add(7) btp.bufferPiecesProgress[curPiece] = 0 btp.torrentHandle.Set_piece_deadline(curPiece, 0, 0) } numPieces := btp.torrentInfo.Num_pieces() for _ = 0; curPiece < numPieces; curPiece++ { piecesPriorities.Add(0) } btp.torrentHandle.Prioritize_pieces(piecesPriorities) }