Example #1
0
func AddSegmentToSchedule(s *datastore.Schedule, bs BroadcastSegment) error {
	playlistBlob := datastore.Blob{s.CacheKey()}

	if s.MediaPlaylist == nil { // If no playlist exists
		s.MediaPlaylist, _ = m3u8.NewMediaPlaylist(100, 100) // alloc a new one
		if plistData, err := playlistBlob.GetBuffer(); err == nil {
			s.MediaPlaylist.Decode(*plistData, false)
		}
	}

	err := s.MediaPlaylist.Append(bs.MediaURL, bs.TargetDuration, bs.Title) // append the item
	if err != nil {
		s.MediaPlaylist.Slide(bs.MediaURL, bs.TargetDuration, bs.Title) // or slide if playlist full
	}
	s.MediaPlaylist.SetProgramDateTime(time.Now())

	if bs.Discontinuity { // If the video format is different than the last segment
		if err := s.MediaPlaylist.SetDiscontinuity(); err != nil { // let the player know
			return err
		}
	}

	if err := playlistBlob.Set(s.PlaylistData()); err != nil {
		return err
	}

	return nil
}
Example #2
0
func HandleDeleteProgram(s *datastore.Schedule, w http.ResponseWriter, r *http.Request) {
	urlComps := strings.Split(r.URL.Path, "/")
	if len(urlComps) < 2 {
		http.Error(w, "Cannot delete schedule", 400)
		return
	}

	programId := urlComps[1]
	programs, err := s.ListPrograms(1)
	if err != nil {
		http.Error(w, "Error finding programs", 500)
		return
	}

	for i := range programs {
		program := programs[i]
		if program.UUID == programId {
			err := s.DeleteProgram(program)
			if err != nil {
				http.Error(w, "Error deleting program", 500)
			}
			return
		}
	}

	http.Error(w, "Program not found", 404)
}
Example #3
0
func Start(s *datastore.Schedule) {
	log.Println("Checking if ready to play", s.Id, "channel")
	if ok := s.ReadyToPlay(); !ok {
		log.Println("Not ready to play")
		return
	}

	for err := runCurrentProgram(s); err == nil; err = runCurrentProgram(s) {
	}
}
Example #4
0
func HandleListPrograms(s *datastore.Schedule, w http.ResponseWriter, r *http.Request) {
	programs, err := s.ListPrograms(1)
	if err != nil {
		http.Error(w, "Error retrieving schedule for channel", 500)
		return
	}

	w.Header().Add("Content-Type", "application/json")
	json.NewEncoder(w).Encode(programs)
}
Example #5
0
func HandleAddProgram(s *datastore.Schedule, w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)

	if _, err := dec.Token(); err != nil {
		http.Error(w, "Error parsing program", 400)
		return
	}

	var p datastore.Program
	for dec.More() {
		if err := dec.Decode(&p); err != nil {
			http.Error(w, "Error parsing program instance", 400)
			return
		}
		s.AddProgram(p)
	}

	HandleListPrograms(s, w, r)
}
Example #6
0
func runCurrentProgram(s *datastore.Schedule) error {
	cp, err := s.CurrentProgram()
	if err != nil {
		log.Println("Error fetching current program:", s.Id, err)
		return err
	}

	// Rewrite the start_at parameter to resume position from playback counter
	params := cp.Parameters
	pc := cp.PlaybackCounter(s.Id)
	startAt, _ := params["start_at"]
	startParam, parseErr := strconv.ParseInt(startAt, 10, 64)
	if parseErr != nil {
		startParam = 0
	}

	newStart := startParam + pc
	params["start_at"] = fmt.Sprintf("%d", newStart)

	// now we run the function
	pipeline := RunPipe(cp.FunctionId, cp.UUID, params)
	checkTicker := time.Tick(100 * time.Millisecond)
	pipeline.Close()

	cancelStillPlaying := make(chan int, 3)
	go func() {
		timeout := time.Tick(1 * time.Second)
		s.StillPlaying() // lock schedule playback
		for {
			select {
			case <-cancelStillPlaying:
				return
			case <-timeout:
				s.StillPlaying()
			}
		}
	}()
	cancelPlayingMarker := func() { cancelStillPlaying <- 0 }
	defer cancelPlayingMarker()

	startDiscontinuityWritten := false // always write discontinuity when starting
	segmentTimer := time.After(1 * time.Nanosecond)

	for pipeout := pipeline.MediaChannel(); true; {
		select {
		case <-segmentTimer: // if it's time to send the next segment
			v, more := <-pipeout
			segment := v.Segment
			segmentDuration := time.Duration(segment.TargetDuration * 1000000)
			segmentTimer = time.After(segmentDuration * time.Microsecond)
			cp.IncrementPlaybackCounter(s.Id)
			if !more {
				cp.ResetPlaybackCounter(s.Id)
				return nil
			}

			if !startDiscontinuityWritten {
				segment.Discontinuity = true
				startDiscontinuityWritten = true
			}

			if err := AddSegmentToSchedule(s, segment); err != nil {
				log.Println("Error adding new segment to schedule:", err)
			}
		case <-checkTicker: // if it's time to check for the next program
			nextProgramStart, err := s.NextProgramStart()
			if err == nil && time.Now().After(nextProgramStart) {
				s.PopCurrentProgram()
				cp.ResetPlaybackCounter(s.Id)
				return nil
			}
		}
	}

	return nil
}