diff --git a/src/apps/mediaplayer/Controller.cpp b/src/apps/mediaplayer/Controller.cpp new file mode 100644 index 0000000000..748070944d --- /dev/null +++ b/src/apps/mediaplayer/Controller.cpp @@ -0,0 +1,278 @@ +/* + * Controller.cpp - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include +#include +#include +#include +#include + +#include "Controller.h" +#include "DeviceRoster.h" +#include "VideoView.h" +#include "VideoNode.h" + + +media_node dvb_node; +media_node audio_mixer_node; +media_node video_window_node; +media_node time_node; + +media_input audio_input; +media_output audio_output; +media_input video_input; +media_output video_output; + +BMediaRoster *gMediaRoster; + +void +HandleError(const char *text, status_t err) +{ + if (err != B_OK) { + printf("%s. error 0x%08x (%s)\n",text, (int)err, strerror(err)); + fflush(NULL); + exit(1); + } +} + + +Controller::Controller() + : fCurrentInterface(-1) + , fCurrentChannel(-1) + , fVideoView(NULL) + , fVideoNode(NULL) + , fWeb(NULL) + , fChannelParam(NULL) + , fConnected(false) + , fInput() + , fOutput() +{ + gMediaRoster = BMediaRoster::Roster(); +} + + +Controller::~Controller() +{ + delete fWeb; +} + + +void +Controller::SetVideoView(VideoView *view) +{ + fVideoView = view; +} + + +void +Controller::SetVideoNode(VideoNode *node) +{ + fVideoNode = node; +} + + +void +Controller::VolumeUp() +{ +} + + +void +Controller::VolumeDown() +{ +} + + +status_t +Controller::ConnectNodes() +{ + status_t err; + +// dvb_node = gDeviceRoster->DeviceNode(fCurrentInterface); + + err = gMediaRoster->GetNodeFor(gDeviceRoster->DeviceNode(fCurrentInterface).node, &dvb_node); + HandleError("GetNodeFor failed", err); + + video_window_node = fVideoNode->Node(); + + err = gMediaRoster->GetAudioMixer(&audio_mixer_node); + HandleError("GetAudioMixer failed", err); + + media_input input; + media_output output; + media_format fmt; + int32 count; + + // Connect audio + + err = gMediaRoster->GetFreeOutputsFor(dvb_node, &output, 1, &count, B_MEDIA_RAW_AUDIO); + HandleError("Can't find free audio output", err); + if (count < 1) + HandleError("No free audio output", -1); + + err = gMediaRoster->GetFreeInputsFor(audio_mixer_node, &input, 1, &count, B_MEDIA_RAW_AUDIO); + HandleError("Can't find free audio input", err); + if (count < 1) + HandleError("No free audio input", -1); + + memset(&fmt, 0, sizeof(fmt)); + err = gMediaRoster->Connect(output.source, input.destination, &fmt, &audio_output, &audio_input); + HandleError("Can't connect audio", err); + + // Connect video + + err = gMediaRoster->GetFreeOutputsFor(dvb_node, &output, 1, &count, B_MEDIA_RAW_VIDEO); + HandleError("Can't find free video output", err); + if (count < 1) + HandleError("No free video output", -1); + + err = gMediaRoster->GetFreeInputsFor(video_window_node, &input, 1, &count, B_MEDIA_RAW_VIDEO); + HandleError("Can't find free video input", err); + if (count < 1) + HandleError("No free video input", -1); + + color_space cspaces_overlay[] = { B_YCbCr422, B_RGB32, B_NO_COLOR_SPACE }; + color_space cspaces_bitmap[] = { B_RGB32, B_NO_COLOR_SPACE }; + + fVideoNode->SetOverlayEnabled(true); + for (int i = 0; cspaces_overlay[i] != B_NO_COLOR_SPACE; i++) { + printf("trying connect with colorspace 0x%08x\n", cspaces_overlay[i]); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = B_MEDIA_RAW_VIDEO; + fmt.u.raw_video.display.format = cspaces_overlay[i]; + err = gMediaRoster->Connect(output.source, input.destination, &fmt, &video_output, &video_input); + if (err == B_OK) + break; + } + if (err) { + fVideoNode->SetOverlayEnabled(false); + for (int i = 0; cspaces_bitmap[i] != B_NO_COLOR_SPACE; i++) { + printf("trying connect with colorspace 0x%08x\n", cspaces_bitmap[i]); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = B_MEDIA_RAW_VIDEO; + fmt.u.raw_video.display.format = cspaces_bitmap[i]; + err = gMediaRoster->Connect(output.source, input.destination, &fmt, &video_output, &video_input); + if (err == B_OK) + break; + } + } + HandleError("Can't connect video", err); + + // set time sources + + err = gMediaRoster->GetTimeSource(&time_node); + HandleError("Can't get time source", err); + + BTimeSource *ts = gMediaRoster->MakeTimeSourceFor(time_node); + + err = gMediaRoster->SetTimeSourceFor(dvb_node.node, time_node.node); + HandleError("Can't set dvb time source", err); + + err = gMediaRoster->SetTimeSourceFor(audio_mixer_node.node, time_node.node); + HandleError("Can't set audio mixer time source", err); + + err = gMediaRoster->SetTimeSourceFor(video_window_node.node, time_node.node); + HandleError("Can't set video window time source", err); + + // Add a delay of (2 video frames) to the buffers send by the DVB node, + // because as a capture device in B_RECORDING run mode it's supposed to + // deliver buffers that were captured in the past (and does so). + // It is important that the audio buffer size used by the connection with + // the DVB node is smaller than this, optimum is the same length as one + // video frame (40 ms). However, this is not yet guaranteed. + err = gMediaRoster->SetProducerRunModeDelay(dvb_node, 80000); + HandleError("Can't set DVB producer delay", err); + + bigtime_t start_time = ts->Now() + 50000; + + ts->Release(); + + // start nodes + + err = gMediaRoster->StartNode(dvb_node, start_time); + HandleError("Can't start dvb node", err); + + err = gMediaRoster->StartNode(audio_mixer_node, start_time); + HandleError("Can't start audio mixer node", err); + + err = gMediaRoster->StartNode(video_window_node, start_time); + HandleError("Can't start video window node", err); + + printf("running...\n"); + + fConnected = true; + + return B_OK; +} + + +status_t +Controller::DisconnectNodes() +{ + printf("stopping...\n"); + + if (!fConnected) + return B_OK; + + status_t err; + + // stop nodes + + err = gMediaRoster->StopNode(dvb_node, 0, true); + HandleError("Can't stop dvb node", err); + + err = gMediaRoster->StopNode(audio_mixer_node, 0, true); + HandleError("Can't stop audio mixer node", err); + + err = gMediaRoster->StopNode(video_window_node, 0, true); + HandleError("Can't stop video window node", err); + + // disconnect nodes + + err = MediaRoster_Disconnect(video_output, video_input); + HandleError("Can't disconnect video", err); + + err = MediaRoster_Disconnect(audio_output, audio_input); + HandleError("Can't disconnect audio", err); + + // disable overlay, or erase image + + fVideoView->RemoveVideoDisplay(); + + // release other nodes + + err = gMediaRoster->ReleaseNode(audio_mixer_node); + HandleError("Can't release audio mixer node", err); + +// err = gMediaRoster->ReleaseNode(video_window_node); +// HandleError("Can't release video window node", err); + +// err = gMediaRoster->ReleaseNode(time_node); +// HandleError("Can't release time source node", err); + + // release dvb + + err = gMediaRoster->ReleaseNode(dvb_node); + HandleError("Can't release DVB node", err); + + fConnected = false; + + return B_OK; +} + diff --git a/src/apps/mediaplayer/Controller.h b/src/apps/mediaplayer/Controller.h new file mode 100644 index 0000000000..1046e7fe4a --- /dev/null +++ b/src/apps/mediaplayer/Controller.h @@ -0,0 +1,58 @@ +/* + * Controller.h - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef __CONTROLLER_H +#define __CONTROLLER_H + +#include +#include + +class VideoView; +class VideoNode; + +class Controller +{ +public: + Controller(); + virtual ~Controller(); + + void SetVideoView(VideoView *view); + void SetVideoNode(VideoNode *node); + + void VolumeUp(); + void VolumeDown(); + +private: + status_t ConnectNodes(); + status_t DisconnectNodes(); + +private: + int fCurrentInterface; + int fCurrentChannel; + VideoView * fVideoView; + VideoNode * fVideoNode; + BParameterWeb * fWeb; + BDiscreteParameter * fChannelParam; + bool fConnected; + media_input fInput; + media_output fOutput; +}; + + +#endif diff --git a/src/apps/mediaplayer/MainApp.cpp b/src/apps/mediaplayer/MainApp.cpp new file mode 100644 index 0000000000..6561e94350 --- /dev/null +++ b/src/apps/mediaplayer/MainApp.cpp @@ -0,0 +1,77 @@ +/* + * MainApp.cpp - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include +#include +#include +#include +#include +#include + +#include "MainApp.h" +#include "config.h" +#include "DeviceRoster.h" + +MainApp *gMainApp; + +MainApp::MainApp() + : BApplication(APP_SIG) +{ + InitPrefs(); + + gDeviceRoster = new DeviceRoster; + + fMainWindow = NewWindow(); +} + + +MainApp::~MainApp() +{ + delete gDeviceRoster; +} + + +status_t +MainApp::InitPrefs() +{ + return B_OK; +} + + +BWindow * +MainApp::NewWindow() +{ + static int i = 0; + BRect rect(200, 200, 750, 300); + rect.OffsetBy(i * 25, i * 25); + i = (i + 1) % 15; + BWindow *win = new MainWin(rect); + win->Show(); + return win; +} + + +int +main() +{ + gMainApp = new MainApp; + gMainApp->Run(); + delete gMainApp; + return 0; +} diff --git a/src/apps/mediaplayer/MainApp.h b/src/apps/mediaplayer/MainApp.h new file mode 100644 index 0000000000..ada55e4e8a --- /dev/null +++ b/src/apps/mediaplayer/MainApp.h @@ -0,0 +1,40 @@ +/* + * MainApp.h - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef __MAIN_APP_H +#define __MAIN_APP_H + +#include +#include "MainWin.h" + +class MainApp : public BApplication +{ +public: + MainApp(); + ~MainApp(); + status_t InitPrefs(); + BWindow * NewWindow(); + +private: + BWindow * fMainWindow; +}; + +extern MainApp *gMainApp; + +#endif diff --git a/src/apps/mediaplayer/MainWin.cpp b/src/apps/mediaplayer/MainWin.cpp new file mode 100644 index 0000000000..3b471128d2 --- /dev/null +++ b/src/apps/mediaplayer/MainWin.cpp @@ -0,0 +1,994 @@ +/* + * MainWin.cpp - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "MainWin.h" +#include "MainApp.h" +#include "Controller.h" +#include "config.h" +#include "DeviceRoster.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum +{ + M_DUMMY = 0x100, + M_CHECK_TB, + M_FILE_ABOUT, + M_FILE_QUIT, + M_SCALE_TO_NATIVE_SIZE, + M_TOGGLE_FULLSCREEN, + M_TOGGLE_NO_BORDER, + M_TOGGLE_NO_MENU, + M_TOGGLE_NO_BORDER_NO_MENU, + M_TOGGLE_ALWAYS_ON_TOP, + M_TOGGLE_KEEP_ASPECT_RATIO, + M_PREFERENCES, + M_CHANNEL_NEXT, + M_CHANNEL_PREV, + M_VOLUME_UP, + M_VOLUME_DOWN, + M_ASPECT_100000_1, + M_ASPECT_106666_1, + M_ASPECT_109091_1, + M_ASPECT_141176_1, + M_ASPECT_720_576, + M_ASPECT_704_576, + M_ASPECT_544_576, + M_SELECT_INTERFACE = 0x00000800, + M_SELECT_INTERFACE_END = 0x00000fff, + M_SELECT_CHANNEL = 0x00010000, + M_SELECT_CHANNEL_END = 0x000fffff, // this limits possible channel count to 0xeffff = 983039 +}; + +//#define printf(a...) + + +MainWin::MainWin(BRect frame_rect) + : BWindow(frame_rect, NAME, B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS /* | B_WILL_ACCEPT_FIRST_CLICK */) + , fController(new Controller) + , fIsFullscreen(false) + , fKeepAspectRatio(true) + , fAlwaysOnTop(false) + , fNoMenu(false) + , fNoBorder(false) + , fSourceWidth(720) + , fSourceHeight(576) + , fWidthScale(1.0) + , fHeightScale(1.0) + , fMouseDownTracking(false) + , fFrameResizedTriggeredAutomatically(false) + , fIgnoreFrameResized(false) + , fFrameResizedCalled(true) +{ + BRect rect = Bounds(); + + // background + fBackground = new BView(rect, "background", B_FOLLOW_ALL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + fBackground->SetViewColor(0,0,0); + AddChild(fBackground); + + // menu + fMenuBar = new BMenuBar(fBackground->Bounds(), "menu"); + CreateMenu(); + fBackground->AddChild(fMenuBar); + fMenuBar->ResizeToPreferred(); + fMenuBarHeight = (int)fMenuBar->Frame().Height() + 1; + fMenuBar->SetResizingMode(B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT); + + // video view + BRect video_rect = BRect(0, fMenuBarHeight, rect.right, rect.bottom); + fVideoView = new VideoView(video_rect, "video display", B_FOLLOW_ALL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + fBackground->AddChild(fVideoView); + + fVideoView->MakeFocus(); + +// SetSizeLimits(fControlViewMinWidth - 1, 32767, +// fMenuBarHeight + fControlViewHeight - 1, fMenuBarHeight + fControlViewHeight - 1); + +// SetSizeLimits(320 - 1, 32767, 240 + fMenuBarHeight - 1, 32767); + + SetSizeLimits(0, 32767, fMenuBarHeight - 1, 32767); + + fController->SetVideoView(fVideoView); + fController->SetVideoNode(fVideoView->Node()); + + fVideoView->IsOverlaySupported(); + + SetupInterfaceMenu(); + SelectInitialInterface(); + SetInterfaceMenuMarker(); + SetupChannelMenu(); + SetChannelMenuMarker(); + + VideoFormatChange(fSourceWidth, fSourceHeight, fWidthScale, fHeightScale); +} + + +MainWin::~MainWin() +{ + printf("MainWin::~MainWin\n"); + fController->DisconnectInterface(); + delete fController; +} + + +void +MainWin::CreateMenu() +{ + fFileMenu = new BMenu(NAME); + fChannelMenu = new BMenu("Channel"); + fInterfaceMenu = new BMenu("Interface"); + fSettingsMenu = new BMenu("Settings"); + fDebugMenu = new BMenu("Debug"); + + fMenuBar->AddItem(fFileMenu); + fMenuBar->AddItem(fChannelMenu); + fMenuBar->AddItem(fInterfaceMenu); + fMenuBar->AddItem(fSettingsMenu); + fMenuBar->AddItem(fDebugMenu); + + fFileMenu->AddItem(new BMenuItem("About", new BMessage(M_FILE_ABOUT), 'A', B_COMMAND_KEY)); + fFileMenu->AddSeparatorItem(); + fFileMenu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q', B_COMMAND_KEY)); + + + fSettingsMenu->AddItem(new BMenuItem("Scale to native size", new BMessage(M_SCALE_TO_NATIVE_SIZE), 'N', B_COMMAND_KEY)); + fSettingsMenu->AddItem(new BMenuItem("Full Screen", new BMessage(M_TOGGLE_FULLSCREEN), 'F', B_COMMAND_KEY)); + fSettingsMenu->AddSeparatorItem(); + fSettingsMenu->AddItem(new BMenuItem("No Menu", new BMessage(M_TOGGLE_NO_MENU), 'M', B_COMMAND_KEY)); + fSettingsMenu->AddItem(new BMenuItem("No Border", new BMessage(M_TOGGLE_NO_BORDER), 'B', B_COMMAND_KEY)); + fSettingsMenu->AddItem(new BMenuItem("Always on Top", new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T', B_COMMAND_KEY)); + fSettingsMenu->AddItem(new BMenuItem("Keep Aspect Ratio", new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K', B_COMMAND_KEY)); + fSettingsMenu->AddSeparatorItem(); + fSettingsMenu->AddItem(new BMenuItem("Preferences"B_UTF8_ELLIPSIS, new BMessage(M_PREFERENCES), 'P', B_COMMAND_KEY)); + + fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.00000:1", new BMessage(M_ASPECT_100000_1))); + fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.06666:1", new BMessage(M_ASPECT_106666_1))); + fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.09091:1", new BMessage(M_ASPECT_109091_1))); + fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.41176:1", new BMessage(M_ASPECT_141176_1))); + fDebugMenu->AddItem(new BMenuItem("force 720 x 576, display aspect 4:3", new BMessage(M_ASPECT_720_576))); + fDebugMenu->AddItem(new BMenuItem("force 704 x 576, display aspect 4:3", new BMessage(M_ASPECT_704_576))); + fDebugMenu->AddItem(new BMenuItem("force 544 x 576, display aspect 4:3", new BMessage(M_ASPECT_544_576))); + + fSettingsMenu->ItemAt(1)->SetMarked(fIsFullscreen); + fSettingsMenu->ItemAt(3)->SetMarked(fNoMenu); + fSettingsMenu->ItemAt(4)->SetMarked(fNoBorder); + fSettingsMenu->ItemAt(5)->SetMarked(fAlwaysOnTop); + fSettingsMenu->ItemAt(6)->SetMarked(fKeepAspectRatio); + fSettingsMenu->ItemAt(8)->SetEnabled(false); // XXX disable unused preference menu +} + + + +bool +MainWin::QuitRequested() +{ + be_app->PostMessage(B_QUIT_REQUESTED); + return true; +} + + +void +MainWin::MouseDown(BMessage *msg) +{ + BPoint screen_where; + uint32 buttons = msg->FindInt32("buttons"); + + // On Zeta, only "screen_where" is relyable, "where" and "be:view_where" seem to be broken + if (B_OK != msg->FindPoint("screen_where", &screen_where)) { + // Workaround for BeOS R5, it has no "screen_where" + fVideoView->GetMouse(&screen_where, &buttons, false); + fVideoView->ConvertToScreen(&screen_where); + } + +// msg->PrintToStream(); + +// if (1 == msg->FindInt32("buttons") && msg->FindInt32("clicks") == 1) { + + if (1 == buttons && msg->FindInt32("clicks") % 2 == 0) { + BRect r(screen_where.x - 1, screen_where.y - 1, screen_where.x + 1, screen_where.y + 1); + if (r.Contains(fMouseDownMousePos)) { + PostMessage(M_TOGGLE_FULLSCREEN); + return; + } + } + + if (2 == buttons && msg->FindInt32("clicks") % 2 == 0) { + BRect r(screen_where.x - 1, screen_where.y - 1, screen_where.x + 1, screen_where.y + 1); + if (r.Contains(fMouseDownMousePos)) { + PostMessage(M_TOGGLE_NO_BORDER_NO_MENU); + return; + } + } + +/* + // very broken in Zeta: + fMouseDownMousePos = fVideoView->ConvertToScreen(msg->FindPoint("where")); +*/ + fMouseDownMousePos = screen_where; + fMouseDownWindowPos = Frame().LeftTop(); + + if (buttons == 1 && !fIsFullscreen) { + // start mouse tracking + fVideoView->SetMouseEventMask(B_POINTER_EVENTS | B_NO_POINTER_HISTORY /* | B_LOCK_WINDOW_FOCUS */); + fMouseDownTracking = true; + } + + // pop up a context menu if right mouse button is down for 200 ms + + if ((buttons & 2) == 0) + return; + bigtime_t start = system_time(); + bigtime_t delay = 200000; + BPoint location; + do { + fVideoView->GetMouse(&location, &buttons); + if ((buttons & 2) == 0) + break; + snooze(1000); + } while (system_time() - start < delay); + + if (buttons & 2) + ShowContextMenu(screen_where); +} + + +void +MainWin::MouseMoved(BMessage *msg) +{ +// msg->PrintToStream(); + + BPoint mousePos; + uint32 buttons = msg->FindInt32("buttons"); + + if (1 == buttons && fMouseDownTracking && !fIsFullscreen) { +/* + // very broken in Zeta: + BPoint mousePos = msg->FindPoint("where"); + printf("view where: %.0f, %.0f => ", mousePos.x, mousePos.y); + fVideoView->ConvertToScreen(&mousePos); +*/ + // On Zeta, only "screen_where" is relyable, "where" and "be:view_where" seem to be broken + if (B_OK != msg->FindPoint("screen_where", &mousePos)) { + // Workaround for BeOS R5, it has no "screen_where" + fVideoView->GetMouse(&mousePos, &buttons, false); + fVideoView->ConvertToScreen(&mousePos); + } +// printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y); + float delta_x = mousePos.x - fMouseDownMousePos.x; + float delta_y = mousePos.y - fMouseDownMousePos.y; + float x = fMouseDownWindowPos.x + delta_x; + float y = fMouseDownWindowPos.y + delta_y; +// printf("move window to %.0f, %.0f\n", x, y); + MoveTo(x, y); + } +} + + +void +MainWin::MouseUp(BMessage *msg) +{ +// msg->PrintToStream(); + fMouseDownTracking = false; +} + + +void +MainWin::ShowContextMenu(const BPoint &screen_point) +{ + printf("Show context menu\n"); + BPopUpMenu *menu = new BPopUpMenu("context menu", false, false); + BMenuItem *item; + menu->AddItem(new BMenuItem("Scale to native size", new BMessage(M_SCALE_TO_NATIVE_SIZE), 'N', B_COMMAND_KEY)); + menu->AddItem(item = new BMenuItem("Full Screen", new BMessage(M_TOGGLE_FULLSCREEN), 'F', B_COMMAND_KEY)); + item->SetMarked(fIsFullscreen); + menu->AddSeparatorItem(); + menu->AddItem(item = new BMenuItem("No Menu", new BMessage(M_TOGGLE_NO_MENU), 'M', B_COMMAND_KEY)); + item->SetMarked(fNoMenu); + menu->AddItem(item = new BMenuItem("No Border", new BMessage(M_TOGGLE_NO_BORDER), 'B', B_COMMAND_KEY)); + item->SetMarked(fNoBorder); + menu->AddItem(item = new BMenuItem("Always on Top", new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T', B_COMMAND_KEY)); + item->SetMarked(fAlwaysOnTop); + menu->AddItem(item = new BMenuItem("Keep Aspect Ratio", new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K', B_COMMAND_KEY)); + item->SetMarked(fKeepAspectRatio); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("About", new BMessage(M_FILE_ABOUT), 'A', B_COMMAND_KEY)); + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q', B_COMMAND_KEY)); + + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem("pixel aspect ratio 1.00000:1", new BMessage(M_ASPECT_100000_1))); + menu->AddItem(new BMenuItem("pixel aspect ratio 1.06666:1", new BMessage(M_ASPECT_106666_1))); + menu->AddItem(new BMenuItem("pixel aspect ratio 1.09091:1", new BMessage(M_ASPECT_109091_1))); + menu->AddItem(new BMenuItem("pixel aspect ratio 1.41176:1", new BMessage(M_ASPECT_141176_1))); + menu->AddItem(new BMenuItem("force 720 x 576, display aspect 4:3", new BMessage(M_ASPECT_720_576))); + menu->AddItem(new BMenuItem("force 704 x 576, display aspect 4:3", new BMessage(M_ASPECT_704_576))); + menu->AddItem(new BMenuItem("force 544 x 576, display aspect 4:3", new BMessage(M_ASPECT_544_576))); + + menu->SetTargetForItems(this); + BRect r(screen_point.x - 5, screen_point.y - 5, screen_point.x + 5, screen_point.y + 5); + menu->Go(screen_point, true, true, r, true); +} + + +void +MainWin::VideoFormatChange(int width, int height, float width_scale, float height_scale) +{ + // called when video format or aspect ratio changes + + printf("VideoFormatChange enter: width %d, height %d, width_scale %.6f, height_scale %.6f\n", width, height, width_scale, height_scale); + + if (width_scale < 1.0 && height_scale >= 1.0) { + width_scale = 1.0 / width_scale; + height_scale = 1.0 / height_scale; + printf("inverting! new values: width_scale %.6f, height_scale %.6f\n", width_scale, height_scale); + } + + fSourceWidth = width; + fSourceHeight = height; + fWidthScale = width_scale; + fHeightScale = height_scale; + +// ResizeWindow(Bounds().Width() + 1, Bounds().Height() + 1, true); + + if (fIsFullscreen) { + AdjustFullscreenRenderer(); + } else { + AdjustWindowedRenderer(false); + } + + printf("VideoFormatChange leave\n"); + +} + + +void +MainWin::Zoom(BPoint rec_position, float rec_width, float rec_height) +{ + PostMessage(M_TOGGLE_FULLSCREEN); +} + + +void +MainWin::FrameResized(float new_width, float new_height) +{ + // called when the window got resized + fFrameResizedCalled = true; + + if (new_width != Bounds().Width() || new_height != Bounds().Height()) { + debugger("size wrong\n"); + } + + + printf("FrameResized enter: new_width %.0f, new_height %.0f, bounds width %.0f, bounds height %.0f\n", new_width, new_height, Bounds().Width(), Bounds().Height()); + + if (fIsFullscreen) { + + printf("FrameResized in fullscreen mode\n"); + + fIgnoreFrameResized = false; + AdjustFullscreenRenderer(); + + } else { + + if (fFrameResizedTriggeredAutomatically) { + fFrameResizedTriggeredAutomatically = false; + printf("FrameResized triggered automatically\n"); + + fIgnoreFrameResized = false; + + AdjustWindowedRenderer(false); + } else { + printf("FrameResized by user in window mode\n"); + + if (fIgnoreFrameResized) { + fIgnoreFrameResized = false; + printf("FrameResized ignored\n"); + return; + } + + AdjustWindowedRenderer(true); + } + + } + + printf("FrameResized leave\n"); +} + + + +void +MainWin::UpdateWindowTitle() +{ + char buf[100]; + sprintf(buf, "%s - %d x %d, %.3f:%.3f => %.0f x %.0f", NAME, fSourceWidth, fSourceHeight, fWidthScale, fHeightScale, fVideoView->Bounds().Width() + 1, fVideoView->Bounds().Height() + 1); + SetTitle(buf); +} + + +void +MainWin::AdjustFullscreenRenderer() +{ + // n.b. we don't have a menu in fullscreen mode! + + if (fKeepAspectRatio) { + + // Keep aspect ratio, place render inside + // the background area (may create black bars). + float max_width = fBackground->Bounds().Width() + 1.0f; + float max_height = fBackground->Bounds().Height() + 1.0f; + float scaled_width = fSourceWidth * fWidthScale; + float scaled_height = fSourceHeight * fHeightScale; + float factor = min_c(max_width / scaled_width, max_height / scaled_height); + int render_width = int(scaled_width * factor); + int render_height = int(scaled_height * factor); + int x_ofs = (int(max_width) - render_width) / 2; + int y_ofs = (int(max_height) - render_height) / 2; + + printf("AdjustFullscreenRenderer: background %.1f x %.1f, src video %d x %d, " + "scaled video %.3f x %.3f, factor %.3f, render %d x %d, x-ofs %d, y-ofs %d\n", + max_width, max_height, fSourceWidth, fSourceHeight, scaled_width, scaled_height, + factor, render_width, render_height, x_ofs, y_ofs); + + fVideoView->MoveTo(x_ofs, y_ofs); + fVideoView->ResizeTo(render_width - 1, render_height - 1); + + } else { + + printf("AdjustFullscreenRenderer: using whole background area\n"); + + // no need to keep aspect ratio, make + // render cover the whole background + fVideoView->MoveTo(0, 0); + fVideoView->ResizeTo(fBackground->Bounds().Width(), fBackground->Bounds().Height()); + + } +} + + +void +MainWin::AdjustWindowedRenderer(bool user_resized) +{ + printf("AdjustWindowedRenderer enter - user_resized %d\n", user_resized); + + // In windowed mode, the renderer always covers the + // whole background, accounting for the menu + fVideoView->MoveTo(0, fNoMenu ? 0 : fMenuBarHeight); + fVideoView->ResizeTo(fBackground->Bounds().Width(), fBackground->Bounds().Height() - (fNoMenu ? 0 : fMenuBarHeight)); + + if (fKeepAspectRatio) { + // To keep the aspect ratio correct, we + // do resize the window as required + + float max_width = Bounds().Width() + 1.0f; + float max_height = Bounds().Height() + 1.0f - (fNoMenu ? 0 : fMenuBarHeight); + float scaled_width = fSourceWidth * fWidthScale; + float scaled_height = fSourceHeight * fHeightScale; + + if (!user_resized && (scaled_width > max_width || scaled_height > max_height)) { + // A format switch occured, and the window was + // smaller then the video source. As it was not + // initiated by the user resizing the window, we + // enlarge the window to fit the video. + fIgnoreFrameResized = true; + ResizeTo(scaled_width - 1, scaled_height - 1 + (fNoMenu ? 0 : fMenuBarHeight)); +// Sync(); + return; + } + + float display_aspect_ratio = scaled_width / scaled_height; + int new_width = int(max_width); + int new_height = int(max_width / display_aspect_ratio + 0.5); + + printf("AdjustWindowedRenderer: old display %d x %d, src video %d x %d, " + "scaled video %.3f x %.3f, aspect ratio %.3f, new display %d x %d\n", + int(max_width), int(max_height), fSourceWidth, fSourceHeight, scaled_width, scaled_height, + display_aspect_ratio, new_width, new_height); + + fIgnoreFrameResized = true; + ResizeTo(new_width - 1, new_height - 1 + (fNoMenu ? 0 : fMenuBarHeight)); +// Sync(); + } + + printf("AdjustWindowedRenderer leave\n"); +} + + +void +MainWin::ToggleNoBorderNoMenu() +{ + if (!fNoMenu && fNoBorder) { + // if no border, switch of menu, too + PostMessage(M_TOGGLE_NO_MENU); + } else + if (fNoMenu && !fNoBorder) { + // if no menu, switch of border, too + PostMessage(M_TOGGLE_NO_BORDER); + } else { + // both are either on or off, toggle both + PostMessage(M_TOGGLE_NO_MENU); + PostMessage(M_TOGGLE_NO_BORDER); + } +} + + +void +MainWin::ToggleFullscreen() +{ + printf("ToggleFullscreen enter\n"); + + if (!fFrameResizedCalled) { + printf("ToggleFullscreen - ignoring, as FrameResized wasn't called since last switch\n"); + return; + } + fFrameResizedCalled = false; + + + fIsFullscreen = !fIsFullscreen; + + if (fIsFullscreen) { + // switch to fullscreen + + fSavedFrame = Frame(); + printf("saving current frame: %d %d %d %d\n", int(fSavedFrame.left), int(fSavedFrame.top), int(fSavedFrame.right), int(fSavedFrame.bottom)); + BScreen screen(this); + BRect rect(screen.Frame()); + + Hide(); + if (!fNoMenu) { + // if we have a menu, remove it now + fMenuBar->Hide(); + } + fFrameResizedTriggeredAutomatically = true; + MoveTo(rect.left, rect.top); + ResizeTo(rect.Width(), rect.Height()); + Show(); + + } else { + // switch back from full screen mode + + Hide(); + // if we need a menu, show it now + if (!fNoMenu) { + fMenuBar->Show(); + } + fFrameResizedTriggeredAutomatically = true; + MoveTo(fSavedFrame.left, fSavedFrame.top); + ResizeTo(fSavedFrame.Width(), fSavedFrame.Height()); + Show(); + + } + + // FrameResized() will do the required adjustments + + printf("ToggleFullscreen leave\n"); +} + + +void +MainWin::ToggleNoMenu() +{ + printf("ToggleNoMenu enter\n"); + + fNoMenu = !fNoMenu; + + if (fIsFullscreen) { + // fullscreen is always without menu + printf("ToggleNoMenu leave, doing nothing, we are fullscreen\n"); + return; + } + +// fFrameResizedTriggeredAutomatically = true; + fIgnoreFrameResized = true; + + if (fNoMenu) { + fMenuBar->Hide(); + fVideoView->MoveTo(0, 0); + fVideoView->ResizeBy(0, fMenuBarHeight); + MoveBy(0, fMenuBarHeight); + ResizeBy(0, - fMenuBarHeight); +// Sync(); + } else { + fMenuBar->Show(); + fVideoView->MoveTo(0, fMenuBarHeight); + fVideoView->ResizeBy(0, -fMenuBarHeight); + MoveBy(0, - fMenuBarHeight); + ResizeBy(0, fMenuBarHeight); +// Sync(); + } + + printf("ToggleNoMenu leave\n"); +} + + +void +MainWin::ToggleNoBorder() +{ + printf("ToggleNoBorder enter\n"); + fNoBorder = !fNoBorder; +// SetLook(fNoBorder ? B_NO_BORDER_WINDOW_LOOK : B_TITLED_WINDOW_LOOK); + SetLook(fNoBorder ? B_BORDERED_WINDOW_LOOK : B_TITLED_WINDOW_LOOK); + printf("ToggleNoBorder leave\n"); +} + + +void +MainWin::ToggleAlwaysOnTop() +{ + printf("ToggleAlwaysOnTop enter\n"); + fAlwaysOnTop = !fAlwaysOnTop; + SetFeel(fAlwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL); + printf("ToggleAlwaysOnTop leave\n"); +} + + +void +MainWin::ToggleKeepAspectRatio() +{ + printf("ToggleKeepAspectRatio enter\n"); + fKeepAspectRatio = !fKeepAspectRatio; + + fFrameResizedTriggeredAutomatically = true; + FrameResized(Bounds().Width(), Bounds().Height()); +// if (fIsFullscreen) { +// AdjustFullscreenRenderer(); +// } else { +// AdjustWindowedRenderer(false); +// } + printf("ToggleKeepAspectRatio leave\n"); +} + + +/* Trap keys that are about to be send to background or renderer view. + * Return B_OK if it shouldn't be passed to the view + */ +status_t +MainWin::KeyDown(BMessage *msg) +{ +// msg->PrintToStream(); + + uint32 key = msg->FindInt32("key"); + uint32 raw_char = msg->FindInt32("raw_char"); + uint32 modifiers = msg->FindInt32("modifiers"); + + printf("key 0x%lx, raw_char 0x%lx, modifiers 0x%lx\n", key, raw_char, modifiers); + + switch (raw_char) { + case B_SPACE: + PostMessage(M_TOGGLE_NO_BORDER_NO_MENU); + return B_OK; + + case B_ESCAPE: + if (fIsFullscreen) { + PostMessage(M_TOGGLE_FULLSCREEN); + return B_OK; + } else + break; + + case B_ENTER: // Enter / Return + if (modifiers & B_COMMAND_KEY) { + PostMessage(M_TOGGLE_FULLSCREEN); + return B_OK; + } else + break; + + case B_TAB: + if ((modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY)) == 0) { + PostMessage(M_TOGGLE_FULLSCREEN); + return B_OK; + } else + break; + + case B_UP_ARROW: + if (modifiers & B_COMMAND_KEY) { + PostMessage(M_CHANNEL_NEXT); + } else { + PostMessage(M_VOLUME_UP); + } + return B_OK; + + case B_DOWN_ARROW: + if (modifiers & B_COMMAND_KEY) { + PostMessage(M_CHANNEL_PREV); + } else { + PostMessage(M_VOLUME_DOWN); + } + return B_OK; + + case B_RIGHT_ARROW: + if (modifiers & B_COMMAND_KEY) { + PostMessage(M_VOLUME_UP); + } else { + PostMessage(M_CHANNEL_NEXT); + } + return B_OK; + + case B_LEFT_ARROW: + if (modifiers & B_COMMAND_KEY) { + PostMessage(M_VOLUME_DOWN); + } else { + PostMessage(M_CHANNEL_PREV); + } + return B_OK; + + case B_PAGE_UP: + PostMessage(M_CHANNEL_NEXT); + return B_OK; + + case B_PAGE_DOWN: + PostMessage(M_CHANNEL_PREV); + return B_OK; + } + + switch (key) { + case 0x3a: // numeric keypad + + if ((modifiers & B_COMMAND_KEY) == 0) { + printf("if\n"); + PostMessage(M_VOLUME_UP); + return B_OK; + } else { + printf("else\n"); + break; + } + + case 0x25: // numeric keypad - + if ((modifiers & B_COMMAND_KEY) == 0) { + PostMessage(M_VOLUME_DOWN); + return B_OK; + } else { + break; + } + + case 0x38: // numeric keypad up arrow + PostMessage(M_VOLUME_UP); + return B_OK; + + case 0x59: // numeric keypad down arrow + PostMessage(M_VOLUME_DOWN); + return B_OK; + + case 0x39: // numeric keypad page up + case 0x4a: // numeric keypad right arrow + PostMessage(M_CHANNEL_NEXT); + return B_OK; + + case 0x5a: // numeric keypad page down + case 0x48: // numeric keypad left arrow + PostMessage(M_CHANNEL_PREV); + return B_OK; + } + + return B_ERROR; +} + + +void +MainWin::DispatchMessage(BMessage *msg, BHandler *handler) +{ + if ((msg->what == B_MOUSE_DOWN) && (handler == fBackground || handler == fVideoView)) + MouseDown(msg); + if ((msg->what == B_MOUSE_MOVED) && (handler == fBackground || handler == fVideoView)) + MouseMoved(msg); + if ((msg->what == B_MOUSE_UP) && (handler == fBackground || handler == fVideoView)) + MouseUp(msg); + + if ((msg->what == B_KEY_DOWN) && (handler == fBackground || handler == fVideoView)) { + + // special case for PrintScreen key + if (msg->FindInt32("key") == B_PRINT_KEY) { + fVideoView->OverlayScreenshotPrepare(); + BWindow::DispatchMessage(msg, handler); + fVideoView->OverlayScreenshotCleanup(); + return; + } + + // every other key gets dispatched to our KeyDown first + if (KeyDown(msg) == B_OK) { + // it got handled, don't pass it on + return; + } + } + + BWindow::DispatchMessage(msg, handler); +} + + +void +MainWin::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + case B_ACQUIRE_OVERLAY_LOCK: + printf("B_ACQUIRE_OVERLAY_LOCK\n"); + fVideoView->OverlayLockAcquire(); + break; + + case B_RELEASE_OVERLAY_LOCK: + printf("B_RELEASE_OVERLAY_LOCK\n"); + fVideoView->OverlayLockRelease(); + break; + + case B_MOUSE_WHEEL_CHANGED: + { + printf("B_MOUSE_WHEEL_CHANGED\n"); + float dx = msg->FindFloat("be:wheel_delta_x"); + float dy = msg->FindFloat("be:wheel_delta_y"); + bool inv = modifiers() & B_COMMAND_KEY; + if (dx > 0.1) PostMessage(inv ? M_VOLUME_DOWN : M_CHANNEL_PREV); + if (dx < -0.1) PostMessage(inv ? M_VOLUME_UP : M_CHANNEL_NEXT); + if (dy > 0.1) PostMessage(inv ? M_CHANNEL_PREV : M_VOLUME_DOWN); + if (dy < -0.1) PostMessage(inv ? M_CHANNEL_NEXT : M_VOLUME_UP); + break; + } + + case M_CHANNEL_NEXT: + { + printf("M_CHANNEL_NEXT\n"); + int chan = fController->CurrentChannel(); + if (chan != -1) { + chan++; + if (chan < fController->ChannelCount()) + SelectChannel(chan); + } + break; + } + + case M_CHANNEL_PREV: + { + printf("M_CHANNEL_PREV\n"); + int chan = fController->CurrentChannel(); + if (chan != -1) { + chan--; + if (chan >= 0) + SelectChannel(chan); + } + break; + } + + case M_VOLUME_UP: + printf("M_VOLUME_UP\n"); + fController->VolumeUp(); + break; + + case M_VOLUME_DOWN: + printf("M_VOLUME_DOWN\n"); + fController->VolumeDown(); + break; + + case M_ASPECT_100000_1: + VideoFormatChange(fSourceWidth, fSourceHeight, 1.0, 1.0); + break; + + case M_ASPECT_106666_1: + VideoFormatChange(fSourceWidth, fSourceHeight, 1.06666, 1.0); + break; + + case M_ASPECT_109091_1: + VideoFormatChange(fSourceWidth, fSourceHeight, 1.09091, 1.0); + break; + + case M_ASPECT_141176_1: + VideoFormatChange(fSourceWidth, fSourceHeight, 1.41176, 1.0); + break; + + case M_ASPECT_720_576: + VideoFormatChange(720, 576, 1.06666, 1.0); + break; + + case M_ASPECT_704_576: + VideoFormatChange(704, 576, 1.09091, 1.0); + break; + + case M_ASPECT_544_576: + VideoFormatChange(544, 576, 1.41176, 1.0); + break; + + case B_REFS_RECEIVED: + printf("MainWin::MessageReceived: B_REFS_RECEIVED\n"); +// RefsReceived(msg); + break; + + case B_SIMPLE_DATA: + printf("MainWin::MessageReceived: B_SIMPLE_DATA\n"); +// if (msg->HasRef("refs")) +// RefsReceived(msg); + break; + + case M_FILE_ABOUT: + BAlert *alert; + alert = new BAlert("about", NAME"\n\n" + INFO1 + "\n\nCopyright "COPYRIGHT"\n\nVersion "VERSION"\n\nRevision "REVISION"\n\nBuild "BUILD, "Thanks"); + if (fAlwaysOnTop) { + ToggleAlwaysOnTop(); + alert->Go(); + ToggleAlwaysOnTop(); + } else { + alert->Go(); + } + break; + + case M_FILE_QUIT: +// be_app->PostMessage(B_QUIT_REQUESTED); + PostMessage(B_QUIT_REQUESTED); + break; + + case M_SCALE_TO_NATIVE_SIZE: + printf("M_SCALE_TO_NATIVE_SIZE\n"); + if (fIsFullscreen) { + ToggleFullscreen(); + } + ResizeTo(int(fSourceWidth * fWidthScale), + int(fSourceHeight * fHeightScale) + (fNoMenu ? 0 : fMenuBarHeight)); +// Sync(); + break; + + case M_TOGGLE_FULLSCREEN: + ToggleFullscreen(); + fSettingsMenu->ItemAt(1)->SetMarked(fIsFullscreen); + break; + + case M_TOGGLE_NO_MENU: + ToggleNoMenu(); + fSettingsMenu->ItemAt(3)->SetMarked(fNoMenu); + break; + + case M_TOGGLE_NO_BORDER: + ToggleNoBorder(); + fSettingsMenu->ItemAt(4)->SetMarked(fNoBorder); + break; + + case M_TOGGLE_ALWAYS_ON_TOP: + ToggleAlwaysOnTop(); + fSettingsMenu->ItemAt(5)->SetMarked(fAlwaysOnTop); + break; + + case M_TOGGLE_KEEP_ASPECT_RATIO: + ToggleKeepAspectRatio(); + fSettingsMenu->ItemAt(6)->SetMarked(fKeepAspectRatio); + break; + + case M_TOGGLE_NO_BORDER_NO_MENU: + ToggleNoBorderNoMenu(); + break; + + case M_PREFERENCES: + break; + + default: + if (msg->what >= M_SELECT_CHANNEL && msg->what <= M_SELECT_CHANNEL_END) { + SelectChannel(msg->what - M_SELECT_CHANNEL); + break; + } + if (msg->what >= M_SELECT_INTERFACE && msg->what <= M_SELECT_INTERFACE_END) { + SelectInterface(msg->what - M_SELECT_INTERFACE - 1); + break; + } + } +} diff --git a/src/apps/mediaplayer/MainWin.h b/src/apps/mediaplayer/MainWin.h new file mode 100644 index 0000000000..0f59834685 --- /dev/null +++ b/src/apps/mediaplayer/MainWin.h @@ -0,0 +1,95 @@ +/* + * MainWin.h - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef __MAIN_WIN_H +#define __MAIN_WIN_H + +#include +#include +#include +#include +#include "Controller.h" +#include "VideoView.h" + +class MainWin : public BWindow +{ +public: + MainWin(BRect rect); + ~MainWin(); + + void FrameResized(float new_width, float new_height); + void Zoom(BPoint rec_position, float rec_width, float rec_height); + void DispatchMessage(BMessage *message, BHandler *handler); + void MessageReceived(BMessage *msg); + bool QuitRequested(); + + void MouseDown(BMessage *msg); + void MouseMoved(BMessage *msg); + void MouseUp(BMessage *msg); + status_t KeyDown(BMessage *msg); + + void CreateMenu(); + + void VideoFormatChange(int width, int height, float width_scale, float height_scale); + + void UpdateWindowTitle(); + + void AdjustFullscreenRenderer(); + void AdjustWindowedRenderer(bool user_resized); + + void ToggleFullscreen(); + void ToggleKeepAspectRatio(); + void ToggleAlwaysOnTop(); + void ToggleNoBorder(); + void ToggleNoMenu(); + void ToggleNoBorderNoMenu(); + + void ShowContextMenu(const BPoint &screen_point); + + BMenuBar * fMenuBar; + BView * fBackground; + VideoView * fVideoView; + + BMenu * fFileMenu; + BMenu * fChannelMenu; + BMenu * fInterfaceMenu; + BMenu * fSettingsMenu; + BMenu * fDebugMenu; + + Controller * fController; + volatile bool fIsFullscreen; + volatile bool fKeepAspectRatio; + volatile bool fAlwaysOnTop; + volatile bool fNoMenu; + volatile bool fNoBorder; + int fSourceWidth; + int fSourceHeight; + float fWidthScale; + float fHeightScale; + int fMenuBarHeight; + BRect fSavedFrame; + bool fMouseDownTracking; + BPoint fMouseDownMousePos; + BPoint fMouseDownWindowPos; + bool fFrameResizedTriggeredAutomatically; + bool fIgnoreFrameResized; + bool fFrameResizedCalled; +}; + +#endif diff --git a/src/apps/mediaplayer/VideoNode.cpp b/src/apps/mediaplayer/VideoNode.cpp new file mode 100644 index 0000000000..b7c2387b32 --- /dev/null +++ b/src/apps/mediaplayer/VideoNode.cpp @@ -0,0 +1,380 @@ +/* + * VideoNode.cpp - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VideoNode.h" +#include "VideoView.h" + + +VideoNode::VideoNode(const char *name, VideoView *view) + : BMediaNode(name) + , BMediaEventLooper() + , BBufferConsumer(B_MEDIA_RAW_VIDEO) + , fVideoView(view) + , fInput() + , fOverlayEnabled(true) + , fOverlayActive(false) + , fDirectOverlayBuffer(false) + , fBitmap(0) + , fBitmapLocker(new BLocker("Video Node Locker")) +{ +} + + +VideoNode::~VideoNode() +{ + Quit(); + DeleteBuffers(); + delete fBitmapLocker; +} + + +BMediaAddOn * +VideoNode::AddOn(int32 *internal_id) const +{ + *internal_id = 0; + return NULL; +} + + +void +VideoNode::NodeRegistered() +{ + fInput.node = Node(); + fInput.source = media_source::null; + fInput.destination.port = ControlPort(); + fInput.destination.id = 0; + fInput.format.type = B_MEDIA_RAW_VIDEO; + fInput.format.u.raw_video = media_raw_video_format::wildcard; + strcpy(fInput.name, "video in"); + + SetPriority(B_DISPLAY_PRIORITY); + Run(); +} + + +void +VideoNode::BufferReceived(BBuffer * buffer) +{ + if (RunState() != B_STARTED) { + buffer->Recycle(); + return; + } + if (fOverlayActive && fDirectOverlayBuffer) { + HandleBuffer(buffer); + } else { + media_timed_event event(buffer->Header()->start_time, + BTimedEventQueue::B_HANDLE_BUFFER, + buffer, + BTimedEventQueue::B_RECYCLE_BUFFER); + EventQueue()->AddEvent(event); + } +} + + +status_t +VideoNode::GetNextInput(int32 *cookie, media_input *out_input) +{ + if (*cookie < 1) { + *out_input = fInput; + *cookie += 1; + return B_OK; + } + return B_ERROR; +} + + +void +VideoNode::DisposeInputCookie(int32 cookie) +{ + // nothing to do +} + + +status_t +VideoNode:: HandleMessage(int32 message, + const void *data, + size_t size) +{ + return B_ERROR; +} + + +void +VideoNode::HandleEvent(const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + switch (event->type) { + case BTimedEventQueue::B_START: + break; + case BTimedEventQueue::B_STOP: + EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + HandleBuffer((BBuffer *)event->pointer); + break; + default: + printf("VideoNode::HandleEvent unknown event"); + break; + } +} + + +void +VideoNode::ProducerDataStatus(const media_destination &dst, + int32 status, + bigtime_t at_media_time) +{ + // do nothing +} + + +status_t +VideoNode::GetLatencyFor(const media_destination &dst, + bigtime_t *out_latency, + media_node_id *out_id) +{ + if (dst != fInput.destination) + return B_MEDIA_BAD_DESTINATION; + + *out_latency = 10000; + *out_id = TimeSource()->ID(); + return B_OK; +} + +status_t +VideoNode::AcceptFormat(const media_destination &dst, + media_format *format) +{ + /* The connection process: + * BBufferProducer::FormatProposal + * we are here => BBufferConsumer::AcceptFormat + * BBufferProducer::PrepareToConnect + * BBufferConsumer::Connected + * BBufferProducer::Connect + */ + + if (dst != fInput.destination) + return B_MEDIA_BAD_DESTINATION; + + if (format->type == B_MEDIA_NO_TYPE) + format->type = B_MEDIA_RAW_VIDEO; + + if (format->type != B_MEDIA_RAW_VIDEO) + return B_MEDIA_BAD_FORMAT; + + + return B_OK; +} + + +status_t +VideoNode::Connected(const media_source &src, + const media_destination &dst, + const media_format &format, + media_input *out_input) +{ + /* The connection process: + * BBufferProducer::FormatProposal + * BBufferConsumer::AcceptFormat + * BBufferProducer::PrepareToConnect + * we are here => BBufferConsumer::Connected + * BBufferProducer::Connect + */ + + if (dst != fInput.destination) + return B_MEDIA_BAD_DESTINATION; + + fInput.source = src; + fInput.format = format; + + if (fInput.format.u.raw_video.field_rate < 1.0) + fInput.format.u.raw_video.field_rate = 25.0; + + color_space colorspace = format.u.raw_video.display.format; + BRect frame(0, 0, format.u.raw_video.display.line_width - 1, format.u.raw_video.display.line_count - 1); + status_t err; + + DeleteBuffers(); + err = CreateBuffers(frame, colorspace, fOverlayEnabled); + if (err) { + printf("VideoNode::Connected failed, fOverlayEnabled = %d\n", fOverlayEnabled); + return err; + } + + *out_input = fInput; + + return B_OK; + +} + + +void +VideoNode::Disconnected(const media_source &src, + const media_destination &dst) +{ + if (src != fInput.source) + return; + if (dst != fInput.destination) + return; + + DeleteBuffers(); + + // disconnect the connection + fInput.source = media_source::null; +} + + +status_t +VideoNode::FormatChanged(const media_source &src, + const media_destination &dst, + int32 from_change_count, + const media_format &format) +{ + printf("VideoNode::FormatChanged enter\n"); + if (src != fInput.source) + return B_MEDIA_BAD_SOURCE; + if (dst != fInput.destination) + return B_MEDIA_BAD_DESTINATION; + + color_space colorspace = format.u.raw_video.display.format; + BRect frame(0, 0, format.u.raw_video.display.line_width - 1, format.u.raw_video.display.line_count - 1); + status_t err; + + DeleteBuffers(); + if (fOverlayEnabled) { + fVideoView->RemoveOverlay(); + err = CreateBuffers(frame, colorspace, true); // try overlay + if (err) { + printf("VideoNode::FormatChanged creating overlay buffer failed\n"); + err = CreateBuffers(frame, colorspace, false); // no overlay + } + } else { + err = CreateBuffers(frame, colorspace, false); // no overlay + } + if (err) { + printf("VideoNode::FormatChanged failed (lost buffer group!)\n"); + return B_MEDIA_BAD_FORMAT; + } + + fInput.format = format; + + printf("VideoNode::FormatChanged leave\n"); + return B_OK; +} + + +void +VideoNode::HandleBuffer(BBuffer *buffer) +{ +// printf("VideoNode::HandleBuffer\n"); + + LockBitmap(); + if (fBitmap) { +// bigtime_t start = system_time(); + memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); +// printf("overlay copy: %Ld usec\n", system_time() - start); + } + UnlockBitmap(); + + buffer->Recycle(); + + fVideoView->DrawFrame(); +} + + +void +VideoNode::SetOverlayEnabled(bool yesno) +{ + fOverlayEnabled = yesno; +} + + +void +VideoNode::LockBitmap() +{ + fBitmapLocker->Lock(); +} + + +BBitmap * +VideoNode::Bitmap() +{ + return fBitmap; +} + + +void +VideoNode::UnlockBitmap() +{ + fBitmapLocker->Unlock(); +} + + +bool +VideoNode::IsOverlayActive() +{ + return fOverlayActive; +} + + +status_t +VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay) +{ + printf("VideoNode::CreateBuffers: frame %d,%d,%d,%d colorspace 0x%08x, overlay %d\n", + int(frame.left), int(frame.top), int(frame.right), int(frame.bottom), int(cspace), overlay); + + LockBitmap(); + ASSERT(fBitmap == 0); + uint32 flags = overlay ? (B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL) : 0; + fBitmap = new BBitmap(frame, flags, cspace); + if (!(fBitmap && fBitmap->InitCheck() == B_OK && fBitmap->IsValid())) { + delete fBitmap; + fBitmap = 0; + fOverlayActive = false; + UnlockBitmap(); + printf("VideoNode::CreateBuffers failed\n"); + return B_ERROR; + } + fOverlayActive = overlay; + UnlockBitmap(); + printf("VideoNode::CreateBuffers success\n"); + return B_OK; +} + + +void +VideoNode::DeleteBuffers() +{ + LockBitmap(); + delete fBitmap; + fBitmap = NULL; + UnlockBitmap(); +} diff --git a/src/apps/mediaplayer/VideoNode.h b/src/apps/mediaplayer/VideoNode.h new file mode 100644 index 0000000000..ee12d13af7 --- /dev/null +++ b/src/apps/mediaplayer/VideoNode.h @@ -0,0 +1,106 @@ +/* + * VideoNode.h - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef __VIDEO_NODE_H_ +#define __VIDEO_NODE_H_ + +#include +#include + +class VideoView; + +class VideoNode : public BMediaEventLooper, public BBufferConsumer +{ +public: + VideoNode(const char *name, VideoView *view); + ~VideoNode(); + + void SetOverlayEnabled(bool yesno); + bool IsOverlayActive(); + + void LockBitmap(); + BBitmap * Bitmap(); + void UnlockBitmap(); + +protected: + BMediaAddOn * AddOn(int32 *internal_id) const; + + void NodeRegistered(); + + void BufferReceived(BBuffer * buffer); + + status_t GetNextInput(int32 *cookie, media_input *out_input); + void DisposeInputCookie(int32 cookie); + + status_t HandleMessage( + int32 message, + const void *data, + size_t size); + + void HandleEvent( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent); + + status_t AcceptFormat( + const media_destination &dst, + media_format *format); + + void ProducerDataStatus( + const media_destination &dst, + int32 status, + bigtime_t at_media_time); + + status_t GetLatencyFor( + const media_destination &dst, + bigtime_t *out_latency, + media_node_id *out_id); + + status_t Connected( + const media_source &src, + const media_destination &dst, + const media_format &format, + media_input *out_input); + + void Disconnected( + const media_source &src, + const media_destination &dst); + + status_t FormatChanged( + const media_source &src, + const media_destination &dst, + int32 from_change_count, + const media_format &format); + +protected: + void HandleBuffer(BBuffer *buffer); + status_t CreateBuffers(BRect frame, color_space cspace, bool overlay); + void DeleteBuffers(); + +protected: + VideoView * fVideoView; + media_input fInput; + bool fOverlayEnabled; + bool fOverlayActive; + bool fDirectOverlayBuffer; // If the overlay memory is directly written by the producer node. + BBitmap * fBitmap; + BLocker * fBitmapLocker; +}; + +#endif diff --git a/src/apps/mediaplayer/VideoView.cpp b/src/apps/mediaplayer/VideoView.cpp new file mode 100644 index 0000000000..579a8410c0 --- /dev/null +++ b/src/apps/mediaplayer/VideoView.cpp @@ -0,0 +1,264 @@ +/* + * VideoView.cpp - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include +#include +#include "VideoView.h" +#include "VideoNode.h" + +#include +#include + +VideoView::VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags) + : BView(frame, name, resizeMask, flags) + , fVideoNode(0) + , fOverlayActive(false) +{ + SetViewColor(B_TRANSPARENT_COLOR); + + status_t err = B_OK; + BMediaRoster *mroster = BMediaRoster::Roster(&err); + if (!mroster || err) { + printf("VideoView::VideoView: media_server is dead\n"); + exit(1); + } else { + fVideoNode = new VideoNode("video in", this); + err = mroster->RegisterNode(fVideoNode); + } +} + + +VideoView::~VideoView() +{ + if (fVideoNode) { + BMediaRoster::Roster()->UnregisterNode(fVideoNode); + delete fVideoNode; + } +} + + +void +VideoView::AttachedToWindow() +{ +} + + +VideoNode * +VideoView::Node() +{ + return fVideoNode; +} + + +void +VideoView::OverlayLockAcquire() +{ + printf("VideoView::OverlayLockAcquire\n"); +} + + +void +VideoView::OverlayLockRelease() +{ + printf("VideoView::OverlayLockRelease\n"); + // overlaybitmap->UnlockBits +} + + +void +VideoView::OverlayScreenshotPrepare() +{ + printf("OverlayScreenshotPrepare enter\n"); +/* + fVideoNode->LockBitmap(); + if (fOverlayActive) { + BBitmap *bmp = fVideoNode->Bitmap(); + if (bmp) { +// Window()->UpdateIfNeeded(); +// Sync(); + BBitmap *tmp = new BBitmap(bmp->Bounds(), 0, B_RGB32); +// ConvertBitmap(tmp, bmp); + ClearViewOverlay(); + DrawBitmap(tmp, Bounds()); + delete tmp; +// Sync(); + } + } + fVideoNode->UnlockBitmap(); +*/ + printf("OverlayScreenshotPrepare leave\n"); +} + + +void +VideoView::OverlayScreenshotCleanup() +{ + printf("OverlayScreenshotCleanup enter\n"); +/* + snooze(50000); // give app server some time to take the screenshot + fVideoNode->LockBitmap(); + if (fOverlayActive) { + BBitmap *bmp = fVideoNode->Bitmap(); + if (bmp) { + DrawBitmap(bmp, Bounds()); + SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor, + B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL); + Invalidate(); + } + } + fVideoNode->UnlockBitmap(); +*/ + printf("OverlayScreenshotCleanup leave\n"); +} + + +void +VideoView::RemoveVideoDisplay() +{ + printf("VideoView::RemoveVideoDisplay\n"); + + if (fOverlayActive) { + ClearViewOverlay(); + fOverlayActive = false; + } + Invalidate(); +} + + +void +VideoView::RemoveOverlay() +{ + printf("VideoView::RemoveOverlay\n"); + if (LockLooperWithTimeout(50000) == B_OK) { + ClearViewOverlay(); + fOverlayActive = false; + UnlockLooper(); + } +} + + +void +VideoView::Draw(BRect updateRect) +{ + if (fOverlayActive) { + SetHighColor(fOverlayKeyColor); + FillRect(updateRect); + } else { + fVideoNode->LockBitmap(); + BBitmap *bmp = fVideoNode->Bitmap(); + if (bmp) + DrawBitmap(bmp, Bounds()); + fVideoNode->UnlockBitmap(); + } +} + + +void +VideoView::DrawFrame() +{ +// printf("VideoView::DrawFrame\n"); + + bool want_overlay = fVideoNode->IsOverlayActive(); + + if (!want_overlay && fOverlayActive) { + if (LockLooperWithTimeout(50000) == B_OK) { + ClearViewOverlay(); + UnlockLooper(); + fOverlayActive = false; + } else { + printf("can't ClearViewOverlay, as LockLooperWithTimeout failed\n"); + } + } + if (want_overlay && !fOverlayActive) { + fVideoNode->LockBitmap(); + BBitmap *bmp = fVideoNode->Bitmap(); + if (bmp && LockLooperWithTimeout(50000) == B_OK) { + SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor, + B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL); + fOverlayActive = true; + + Invalidate(); + UnlockLooper(); + } + fVideoNode->UnlockBitmap(); + } + if (!fOverlayActive) { + if (LockLooperWithTimeout(50000) != B_OK) + return; + Invalidate(); + UnlockLooper(); + } +} + + +void +VideoView::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + + default: + BView::MessageReceived(msg); + } +} + + +bool +VideoView::IsOverlaySupported() +{ + struct colorcombo { + color_space colspace; + const char *name; + } colspace[] = { + { B_RGB32, "B_RGB32"}, + { B_RGBA32, "B_RGBA32"}, + { B_RGB24, "B_RGB24"}, + { B_RGB16, "B_RGB16"}, + { B_RGB15, "B_RGB15"}, + { B_RGBA15, "B_RGBA15"}, + { B_RGB32_BIG, "B_RGB32_BIG"}, + { B_RGBA32_BIG, "B_RGBA32_BIG "}, + { B_RGB24_BIG, "B_RGB24_BIG "}, + { B_RGB16_BIG, "B_RGB16_BIG "}, + { B_RGB15_BIG, "B_RGB15_BIG "}, + { B_RGBA15_BIG, "B_RGBA15_BIG "}, + { B_YCbCr422, "B_YCbCr422"}, + { B_YCbCr411, "B_YCbCr411"}, + { B_YCbCr444, "B_YCbCr444"}, + { B_YCbCr420, "B_YCbCr420"}, + { B_YUV422, "B_YUV422"}, + { B_YUV411, "B_YUV411"}, + { B_YUV444, "B_YUV444"}, + { B_YUV420, "B_YUV420"}, + { B_NO_COLOR_SPACE, NULL} + }; + + bool supported = false; + for (int i = 0; colspace[i].name; i++) { + BBitmap *test = new BBitmap(BRect(0,0,320,240), B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL, colspace[i].colspace); + if (test->InitCheck() == B_OK) { + printf("Display supports %s (0x%08x) overlay\n", colspace[i].name, colspace[i].colspace); + supported = true; + } + delete test; +// if (supported) +// break; + } + return supported; +} + diff --git a/src/apps/mediaplayer/VideoView.h b/src/apps/mediaplayer/VideoView.h new file mode 100644 index 0000000000..c5034fcce0 --- /dev/null +++ b/src/apps/mediaplayer/VideoView.h @@ -0,0 +1,59 @@ +/* + * VideoView.h - Media Player for the Haiku Operating System + * + * Copyright (C) 2006 Marcus Overhagen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef __VIDEO_VIEW_H +#define __VIDEO_VIEW_H + +#include + +class VideoNode; + +class VideoView : public BView +{ +public: + VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags); + ~VideoView(); + + void RemoveVideoDisplay(); + void RemoveOverlay(); + + VideoNode * Node(); + + bool IsOverlaySupported(); + + void OverlayLockAcquire(); + void OverlayLockRelease(); + + void OverlayScreenshotPrepare(); + void OverlayScreenshotCleanup(); + + void DrawFrame(); + +private: + void AttachedToWindow(); + void MessageReceived(BMessage *msg); + void Draw(BRect updateRect); + +private: + VideoNode * fVideoNode; + bool fOverlayActive; + rgb_color fOverlayKeyColor; +}; + +#endif