1/*
2 * Controller.cpp - Media Player for the Haiku Operating System
3 *
4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5 * Copyright (C) 2007-2008 Stephan A��mus <superstippi@gmx.de>
6 * Copyright (C) 2007-2009 Fredrik Mod��en <[FirstName]@[LastName].se>
7 *
8 * Released under the terms of the MIT license.
9 */
10
11
12#include "Controller.h"
13
14#include <new>
15#include <stdio.h>
16#include <string.h>
17
18#include <Autolock.h>
19#include <Bitmap.h>
20#include <Catalog.h>
21#include <Debug.h>
22#include <Path.h>
23#include <Window.h> // for debugging only
24
25#include "AutoDeleter.h"
26#include "ControllerView.h"
27#include "MainApp.h"
28#include "PlaybackState.h"
29#include "Settings.h"
30#include "VideoView.h"
31
32// suppliers
33#include "AudioTrackSupplier.h"
34#include "MediaTrackAudioSupplier.h"
35#include "MediaTrackVideoSupplier.h"
36#include "ProxyAudioSupplier.h"
37#include "ProxyVideoSupplier.h"
38#include "SubTitles.h"
39#include "TrackSupplier.h"
40#include "VideoTrackSupplier.h"
41
42
43#undef B_TRANSLATION_CONTEXT
44#define B_TRANSLATION_CONTEXT "MediaPlayer-Controller"
45#define MIN_WIDTH 250
46
47using std::nothrow;
48
49
50class TrackSupplierReleaser {
51public:
52	TrackSupplierReleaser(PlaylistItemRef& owner)
53		:
54		fOwner(owner),
55		fRelease(true)
56		{}
57
58	virtual ~TrackSupplierReleaser()
59	{
60		if (fRelease)
61			fOwner.Get()->ReleaseTrackSupplier();
62	}
63
64	void Detach()
65	{
66		fRelease = false;
67	}
68
69private:
70	PlaylistItemRef&	fOwner;
71	bool				fRelease;
72};
73
74
75void
76HandleError(const char *text, status_t err)
77{
78	if (err != B_OK) {
79		printf("%s. error 0x%08x (%s)\n",text, (int)err, strerror(err));
80		fflush(NULL);
81		exit(1);
82	}
83}
84
85
86// #pragma mark - Controller::Listener
87
88
89Controller::Listener::Listener() {}
90Controller::Listener::~Listener() {}
91void Controller::Listener::FileFinished() {}
92void Controller::Listener::FileChanged(PlaylistItem* item, status_t result) {}
93void Controller::Listener::VideoTrackChanged(int32) {}
94void Controller::Listener::AudioTrackChanged(int32) {}
95void Controller::Listener::SubTitleTrackChanged(int32) {}
96void Controller::Listener::VideoStatsChanged() {}
97void Controller::Listener::AudioStatsChanged() {}
98void Controller::Listener::PlaybackStateChanged(uint32) {}
99void Controller::Listener::PositionChanged(float) {}
100void Controller::Listener::SeekHandled(int64 seekFrame) {}
101void Controller::Listener::VolumeChanged(float) {}
102void Controller::Listener::MutedChanged(bool) {}
103
104
105// #pragma mark - Controller
106
107
108enum {
109	MSG_SET_TO = 'stto'
110};
111
112
113Controller::Controller()
114	:
115	NodeManager(),
116	fVideoView(NULL),
117	fVolume(1.0),
118	fActiveVolume(1.0),
119	fMuted(false),
120
121	fItem(NULL),
122
123	fVideoSupplier(new ProxyVideoSupplier()),
124	fAudioSupplier(new ProxyAudioSupplier(this)),
125	fVideoTrackSupplier(NULL),
126	fAudioTrackSupplier(NULL),
127	fSubTitles(NULL),
128	fSubTitlesIndex(-1),
129
130	fCurrentFrame(0),
131	fDuration(0),
132	fVideoFrameRate(25.0),
133
134	fPendingSeekRequests(0),
135	fSeekFrame(-1),
136	fRequestedSeekFrame(-1),
137
138	fGlobalSettingsListener(this),
139
140	fListeners(4)
141{
142	Settings::Default()->AddListener(&fGlobalSettingsListener);
143	_AdoptGlobalSettings();
144
145	fAutoplay = fAutoplaySetting;
146}
147
148
149Controller::~Controller()
150{
151	Settings::Default()->RemoveListener(&fGlobalSettingsListener);
152	SetTo(NULL);
153}
154
155
156// #pragma mark - NodeManager interface
157
158
159void
160Controller::MessageReceived(BMessage* message)
161{
162	switch (message->what) {
163		case MSG_OBJECT_CHANGED:
164			// received from fGlobalSettingsListener
165			// TODO: find out which object, if we ever watch more than
166			// the global settings instance...
167			_AdoptGlobalSettings();
168			break;
169
170		case MSG_SET_TO:
171		{
172			PlaylistItem* item;
173			if (message->FindPointer("item", (void**)&item) == B_OK) {
174				PlaylistItemRef itemRef(item, true);
175					// The reference was passed with the message.
176				SetTo(itemRef);
177			} else
178				_NotifyFileChanged(NULL, B_BAD_VALUE);
179
180			break;
181		}
182
183		default:
184			NodeManager::MessageReceived(message);
185	}
186}
187
188
189int64
190Controller::Duration()
191{
192	return _FrameDuration();
193}
194
195
196VideoTarget*
197Controller::CreateVideoTarget()
198{
199	return fVideoView;
200}
201
202
203VideoSupplier*
204Controller::CreateVideoSupplier()
205{
206	return fVideoSupplier;
207}
208
209
210AudioSupplier*
211Controller::CreateAudioSupplier()
212{
213	return fAudioSupplier;
214}
215
216
217// #pragma mark -
218
219
220status_t
221Controller::SetToAsync(const PlaylistItemRef& item)
222{
223	PlaylistItemRef additionalReference(item);
224
225	BMessage message(MSG_SET_TO);
226	status_t ret = message.AddPointer("item", item.Get());
227	if (ret != B_OK)
228		return ret;
229
230	ret = PostMessage(&message);
231	if (ret != B_OK)
232		return ret;
233
234	// The additional reference is now passed along with the message...
235	additionalReference.Detach();
236
237	return B_OK;
238}
239
240
241status_t
242Controller::SetTo(const PlaylistItemRef& item)
243{
244	BAutolock _(this);
245
246	if (fItem == item) {
247		if (InitCheck() == B_OK) {
248			if (fAutoplay) {
249				SetPosition(0.0);
250				StartPlaying(true);
251			}
252		}
253		return B_OK;
254	}
255
256	fAudioSupplier->SetSupplier(NULL, fVideoFrameRate);
257	fVideoSupplier->SetSupplier(NULL);
258
259	if (fItem != NULL)
260		TrackSupplierReleaser oldSupplierReleaser(fItem);
261
262	fItem = item;
263
264	// Do not delete the supplier chain until after we called
265	// NodeManager::Init() to setup a new media node chain
266	ObjectDeleter<VideoTrackSupplier> videoSupplierDeleter(
267		fVideoTrackSupplier);
268	ObjectDeleter<AudioTrackSupplier> audioSupplierDeleter(
269		fAudioTrackSupplier);
270
271	fVideoTrackSupplier = NULL;
272	fAudioTrackSupplier = NULL;
273	fSubTitles = NULL;
274	fSubTitlesIndex = -1;
275
276	fCurrentFrame = 0;
277	fDuration = 0;
278	fVideoFrameRate = 25.0;
279
280	fPendingSeekRequests = 0;
281	fSeekFrame = -1;
282	fRequestedSeekFrame = -1;
283
284	if (!fItem.IsSet())
285		return B_BAD_VALUE;
286
287	TrackSupplier* trackSupplier = fItem->GetTrackSupplier();
288	if (trackSupplier == NULL) {
289		_NotifyFileChanged(item.Get(), B_NO_MEMORY);
290		return B_NO_MEMORY;
291	}
292	TrackSupplierReleaser trackSupplierReleaser(fItem);
293
294	status_t err = trackSupplier->InitCheck();
295	if (err != B_OK) {
296		printf("Controller::SetTo: InitCheck failed\n");
297		_NotifyFileChanged(item.Get(), err);
298		return err;
299	}
300
301	if (trackSupplier->CountAudioTracks() == 0
302		&& trackSupplier->CountVideoTracks() == 0) {
303		_NotifyFileChanged(item.Get(), B_MEDIA_NO_HANDLER);
304		return B_MEDIA_NO_HANDLER;
305	}
306
307	SelectAudioTrack(0);
308	SelectVideoTrack(0);
309
310	if (fAudioTrackSupplier == NULL && fVideoTrackSupplier == NULL) {
311		printf("Controller::SetTo: no audio or video tracks found or "
312			"no decoders\n");
313		_NotifyFileChanged(item.Get(), B_MEDIA_NO_HANDLER);
314		return B_MEDIA_NO_HANDLER;
315	}
316
317	trackSupplierReleaser.Detach();
318
319	// prevent blocking the creation of new overlay buffers
320	if (fVideoView)
321		fVideoView->DisableOverlay();
322
323	// get video properties (if there is video at all)
324	bool useOverlays = fVideoView ? fVideoView->UseOverlays() : true;
325
326	int width;
327	int height;
328	GetSize(&width, &height);
329	color_space preferredVideoFormat = B_NO_COLOR_SPACE;
330	if (fVideoTrackSupplier != NULL) {
331		const media_format& format = fVideoTrackSupplier->Format();
332		preferredVideoFormat = format.u.raw_video.display.format;
333	}
334
335	uint32 enabledNodes;
336	if (!fVideoTrackSupplier)
337		enabledNodes = AUDIO_ONLY;
338	else if (!fAudioTrackSupplier)
339		enabledNodes = VIDEO_ONLY;
340	else
341		enabledNodes = AUDIO_AND_VIDEO;
342
343	float audioFrameRate = 44100.0f;
344	uint32 audioChannels = 2;
345	if (fAudioTrackSupplier != NULL) {
346		const media_format& audioTrackFormat = fAudioTrackSupplier->Format();
347		audioFrameRate = audioTrackFormat.u.raw_audio.frame_rate;
348		audioChannels = audioTrackFormat.u.raw_audio.channel_count;
349	}
350
351	if (InitCheck() != B_OK) {
352		Init(BRect(0, 0, width - 1, height - 1), fVideoFrameRate,
353			preferredVideoFormat, audioFrameRate, audioChannels, LOOPING_ALL,
354			false, 1.0, enabledNodes, useOverlays);
355	} else {
356		FormatChanged(BRect(0, 0, width - 1, height - 1), fVideoFrameRate,
357			preferredVideoFormat, audioFrameRate, audioChannels, enabledNodes,
358			useOverlays);
359	}
360
361	_NotifyFileChanged(item.Get(), B_OK);
362
363	if (fAutoplay)
364		StartPlaying(true);
365
366	return B_OK;
367}
368
369
370void
371Controller::PlayerActivated(bool active)
372{
373	if (LockWithTimeout(5000) != B_OK)
374		return;
375
376	if (active && gMainApp->PlayerCount() > 1) {
377		if (fActiveVolume != fVolume)
378			SetVolume(fActiveVolume);
379	} else {
380		fActiveVolume = fVolume;
381		if (gMainApp->PlayerCount() > 1)
382			switch (fBackgroundMovieVolumeMode) {
383				case mpSettings::BG_MOVIES_MUTED:
384					SetVolume(0.0);
385					break;
386				case mpSettings::BG_MOVIES_HALF_VLUME:
387					SetVolume(fVolume * 0.25);
388					break;
389				case mpSettings::BG_MOVIES_FULL_VOLUME:
390				default:
391					break;
392			}
393	}
394
395	Unlock();
396}
397
398
399void
400Controller::GetSize(int *width, int *height, int* _widthAspect,
401	int* _heightAspect)
402{
403	BAutolock _(this);
404
405	if (fVideoTrackSupplier) {
406		media_format format = fVideoTrackSupplier->Format();
407		*height = format.u.raw_video.display.line_count;
408		*width = format.u.raw_video.display.line_width;
409		int widthAspect = 0;
410		int heightAspect = 0;
411		// Ignore format aspect when both values are 1. If they have been
412		// intentionally at 1:1 then no harm is done for quadratic videos,
413		// only if the video is indeed encoded anamorphotic, but supposed
414		// to be displayed quadratic... extremely unlikely.
415		if (format.u.raw_video.pixel_width_aspect
416			!= format.u.raw_video.pixel_height_aspect
417			&& format.u.raw_video.pixel_width_aspect != 1) {
418			widthAspect = format.u.raw_video.pixel_width_aspect;
419			heightAspect = format.u.raw_video.pixel_height_aspect;
420		}
421		if (_widthAspect != NULL)
422			*_widthAspect = widthAspect;
423		if (_heightAspect != NULL)
424			*_heightAspect = heightAspect;
425	} else {
426		*height = 0;
427		*width = 0;
428		if (_widthAspect != NULL)
429			*_widthAspect = 1;
430		if (_heightAspect != NULL)
431			*_heightAspect = 1;
432	}
433}
434
435
436int
437Controller::AudioTrackCount()
438{
439	BAutolock _(this);
440
441	if (fItem != NULL && fItem->HasTrackSupplier())
442		return fItem->GetTrackSupplier()->CountAudioTracks();
443	return 0;
444}
445
446
447int
448Controller::VideoTrackCount()
449{
450	BAutolock _(this);
451
452	if (fItem != NULL && fItem->HasTrackSupplier())
453		return fItem->GetTrackSupplier()->CountVideoTracks();
454	return 0;
455}
456
457
458int
459Controller::SubTitleTrackCount()
460{
461	BAutolock _(this);
462
463	if (fItem != NULL && fItem->HasTrackSupplier())
464		return fItem->GetTrackSupplier()->CountSubTitleTracks();
465	return 0;
466}
467
468
469status_t
470Controller::SelectAudioTrack(int n)
471{
472	BAutolock _(this);
473	if (fItem == NULL || !fItem->HasTrackSupplier())
474		return B_NO_INIT;
475
476	ObjectDeleter<AudioTrackSupplier> deleter(fAudioTrackSupplier);
477	fAudioTrackSupplier
478		= fItem->GetTrackSupplier()->CreateAudioTrackForIndex(n);
479	if (fAudioTrackSupplier == NULL)
480		return B_BAD_INDEX;
481
482	bigtime_t a = fAudioTrackSupplier->Duration();
483	bigtime_t v = fVideoTrackSupplier != NULL
484		? fVideoTrackSupplier->Duration() : 0;
485	fDuration = max_c(a, v);
486	DurationChanged();
487	// TODO: notify duration changed!
488
489	fAudioSupplier->SetSupplier(fAudioTrackSupplier, fVideoFrameRate);
490
491	_NotifyAudioTrackChanged(n);
492	return B_OK;
493}
494
495
496int
497Controller::CurrentAudioTrack()
498{
499	BAutolock _(this);
500
501	if (fAudioTrackSupplier == NULL)
502		return -1;
503
504	return fAudioTrackSupplier->TrackIndex();
505}
506
507
508int
509Controller::AudioTrackChannelCount()
510{
511	media_format format;
512	if (GetEncodedAudioFormat(&format) == B_OK)
513		return format.u.encoded_audio.output.channel_count;
514
515	return 2;
516}
517
518
519status_t
520Controller::SelectVideoTrack(int n)
521{
522	BAutolock _(this);
523
524	if (fItem == NULL || !fItem->HasTrackSupplier())
525		return B_NO_INIT;
526
527	ObjectDeleter<VideoTrackSupplier> deleter(fVideoTrackSupplier);
528	fVideoTrackSupplier
529		= fItem->GetTrackSupplier()->CreateVideoTrackForIndex(n);
530	if (fVideoTrackSupplier == NULL)
531		return B_BAD_INDEX;
532
533	bigtime_t a = fAudioTrackSupplier ? fAudioTrackSupplier->Duration() : 0;
534	bigtime_t v = fVideoTrackSupplier->Duration();
535	fDuration = max_c(a, v);
536	fVideoFrameRate = fVideoTrackSupplier->Format().u.raw_video.field_rate;
537	if (fVideoFrameRate <= 0.0) {
538		printf("Controller::SelectVideoTrack(%d) - invalid video frame rate: %.1f\n",
539			n, fVideoFrameRate);
540		fVideoFrameRate = 25.0;
541	}
542
543	DurationChanged();
544	// TODO: notify duration changed!
545
546	fVideoSupplier->SetSupplier(fVideoTrackSupplier);
547
548	_NotifyVideoTrackChanged(n);
549	return B_OK;
550}
551
552
553int
554Controller::CurrentVideoTrack()
555{
556	BAutolock _(this);
557
558	if (fVideoTrackSupplier == NULL)
559		return -1;
560
561	return fVideoTrackSupplier->TrackIndex();
562}
563
564
565status_t
566Controller::SelectSubTitleTrack(int n)
567{
568	BAutolock _(this);
569
570	if (fItem == NULL || !fItem->HasTrackSupplier())
571		return B_NO_INIT;
572
573	fSubTitlesIndex = n;
574	fSubTitles =
575		fItem->GetTrackSupplier()->SubTitleTrackForIndex(n);
576
577	const SubTitle* subTitle = NULL;
578	if (fSubTitles != NULL)
579		subTitle = fSubTitles->SubTitleAt(_TimePosition());
580	if (subTitle != NULL)
581		fVideoView->SetSubTitle(subTitle->text.String());
582	else
583		fVideoView->SetSubTitle(NULL);
584
585	_NotifySubTitleTrackChanged(n);
586	return B_OK;
587}
588
589
590int
591Controller::CurrentSubTitleTrack()
592{
593	BAutolock _(this);
594
595	if (fSubTitles == NULL)
596		return -1;
597
598	return fSubTitlesIndex;
599}
600
601
602const char*
603Controller::SubTitleTrackName(int n)
604{
605	BAutolock _(this);
606
607	if (fItem == NULL || !fItem->HasTrackSupplier())
608		return NULL;
609
610	const SubTitles* subTitles
611		= fItem->GetTrackSupplier()->SubTitleTrackForIndex(n);
612	if (subTitles == NULL)
613		return NULL;
614
615	return subTitles->Name();
616}
617
618
619// #pragma mark -
620
621
622void
623Controller::Stop()
624{
625	//printf("Controller::Stop\n");
626
627	BAutolock _(this);
628
629	StopPlaying();
630	SetPosition(0.0);
631
632	fAutoplay = fAutoplaySetting;
633}
634
635
636void
637Controller::Play()
638{
639	//printf("Controller::Play\n");
640
641	BAutolock _(this);
642
643	StartPlaying();
644	fAutoplay = true;
645}
646
647
648void
649Controller::Pause()
650{
651//	printf("Controller::Pause\n");
652
653	BAutolock _(this);
654
655	PausePlaying();
656
657	fAutoplay = fAutoplaySetting;
658}
659
660
661void
662Controller::TogglePlaying()
663{
664//	printf("Controller::TogglePlaying\n");
665
666	BAutolock _(this);
667
668	if (InitCheck() == B_OK) {
669		NodeManager::TogglePlaying();
670
671		fAutoplay = IsPlaying() || fAutoplaySetting;
672	}
673}
674
675
676uint32
677Controller::PlaybackState()
678{
679	BAutolock _(this);
680
681	return _PlaybackState(PlaybackManager::PlayMode());
682}
683
684
685bigtime_t
686Controller::TimeDuration()
687{
688	BAutolock _(this);
689
690	return fDuration;
691}
692
693
694bigtime_t
695Controller::TimePosition()
696{
697	BAutolock _(this);
698
699	return _TimePosition();
700}
701
702
703status_t
704Controller::SaveState(bool reset)
705{
706	if (!fItem.IsSet())
707		return B_OK;
708	if (reset)
709		fCurrentFrame = 0;
710	status_t status = fItem.Get()->SetLastVolume(fVolume);
711	if (status == B_OK)
712		status = fItem.Get()->SetLastFrame(fCurrentFrame);
713	else
714		fItem.Get()->SetLastFrame(fCurrentFrame);
715	return status;
716}
717
718
719void
720Controller::RestoreState()
721{
722	PlaylistItem *item =fItem.Get();
723	if (item == NULL)
724		return;
725
726	int lastFrame = item->LastFrame();
727	float lastVolume = item->LastVolume();
728
729	// Don't Pause()/Play() if we have nothing to do.
730	if (lastFrame <= 0 && lastVolume < 0)
731		return;
732
733	Pause();
734
735	if (lastFrame > 0) {
736		bool resume = fResume == mpSettings::RESUME_ALWAYS;
737		if (fResume == mpSettings::RESUME_ASK) {
738			BString label;
739			int time = (int)((float)lastFrame * TimeDuration() / (1000000 * _FrameDuration()));
740			label.SetToFormat(B_TRANSLATE("Do you want to resume %s at %dm%ds?"),
741					item->Name().String(), time / 60, time % 60);
742			BAlert *alert = new BAlert(B_TRANSLATE("Resume?"), label,
743					B_TRANSLATE("Resume"), B_TRANSLATE("Reset"));
744			resume = alert->Go() == 0;
745		}
746
747		if (resume)
748			SetFramePosition(lastFrame);
749	}
750
751	if (lastVolume >= 0)
752		SetVolume(lastVolume);
753
754	Play();
755}
756
757
758void
759Controller::SetVolume(float value)
760{
761//	printf("Controller::SetVolume %.4f\n", value);
762	BAutolock _(this);
763
764	value = max_c(0.0, min_c(2.0, value));
765
766	if (fVolume != value) {
767		if (fMuted)
768			ToggleMute();
769
770		fVolume = value;
771		fAudioSupplier->SetVolume(fVolume);
772
773		_NotifyVolumeChanged(fVolume);
774	}
775}
776
777
778void
779Controller::VolumeUp()
780{
781	// TODO: linear <-> exponential
782	SetVolume(Volume() + 0.05);
783}
784
785
786void
787Controller::VolumeDown()
788{
789	// TODO: linear <-> exponential
790	SetVolume(Volume() - 0.05);
791}
792
793
794void
795Controller::ToggleMute()
796{
797	BAutolock _(this);
798
799	fMuted = !fMuted;
800
801	if (fMuted)
802		fAudioSupplier->SetVolume(0.0);
803	else
804		fAudioSupplier->SetVolume(fVolume);
805
806	_NotifyMutedChanged(fMuted);
807}
808
809
810float
811Controller::Volume()
812{
813	BAutolock _(this);
814
815	return fVolume;
816}
817
818
819int64
820Controller::SetPosition(float value)
821{
822	BAutolock _(this);
823
824	return SetFramePosition(_FrameDuration() * value);
825}
826
827
828int64
829Controller::SetFramePosition(int64 value)
830{
831	BAutolock _(this);
832
833	fPendingSeekRequests++;
834	fRequestedSeekFrame = max_c(0, min_c(_FrameDuration(), value));
835	fSeekFrame = fRequestedSeekFrame;
836
837	int64 currentFrame = CurrentFrame();
838
839	// Snap to a video keyframe, since that will be the fastest
840	// to display and seeking will feel more snappy. Note that we
841	// don't store this change in fSeekFrame, since we still want
842	// to report the originally requested seek frame in TimePosition()
843	// until we could reach that frame.
844	if (Duration() > 240 && fVideoTrackSupplier != NULL
845		&& abs(value - currentFrame) > 5) {
846		fVideoTrackSupplier->FindKeyFrameForFrame(&fSeekFrame);
847	}
848
849//printf("SetFramePosition(%lld) -> %lld (current: %lld, duration: %lld) "
850//"(video: %p)\n", value, fSeekFrame, currentFrame, _FrameDuration(),
851//fVideoTrackSupplier);
852	if (fSeekFrame != currentFrame) {
853		int64 seekFrame = fSeekFrame;
854		SetCurrentFrame(fSeekFrame);
855			// May trigger the notification and reset fSeekFrame,
856			// if next current frame == seek frame.
857		return seekFrame;
858	} else
859		NotifySeekHandled(fRequestedSeekFrame);
860	return currentFrame;
861}
862
863
864int64
865Controller::SetTimePosition(bigtime_t value)
866{
867	BAutolock _(this);
868
869	return SetPosition((float)value / TimeDuration());
870}
871
872
873// #pragma mark -
874
875
876bool
877Controller::HasFile()
878{
879	// you need to hold the data lock
880	return fItem != NULL && fItem->HasTrackSupplier();
881}
882
883
884status_t
885Controller::GetFileFormatInfo(media_file_format* fileFormat)
886{
887	// you need to hold the data lock
888	if (fItem == NULL || !fItem->HasTrackSupplier())
889		return B_NO_INIT;
890	return fItem->GetTrackSupplier()->GetFileFormatInfo(fileFormat);
891}
892
893
894status_t
895Controller::GetCopyright(BString* copyright)
896{
897	// you need to hold the data lock
898	if (fItem == NULL || !fItem->HasTrackSupplier())
899		return B_NO_INIT;
900	return fItem->GetTrackSupplier()->GetCopyright(copyright);
901}
902
903
904status_t
905Controller::GetLocation(BString* location)
906{
907	// you need to hold the data lock
908	if (!fItem.IsSet())
909		return B_NO_INIT;
910	*location = fItem->LocationURI();
911	return B_OK;
912}
913
914
915status_t
916Controller::GetName(BString* name)
917{
918	// you need to hold the data lock
919	if (!fItem.IsSet())
920		return B_NO_INIT;
921	*name = fItem->Name();
922	return B_OK;
923}
924
925
926status_t
927Controller::GetEncodedVideoFormat(media_format* format)
928{
929	// you need to hold the data lock
930	if (fVideoTrackSupplier)
931		return fVideoTrackSupplier->GetEncodedFormat(format);
932	return B_NO_INIT;
933}
934
935
936status_t
937Controller::GetVideoCodecInfo(media_codec_info* info)
938{
939	// you need to hold the data lock
940	if (fVideoTrackSupplier)
941		return fVideoTrackSupplier->GetCodecInfo(info);
942	return B_NO_INIT;
943}
944
945
946status_t
947Controller::GetEncodedAudioFormat(media_format* format)
948{
949	// you need to hold the data lock
950	if (fAudioTrackSupplier)
951		return fAudioTrackSupplier->GetEncodedFormat(format);
952	return B_NO_INIT;
953}
954
955
956status_t
957Controller::GetAudioCodecInfo(media_codec_info* info)
958{
959	// you need to hold the data lock
960	if (fAudioTrackSupplier)
961		return fAudioTrackSupplier->GetCodecInfo(info);
962	return B_NO_INIT;
963}
964
965
966status_t
967Controller::GetMetaData(BMessage* metaData)
968{
969	// you need to hold the data lock
970	if (fItem == NULL || !fItem->HasTrackSupplier())
971		return B_NO_INIT;
972	return fItem->GetTrackSupplier()->GetMetaData(metaData);
973}
974
975
976status_t
977Controller::GetVideoMetaData(int32 index, BMessage* metaData)
978{
979	// you need to hold the data lock
980	if (fItem == NULL || !fItem->HasTrackSupplier())
981		return B_NO_INIT;
982	return fItem->GetTrackSupplier()->GetVideoMetaData(index, metaData);
983}
984
985
986status_t
987Controller::GetAudioMetaData(int32 index, BMessage* metaData)
988{
989	// you need to hold the data lock
990	if (fItem == NULL || !fItem->HasTrackSupplier())
991		return B_NO_INIT;
992	return fItem->GetTrackSupplier()->GetAudioMetaData(index, metaData);
993}
994
995
996// #pragma mark -
997
998
999void
1000Controller::SetVideoView(VideoView *view)
1001{
1002	BAutolock _(this);
1003
1004	fVideoView = view;
1005}
1006
1007
1008bool
1009Controller::IsOverlayActive()
1010{
1011	if (fVideoView)
1012		return fVideoView->IsOverlayActive();
1013
1014	return false;
1015}
1016
1017
1018// #pragma mark -
1019
1020
1021bool
1022Controller::AddListener(Listener* listener)
1023{
1024	BAutolock _(this);
1025
1026	if (listener && !fListeners.HasItem(listener))
1027		return fListeners.AddItem(listener);
1028	return false;
1029}
1030
1031
1032void
1033Controller::RemoveListener(Listener* listener)
1034{
1035	BAutolock _(this);
1036
1037	fListeners.RemoveItem(listener);
1038}
1039
1040
1041// #pragma mark - Private
1042
1043
1044void
1045Controller::_AdoptGlobalSettings()
1046{
1047	mpSettings settings;
1048	Settings::Default()->Get(settings);
1049
1050	fAutoplaySetting = settings.autostart;
1051	// not yet used:
1052	fLoopMovies = settings.loopMovie;
1053	fLoopSounds = settings.loopSound;
1054	fBackgroundMovieVolumeMode = settings.backgroundMovieVolumeMode;
1055	fResume = settings.resume;
1056}
1057
1058
1059uint32
1060Controller::_PlaybackState(int32 playingMode) const
1061{
1062	uint32 state = 0;
1063	switch (playingMode) {
1064		case MODE_PLAYING_PAUSED_FORWARD:
1065		case MODE_PLAYING_PAUSED_BACKWARD:
1066			state = PLAYBACK_STATE_PAUSED;
1067			break;
1068		case MODE_PLAYING_FORWARD:
1069		case MODE_PLAYING_BACKWARD:
1070			state = PLAYBACK_STATE_PLAYING;
1071			break;
1072
1073		default:
1074			state = PLAYBACK_STATE_STOPPED;
1075			break;
1076	}
1077	return state;
1078}
1079
1080
1081bigtime_t
1082Controller::_TimePosition() const
1083{
1084	if (fDuration == 0)
1085		return 0;
1086
1087	// Check if we are still waiting to reach the seekframe,
1088	// pass the last pending seek frame back to the caller, so
1089	// that the view of the current frame/time from the outside
1090	// does not depend on the internal latency to reach requested
1091	// frames asynchronously.
1092	int64 frame;
1093	if (fPendingSeekRequests > 0)
1094		frame = fRequestedSeekFrame;
1095	else
1096		frame = fCurrentFrame;
1097
1098	return frame * fDuration / _FrameDuration();
1099}
1100
1101
1102int64
1103Controller::_FrameDuration() const
1104{
1105	// This should really be total frames (video frames at that)
1106	// TODO: It is not so nice that the MediaPlayer still measures
1107	// in video frames if only playing audio. Here for example, it will
1108	// return a duration of 0 if the audio clip happens to be shorter than
1109	// one video frame at 25 fps.
1110	return (int64)((double)fDuration * fVideoFrameRate / 1000000.0);
1111}
1112
1113
1114// #pragma mark - Notifications
1115
1116
1117void
1118Controller::_NotifyFileChanged(PlaylistItem* item, status_t result) const
1119{
1120	BList listeners(fListeners);
1121	int32 count = listeners.CountItems();
1122	for (int32 i = 0; i < count; i++) {
1123		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1124		listener->FileChanged(item, result);
1125	}
1126}
1127
1128
1129void
1130Controller::_NotifyFileFinished() const
1131{
1132	BList listeners(fListeners);
1133	int32 count = listeners.CountItems();
1134	for (int32 i = 0; i < count; i++) {
1135		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1136		listener->FileFinished();
1137	}
1138}
1139
1140
1141void
1142Controller::_NotifyVideoTrackChanged(int32 index) const
1143{
1144	BList listeners(fListeners);
1145	int32 count = listeners.CountItems();
1146	for (int32 i = 0; i < count; i++) {
1147		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1148		listener->VideoTrackChanged(index);
1149	}
1150}
1151
1152
1153void
1154Controller::_NotifyAudioTrackChanged(int32 index) const
1155{
1156	BList listeners(fListeners);
1157	int32 count = listeners.CountItems();
1158	for (int32 i = 0; i < count; i++) {
1159		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1160		listener->AudioTrackChanged(index);
1161	}
1162}
1163
1164
1165void
1166Controller::_NotifySubTitleTrackChanged(int32 index) const
1167{
1168	BList listeners(fListeners);
1169	int32 count = listeners.CountItems();
1170	for (int32 i = 0; i < count; i++) {
1171		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1172		listener->SubTitleTrackChanged(index);
1173	}
1174}
1175
1176
1177void
1178Controller::_NotifyVideoStatsChanged() const
1179{
1180	BList listeners(fListeners);
1181	int32 count = listeners.CountItems();
1182	for (int32 i = 0; i < count; i++) {
1183		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1184		listener->VideoStatsChanged();
1185	}
1186}
1187
1188
1189void
1190Controller::_NotifyAudioStatsChanged() const
1191{
1192	BList listeners(fListeners);
1193	int32 count = listeners.CountItems();
1194	for (int32 i = 0; i < count; i++) {
1195		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1196		listener->AudioStatsChanged();
1197	}
1198}
1199
1200
1201void
1202Controller::_NotifyPlaybackStateChanged(uint32 state) const
1203{
1204	BList listeners(fListeners);
1205	int32 count = listeners.CountItems();
1206	for (int32 i = 0; i < count; i++) {
1207		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1208		listener->PlaybackStateChanged(state);
1209	}
1210}
1211
1212
1213void
1214Controller::_NotifyPositionChanged(float position) const
1215{
1216	BList listeners(fListeners);
1217	int32 count = listeners.CountItems();
1218	for (int32 i = 0; i < count; i++) {
1219		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1220		listener->PositionChanged(position);
1221	}
1222}
1223
1224
1225void
1226Controller::_NotifySeekHandled(int64 seekFrame) const
1227{
1228	BList listeners(fListeners);
1229	int32 count = listeners.CountItems();
1230	for (int32 i = 0; i < count; i++) {
1231		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1232		listener->SeekHandled(seekFrame);
1233	}
1234}
1235
1236
1237void
1238Controller::_NotifyVolumeChanged(float volume) const
1239{
1240	BList listeners(fListeners);
1241	int32 count = listeners.CountItems();
1242	for (int32 i = 0; i < count; i++) {
1243		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1244		listener->VolumeChanged(volume);
1245	}
1246}
1247
1248
1249void
1250Controller::_NotifyMutedChanged(bool muted) const
1251{
1252	BList listeners(fListeners);
1253	int32 count = listeners.CountItems();
1254	for (int32 i = 0; i < count; i++) {
1255		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1256		listener->MutedChanged(muted);
1257	}
1258}
1259
1260
1261void
1262Controller::NotifyPlayModeChanged(int32 mode) const
1263{
1264	uint32 state = _PlaybackState(mode);
1265	if (fVideoView)
1266		fVideoView->SetPlaying(state == PLAYBACK_STATE_PLAYING);
1267	_NotifyPlaybackStateChanged(state);
1268}
1269
1270
1271void
1272Controller::NotifyLoopModeChanged(int32 mode) const
1273{
1274}
1275
1276
1277void
1278Controller::NotifyLoopingEnabledChanged(bool enabled) const
1279{
1280}
1281
1282
1283void
1284Controller::NotifyVideoBoundsChanged(BRect bounds) const
1285{
1286}
1287
1288
1289void
1290Controller::NotifyFPSChanged(float fps) const
1291{
1292}
1293
1294
1295void
1296Controller::NotifyCurrentFrameChanged(int64 frame) const
1297{
1298	fCurrentFrame = frame;
1299	bigtime_t timePosition = _TimePosition();
1300	_NotifyPositionChanged((float)timePosition / fDuration);
1301
1302	if (fSubTitles != NULL) {
1303		const SubTitle* subTitle = fSubTitles->SubTitleAt(timePosition);
1304		if (subTitle != NULL)
1305			fVideoView->SetSubTitle(subTitle->text.String());
1306		else
1307			fVideoView->SetSubTitle(NULL);
1308	}
1309}
1310
1311
1312void
1313Controller::NotifySpeedChanged(float speed) const
1314{
1315}
1316
1317
1318void
1319Controller::NotifyFrameDropped() const
1320{
1321//	printf("Controller::NotifyFrameDropped()\n");
1322}
1323
1324
1325void
1326Controller::NotifyStopFrameReached() const
1327{
1328	// Currently, this means we reached the end of the current
1329	// file and should play the next file
1330	_NotifyFileFinished();
1331}
1332
1333
1334void
1335Controller::NotifySeekHandled(int64 seekedFrame) const
1336{
1337	if (fPendingSeekRequests == 0)
1338		return;
1339
1340	fPendingSeekRequests--;
1341	if (fPendingSeekRequests == 0) {
1342		fSeekFrame = -1;
1343		fRequestedSeekFrame = -1;
1344	}
1345
1346	_NotifySeekHandled(seekedFrame);
1347}
1348
1349