1// PlaybackManager.cpp
2
3
4#include <algorithm>
5#include <stdio.h>
6
7#include <Message.h>
8#include <OS.h>
9#include <Window.h>
10
11#include "EventQueue.h"
12#include "MessageEvent.h"
13#include "PlaybackListener.h"
14
15#include "PlaybackManager.h"
16
17
18using namespace std;
19
20
21//#define TRACE_NODE_MANAGER
22#ifdef TRACE_NODE_MANAGER
23#	define TRACE(x...)	printf(x)
24#	define ERROR(x...)	fprintf(stderr, x)
25#else
26#	define TRACE(x...)
27#	define ERROR(x...)	fprintf(stderr, x)
28#endif
29
30
31void
32tdebug(const char* str)
33{
34	TRACE("[%lx, %lld] ", find_thread(NULL), system_time());
35	TRACE(str);
36}
37
38
39#define SUPPORT_SPEED_CHANGES 0
40
41
42struct PlaybackManager::PlayingState {
43	int64		start_frame;
44	int64		end_frame;
45	int64		frame_count;
46	int64		first_visible_frame;
47	int64		last_visible_frame;
48	int64		max_frame_count;
49	int32		play_mode;
50	int32		loop_mode;
51	bool		looping_enabled;
52	bool		is_seek_request;
53	int64		current_frame;			// Playlist frame
54	int64		range_index;			// playing range index of current_frame
55	int64		activation_frame;		// absolute video frame
56
57	PlayingState()
58	{
59	}
60
61	PlayingState(const PlayingState& other)
62		:
63		start_frame(other.start_frame),
64		end_frame(other.end_frame),
65		frame_count(other.frame_count),
66		first_visible_frame(other.first_visible_frame),
67		last_visible_frame(other.last_visible_frame),
68		max_frame_count(other.max_frame_count),
69		play_mode(other.play_mode),
70		loop_mode(other.loop_mode),
71		looping_enabled(other.looping_enabled),
72		is_seek_request(false),
73		current_frame(other.current_frame),
74		range_index(other.range_index),
75		activation_frame(other.activation_frame)
76	{
77	}
78};
79
80
81#if SUPPORT_SPEED_CHANGES
82struct PlaybackManager::SpeedInfo {
83	int64		activation_frame;		// absolute video frame
84	bigtime_t	activation_time;		// performance time
85	float		speed;					// speed to be used for calculations,
86										// is 1.0 if not playing
87	float		set_speed;				// speed set by the user
88};
89#endif
90
91
92// #pragma mark - PlaybackManager
93
94
95PlaybackManager::PlaybackManager()
96	:
97	BLooper("playback manager"),
98	fStates(10),
99	fSpeeds(10),
100	fCurrentAudioTime(0),
101	fCurrentVideoTime(0),
102	fPerformanceTime(0),
103	fPerformanceTimeEvent(NULL),
104	fFrameRate(1.0),
105	fStopPlayingFrame(-1),
106	fListeners(),
107	fNoAudio(false)
108{
109	Run();
110}
111
112
113PlaybackManager::~PlaybackManager()
114{
115	Cleanup();
116}
117
118
119void
120PlaybackManager::Init(float frameRate, bool initPerformanceTimes,
121	int32 loopingMode, bool loopingEnabled, float playbackSpeed,
122	int32 playMode, int32 currentFrame)
123{
124	// cleanup first
125	Cleanup();
126
127	// set the new frame rate
128	fFrameRate = frameRate;
129	if (initPerformanceTimes) {
130		fCurrentAudioTime = 0;
131		fCurrentVideoTime = 0;
132		fPerformanceTime = 0;
133	}
134	fStopPlayingFrame = -1;
135
136#if SUPPORT_SPEED_CHANGES
137	// set up the initial speed
138	SpeedInfo* speed = new SpeedInfo;
139	speed->activation_frame = 0;
140	speed->activation_time = 0;
141	speed->speed = playbackSpeed;
142	speed->set_speed = playbackSpeed;
143	_PushSpeedInfo(speed);
144#endif
145
146	// set up the initial state
147	PlayingState* state = new PlayingState;
148	state->frame_count = Duration();
149	state->start_frame = 0;
150	state->end_frame = 0;
151	state->first_visible_frame = 0;
152	state->last_visible_frame = 0;
153	state->max_frame_count = state->frame_count;
154
155	state->play_mode = MODE_PLAYING_PAUSED_FORWARD;
156	state->loop_mode = loopingMode;
157	state->looping_enabled = loopingEnabled;
158	state->is_seek_request = false;
159	state->current_frame = currentFrame;
160	state->activation_frame = 0;
161	fStates.AddItem(state);
162
163	TRACE("initial state pushed: state count: %ld\n", fStates.CountItems());
164
165	// notify the listeners
166	NotifyPlayModeChanged(PlayMode());
167	NotifyLoopModeChanged(LoopMode());
168	NotifyLoopingEnabledChanged(IsLoopingEnabled());
169	NotifyVideoBoundsChanged(VideoBounds());
170	NotifyFPSChanged(FramesPerSecond());
171	NotifySpeedChanged(Speed());
172	NotifyCurrentFrameChanged(CurrentFrame());
173	SetPlayMode(playMode);
174}
175
176
177void
178PlaybackManager::Cleanup()
179{
180	if (EventQueue::Default().RemoveEvent(fPerformanceTimeEvent))
181		delete fPerformanceTimeEvent;
182	fPerformanceTimeEvent = NULL;
183	// delete states
184	for (int32 i = 0; PlayingState* state = _StateAt(i); i++)
185		delete state;
186	fStates.MakeEmpty();
187
188#if SUPPORT_SPEED_CHANGES
189	// delete speed infos
190	for (int32 i = 0; SpeedInfo* speed = _SpeedInfoAt(i); i++)
191		delete speed;
192	fSpeeds.MakeEmpty();
193#endif
194}
195
196
197void
198PlaybackManager::MessageReceived(BMessage* message)
199{
200	switch (message->what) {
201		case MSG_EVENT:
202		{
203			if (fPerformanceTimeEvent == NULL) {
204				// Stale event message. There is a natural race
205				// condition when removing the event from the queue,
206				// it may have already fired, but we have not processed
207				// the message yet. Simply ignore the event.
208				break;
209			}
210
211			bigtime_t eventTime;
212			message->FindInt64("time", &eventTime);
213//			bigtime_t now = system_time();
214			fPerformanceTimeEvent = NULL;
215
216//			SetPerformanceTime(TimeForRealTime(now));
217			SetPerformanceTime(TimeForRealTime(eventTime));
218//TRACE("MSG_EVENT: rt: %lld, pt: %lld\n", now, fPerformanceTime);
219//printf("MSG_EVENT: et: %lld, rt: %lld, pt: %lld\n", eventTime, now, fPerformanceTime);
220			break;
221		}
222
223		case MSG_PLAYBACK_FORCE_UPDATE:
224		{
225			int64 frame;
226			if (message->FindInt64("frame", &frame) < B_OK)
227				frame = CurrentFrame();
228			SetCurrentFrame(frame);
229			break;
230		}
231
232		case MSG_PLAYBACK_SET_RANGE:
233		{
234			int64 startFrame = _LastState()->start_frame;
235			int64 endFrame = _LastState()->end_frame;
236			message->FindInt64("start frame", &startFrame);
237			message->FindInt64("end frame", &endFrame);
238			if (startFrame != _LastState()->start_frame
239				|| endFrame != _LastState()->end_frame) {
240				PlayingState* state = new PlayingState(*_LastState());
241				state->start_frame = startFrame;
242				state->end_frame = endFrame;
243				_PushState(state, true);
244			}
245			break;
246		}
247
248		case MSG_PLAYBACK_SET_VISIBLE:
249		{
250			int64 startFrame = _LastState()->first_visible_frame;
251			int64 endFrame = _LastState()->last_visible_frame;
252			message->FindInt64("start frame", &startFrame);
253			message->FindInt64("end frame", &endFrame);
254			if (startFrame != _LastState()->first_visible_frame
255				|| endFrame != _LastState()->last_visible_frame) {
256				PlayingState* state = new PlayingState(*_LastState());
257				state->first_visible_frame = startFrame;
258				state->last_visible_frame = endFrame;
259				_PushState(state, true);
260			}
261			break;
262		}
263
264		case MSG_PLAYBACK_SET_LOOP_MODE:
265		{
266			int32 loopMode = _LastState()->loop_mode;
267			message->FindInt32("mode", &loopMode);
268			if (loopMode != _LastState()->loop_mode) {
269				PlayingState* state = new PlayingState(*_LastState());
270				state->loop_mode = loopMode;
271				_PushState(state, true);
272			}
273			break;
274		}
275
276		// other messages
277		default:
278			BLooper::MessageReceived(message);
279			break;
280	}
281}
282
283// #pragma mark - playback control
284
285
286void
287PlaybackManager::StartPlaying(bool atBeginning)
288{
289	TRACE("PlaybackManager::StartPlaying()\n");
290	int32 playMode = PlayMode();
291	if (playMode == MODE_PLAYING_PAUSED_FORWARD)
292		SetPlayMode(MODE_PLAYING_FORWARD, !atBeginning);
293	if (playMode == MODE_PLAYING_PAUSED_BACKWARD)
294		SetPlayMode(MODE_PLAYING_BACKWARD, !atBeginning);
295}
296
297
298void
299PlaybackManager::StopPlaying()
300{
301	TRACE("PlaybackManager::StopPlaying()\n");
302	int32 playMode = PlayMode();
303	if (playMode == MODE_PLAYING_FORWARD)
304		SetPlayMode(MODE_PLAYING_PAUSED_FORWARD, true);
305	if (playMode == MODE_PLAYING_BACKWARD)
306		SetPlayMode(MODE_PLAYING_PAUSED_BACKWARD, true);
307}
308
309
310void
311PlaybackManager::TogglePlaying(bool atBeginning)
312{
313	// playmodes (paused <-> playing) are the negative of each other
314	SetPlayMode(-PlayMode(), !atBeginning);
315}
316
317
318void
319PlaybackManager::PausePlaying()
320{
321	if (PlayMode() > 0)
322		TogglePlaying();
323}
324
325
326bool
327PlaybackManager::IsPlaying() const
328{
329	return (PlayMode() > 0);
330}
331
332
333int32
334PlaybackManager::PlayMode() const
335{
336	if (!_LastState())
337		return MODE_PLAYING_PAUSED_FORWARD;
338	return _LastState()->play_mode;
339}
340
341
342int32
343PlaybackManager::LoopMode() const
344{
345	if (!_LastState())
346		return LOOPING_ALL;
347	return _LastState()->loop_mode;
348}
349
350
351bool
352PlaybackManager::IsLoopingEnabled() const
353{
354	if (!_LastState())
355		return true;
356	return _LastState()->looping_enabled;
357}
358
359
360int64
361PlaybackManager::CurrentFrame() const
362{
363	return PlaylistFrameAtFrame(FrameForTime(fPerformanceTime));
364}
365
366
367float
368PlaybackManager::Speed() const
369{
370#if SUPPORT_SPEED_CHANGES
371	if (!_LastState())
372		return 1.0;
373	return _LastSpeedInfo()->set_speed;
374#else
375	return 1.0;
376#endif
377}
378
379
380void
381PlaybackManager::SetFramesPerSecond(float framesPerSecond)
382{
383	if (framesPerSecond != fFrameRate) {
384		fFrameRate = framesPerSecond;
385		NotifyFPSChanged(fFrameRate);
386	}
387}
388
389
390float
391PlaybackManager::FramesPerSecond() const
392{
393	return fFrameRate;
394}
395
396
397void
398PlaybackManager::FrameDropped() const
399{
400	NotifyFrameDropped();
401}
402
403
404void
405PlaybackManager::DurationChanged()
406{
407	if (!_LastState())
408		return;
409	int32 frameCount = Duration();
410	if (frameCount != _LastState()->frame_count) {
411		PlayingState* state = new PlayingState(*_LastState());
412		state->frame_count = frameCount;
413		state->max_frame_count = frameCount;
414
415		_PushState(state, true);
416	}
417}
418
419/*! Creates a new playing state that equals the previous one aside from
420	its current frame which is set to /frame/.
421	The new state will be activated as soon as possible. */
422void
423PlaybackManager::SetCurrentFrame(int64 frame)
424{
425	if (CurrentFrame() == frame) {
426		NotifySeekHandled(frame);
427		return;
428	}
429	PlayingState* newState = new PlayingState(*_LastState());
430	newState->current_frame = frame;
431	newState->is_seek_request = true;
432	_PushState(newState, false);
433}
434
435
436/*!	Creates a new playing state that equals the previous one aside from
437	its playing mode which is set to /mode/.
438	The new state will be activated as soon as possible.
439	If /continuePlaying/ is true and the new state is a `not stopped'
440	state, playing continues at the frame the last state reaches when the
441	new state becomes active (or the next frame in the playing range).
442	If /continuePlaying/ is false, playing starts at the beginning of the
443	playing range (taking the playing direction into consideration). */
444void
445PlaybackManager::SetPlayMode(int32 mode, bool continuePlaying)
446{
447//printf("PlaybackManager::SetPlayMode(%ld, %d)\n", mode, continuePlaying);
448	PlayingState* newState = new PlayingState(*_LastState());
449	newState->play_mode = mode;
450	// Jump to the playing start frame if we should not continue, where we
451	// stop.
452	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
453		newState->current_frame = _PlayingStartFrameFor(newState);
454	_PushState(newState, continuePlaying);
455	NotifyPlayModeChanged(mode);
456}
457
458
459/*!	Creates a new playing state that equals the previous one aside from
460	its loop mode which is set to /mode/.
461	The new state will be activated as soon as possible.
462	If /continuePlaying/ is true and the new state is a `not stopped'
463	state, playing continues at the frame the last state reaches when the
464	new state becomes active (or the next frame in the playing range).
465	If /continuePlaying/ is false, playing starts at the beginning of the
466	playing range (taking the playing direction into consideration). */
467void
468PlaybackManager::SetLoopMode(int32 mode, bool continuePlaying)
469{
470	PlayingState* newState = new PlayingState(*_LastState());
471	newState->loop_mode = mode;
472	// Jump to the playing start frame if we should not continue, where we
473	// stop.
474	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
475		newState->current_frame = _PlayingStartFrameFor(newState);
476	_PushState(newState, continuePlaying);
477	NotifyLoopModeChanged(mode);
478}
479
480
481/*	Creates a new playing state that equals the previous one aside from
482	its looping enabled flag which is set to /enabled/.
483	The new state will be activated as soon as possible.
484	If /continuePlaying/ is true and the new state is a `not stopped'
485	state, playing continues at the frame the last state reaches when the
486	new state becomes active (or the next frame in the playing range).
487	If /continuePlaying/ is false, playing starts at the beginning of the
488	playing range (taking the playing direction into consideration). */
489void
490PlaybackManager::SetLoopingEnabled(bool enabled, bool continuePlaying)
491{
492	PlayingState* newState = new PlayingState(*_LastState());
493	newState->looping_enabled = enabled;
494	// Jump to the playing start frame if we should not continue, where we
495	// stop.
496	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
497		newState->current_frame = _PlayingStartFrameFor(newState);
498	_PushState(newState, continuePlaying);
499	NotifyLoopingEnabledChanged(enabled);
500}
501
502
503void
504PlaybackManager::SetSpeed(float speed)
505{
506#if SUPPORT_SPEED_CHANGES
507	SpeedInfo* lastSpeed = _LastSpeedInfo();
508	if (speed != lastSpeed->set_speed) {
509		SpeedInfo* info = new SpeedInfo(*lastSpeed);
510		info->activation_frame = NextFrame();
511		info->set_speed = speed;
512		if (_PlayingDirectionFor(_StateAtFrame(info->activation_frame)) != 0)
513			info->speed = info->set_speed;
514		else
515			info->speed = 1.0;
516		_PushSpeedInfo(info);
517	}
518#endif
519}
520
521
522// #pragma mark -
523
524
525/*!	Returns the first frame at which a new playing state could become active,
526	that is the first frame for that neither the audio nor the video producer
527	have fetched data.*/
528int64
529PlaybackManager::NextFrame() const
530{
531	if (fNoAudio)
532		return FrameForTime(fCurrentVideoTime - 1) + 1;
533
534	return FrameForTime(max((bigtime_t)fCurrentAudioTime,
535		(bigtime_t)fCurrentVideoTime) - 1) + 1;
536}
537
538
539//! Returns the Playlist frame for NextFrame().
540int64
541PlaybackManager::NextPlaylistFrame() const
542{
543	return PlaylistFrameAtFrame(NextFrame());
544}
545
546
547int64
548PlaybackManager::FirstPlaybackRangeFrame()
549{
550	PlayingState* state = _StateAtFrame(CurrentFrame());
551	switch (state->loop_mode) {
552		case LOOPING_RANGE:
553			return state->start_frame;
554		case LOOPING_SELECTION:
555			// TODO: ...
556			return 0;
557		case LOOPING_VISIBLE:
558			return state->first_visible_frame;
559
560		case LOOPING_ALL:
561		default:
562			return 0;
563	}
564}
565
566
567int64
568PlaybackManager::LastPlaybackRangeFrame()
569{
570	PlayingState* state = _StateAtFrame(CurrentFrame());
571	switch (state->loop_mode) {
572		case LOOPING_RANGE:
573			return state->end_frame;
574		case LOOPING_SELECTION:
575			// TODO: ...
576			return state->frame_count - 1;
577		case LOOPING_VISIBLE:
578			return state->last_visible_frame;
579
580		case LOOPING_ALL:
581		default:
582			return state->frame_count - 1;
583	}
584}
585
586
587// #pragma mark -
588
589
590int64
591PlaybackManager::StartFrameAtFrame(int64 frame)
592{
593	return _StateAtFrame(frame)->start_frame;
594}
595
596
597int64
598PlaybackManager::StartFrameAtTime(bigtime_t time)
599{
600	return _StateAtTime(time)->start_frame;
601}
602
603
604int64
605PlaybackManager::EndFrameAtFrame(int64 frame)
606{
607	return _StateAtTime(frame)->end_frame;
608}
609
610
611int64
612PlaybackManager::EndFrameAtTime(bigtime_t time)
613{
614	return _StateAtTime(time)->end_frame;
615}
616
617
618int64
619PlaybackManager::FrameCountAtFrame(int64 frame)
620{
621	return _StateAtTime(frame)->frame_count;
622}
623
624
625int64
626PlaybackManager::FrameCountAtTime(bigtime_t time)
627{
628	return _StateAtTime(time)->frame_count;
629}
630
631
632int32
633PlaybackManager::PlayModeAtFrame(int64 frame)
634{
635	return _StateAtTime(frame)->play_mode;
636}
637
638
639int32
640PlaybackManager::PlayModeAtTime(bigtime_t time)
641{
642	return _StateAtTime(time)->play_mode;
643}
644
645
646int32
647PlaybackManager::LoopModeAtFrame(int64 frame)
648{
649	return _StateAtTime(frame)->loop_mode;
650}
651
652
653int32
654PlaybackManager::LoopModeAtTime(bigtime_t time)
655{
656	return _StateAtTime(time)->loop_mode;
657}
658
659
660/*!	Returns which Playlist frame should be displayed at (performance) video
661	frame /frame/. Additionally the playing direction (0, 1, -1) is returned,
662	as well as if a new playing state becomes active with this frame.
663	A new playing state is installed, if either some data directly concerning
664	the playing (play mode, loop mode, playing ranges, selection...) or the
665	Playlist has changed. */
666int64
667PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection,
668	bool& newState) const
669{
670//TRACE("PlaybackManager::PlaylistFrameAtFrame(%lld)\n", frame);
671	PlayingState* state = _StateAtFrame(frame);
672	newState = (state->activation_frame == frame);
673	playingDirection = _PlayingDirectionFor(state);
674//TRACE("playing state: activation frame: %lld, current frame: %lld, dir: %ld\n",
675//state->activation_frame, state->current_frame, state->play_mode);
676	// The first part of the index calculation is invariable for a state. We
677	// could add it to the state data.
678	int64 result = state->current_frame;
679	if (playingDirection != 0) {
680//		int64 index = _RangeFrameForFrame(state, state->current_frame)
681		int64 index = state->range_index
682			+ (frame - state->activation_frame) * playingDirection;
683		result = _FrameForRangeFrame(state, index);
684	}
685//TRACE("PlaybackManager::PlaylistFrameAtFrame() done: %lld\n", result);
686//printf("PlaybackManager::PlaylistFrameAtFrame(%lld): %lld, direction: %ld\n",
687//	frame, result, playingDirection);
688	return result;
689}
690
691
692int64
693PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection) const
694{
695	bool newState;
696	return PlaylistFrameAtFrame(frame, playingDirection, newState);
697}
698
699
700int64
701PlaybackManager::PlaylistFrameAtFrame(int64 frame) const
702{
703	bool newState;
704	int32 playingDirection;
705	return PlaylistFrameAtFrame(frame, playingDirection, newState);
706}
707
708
709/*!	Returns the index of the next frame after /startFrame/ at which a
710	playing state or speed change occurs or /endFrame/, if this happens to be
711	earlier. */
712int64
713PlaybackManager::NextChangeFrame(int64 startFrame, int64 endFrame) const
714{
715	int32 startIndex = _IndexForFrame(startFrame);
716	int32 endIndex = _IndexForFrame(endFrame);
717	if (startIndex < endIndex)
718		endFrame = _StateAt(startIndex + 1)->activation_frame;
719#if SUPPORT_SPEED_CHANGES
720	startIndex = _SpeedInfoIndexForFrame(startFrame);
721	endIndex = _SpeedInfoIndexForFrame(endFrame);
722	if (startIndex < endIndex)
723		endFrame = _SpeedInfoAt(startIndex + 1)->activation_frame;
724#endif
725	return endFrame;
726}
727
728
729/*!	Returns the next time after /startTime/ at which a playing state or
730	speed change occurs or /endTime/, if this happens to be earlier. */
731bigtime_t
732PlaybackManager::NextChangeTime(bigtime_t startTime, bigtime_t endTime) const
733{
734	int32 startIndex = _IndexForTime(startTime);
735	int32 endIndex = _IndexForTime(endTime);
736	if (startIndex < endIndex)
737		endTime = TimeForFrame(_StateAt(startIndex + 1)->activation_frame);
738#if SUPPORT_SPEED_CHANGES
739	startIndex = _SpeedInfoIndexForTime(startTime);
740	endIndex = _SpeedInfoIndexForTime(endTime);
741	if (startIndex < endIndex)
742		endTime = TimeForFrame(_SpeedInfoAt(startIndex + 1)->activation_frame);
743#endif
744	return endTime;
745}
746
747
748/*!	Returns a contiguous Playlist frame interval for a given frame interval.
749	The returned interval may be smaller than the supplied one. Therefore
750	the supplied /endFrame/ is adjusted.
751	The value written to /xEndFrame/ is the first frame that doesn't belong
752	to the interval. /playingDirection/ may either be -1 for backward,
753	1 for forward or 0 for not playing. */
754void
755PlaybackManager::GetPlaylistFrameInterval(int64 startFrame, int64& endFrame,
756	int64& xStartFrame, int64& xEndFrame, int32& playingDirection) const
757{
758	// limit the interval to one state and speed info
759	endFrame = NextChangeFrame(startFrame, endFrame);
760	// init return values
761	xStartFrame = PlaylistFrameAtFrame(startFrame);
762	xEndFrame = xStartFrame;
763	playingDirection = _PlayingDirectionFor(_StateAtFrame(startFrame));
764	// Step through the interval and check whether the respective Playlist
765	// frames belong to the Playlist interval.
766	bool endOfInterval = false;
767	int64 intervalLength = 0;
768	while (startFrame < endFrame && !endOfInterval) {
769		intervalLength++;
770		startFrame++;
771		xEndFrame += playingDirection;
772		endOfInterval = (PlaylistFrameAtFrame(startFrame) != xEndFrame);
773	}
774	// order the interval bounds, if playing backwards
775	if (xStartFrame > xEndFrame) {
776		swap(xStartFrame, xEndFrame);
777		xStartFrame++;
778		xEndFrame++;
779	}
780}
781
782
783/*!	The same as GetPlaylistFrameInterval() just for time instead of frame
784	intervals. Note, that /startTime/ and /endTime/ measure
785	performance times whereas /xStartTime/ and /xEndTime/ specifies an
786	Playlist time interval. In general it does not hold
787	xEndTime - xStartTime == endTime - startTime, even if playing (think
788	of a playing speed != 1.0). */
789void
790PlaybackManager::GetPlaylistTimeInterval(bigtime_t startTime,
791	bigtime_t& endTime, bigtime_t& xStartTime, bigtime_t& xEndTime,
792	float& playingSpeed) const
793{
794	// Get the frames that bound the given time interval. The end frame might
795	// be greater than necessary, but that doesn't harm.
796	int64 startFrame = FrameForTime(startTime);
797	int64 endFrame = FrameForTime(endTime) + 1;
798#if SUPPORT_SPEED_CHANGES
799	SpeedInfo* info = _SpeedInfoForFrame(startFrame)->speed;
800#endif
801	// Get the Playlist frame interval that belongs to the frame interval.
802	int64 xStartFrame;
803	int64 xEndFrame;
804	int32 playingDirection;
805	GetPlaylistFrameInterval(startFrame, endFrame, xStartFrame, xEndFrame,
806		playingDirection);
807	// Calculate the performance time interval end/length.
808	bigtime_t endTimeForFrame = TimeForFrame(endFrame);
809	endTime = min(endTime, endTimeForFrame);
810	bigtime_t intervalLength = endTime - startTime;
811
812	// Finally determine the time bounds for the Playlist interval (depending
813	// on the playing direction).
814	switch (playingDirection) {
815		// forward
816		case 1:
817		{
818#if SUPPORT_SPEED_CHANGES
819// TODO: The current method does not handle the times the same way.
820//       It may happen, that for the same performance time different
821//       Playlist times (within a frame) are returned when passing it
822//       one time as a start time and another time as an end time.
823			xStartTime = PlaylistTimeForFrame(xStartFrame)
824				+ bigtime_t(double(startTime - TimeForFrame(startFrame))
825					* info->speed);
826			xEndTime = xStartTime
827				+ bigtime_t((double)intervalLength * info->speed);
828#else
829			xStartTime = PlaylistTimeForFrame(xStartFrame)
830				+ startTime - TimeForFrame(startFrame);
831			xEndTime = xStartTime + intervalLength;
832#endif
833			break;
834		}
835		// backward
836		case -1:
837		{
838#if SUPPORT_SPEED_CHANGES
839			xEndTime = PlaylistTimeForFrame(xEndFrame)
840				- bigtime_t(double(startTime - TimeForFrame(startFrame))
841					* info->speed);
842			xStartTime = xEndTime
843				- bigtime_t((double)intervalLength * info->speed);
844#else
845			xEndTime = PlaylistTimeForFrame(xEndFrame)
846				- startTime + TimeForFrame(startFrame);
847			xStartTime = xEndTime - intervalLength;
848#endif
849			break;
850		}
851		// not playing
852		case 0:
853		default:
854			xStartTime = PlaylistTimeForFrame(xStartFrame);
855			xEndTime = xStartTime;
856			break;
857	}
858#if SUPPORT_SPEED_CHANGES
859	playingSpeed = (float)playingDirection * info->speed;
860#else
861	playingSpeed = (float)playingDirection;
862#endif
863}
864
865
866/*!	Returns the frame that is being performed at the supplied time.
867	It holds TimeForFrame(frame) <= time < TimeForFrame(frame + 1). */
868int64
869PlaybackManager::FrameForTime(bigtime_t time) const
870{
871//TRACE("PlaybackManager::FrameForTime(%lld)\n", time);
872	// In order to avoid problems caused by rounding errors, we check
873	// if for the resulting frame holds
874	// TimeForFrame(frame) <= time < TimeForFrame(frame + 1).
875#if SUPPORT_SPEED_CHANGES
876	SpeedInfo* info = _SpeedInfoForTime(time);
877if (!info) {
878	fprintf(stderr, "PlaybackManager::FrameForTime() - no SpeedInfo!\n");
879	return 0;
880}
881	int64 frame = (int64)(((double)time - info->activation_time)
882		* (double)fFrameRate * info->speed / 1000000.0)
883		+ info->activation_frame;
884
885#else
886	int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
887#endif
888	if (TimeForFrame(frame) > time)
889		frame--;
890	else if (TimeForFrame(frame + 1) <= time)
891		frame++;
892//TRACE("PlaybackManager::FrameForTime() done: %lld\n", frame);
893//printf("PlaybackManager::FrameForTime(%lld): %lld\n", time, frame);
894	return frame;
895}
896
897
898/*!	Returns the time at which the supplied frame should be performed (started
899	to be performed). */
900bigtime_t
901PlaybackManager::TimeForFrame(int64 frame) const
902{
903#if SUPPORT_SPEED_CHANGES
904	SpeedInfo* info = _SpeedInfoForFrame(frame);
905if (!info) {
906	fprintf(stderr, "PlaybackManager::TimeForFrame() - no SpeedInfo!\n");
907	return 0;
908}
909	return (bigtime_t)((double)(frame - info->activation_frame) * 1000000.0
910					   / ((double)fFrameRate * info->speed))
911 		   + info->activation_time;
912#else
913	return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
914#endif
915}
916
917
918/*!	Returns the Playlist frame for an Playlist time.
919	It holds PlaylistTimeForFrame(frame) <= time <
920	PlaylistTimeForFrame(frame + 1). */
921int64
922PlaybackManager::PlaylistFrameForTime(bigtime_t time) const
923{
924	// In order to avoid problems caused by rounding errors, we check
925	// if for the resulting frame holds
926	// PlaylistTimeForFrame(frame) <= time < PlaylistTimeForFrame(frame + 1).
927	int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
928	if (TimeForFrame(frame) > time)
929		frame--;
930	else if (TimeForFrame(frame + 1) <= time)
931		frame++;
932	return frame;
933}
934
935
936//! Returns the Playlist start time for an Playlist frame.
937bigtime_t
938PlaybackManager::PlaylistTimeForFrame(int64 frame) const
939{
940	return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
941}
942
943
944//! To be called when done with all activities concerning audio before /time/.
945void
946PlaybackManager::SetCurrentAudioTime(bigtime_t time)
947{
948	TRACE("PlaybackManager::SetCurrentAudioTime(%lld)\n", time);
949	bigtime_t lastFrameTime = _TimeForLastFrame();
950	fCurrentAudioTime = time;
951	bigtime_t newLastFrameTime = _TimeForLastFrame();
952	if (lastFrameTime != newLastFrameTime) {
953		if (fPerformanceTimeEvent == NULL) {
954			bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
955			fPerformanceTimeEvent = new MessageEvent(eventTime, this);
956			EventQueue::Default().AddEvent(fPerformanceTimeEvent);
957		}
958		_CheckStopPlaying();
959	}
960}
961
962
963//! To be called when done with all activities concerning video before /frame/.
964void
965PlaybackManager::SetCurrentVideoFrame(int64 frame)
966{
967	SetCurrentVideoTime(TimeForFrame(frame));
968}
969
970
971//! To be called when done with all activities concerning video before /time/.
972void
973PlaybackManager::SetCurrentVideoTime(bigtime_t time)
974{
975	TRACE("PlaybackManager::SetCurrentVideoTime(%lld)\n", time);
976	bigtime_t lastFrameTime = _TimeForLastFrame();
977	fCurrentVideoTime = time;
978	bigtime_t newLastFrameTime = _TimeForLastFrame();
979	if (lastFrameTime != newLastFrameTime) {
980		if (fPerformanceTimeEvent == NULL) {
981			bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
982			fPerformanceTimeEvent = new MessageEvent(eventTime, this);
983			EventQueue::Default().AddEvent(fPerformanceTimeEvent);
984		}
985		_CheckStopPlaying();
986	}
987}
988
989
990//! To be called as soon as video frame /frame/ is being performed.
991void
992PlaybackManager::SetPerformanceFrame(int64 frame)
993{
994	SetPerformanceTime(TimeForFrame(frame));
995}
996
997
998/*!	Similar to SetPerformanceFrame() just with a time instead of a frame
999	argument. */
1000void
1001PlaybackManager::SetPerformanceTime(bigtime_t time)
1002{
1003	int64 oldCurrentFrame = CurrentFrame();
1004	fPerformanceTime = time;
1005	_UpdateStates();
1006	_UpdateSpeedInfos();
1007	int64 currentFrame = CurrentFrame();
1008
1009//printf("PlaybackManager::SetPerformanceTime(%lld): %ld -> %ld\n",
1010//	time, oldCurrentFrame, currentFrame);
1011
1012	if (currentFrame != oldCurrentFrame)
1013		NotifyCurrentFrameChanged(currentFrame);
1014}
1015
1016
1017// #pragma mark - Listeners
1018
1019
1020void
1021PlaybackManager::AddListener(PlaybackListener* listener)
1022{
1023	if (!listener)
1024		return;
1025
1026	if (!Lock())
1027		return;
1028
1029	if (!fListeners.HasItem(listener) && fListeners.AddItem(listener)) {
1030		// bring listener up2date, if we have been initialized
1031		if (_LastState()) {
1032			listener->PlayModeChanged(PlayMode());
1033			listener->LoopModeChanged(LoopMode());
1034			listener->LoopingEnabledChanged(IsLoopingEnabled());
1035			listener->VideoBoundsChanged(VideoBounds());
1036			listener->FramesPerSecondChanged(FramesPerSecond());
1037			listener->SpeedChanged(Speed());
1038			listener->CurrentFrameChanged(CurrentFrame());
1039		}
1040	}
1041
1042	Unlock();
1043}
1044
1045
1046void
1047PlaybackManager::RemoveListener(PlaybackListener* listener)
1048{
1049	if (listener && Lock()) {
1050		fListeners.RemoveItem(listener);
1051		Unlock();
1052	}
1053}
1054
1055
1056void
1057PlaybackManager::NotifyPlayModeChanged(int32 mode) const
1058{
1059	for (int32 i = 0;
1060		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1061		 i++) {
1062		listener->PlayModeChanged(mode);
1063	}
1064}
1065
1066
1067void
1068PlaybackManager::NotifyLoopModeChanged(int32 mode) const
1069{
1070	for (int32 i = 0;
1071		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1072		 i++) {
1073		listener->LoopModeChanged(mode);
1074	}
1075}
1076
1077
1078void
1079PlaybackManager::NotifyLoopingEnabledChanged(bool enabled) const
1080{
1081	for (int32 i = 0;
1082		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1083		 i++) {
1084		listener->LoopingEnabledChanged(enabled);
1085	}
1086}
1087
1088
1089void
1090PlaybackManager::NotifyVideoBoundsChanged(BRect bounds) const
1091{
1092	for (int32 i = 0;
1093		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1094		 i++) {
1095		listener->VideoBoundsChanged(bounds);
1096	}
1097}
1098
1099
1100void
1101PlaybackManager::NotifyFPSChanged(float fps) const
1102{
1103	for (int32 i = 0;
1104		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1105		 i++) {
1106		listener->FramesPerSecondChanged(fps);
1107	}
1108}
1109
1110
1111void
1112PlaybackManager::NotifyCurrentFrameChanged(int64 frame) const
1113{
1114	for (int32 i = 0;
1115		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1116		 i++) {
1117		listener->CurrentFrameChanged(frame);
1118	}
1119}
1120
1121
1122void
1123PlaybackManager::NotifySpeedChanged(float speed) const
1124{
1125	for (int32 i = 0;
1126		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1127		 i++) {
1128		listener->SpeedChanged(speed);
1129	}
1130}
1131
1132
1133void
1134PlaybackManager::NotifyFrameDropped() const
1135{
1136	for (int32 i = 0;
1137		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1138		 i++) {
1139		listener->FrameDropped();
1140	}
1141}
1142
1143
1144void
1145PlaybackManager::NotifyStopFrameReached() const
1146{
1147	// not currently implemented in PlaybackListener interface
1148}
1149
1150
1151void
1152PlaybackManager::NotifySeekHandled(int64 frame) const
1153{
1154	// not currently implemented in PlaybackListener interface
1155}
1156
1157
1158void
1159PlaybackManager::PrintState(PlayingState* state)
1160{
1161	TRACE("state: activation frame: %lld, current frame: %lld, "
1162		   "start frame: %lld, end frame: %lld, frame count: %lld, "
1163		   "first visible: %lld, last visible: %lld, selection (...), "
1164		   "play mode: %ld, loop mode: %ld\n",
1165			state->activation_frame,
1166			state->current_frame,
1167			state->start_frame,
1168			state->end_frame,
1169			state->frame_count,
1170			state->first_visible_frame,
1171			state->last_visible_frame,
1172//			state->selection,
1173			state->play_mode,
1174			state->loop_mode);
1175}
1176
1177
1178void
1179PlaybackManager::PrintStateAtFrame(int64 frame)
1180{
1181	TRACE("frame %lld: ", frame);
1182	PrintState(_StateAtFrame(frame));
1183}
1184
1185
1186// #pragma mark -
1187
1188
1189/*!	Appends the supplied state to the list of states. If the state would
1190	become active at the same time as _LastState() the latter is removed
1191	and deleted. However, the activation time for the new state is adjusted,
1192	so that it is >= that of the last state and >= the current audio and
1193	video time. If the additional parameter /adjustCurrentFrame/ is true,
1194	the new state's current frame is tried to be set to the frame that is
1195	reached at the time the state will become active. In every case
1196	it is ensured that the current frame lies within the playing range
1197	(if playing). */
1198void
1199PlaybackManager::_PushState(PlayingState* state, bool adjustCurrentFrame)
1200{
1201//	if (!_LastState())
1202//		debugger("PlaybackManager::_PushState() used before Init()\n");
1203
1204TRACE("PlaybackManager::_PushState()\n");
1205	if (state == NULL)
1206		return;
1207
1208	// unset fStopPlayingFrame
1209	int64 oldStopPlayingFrame = fStopPlayingFrame;
1210	fStopPlayingFrame = -1;
1211	// get last state
1212	PlayingState* lastState = _LastState();
1213	int64 activationFrame = max(max(state->activation_frame,
1214									lastState->activation_frame),
1215								NextFrame());
1216	int64 currentFrame = 0;
1217	// remember the current frame, if necessary
1218	if (adjustCurrentFrame) {
1219		currentFrame = PlaylistFrameAtFrame(activationFrame);
1220		if (currentFrame == CurrentFrame()) {
1221			// Seems to be paused, force using the next frame
1222			currentFrame++;
1223		}
1224	}
1225	// Check whether the last state has already become active
1226	// (NOTE: We may want to keep the last state, if it is not active,
1227	//  but then the new state should become active after the last one.
1228	//  Thus we had to replace `NextFrame()' with `activationFrame'.)
1229TRACE("  state activation frame: %lld, last state activation frame: %lld, "
1230"NextFrame(): %lld, currentFrame: %lld, next currentFrame: %lld\n",
1231state->activation_frame, lastState->activation_frame, NextFrame(),
1232CurrentFrame(), currentFrame);
1233	if (lastState->activation_frame >= NextFrame()) {
1234		// it isn't -- remove it
1235		fStates.RemoveItem(fStates.CountItems() - 1);
1236TRACE("deleting last \n");
1237PrintState(lastState);
1238		_NotifySeekHandledIfNecessary(lastState);
1239		delete lastState;
1240	} else {
1241		// it is -- keep it
1242	}
1243	// adjust the new state's current frame and activation frame
1244	if (adjustCurrentFrame)
1245		state->current_frame = currentFrame;
1246	int32 playingDirection = _PlayingDirectionFor(state);
1247	if (playingDirection != 0) {
1248		state->current_frame
1249			= _NextFrameInRange(state, state->current_frame);
1250	} else {
1251		// If not playing, we check at least, if the current frame lies
1252		// within the interval [0, max_frame_count).
1253		if (state->current_frame >= state->max_frame_count)
1254			state->current_frame = state->max_frame_count - 1;
1255		if (state->current_frame < 0)
1256			state->current_frame = 0;
1257	}
1258	state->range_index = _RangeFrameForFrame(state, state->current_frame);
1259	state->activation_frame = activationFrame;
1260	fStates.AddItem(state);
1261PrintState(state);
1262TRACE("_PushState: state count: %ld\n", fStates.CountItems());
1263#if SUPPORT_SPEED_CHANGES
1264	// push a new speed info
1265	SpeedInfo* speedInfo = new SpeedInfo(*_LastSpeedInfo());
1266	if (playingDirection == 0)
1267		speedInfo->speed = 1.0;
1268	else
1269		speedInfo->speed = speedInfo->set_speed;
1270	speedInfo->activation_frame = state->activation_frame;
1271	_PushSpeedInfo(speedInfo);
1272#endif
1273	// If the new state is a playing state and looping is turned off,
1274	// determine when playing shall stop.
1275	if (playingDirection != 0 && !state->looping_enabled) {
1276		int64 startFrame, endFrame, frameCount;
1277		_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1278		if (playingDirection == -1)
1279			swap(startFrame, endFrame);
1280		// If we shall stop at the frame we start, set the current frame
1281		// to the beginning of the range.
1282		// We have to take care, since this state may equal the one
1283		// before (or probably differs in just one (unimportant)
1284		// parameter). This happens for instance, if the user changes the
1285		// data or start/end frame... while playing. In this case setting
1286		// the current frame to the start frame is unwanted. Therefore
1287		// we check whether the previous state was intended to stop
1288		// at the activation frame of this state.
1289		if (oldStopPlayingFrame != state->activation_frame
1290			&& state->current_frame == endFrame && frameCount > 1) {
1291			state->current_frame = startFrame;
1292			state->range_index
1293				= _RangeFrameForFrame(state, state->current_frame);
1294		}
1295		if (playingDirection == 1) {	// forward
1296			fStopPlayingFrame = state->activation_frame
1297								+ frameCount - state->range_index - 1;
1298		} else {						// backwards
1299			fStopPlayingFrame = state->activation_frame
1300								+ state->range_index;
1301		}
1302		_CheckStopPlaying();
1303	}
1304TRACE("PlaybackManager::_PushState() done\n");
1305}
1306
1307
1308/*!	Removes and deletes all states that are obsolete, that is which stopped
1309	being active at or before the current audio and video time. */
1310void
1311PlaybackManager::_UpdateStates()
1312{
1313//	int32 firstActive = min(_IndexForTime(fCurrentAudioTime),
1314//							_IndexForTime(fCurrentVideoTime));
1315	// Performance time should always be the least one.
1316	int32 firstActive = _IndexForTime(fPerformanceTime);
1317//TRACE("firstActive: %ld, numStates: %ld\n", firstActive, fStates.CountItems());
1318	for (int32 i = 0; i < firstActive; i++) {
1319		PlayingState* state = _StateAt(i);
1320		_NotifySeekHandledIfNecessary(state);
1321		delete state;
1322	}
1323	if (firstActive > 0)
1324{
1325		fStates.RemoveItems(0, firstActive);
1326TRACE("_UpdateStates: states removed: %ld, state count: %ld\n",
1327firstActive, fStates.CountItems());
1328}
1329	_NotifySeekHandledIfNecessary(_StateAt(0));
1330}
1331
1332
1333/*!	Returns the index of the state, that is active at frame /frame/.
1334	The index of the first frame (0) is returned, if /frame/ is even less
1335	than the frame at which this state becomes active. */
1336int32
1337PlaybackManager::_IndexForFrame(int64 frame) const
1338{
1339	int32 index = 0;
1340	PlayingState* state;
1341	while (((state = _StateAt(index + 1))) && state->activation_frame <= frame)
1342		index++;
1343	return index;
1344}
1345
1346/*!	Returns the index of the state, that is active at time /time/.
1347	The index of the first frame (0) is returned, if /time/ is even less
1348	than the time at which this state becomes active. */
1349int32
1350PlaybackManager::_IndexForTime(bigtime_t time) const
1351{
1352	return _IndexForFrame(FrameForTime(time));
1353}
1354
1355
1356//! Returns the state that reflects the most recent changes.
1357PlaybackManager::PlayingState*
1358PlaybackManager::_LastState() const
1359{
1360	return _StateAt(fStates.CountItems() - 1);
1361}
1362
1363
1364PlaybackManager::PlayingState*
1365PlaybackManager::_StateAt(int32 index) const
1366{
1367	return (PlayingState*)fStates.ItemAt(index);
1368}
1369
1370
1371PlaybackManager::PlayingState*
1372PlaybackManager::_StateAtFrame(int64 frame) const
1373{
1374	return _StateAt(_IndexForFrame(frame));
1375}
1376
1377
1378PlaybackManager::PlayingState*
1379PlaybackManager::_StateAtTime(bigtime_t time) const
1380{
1381	return _StateAt(_IndexForTime(time));
1382}
1383
1384
1385int32
1386PlaybackManager::_PlayingDirectionFor(int32 playingMode)
1387{
1388	int32 direction = 0;
1389	switch (playingMode) {
1390		case MODE_PLAYING_FORWARD:
1391			direction = 1;
1392			break;
1393		case MODE_PLAYING_BACKWARD:
1394			direction = -1;
1395			break;
1396		case MODE_PLAYING_PAUSED_FORWARD:
1397		case MODE_PLAYING_PAUSED_BACKWARD:
1398			break;
1399	}
1400	return direction;
1401}
1402
1403
1404int32
1405PlaybackManager::_PlayingDirectionFor(PlayingState* state)
1406{
1407	return _PlayingDirectionFor(state->play_mode);
1408}
1409
1410
1411/*!	Returns the Playlist frame range that bounds the playing range of a given
1412	state.
1413	\a startFrame is the lower and \a endFrame the upper bound of the range,
1414	and \a frameCount the number of frames in the range; it is guaranteed to
1415	be >= 1, even for an empty selection. */
1416void
1417PlaybackManager::_GetPlayingBoundsFor(PlayingState* state, int64& startFrame,
1418	int64& endFrame, int64& frameCount)
1419{
1420	switch (state->loop_mode) {
1421		case LOOPING_ALL:
1422			startFrame = 0;
1423			endFrame = max(startFrame, state->frame_count - 1);
1424			frameCount = endFrame - startFrame + 1;
1425			break;
1426		case LOOPING_RANGE:
1427			startFrame = state->start_frame;
1428			endFrame = state->end_frame;
1429			frameCount = endFrame - startFrame + 1;
1430			break;
1431//		case LOOPING_SELECTION:
1432//			if (!state->selection.IsEmpty()) {
1433//				startFrame = state->selection.FirstIndex();
1434//				endFrame = state->selection.LastIndex();
1435//				frameCount = state->selection.CountIndices();
1436//TRACE("  LOOPING_SELECTION: %lld - %lld (%lld)\n", startFrame, endFrame,
1437//frameCount);
1438//			} else {
1439//				startFrame = state->current_frame;
1440//				endFrame = state->current_frame;
1441//				frameCount = 1;
1442//			}
1443//			break;
1444		case LOOPING_VISIBLE:
1445			startFrame = state->first_visible_frame;
1446			endFrame = state->last_visible_frame;
1447			frameCount = endFrame - startFrame + 1;
1448			break;
1449	}
1450}
1451
1452
1453/*!	Returns the frame at which playing would start for a given playing
1454	state. If the playing mode for the supplied state specifies a stopped
1455	or undefined mode, the result is the state's current frame. */
1456int64
1457PlaybackManager::_PlayingStartFrameFor(PlayingState* state)
1458{
1459	int64 startFrame, endFrame, frameCount, frame = 0;
1460	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1461	switch (_PlayingDirectionFor(state)) {
1462		case -1:
1463			frame = endFrame;
1464			break;
1465		case 1:
1466			frame = startFrame;
1467			break;
1468		default:
1469			frame = state->current_frame;
1470			break;
1471	}
1472	return frame;
1473}
1474
1475
1476/*!	Returns the frame at which playing would end for a given playing
1477	state. If the playing mode for the supplied state specifies a stopped
1478	or undefined mode, the result is the state's current frame. */
1479int64
1480PlaybackManager::_PlayingEndFrameFor(PlayingState* state)
1481{
1482	int64 startFrame, endFrame, frameCount, frame = 0;
1483	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1484	switch (_PlayingDirectionFor(state)) {
1485		case -1:
1486			frame = startFrame;
1487			break;
1488		case 1:
1489			frame = endFrame;
1490			break;
1491		default:
1492			frame = state->current_frame;
1493			break;
1494	}
1495	return frame;
1496}
1497
1498
1499/*!	Returns the index that the supplied frame has within a playing range.
1500	If the state specifies a not-playing mode, 0 is returned. The supplied
1501	frame has to lie within the bounds of the playing range, but it doesn't
1502	need to be contained, e.g. if the range is not contiguous. In this case
1503	the index of the next frame within the range (in playing direction) is
1504	returned. */
1505int64
1506PlaybackManager::_RangeFrameForFrame(PlayingState* state, int64 frame)
1507{
1508TRACE("PlaybackManager::_RangeFrameForFrame(%lld)\n", frame);
1509	int64 startFrame, endFrame, frameCount;
1510	int64 index = 0;
1511	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1512TRACE("  start frame: %lld, end frame: %lld, frame count: %lld\n",
1513startFrame, endFrame, frameCount);
1514	switch (state->loop_mode) {
1515		case LOOPING_ALL:
1516		case LOOPING_RANGE:
1517		case LOOPING_VISIBLE:
1518			index = frame - startFrame;
1519			break;
1520	}
1521TRACE("PlaybackManager::_RangeFrameForFrame() done: %lld\n", index);
1522	return index;
1523}
1524
1525
1526/*!	Returns the Playlist frame for a playing range index. /index/ doesn't need
1527	to be in the playing range -- it is mapped into. */
1528int64
1529PlaybackManager::_FrameForRangeFrame(PlayingState* state, int64 index)
1530{
1531TRACE("PlaybackManager::_FrameForRangeFrame(%lld)\n", index);
1532	int64 startFrame, endFrame, frameCount;
1533	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1534TRACE("  frame range: %lld - %lld, count: %lld\n", startFrame, endFrame,
1535frameCount);
1536	// map the index into the index interval of the playing range
1537	if (frameCount > 1)
1538		index = (index % frameCount + frameCount) % frameCount;
1539
1540	// get the frame for the index
1541	int64 frame = startFrame;
1542	switch (state->loop_mode) {
1543		case LOOPING_ALL:
1544		case LOOPING_RANGE:
1545		case LOOPING_VISIBLE:
1546			frame = startFrame + index;
1547			break;
1548	}
1549	TRACE("PlaybackManager::_FrameForRangeFrame() done: %" PRId64 "\n", frame);
1550	return frame;
1551}
1552
1553
1554/*!	Given an arbitrary Playlist frame this function returns the next frame within
1555	the playing range for the supplied playing state. */
1556int64
1557PlaybackManager::_NextFrameInRange(PlayingState* state, int64 frame)
1558{
1559	int64 newFrame = frame;
1560	int64 startFrame, endFrame, frameCount;
1561	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1562	if (frame < startFrame || frame > endFrame)
1563		newFrame = _PlayingStartFrameFor(state);
1564	else {
1565		int64 index = _RangeFrameForFrame(state, frame);
1566		newFrame = _FrameForRangeFrame(state, index);
1567		if (newFrame > frame && _PlayingDirectionFor(state) == -1)
1568			newFrame = _FrameForRangeFrame(state, index - 1);
1569	}
1570	return newFrame;
1571}
1572
1573
1574void
1575PlaybackManager::_PushSpeedInfo(SpeedInfo* info)
1576{
1577#if SUPPORT_SPEED_CHANGES
1578	if (info == NULL)
1579		return;
1580	// check the last state
1581	if (SpeedInfo* lastSpeed = _LastSpeedInfo()) {
1582		// set the activation time
1583		info->activation_time = TimeForFrame(info->activation_frame);
1584		// Remove the last state, if it won't be activated.
1585		if (lastSpeed->activation_frame == info->activation_frame) {
1586//fprintf(stderr, "  replacing last speed info\n");
1587			fSpeeds.RemoveItem(lastSpeed);
1588			delete lastSpeed;
1589		}
1590	}
1591	fSpeeds.AddItem(info);
1592//fprintf(stderr, "  speed info pushed: activation frame: %lld, "
1593//"activation time: %lld, speed: %f\n", info->activation_frame,
1594//info->activation_time, info->speed);
1595	// ...
1596#endif
1597}
1598
1599
1600PlaybackManager::SpeedInfo*
1601PlaybackManager::_LastSpeedInfo() const
1602{
1603#if SUPPORT_SPEED_CHANGES
1604	return (SpeedInfo*)fSpeeds.ItemAt(fSpeeds.CountItems() - 1);
1605#else
1606	return NULL;
1607#endif
1608}
1609
1610
1611PlaybackManager::SpeedInfo*
1612PlaybackManager::_SpeedInfoAt(int32 index) const
1613{
1614#if SUPPORT_SPEED_CHANGES
1615	return (SpeedInfo*)fSpeeds.ItemAt(index);
1616#else
1617	return NULL;
1618#endif
1619}
1620
1621
1622int32
1623PlaybackManager::_SpeedInfoIndexForFrame(int64 frame) const
1624{
1625	int32 index = 0;
1626#if SUPPORT_SPEED_CHANGES
1627	SpeedInfo* info;
1628	while (((info = _SpeedInfoAt(index + 1)))
1629		   && info->activation_frame <= frame) {
1630		index++;
1631	}
1632//fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForFrame(%lld): %ld\n",
1633//frame, index);
1634#endif
1635	return index;
1636}
1637
1638
1639int32
1640PlaybackManager::_SpeedInfoIndexForTime(bigtime_t time) const
1641{
1642	int32 index = 0;
1643#if SUPPORT_SPEED_CHANGES
1644	SpeedInfo* info;
1645	while (((info = _SpeedInfoAt(index + 1)))
1646		   && info->activation_time <= time) {
1647		index++;
1648	}
1649//fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForTime(%lld): %ld\n",
1650//time, index);
1651#endif
1652	return index;
1653}
1654
1655
1656PlaybackManager::SpeedInfo*
1657PlaybackManager::_SpeedInfoForFrame(int64 frame) const
1658{
1659	return _SpeedInfoAt(_SpeedInfoIndexForFrame(frame));
1660}
1661
1662
1663PlaybackManager::SpeedInfo*
1664PlaybackManager::_SpeedInfoForTime(bigtime_t time) const
1665{
1666	return _SpeedInfoAt(_SpeedInfoIndexForTime(time));
1667}
1668
1669
1670void
1671PlaybackManager::_UpdateSpeedInfos()
1672{
1673#if SUPPORT_SPEED_CHANGES
1674	int32 firstActive = _SpeedInfoIndexForTime(fPerformanceTime);
1675	for (int32 i = 0; i < firstActive; i++)
1676		delete _SpeedInfoAt(i);
1677	if (firstActive > 0)
1678		fSpeeds.RemoveItems(0, firstActive);
1679//fprintf(stderr, "  speed infos 0 - %ld removed\n", firstActive);
1680#endif
1681}
1682
1683
1684/*!	Returns the end time for the last video frame that the video and the
1685	audio producer both have completely processed. */
1686bigtime_t
1687PlaybackManager::_TimeForLastFrame() const
1688{
1689	if (fNoAudio)
1690		return TimeForFrame(FrameForTime(fCurrentVideoTime));
1691
1692	return TimeForFrame(FrameForTime(min((bigtime_t)fCurrentAudioTime,
1693										 (bigtime_t)fCurrentVideoTime)));
1694}
1695
1696
1697/*!	Returns the start time for the first video frame for which
1698	neither the video nor the audio producer have fetched data. */
1699bigtime_t
1700PlaybackManager::_TimeForNextFrame() const
1701{
1702	return TimeForFrame(NextFrame());
1703}
1704
1705
1706void
1707PlaybackManager::_CheckStopPlaying()
1708{
1709//printf("_CheckStopPlaying() - %lld, next: %lld\n", fStopPlayingFrame, NextFrame());
1710	if (fStopPlayingFrame > 0 && fStopPlayingFrame <= NextFrame()) {
1711		StopPlaying();
1712		NotifyStopFrameReached();
1713	}
1714}
1715
1716
1717void
1718PlaybackManager::_NotifySeekHandledIfNecessary(PlayingState* state)
1719{
1720	if (state->is_seek_request) {
1721		state->is_seek_request = false;
1722		NotifySeekHandled(state->current_frame);
1723	}
1724}
1725