From d4161a35c7714e0189910d31d0a9cee9dff581ab Mon Sep 17 00:00:00 2001 From: Marcus Overhagen Date: Sun, 15 Apr 2007 18:58:05 +0000 Subject: [PATCH] The DVB TV application, can probably be used as a generic TV application, or even merged with MediaPlayer and CDPlayer later. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20709 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/tv/Controller.cpp | 494 ++++++++++++++ src/apps/tv/Controller.h | 75 +++ src/apps/tv/ConvertBitmap.cpp | 56 ++ src/apps/tv/ConvertBitmap.h | 30 + src/apps/tv/DeviceRoster.cpp | 144 ++++ src/apps/tv/DeviceRoster.h | 66 ++ src/apps/tv/MainApp.cpp | 86 +++ src/apps/tv/MainApp.h | 45 ++ src/apps/tv/MainWin.cpp | 1177 +++++++++++++++++++++++++++++++++ src/apps/tv/MainWin.h | 111 ++++ src/apps/tv/VideoNode.cpp | 447 +++++++++++++ src/apps/tv/VideoNode.h | 111 ++++ src/apps/tv/VideoView.cpp | 399 +++++++++++ src/apps/tv/VideoView.h | 71 ++ src/apps/tv/config.h | 39 ++ 15 files changed, 3351 insertions(+) create mode 100644 src/apps/tv/Controller.cpp create mode 100644 src/apps/tv/Controller.h create mode 100644 src/apps/tv/ConvertBitmap.cpp create mode 100644 src/apps/tv/ConvertBitmap.h create mode 100644 src/apps/tv/DeviceRoster.cpp create mode 100644 src/apps/tv/DeviceRoster.h create mode 100644 src/apps/tv/MainApp.cpp create mode 100644 src/apps/tv/MainApp.h create mode 100644 src/apps/tv/MainWin.cpp create mode 100644 src/apps/tv/MainWin.h create mode 100644 src/apps/tv/VideoNode.cpp create mode 100644 src/apps/tv/VideoNode.h create mode 100644 src/apps/tv/VideoView.cpp create mode 100644 src/apps/tv/VideoView.h create mode 100644 src/apps/tv/config.h diff --git a/src/apps/tv/Controller.cpp b/src/apps/tv/Controller.cpp new file mode 100644 index 0000000000..512c41f50e --- /dev/null +++ b/src/apps/tv/Controller.cpp @@ -0,0 +1,494 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "Controller.h" +#include "DeviceRoster.h" +#include "VideoView.h" +#include "VideoNode.h" + +extern bool gOverlayDisabled; + +status_t MediaRoster_Disconnect(const media_output &output, const media_input &input); + + + +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::DisconnectInterface() +{ + DisconnectNodes(); + fCurrentInterface = -1; + fCurrentChannel = -1; + delete fWeb; + fWeb = 0; + fChannelParam = 0; +} + + +status_t +Controller::ConnectInterface(int i) +{ + if (i < 0) { + printf("Controller::ConnectInterface: wrong index\n"); + return B_ERROR; + } + if (fCurrentInterface != -1) { + printf("Controller::ConnectInterface: already connected\n"); + return B_ERROR; + } + + BParameterWeb *web; + status_t err; + + err = gDeviceRoster->MediaRoster()->GetParameterWebFor(gDeviceRoster->DeviceNode(i), &web); + if (err != B_OK) { + printf("Controller::ConnectInterface: can't get parameter web\n"); + return B_ERROR; + } + + delete fWeb; + fWeb = web; + fCurrentInterface = i; + + // XXX we may need to monitor for parameter web changes + // and reassing fWeb and fChannelParam on demand. + + // find the channel control and assign it to fChannelParam + fChannelParam = NULL; + int count = fWeb->CountParameters(); + for (int i = 0; i < count; i++) { + BParameter *parameter = fWeb->ParameterAt(i); + + printf("parameter %d\n", i); + printf(" name '%s'\n", parameter->Name()); + printf(" kind '%s'\n", parameter->Kind()); + printf(" unit '%s'\n", parameter->Unit()); + printf(" flags 0x%08lx\n", parameter->Flags()); + + // XXX TODO: matching on Name is weak + if (strcmp(parameter->Name(), "Channel") == 0 || strcmp(parameter->Kind(), B_TUNER_CHANNEL) == 0) { + fChannelParam = dynamic_cast(parameter); + if (fChannelParam) + break; + } + } + if (!fChannelParam) { + printf("Controller::ConnectInterface: can't find channel parameter control\n"); + fCurrentChannel = -1; + } else { + if (fChannelParam->CountItems() == 0) { + fCurrentChannel = -1; + printf("Controller::ConnectInterface: channel control has 0 items\n"); + } else { + int32 index; + size_t size; + status_t err; + bigtime_t when; + size = sizeof(index); + err = fChannelParam->GetValue(&index, &size, &when); + if (err == B_OK && size == sizeof(index)) { + fCurrentChannel = index; + printf("Controller::ConnectInterface: selected channel is %d\n", fCurrentChannel); + } else { + fCurrentChannel = -1; + printf("Controller::ConnectInterface: can't get channel control value\n"); + } + } + } + + ConnectNodes(); + + return B_OK; +} + + +bool +Controller::IsInterfaceAvailable(int i) +{ + return gDeviceRoster->IsRawAudioOutputFree(i) && gDeviceRoster->IsRawVideoOutputFree(i); +} + + +int +Controller::CurrentInterface() +{ + return fCurrentInterface; +} + + +int +Controller::CurrentChannel() +{ + return fCurrentChannel; +} + + +status_t +Controller::SelectChannel(int i) +{ + if (!fChannelParam) + return B_ERROR; + + int32 index; + status_t err; + index = i; + err = fChannelParam->SetValue(&index, sizeof(index), 0); + if (err != B_OK) { + printf("Controller::SelectChannel %d failed\n", i); + return err; + } + + fCurrentChannel = i; + return B_OK; +} + + +int +Controller::ChannelCount() +{ + if (fCurrentInterface == -1) + return 0; + + if (!fChannelParam) + return 0; + + return fChannelParam->CountItems(); +} + + +const char * +Controller::ChannelName(int i) +{ + if (fCurrentInterface == -1) + return NULL; + + if (!fChannelParam) + return NULL; + + return fChannelParam->ItemNameAt(i); +} + + +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 }; + + if (gOverlayDisabled) { + err = B_ERROR; + } else { + 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; +} + + +status_t +MediaRoster_Disconnect(const media_output &output, const media_input &input) +{ + if (output.node.node <= 0) { + printf("MediaRoster_Disconnect: output.node.node %d invalid\n", + (int)output.node.node); + return B_MEDIA_BAD_NODE; + } + if (input.node.node <= 0) { + printf("MediaRoster_Disconnect: input.node.node %d invalid\n", + (int)input.node.node); + return B_MEDIA_BAD_NODE; + } + if (!(output.node.kind & B_BUFFER_PRODUCER)) { + printf("MediaRoster_Disconnect: output.node.kind 0x%x is no B_BUFFER_PRODUCER\n", + (int)output.node.kind); + return B_MEDIA_BAD_NODE; + } + if (!(input.node.kind & B_BUFFER_CONSUMER)) { + printf("MediaRoster_Disconnect: input.node.kind 0x%x is no B_BUFFER_PRODUCER\n", + (int)input.node.kind); + return B_MEDIA_BAD_NODE; + } + if (input.source.port != output.source.port) { + printf("MediaRoster_Disconnect: input.source.port %d doesn't match output.source.port %d\n", + (int)input.source.port, (int)output.source.port); + return B_MEDIA_BAD_NODE; + } + if (input.source.id != output.source.id) { + printf("MediaRoster_Disconnect: input.source.id %d doesn't match output.source.id %d\n", + (int)input.source.id, (int)output.source.id); + return B_MEDIA_BAD_NODE; + } + if (input.destination.port != output.destination.port) { + printf("MediaRoster_Disconnect: input.destination.port %d doesn't match output.destination.port %d\n", + (int)input.destination.port, (int)output.destination.port); + return B_MEDIA_BAD_NODE; + } + if (input.destination.id != output.destination.id) { + printf("MediaRoster_Disconnect: input.destination.id %d doesn't match output.destination.id %d\n", + (int)input.destination.id, (int)output.destination.id); + return B_MEDIA_BAD_NODE; + } + return BMediaRoster::Roster()->Disconnect(output.node.node, output.source, input.node.node, input.destination); +} diff --git a/src/apps/tv/Controller.h b/src/apps/tv/Controller.h new file mode 100644 index 0000000000..97d31fbbcd --- /dev/null +++ b/src/apps/tv/Controller.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CONTROLLER_H +#define __CONTROLLER_H + +#include +#include +#include + +class VideoView; +class VideoNode; + +class Controller +{ +public: + Controller(); + virtual ~Controller(); + + void SetVideoView(VideoView *view); + void SetVideoNode(VideoNode *node); + + void DisconnectInterface(); + status_t ConnectInterface(int i); + + bool IsInterfaceAvailable(int i); + int CurrentInterface(); + + int ChannelCount(); + int CurrentChannel(); + status_t SelectChannel(int i); + const char * ChannelName(int i); + + 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/tv/ConvertBitmap.cpp b/src/apps/tv/ConvertBitmap.cpp new file mode 100644 index 0000000000..adafff1918 --- /dev/null +++ b/src/apps/tv/ConvertBitmap.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "ConvertBitmap.h" + +status_t ConvertBitmap_YCbCr422_to_RGB32(BBitmap *dst, const BBitmap *src); + +status_t +ConvertBitmap(BBitmap *dst, const BBitmap *src) +{ + if (dst->Bounds() != src->Bounds()) + return B_BAD_VALUE; + + if (dst->ColorSpace() == src->ColorSpace() && dst->BytesPerRow() == src->BytesPerRow() ) { + memcpy(dst->Bits(), src->Bits(), src->BitsLength()); + return B_OK; + } + + if (src->ColorSpace() == B_YCbCr422 && dst->ColorSpace() == B_RGB32) + return ConvertBitmap_YCbCr422_to_RGB32(dst, src); + + return B_ERROR; +} + + +status_t +ConvertBitmap_YCbCr422_to_RGB32(BBitmap *dst, const BBitmap *src) +{ + return B_ERROR; +} diff --git a/src/apps/tv/ConvertBitmap.h b/src/apps/tv/ConvertBitmap.h new file mode 100644 index 0000000000..d3d3c7f78d --- /dev/null +++ b/src/apps/tv/ConvertBitmap.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CONVERT_BITMAP_H +#define __CONVERT_BITMAP_H + +status_t ConvertBitmap(BBitmap *dst, const BBitmap *src); + +#endif diff --git a/src/apps/tv/DeviceRoster.cpp b/src/apps/tv/DeviceRoster.cpp new file mode 100644 index 0000000000..7750a13687 --- /dev/null +++ b/src/apps/tv/DeviceRoster.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "DeviceRoster.h" + +#include +#include +#include + + +DeviceRoster *gDeviceRoster; + + + +DeviceRoster::DeviceRoster() +{ + live_node_info info[MAX_DEVICE_COUNT]; + int32 info_count = MAX_DEVICE_COUNT; + status_t err; + err = MediaRoster()->GetLiveNodes(info, &info_count, NULL, NULL, "DVB*", B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); + if (err != B_OK || info_count < 1) { + printf("Can't find live DVB node. Found %ld nodes, error %08lx (%s)\n", info_count, err, strerror(err)); + fDeviceCount = 0; + } else { + fDeviceCount = info_count; + for (int i = 0; i < fDeviceCount; i++) { + fDeviceInfo[i].node = info[i].node; + strcpy(fDeviceInfo[i].name, info[i].name); + } + } +} + + +DeviceRoster::~DeviceRoster() +{ +} + + +int +DeviceRoster::DeviceCount() +{ + return fDeviceCount; +} + + +const char * +DeviceRoster::DeviceName(int i) +{ + ASSERT(i >= 0 && i < fDeviceCount); + return fDeviceInfo[i].name; +} + + +media_node +DeviceRoster::DeviceNode(int i) +{ + ASSERT(i >= 0 && i < fDeviceCount); + return fDeviceInfo[i].node; +} + + +bool +DeviceRoster::IsRawAudioOutputFree(int i) +{ + ASSERT(i >= 0 && i < fDeviceCount); + + media_output output; + int32 count; + status_t err; + + err = MediaRoster()->GetFreeOutputsFor(fDeviceInfo[i].node, &output, 1, &count, B_MEDIA_RAW_AUDIO); + return (err == B_OK) && (count == 1); +} + + +bool +DeviceRoster::IsEncAudioOutputFree(int i) +{ + ASSERT(i >= 0 && i < fDeviceCount); + + media_output output; + int32 count; + status_t err; + + err = MediaRoster()->GetFreeOutputsFor(fDeviceInfo[i].node, &output, 1, &count, B_MEDIA_ENCODED_AUDIO); + return (err == B_OK) && (count == 1); +} + + +bool +DeviceRoster::IsRawVideoOutputFree(int i) +{ + ASSERT(i >= 0 && i < fDeviceCount); + + media_output output; + int32 count; + status_t err; + + err = MediaRoster()->GetFreeOutputsFor(fDeviceInfo[i].node, &output, 1, &count, B_MEDIA_RAW_VIDEO); + return (err == B_OK) && (count == 1); +} + + +bool +DeviceRoster::IsEncVideoOutputFree(int i) +{ + ASSERT(i >= 0 && i < fDeviceCount); + + media_output output; + int32 count; + status_t err; + + err = MediaRoster()->GetFreeOutputsFor(fDeviceInfo[i].node, &output, 1, &count, B_MEDIA_ENCODED_VIDEO); + return (err == B_OK) && (count == 1); +} + + +BMediaRoster * +DeviceRoster::MediaRoster() +{ + return BMediaRoster::Roster(); +} + diff --git a/src/apps/tv/DeviceRoster.h b/src/apps/tv/DeviceRoster.h new file mode 100644 index 0000000000..0ca5910b33 --- /dev/null +++ b/src/apps/tv/DeviceRoster.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __DEVICE_ROSTER_H +#define __DEVICE_ROSTER_H + +#include + +class DeviceRoster +{ +public: + DeviceRoster(); + ~DeviceRoster(); + + int DeviceCount(); + + const char * DeviceName(int i); + media_node DeviceNode(int i); + + bool IsRawAudioOutputFree(int i); + bool IsEncAudioOutputFree(int i); + bool IsRawVideoOutputFree(int i); + bool IsEncVideoOutputFree(int i); + +public: + BMediaRoster* MediaRoster(); + +private: + enum { MAX_DEVICE_COUNT = 8 }; + + class device_info + { + public: + media_node node; + char name[B_MEDIA_NAME_LENGTH]; + }; + +private: + device_info fDeviceInfo[MAX_DEVICE_COUNT]; + int fDeviceCount; +}; + +extern DeviceRoster *gDeviceRoster; + +#endif diff --git a/src/apps/tv/MainApp.cpp b/src/apps/tv/MainApp.cpp new file mode 100644 index 0000000000..919b7fc79b --- /dev/null +++ b/src/apps/tv/MainApp.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "MainApp.h" +#include "config.h" +#include "DeviceRoster.h" + +MainApp *gMainApp; + +bool gOverlayDisabled = false; + +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(int argc, const char *argv[]) +{ + if (argc > 1) + gOverlayDisabled = true; + gMainApp = new MainApp; + gMainApp->Run(); + delete gMainApp; + return 0; +} diff --git a/src/apps/tv/MainApp.h b/src/apps/tv/MainApp.h new file mode 100644 index 0000000000..c7749184c7 --- /dev/null +++ b/src/apps/tv/MainApp.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#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/tv/MainWin.cpp b/src/apps/tv/MainWin.cpp new file mode 100644 index 0000000000..b5c4c3f92e --- /dev/null +++ b/src/apps/tv/MainWin.cpp @@ -0,0 +1,1177 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#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_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)); + +/* + fChannelMenu->AddItem(new BMenuItem("Next Channel", new BMessage(M_CHANNEL_NEXT), '+', B_COMMAND_KEY)); + fChannelMenu->AddItem(new BMenuItem("Previous Channel", new BMessage(M_CHANNEL_PREV), '-', B_COMMAND_KEY)); + fChannelMenu->AddSeparatorItem(); + fChannelMenu->AddItem(new BMenuItem("RTL", new BMessage(M_DUMMY), '0', B_COMMAND_KEY)); + fChannelMenu->AddItem(new BMenuItem("Pro7", new BMessage(M_DUMMY), '1', B_COMMAND_KEY)); + + fInterfaceMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY))); + fInterfaceMenu->AddItem(new BMenuItem("none 1", new BMessage(M_DUMMY))); + fInterfaceMenu->AddItem(new BMenuItem("none 2", new BMessage(M_DUMMY))); +*/ + + 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 +} + + +void +MainWin::SetupInterfaceMenu() +{ + fInterfaceMenu->RemoveItems(0, fInterfaceMenu->CountItems(), true); + + fInterfaceMenu->AddItem(new BMenuItem("None", new BMessage(M_SELECT_INTERFACE))); + + int count = gDeviceRoster->DeviceCount(); + + if (count > 0) + fInterfaceMenu->AddSeparatorItem(); + + for (int i = 0; i < count; i++) { + // 1 gets subtracted in MessageReceived, so -1 is Interface None, and 0 == Interface 0 in SelectInterface() + fInterfaceMenu->AddItem(new BMenuItem(gDeviceRoster->DeviceName(i), new BMessage(M_SELECT_INTERFACE + i + 1))); + } +} + + +void +MainWin::SetupChannelMenu() +{ + fChannelMenu->RemoveItems(0, fChannelMenu->CountItems(), true); + + int interface = fController->CurrentInterface(); + printf("MainWin::SetupChannelMenu: interface %d\n", interface); + + int channels = fController->ChannelCount(); + + if (channels == 0) { + fChannelMenu->AddItem(new BMenuItem("None", new BMessage(M_DUMMY))); + } else { + fChannelMenu->AddItem(new BMenuItem("Next Channel", new BMessage(M_CHANNEL_NEXT), '+', B_COMMAND_KEY)); + fChannelMenu->AddItem(new BMenuItem("Previous Channel", new BMessage(M_CHANNEL_PREV), '-', B_COMMAND_KEY)); + fChannelMenu->AddSeparatorItem(); + } + + char s[100]; + for (int i = 0; i < channels; i++) { + sprintf(s, "%s%d %s", (i < 9) ? " " : "", i + 1, fController->ChannelName(i)); + fChannelMenu->AddItem(new BMenuItem(s, new BMessage(M_SELECT_CHANNEL + i))); + } +} + + +void +MainWin::SetInterfaceMenuMarker() +{ + BMenuItem *item; + + int interface = fController->CurrentInterface(); + printf("MainWin::SetInterfaceMenuMarker: interface %d\n", interface); + + // remove old marker + item = fInterfaceMenu->FindMarked(); + if (item) + item->SetMarked(false); + + // set new marker + int index = (interface < 0) ? 0 : interface + 2; + item = fInterfaceMenu->ItemAt(index); + if (item) + item->SetMarked(true); +} + + +void +MainWin::SetChannelMenuMarker() +{ + BMenuItem *item; + + int channel = fController->CurrentChannel(); + printf("MainWin::SetChannelMenuMarker: channel %d\n", channel); + + // remove old marker + item = fChannelMenu->FindMarked(); + if (item) + item->SetMarked(false); + + // set new marker + int index = (channel < 0) ? 0 : channel + 3; + item = fChannelMenu->ItemAt(index); + if (item) + item->SetMarked(true); +} + + +void +MainWin::SelectChannel(int i) +{ + printf("MainWin::SelectChannel %d\n", i); + + if (B_OK != fController->SelectChannel(i)) + return; + + SetChannelMenuMarker(); +} + + +void +MainWin::SelectInterface(int i) +{ + printf("MainWin::SelectInterface %d\n", i); + printf(" CurrentInterface %d\n", fController->CurrentInterface()); + printf(" CurrentChannel %d\n", fController->CurrentChannel()); + + // i = -1 means "None" + + if (i < 0) { + fController->DisconnectInterface(); + goto done; + } + + if (!fController->IsInterfaceAvailable(i)) { + BString s; + s << "Error, interface is busy:\n\n" << gDeviceRoster->DeviceName(i); + (new BAlert("error", s.String(), "OK"))->Go(); + return; + } + + fController->DisconnectInterface(); + if (fController->ConnectInterface(i) != B_OK) { + BString s; + s << "Error, connecting to interface failed:\n\n" << gDeviceRoster->DeviceName(i); + (new BAlert("error", s.String(), "OK"))->Go(); + } + +done: + printf("MainWin::SelectInterface done:\n"); + printf(" CurrentInterface %d\n", fController->CurrentInterface()); + printf(" CurrentChannel %d\n", fController->CurrentChannel()); + + SetInterfaceMenuMarker(); + SetupChannelMenu(); + SetChannelMenuMarker(); +} + + +void +MainWin::SelectInitialInterface() +{ + printf("MainWin::SelectInitialInterface enter\n"); + + int count = gDeviceRoster->DeviceCount(); + for (int i = 0; i < count; i++) { + if (fController->IsInterfaceAvailable(i) && B_OK == fController->ConnectInterface(i)) { + printf("MainWin::SelectInitialInterface connected to interface %d\n", i); + break; + } + } + + printf("MainWin::SelectInitialInterface leave\n"); +} + + +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 + + // Sync here is probably not required +// Sync(); + + 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(); + +// Sync(); + + } 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(); + + // We *must* make sure that the window is at + // the correct position before continuing, or + // rapid fullscreen switching by holding down + // the TAB key will expose strange bugs. + // Never remove this Sync! +// Sync(); + } + + // 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 + #if TIME_BOMB_ACTIVE + "\n\n" INFO2 + #endif + "\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/tv/MainWin.h b/src/apps/tv/MainWin.h new file mode 100644 index 0000000000..c676b89418 --- /dev/null +++ b/src/apps/tv/MainWin.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#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 SetupInterfaceMenu(); + void SetupChannelMenu(); + + void SelectInitialInterface(); + + void SelectInterface(int i); + void SelectChannel(int i); + + void SetInterfaceMenuMarker(); + void SetChannelMenuMarker(); + + 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/tv/VideoNode.cpp b/src/apps/tv/VideoNode.cpp new file mode 100644 index 0000000000..15f76627d6 --- /dev/null +++ b/src/apps/tv/VideoNode.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VideoNode.h" +#include "VideoView.h" + +void +overlay_copy(uint32 lines, void *dst, uint32 dst_bpr, const void *src, uint32 src_bpr) +{ +// bigtime_t start = system_time(); + int len = min_c(dst_bpr, src_bpr); +// int len4 = len / 4; + while (lines--) { +/* + // this does not copy the last few bytes, if length is not aligned to 4 bytes + asm ("rep\n\t""movsl" + : + : "c" (len4), "S" (src), "D" (dst) + : "eax"); +*/ +/* + const uint32 *s4 = (const uint32 *)src; + uint32 *d4 = (uint32 *)dst; + for (int i = 0; i < len4; i++) + d4[i] = s4[i]; +*/ +/* + const uint8 *s1 = (const uint8 *)s4; + uint8 *d1 = (uint8 *)d4; + int l1 = len1; + while (l1--) + *d1++ = *s1++; +*/ + memcpy(dst, src, len); + src = (char *)src + src_bpr; + dst = (char *)dst + dst_bpr; + } +// printf("overlay copy: %.06f sec\n", (system_time() - start) / 1000000.0); +} + + +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(); + if (fOverlayActive) { + if (B_OK == fBitmap->LockBits()) { + +// memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); + +// fBitmap->SetBits(buffer->Data(), fBitmap->BitsLength(), 0, fInput.format.u.raw_video.display.format); + + overlay_copy(fBitmap->Bounds().IntegerHeight() + 1, + fBitmap->Bits(), fBitmap->BytesPerRow(), + buffer->Data(), fInput.format.u.raw_video.display.bytes_per_row); + + + fBitmap->UnlockBits(); + } + } else { +// memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength()); + + overlay_copy(fBitmap->Bounds().IntegerHeight() + 1, + fBitmap->Bits(), fBitmap->BytesPerRow(), + buffer->Data(), fInput.format.u.raw_video.display.bytes_per_row); + } +// 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); + +// int32 bytesPerRow = B_ANY_BYTES_PER_ROW; +// if (cspace == B_YCbCr422) +// bytesPerRow = (int(frame.Width()) + 1) * 2; + +// printf("overlay bitmap: requesting: bytes per row: %d\n", bytesPerRow); + + LockBitmap(); + ASSERT(fBitmap == 0); + uint32 flags = overlay ? (B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL) : 0; +// fBitmap = new BBitmap(frame, flags, cspace, bytesPerRow); + 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; + } + printf("overlay bitmap: got: bytes per row: %d\n", fBitmap->BytesPerRow()); + 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/tv/VideoNode.h b/src/apps/tv/VideoNode.h new file mode 100644 index 0000000000..e79804cbc8 --- /dev/null +++ b/src/apps/tv/VideoNode.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#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/tv/VideoView.cpp b/src/apps/tv/VideoView.cpp new file mode 100644 index 0000000000..be17d1cbed --- /dev/null +++ b/src/apps/tv/VideoView.cpp @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "VideoView.h" +#include "VideoNode.h" +#include "ConvertBitmap.h" + +#include +#include + +#define CHECK_ACTIVITY 'ChkA' + +VideoView::VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags) + : BView(frame, name, resizeMask, flags) + , fVideoNode(0) + , fVideoActive(false) + , fOverlayActive(false) + , fActivityCheckMsgRunner(0) + , fLastFrame(0) +{ + 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() +{ + delete fActivityCheckMsgRunner; + + if (fVideoNode) { + BMediaRoster::Roster()->UnregisterNode(fVideoNode); + delete fVideoNode; + } +} + + +void +VideoView::AttachedToWindow() +{ + BMessage msg(CHECK_ACTIVITY); + fActivityCheckMsgRunner = new BMessageRunner(BMessenger(this), &msg, 200000); +} + + +VideoNode * +VideoView::Node() +{ + return fVideoNode; +} + + +void +VideoView::OverlayLockAcquire() +{ + printf("VideoView::OverlayLockAcquire\n"); +} + + +void +VideoView::OverlayLockRelease() +{ /* [19:54] Rudolf forwarded me a mail once about it + * [19:55] when you get relmease msg you are supposed to UnlockBits() on the overlay bitmaps you use + * [19:55] it's used when switching workspaces + * [19:55] as the bits might get relocated + */ + printf("VideoView::OverlayLockRelease\n"); + +} + + +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; + } + fVideoActive = false; + Invalidate(); +} + + +void +VideoView::RemoveOverlay() +{ + printf("VideoView::RemoveOverlay\n"); + if (LockLooperWithTimeout(50000) == B_OK) { + ClearViewOverlay(); + fOverlayActive = false; + UnlockLooper(); + } +} + + +void +VideoView::CheckActivity() +{ + if (!fVideoActive) + return; + if (system_time() - fLastFrame < 700000) + return; + + printf("VideoView::CheckActivity: lag detected\n"); + + fVideoActive = false; + Invalidate(); +} + + +void +VideoView::Draw(BRect updateRect) +{ + if (!fVideoActive) { + DrawTestImage(); + return; + } + 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"); + if (!fVideoActive) { + fVideoActive = true; + if (LockLooperWithTimeout(50000) != B_OK) + return; + Invalidate(); + UnlockLooper(); + } + fLastFrame = system_time(); + + 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::DrawTestImage() +{ + static const rgb_color cols[8] = { + {255,255,255}, {255,255,0}, {0,255,255}, {0,255,0}, + {255,0,255}, {255,0,0}, {0,0,255}, {0,0,0} +// {255,255,255}, {255,255,0}, {0,255,255}, +// {255,0,255}, {255,0,0}, {0,255,0}, {0,0,255}, {0,0,0} + }; + float bar_width; + float left; + float right; + + BRect bnd = Bounds(); + int seperator_y1 = int(0.60 * (bnd.Height() + 1)); + int seperator_y2 = int(0.80 * (bnd.Height() + 1)); + int steps; + + bar_width = bnd.Width() / 8; + if (bar_width < 1) + bar_width = 1; + + left = 0; + for (int i = 0; i < 8; i++) { + SetHighColor(cols[i]); + right = (i != 7) ? left + bar_width - 1 : bnd.right; + FillRect(BRect(left, 0, right, seperator_y1)); + left = right + 1; + } + + steps = 32; + + bar_width = bnd.Width() / steps; +// if (bar_width < 1) +// bar_width = 1; + + left = 0; + for (int i = 0; i < steps; i++) { + uint8 c = i * 255 / (steps - 1); + SetHighColor(c, c, c); + right = (i != steps - 1) ? left + bar_width - 1 : bnd.right; + FillRect(BRect(left, seperator_y1 + 1, right, seperator_y2)); + left = right + 1; + } + + steps = 256; + + bar_width = bnd.Width() / steps; + if (bar_width < 1) + bar_width = 1; + + left = 0; + for (int i = 0; i < steps; i++) { + uint8 c = 255 - (i * 255 / (steps - 1)); + SetHighColor(c, c, c); + right = (i != steps - 1) ? left + bar_width - 1 : bnd.right; + FillRect(BRect(left, seperator_y2 + 1, right, bnd.bottom)); + left = right + 1; + } +} + + +void +VideoView::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + case CHECK_ACTIVITY: + CheckActivity(); + break; + 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); + overlay_restrictions restrict; + if (B_OK == test->GetOverlayRestrictions(&restrict)) { + printf("Overlay restrictions: source horizontal_alignment %d\n", restrict.source.horizontal_alignment); + printf("Overlay restrictions: source vertical_alignment %d\n", restrict.source.vertical_alignment); + printf("Overlay restrictions: source width_alignment %d\n", restrict.source.width_alignment); + printf("Overlay restrictions: source height_alignment %d\n", restrict.source.height_alignment); + printf("Overlay restrictions: source min_width %d\n", restrict.source.min_width); + printf("Overlay restrictions: source max_width %d\n", restrict.source.max_width); + printf("Overlay restrictions: source min_height %d\n", restrict.source.min_height); + printf("Overlay restrictions: source max_height %d\n", restrict.source.max_height); + printf("Overlay restrictions: destination horizontal_alignment %d\n", restrict.destination.horizontal_alignment); + printf("Overlay restrictions: destination vertical_alignment %d\n", restrict.destination.vertical_alignment); + printf("Overlay restrictions: destination width_alignment %d\n", restrict.destination.width_alignment); + printf("Overlay restrictions: destination height_alignment %d\n", restrict.destination.height_alignment); + printf("Overlay restrictions: destination min_width %d\n", restrict.destination.min_width); + printf("Overlay restrictions: destination max_width %d\n", restrict.destination.max_width); + printf("Overlay restrictions: destination min_height %d\n", restrict.destination.min_height); + printf("Overlay restrictions: destination max_height %d\n", restrict.destination.max_height); + printf("Overlay restrictions: min_width_scale %d\n", restrict.min_width_scale); + printf("Overlay restrictions: max_width_scale %d\n", restrict.max_width_scale); + printf("Overlay restrictions: min_height_scale %d\n", restrict.min_height_scale); + printf("Overlay restrictions: max_height_scale %d\n", restrict.max_height_scale); + } + supported = true; + } + delete test; +// if (supported) +// break; + } + return supported; +} + diff --git a/src/apps/tv/VideoView.h b/src/apps/tv/VideoView.h new file mode 100644 index 0000000000..08ca33485c --- /dev/null +++ b/src/apps/tv/VideoView.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#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); + + void DrawTestImage(); + + void CheckActivity(); + +private: + VideoNode * fVideoNode; + bool fVideoActive; + bool fOverlayActive; + rgb_color fOverlayKeyColor; + BMessageRunner *fActivityCheckMsgRunner; + bigtime_t fLastFrame; +}; + +#endif diff --git a/src/apps/tv/config.h b/src/apps/tv/config.h new file mode 100644 index 0000000000..a6bc0acf19 --- /dev/null +++ b/src/apps/tv/config.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#include +#include +#include "revision.h" + +#define NAME "TV-O-Rama" +#define VERSION "1.1" +#define BUILD __DATE__ " "__TIME__ +#define COPYRIGHT B_UTF8_COPYRIGHT" Marcus Overhagen 2005-2006" +#define INFO1 "DVB - Digital Video Broadcasting TV" +#define APP_SIG "application/x-vnd.MarcusOverhagen."##NAME + +#endif