/* * Copyright 1998-1999 Be, Inc. All Rights Reserved. * Copyright 2003-2019 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #include "CodyCam.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "CodyCam" #define VIDEO_SIZE_X 320 #define VIDEO_SIZE_Y 240 #define WINDOW_SIZE_X (VIDEO_SIZE_X + 80) #define WINDOW_SIZE_Y (VIDEO_SIZE_Y + 230) #define WINDOW_OFFSET_X 28 #define WINDOW_OFFSET_Y 28 #define CALL printf #define ERROR printf #define FTPINFO printf #define INFO printf // Utility functions namespace { // functions for EnumeratedStringValueSettings const char* CaptureRateAt(int32 i) { return (i >= 0 && i < kCaptureRatesCount) ? kCaptureRates[i].name : NULL; } const char* UploadClientAt(int32 i) { return (i >= 0 && i < kUploadClientsCount) ? kUploadClients[i] : NULL; } }; // end anonymous namespace // #pragma mark - CodyCam::CodyCam() : BApplication("application/x-vnd.Haiku-CodyCam"), fMediaRoster(NULL), fVideoConsumer(NULL), fWindow(NULL), fPort(0), fVideoControlWindow(NULL) { int32 index = 0; kCaptureRates[index++].name = B_TRANSLATE("Every 15 seconds"); kCaptureRates[index++].name = B_TRANSLATE("Every 30 seconds"); kCaptureRates[index++].name = B_TRANSLATE("Every minute"); kCaptureRates[index++].name = B_TRANSLATE("Every 5 minutes"); kCaptureRates[index++].name = B_TRANSLATE("Every 10 minutes"); kCaptureRates[index++].name = B_TRANSLATE("Every 15 minutes"); kCaptureRates[index++].name = B_TRANSLATE("Every 30 minutes"); kCaptureRates[index++].name = B_TRANSLATE("Every hour"); kCaptureRates[index++].name = B_TRANSLATE("Every 2 hours"); kCaptureRates[index++].name = B_TRANSLATE("Every 4 hours"); kCaptureRates[index++].name = B_TRANSLATE("Every 8 hours"); kCaptureRates[index++].name = B_TRANSLATE("Every 24 hours"); kCaptureRates[index++].name = B_TRANSLATE("Never"); index = 0; kUploadClients[index++] = B_TRANSLATE("FTP"); kUploadClients[index++] = B_TRANSLATE("SFTP"); kUploadClients[index++] = B_TRANSLATE("Local"); BPath homeDir; if (find_directory(B_USER_DIRECTORY, &homeDir) != B_OK) homeDir.SetTo("/boot/home"); chdir(homeDir.Path()); } CodyCam::~CodyCam() { CALL("CodyCam::~CodyCam\n"); // release the video consumer node // the consumer node cleans up the window if (fVideoConsumer) { fVideoConsumer->Release(); fVideoConsumer = NULL; } CALL("CodyCam::~CodyCam - EXIT\n"); } void CodyCam::ReadyToRun() { fWindow = new VideoWindow( (const char*) B_TRANSLATE_SYSTEM_NAME("CodyCam"), B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS, &fPort); if (_SetUpNodes() != B_OK) fWindow->ToggleMenuOnOff(); ((VideoWindow*)fWindow)->ApplyControls(); } bool CodyCam::QuitRequested() { _TearDownNodes(); snooze(100000); return true; } void CodyCam::MessageReceived(BMessage* message) { switch (message->what) { case msg_start: { BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor( fTimeSourceNode); bigtime_t real = BTimeSource::RealTime(); bigtime_t perf = timeSource->PerformanceTimeFor(real) + 10000; status_t status = fMediaRoster->StartNode(fProducerNode, perf); if (status != B_OK) ERROR("error starting producer!"); timeSource->Release(); break; } case msg_stop: fMediaRoster->StopNode(fProducerNode, 0, true); break; case msg_video: { if (fVideoControlWindow) { fVideoControlWindow->Activate(); break; } BParameterWeb* web = NULL; BView* view = NULL; media_node node = fProducerNode; status_t err = fMediaRoster->GetParameterWebFor(node, &web); if (err >= B_OK && web != NULL) { view = BMediaTheme::ViewFor(web); view->SetResizingMode(B_FOLLOW_ALL_SIDES); fVideoControlWindow = new ControlWindow(view, node); fMediaRoster->StartWatching(BMessenger(NULL, fVideoControlWindow), node, B_MEDIA_WEB_CHANGED); fVideoControlWindow->Show(); } break; } case msg_control_win: // our control window is being asked to go away // set our pointer to NULL fVideoControlWindow = NULL; break; default: BApplication::MessageReceived(message); break; } } status_t CodyCam::_SetUpNodes() { status_t status = B_OK; /* find the media roster */ fMediaRoster = BMediaRoster::Roster(&status); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot find the media roster"), status); return status; } /* find the time source */ status = fMediaRoster->GetTimeSource(&fTimeSourceNode); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot get a time source"), status); return status; } /* find a video producer node */ INFO("CodyCam acquiring VideoInput node\n"); status = fMediaRoster->GetVideoInput(&fProducerNode); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot find a video source.\n" "You need a webcam to use CodyCam."), status); return status; } /* create the video consumer node */ fVideoConsumer = new VideoConsumer("CodyCam", ((VideoWindow*)fWindow)->VideoView(), ((VideoWindow*)fWindow)->StatusLine(), NULL, 0); if (fVideoConsumer == NULL) { fWindow->ErrorAlert(B_TRANSLATE("Cannot create a video window"), B_ERROR); return B_ERROR; } /* register the node */ status = fMediaRoster->RegisterNode(fVideoConsumer); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot register the video window"), status); return status; } fPort = fVideoConsumer->ControlPort(); /* find free producer output */ int32 cnt = 0; status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1, &cnt, B_MEDIA_RAW_VIDEO); if (status != B_OK || cnt < 1) { status = B_RESOURCE_UNAVAILABLE; fWindow->ErrorAlert( B_TRANSLATE("Cannot find an available video stream"), status); return status; } /* find free consumer input */ cnt = 0; status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(), &fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO); if (status != B_OK || cnt < 1) { status = B_RESOURCE_UNAVAILABLE; fWindow->ErrorAlert(B_TRANSLATE("Can't find an available connection to " "the video window"), status); return status; } /* Connect The Nodes!!! */ media_format format; format.type = B_MEDIA_RAW_VIDEO; media_raw_video_format vid_format = {0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT, 1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X * 4, 0, 0}}; format.u.raw_video = vid_format; /* connect producer to consumer */ status = fMediaRoster->Connect(fProducerOut.source, fConsumerIn.destination, &format, &fProducerOut, &fConsumerIn); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot connect the video source to " "the video window"), status); return status; } /* set time sources */ status = fMediaRoster->SetTimeSourceFor(fProducerNode.node, fTimeSourceNode.node); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the " "video source"), status); return status; } status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(), fTimeSourceNode.node); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the " "video window"), status); return status; } /* figure out what recording delay to use */ bigtime_t latency = 0; status = fMediaRoster->GetLatencyFor(fProducerNode, &latency); status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency); /* start the nodes */ bigtime_t initLatency = 0; status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency); if (status < B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Error getting initial latency for the " "capture node"), status); return status; } initLatency += estimate_max_scheduling_latency(); BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode); bool running = timeSource->IsRunning(); /* workaround for people without sound cards */ /* because the system time source won't be running */ bigtime_t real = BTimeSource::RealTime(); if (!running) { status = fMediaRoster->StartTimeSource(fTimeSourceNode, real); if (status != B_OK) { timeSource->Release(); fWindow->ErrorAlert(B_TRANSLATE("Cannot start time source!"), status); return status; } status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real); if (status != B_OK) { timeSource->Release(); fWindow->ErrorAlert(B_TRANSLATE("Cannot seek time source!"), status); return status; } } bigtime_t perf = timeSource->PerformanceTimeFor(real + latency + initLatency); timeSource->Release(); /* start the nodes */ status = fMediaRoster->StartNode(fProducerNode, perf); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video source"), status); return status; } status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video window"), status); return status; } return status; } void CodyCam::_TearDownNodes() { CALL("CodyCam::_TearDownNodes\n"); if (fMediaRoster == NULL) return; if (fVideoConsumer) { /* stop */ INFO("stopping nodes!\n"); // fMediaRoster->StopNode(fProducerNode, 0, true); fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true); /* disconnect */ fMediaRoster->Disconnect(fProducerOut.node.node, fProducerOut.source, fConsumerIn.node.node, fConsumerIn.destination); if (fProducerNode != media_node::null) { INFO("CodyCam releasing fProducerNode\n"); fMediaRoster->ReleaseNode(fProducerNode); fProducerNode = media_node::null; } fMediaRoster->ReleaseNode(fVideoConsumer->Node()); fVideoConsumer = NULL; } } // #pragma mark - Video Window Class VideoWindow::VideoWindow(const char* title, window_type type, uint32 flags, port_id* consumerPort) : BWindow(BRect(50, 50, 50, 50), title, type, flags), fPortPtr(consumerPort), fVideoView(NULL) { fFtpInfo.port = 0; fFtpInfo.rate = 0x7fffffff; fFtpInfo.imageFormat = 0; fFtpInfo.translator = 0; fFtpInfo.passiveFtp = true; fFtpInfo.uploadClient = 0; strcpy(fFtpInfo.fileNameText, "filename"); strcpy(fFtpInfo.serverText, "server"); strcpy(fFtpInfo.loginText, "login"); strcpy(fFtpInfo.passwordText, "password"); strcpy(fFtpInfo.directoryText, "directory"); _SetUpSettings("codycam", ""); BMenuBar* menuBar = new BMenuBar("menu bar"); BMenuItem* menuItem; fMenu = new BMenu(B_TRANSLATE("File")); menuItem = new BMenuItem(B_TRANSLATE("Video settings"), new BMessage(msg_video), 'P'); menuItem->SetTarget(be_app); fMenu->AddItem(menuItem); fMenu->AddSeparatorItem(); menuItem = new BMenuItem(B_TRANSLATE("Start video"), new BMessage(msg_start), 'A'); menuItem->SetTarget(be_app); fMenu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Stop video"), new BMessage(msg_stop), 'O'); menuItem->SetTarget(be_app); fMenu->AddItem(menuItem); fMenu->AddSeparatorItem(); menuItem = new BMenuItem(B_TRANSLATE("Quit"), new BMessage(B_QUIT_REQUESTED), 'Q'); menuItem->SetTarget(be_app); fMenu->AddItem(menuItem); menuBar->AddItem(fMenu); /* add some controls */ _BuildCaptureControls(); BBox* box = new BBox("box"); BGroupLayout* layout = new BGroupLayout(B_VERTICAL); box->SetLayout(layout); layout->SetInsets(2, 2, 2, 2); box->AddChild(fVideoView); box->AddChild(fErrorView); BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .Add(menuBar) .AddGroup(B_VERTICAL) .SetInsets(B_USE_WINDOW_SPACING) .AddGroup(B_HORIZONTAL) .AddGlue() .Add(box) .AddGlue() .End() .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) .Add(fCaptureSetupBox) .Add(fFtpSetupBox) .End() .Add(fStatusLine) .End() .AddGlue(); Show(); } VideoWindow::~VideoWindow() { _QuitSettings(); } bool VideoWindow::QuitRequested() { be_app->PostMessage(B_QUIT_REQUESTED); return false; } void VideoWindow::MessageReceived(BMessage* message) { BControl* control = NULL; message->FindPointer((const char*)"source", (void**)&control); switch (message->what) { case msg_filename: if (control != NULL) { strlcpy(fFtpInfo.fileNameText, ((BTextControl*)control)->Text(), 64); FTPINFO("file is '%s'\n", fFtpInfo.fileNameText); } break; case msg_rate_changed: { int32 seconds; message->FindInt32("seconds", &seconds); if (seconds == 0) { FTPINFO("never\n"); fFtpInfo.rate = (bigtime_t)(B_INFINITE_TIMEOUT); } else { FTPINFO("%" B_PRId32 " seconds\n", seconds); fFtpInfo.rate = (bigtime_t)(seconds * 1000000LL); } break; } case msg_translate: message->FindInt32("be:type", (int32*)&(fFtpInfo.imageFormat)); message->FindInt32("be:translator", &(fFtpInfo.translator)); break; case msg_upl_client: message->FindInt32("client", &(fFtpInfo.uploadClient)); FTPINFO("upl client = %" B_PRId32 "\n", fFtpInfo.uploadClient); _UploadClientChanged(); break; case msg_server: if (control != NULL) { strlcpy(fFtpInfo.serverText, ((BTextControl*)control)->Text(), 64); FTPINFO("server = '%s'\n", fFtpInfo.serverText); } break; case msg_login: if (control != NULL) { strlcpy(fFtpInfo.loginText, ((BTextControl*)control)->Text(), 64); FTPINFO("login = '%s'\n", fFtpInfo.loginText); } break; case msg_password: if (control != NULL) { strlcpy(fFtpInfo.passwordText, ((BTextControl*)control)->Text(), 64); FTPINFO("password = '%s'\n", fFtpInfo.passwordText); } break; case msg_directory: if (control != NULL) { strlcpy(fFtpInfo.directoryText, ((BTextControl*)control)->Text(), 64); FTPINFO("directory = '%s'\n", fFtpInfo.directoryText); } break; case msg_passiveftp: if (control != NULL) { fFtpInfo.passiveFtp = ((BCheckBox*)control)->Value(); if (fFtpInfo.passiveFtp) FTPINFO("using passive ftp\n"); } break; default: BWindow::MessageReceived(message); return; } if (*fPortPtr) write_port(*fPortPtr, FTP_INFO, (void*)&fFtpInfo, sizeof(ftp_msg_info)); } BView* VideoWindow::VideoView() { return fVideoView; } BStringView* VideoWindow::StatusLine() { return fStatusLine; } void VideoWindow::_BuildCaptureControls() { // a view to hold the video image fVideoView = new BView("Video preview", B_WILL_DRAW); fVideoView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); fVideoView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); fErrorView = new BTextView("error"); fErrorView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); // Capture controls BGridLayout* controlsLayout = new BGridLayout(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING); controlsLayout->SetInsets(B_USE_SMALL_SPACING); BView* controlView = new BView("Controls", B_SUPPORTS_LAYOUT, NULL); controlView->SetLayout(controlsLayout); fCaptureSetupBox = new BBox("Capture Controls", B_WILL_DRAW); fCaptureSetupBox->SetLabel(B_TRANSLATE("Capture controls")); fCaptureSetupBox->AddChild(controlView); // file name fFileName = new BTextControl("File Name", B_TRANSLATE("File name:"), fFilenameSetting->Value(), new BMessage(msg_filename)); fFileName->SetTarget(BMessenger(NULL, this)); // format menu fImageFormatMenu = new BPopUpMenu(B_TRANSLATE("Image Format Menu")); BTranslationUtils::AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP); fImageFormatMenu->SetTargetForItems(this); if (fImageFormatSettings->Value() && fImageFormatMenu->FindItem(fImageFormatSettings->Value()) != NULL) { fImageFormatMenu->FindItem( fImageFormatSettings->Value())->SetMarked(true); } else if (fImageFormatMenu->FindItem("JPEG image") != NULL) fImageFormatMenu->FindItem("JPEG image")->SetMarked(true); else fImageFormatMenu->ItemAt(0)->SetMarked(true); fImageFormatSelector = new BMenuField("Format", B_TRANSLATE("Format:"), fImageFormatMenu); // capture rate fCaptureRateMenu = new BPopUpMenu(B_TRANSLATE("Capture Rate Menu")); for (int32 i = 0; i < kCaptureRatesCount; i++) { BMessage* itemMessage = new BMessage(msg_rate_changed); itemMessage->AddInt32("seconds", kCaptureRates[i].seconds); fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRates[i].name, itemMessage)); } fCaptureRateMenu->SetTargetForItems(this); fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true); fCaptureRateSelector = new BMenuField("Rate", B_TRANSLATE("Rate:"), fCaptureRateMenu); BLayoutBuilder::Grid<>(controlsLayout) .AddTextControl(fFileName, 0, 0) .AddMenuField(fImageFormatSelector, 0, 1) .AddMenuField(fCaptureRateSelector, 0, 2) .Add(BSpaceLayoutItem::CreateGlue(), 0, 3, 2, 1); // FTP setup box BGridLayout* ftpLayout = new BGridLayout(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING); ftpLayout->SetInsets(B_USE_SMALL_SPACING); BView* outputView = new BView("Output", B_SUPPORTS_LAYOUT, NULL); outputView->SetLayout(ftpLayout); fFtpSetupBox = new BBox("FTP Setup", B_WILL_DRAW); fFtpSetupBox->SetLabel(B_TRANSLATE("Output")); fFtpSetupBox->AddChild(outputView); float minWidth = be_plain_font->StringWidth( "The server label plus ftp.reasonably.com"); fFtpSetupBox->SetExplicitMinSize(BSize(minWidth, B_SIZE_UNSET)); fFtpSetupBox->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); fUploadClientMenu = new BPopUpMenu(B_TRANSLATE("Send to" B_UTF8_ELLIPSIS)); for (int i = 0; i < kUploadClientsCount; i++) { BMessage* m = new BMessage(msg_upl_client); m->AddInt32("client", i); fUploadClientMenu->AddItem(new BMenuItem(kUploadClients[i], m)); } fUploadClientMenu->SetTargetForItems(this); fUploadClientMenu->FindItem(fUploadClientSetting->Value())->SetMarked(true); fUploadClientSelector = new BMenuField("UploadClient", NULL, fUploadClientMenu); fUploadClientSelector->SetLabel(B_TRANSLATE("Type:")); fServerName = new BTextControl("Server", B_TRANSLATE("Server:"), fServerSetting->Value(), new BMessage(msg_server)); fServerName->SetTarget(this); fLoginId = new BTextControl("Login", B_TRANSLATE("Login:"), fLoginSetting->Value(), new BMessage(msg_login)); fLoginId->SetTarget(this); fPassword = new BTextControl("Password", B_TRANSLATE("Password:"), fPasswordSetting->Value(), new BMessage(msg_password)); fPassword->SetTarget(this); fPassword->TextView()->HideTyping(true); // BeOS HideTyping() seems broken, it empties the text fPassword->SetText(fPasswordSetting->Value()); fDirectory = new BTextControl("Directory", B_TRANSLATE("Directory:"), fDirectorySetting->Value(), new BMessage(msg_directory)); fDirectory->SetTarget(this); fPassiveFtp = new BCheckBox("Passive FTP", B_TRANSLATE("Passive FTP"), new BMessage(msg_passiveftp)); fPassiveFtp->SetTarget(this); fPassiveFtp->SetValue(fPassiveFtpSetting->Value()); fPassiveFtp->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); BLayoutBuilder::Grid<>(ftpLayout) .AddMenuField(fUploadClientSelector, 0, 0) .AddTextControl(fServerName, 0, 1) .AddTextControl(fLoginId, 0, 2) .AddTextControl(fPassword, 0, 3) .AddTextControl(fDirectory, 0, 4) .Add(fPassiveFtp, 0, 5, 2, 1); fStatusLine = new BStringView("Status Line", B_TRANSLATE("Waiting" B_UTF8_ELLIPSIS)); fStatusLine->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); } void VideoWindow::ApplyControls() { if (!Lock()) return; // apply controls fFileName->Invoke(); PostMessage(fImageFormatMenu->FindMarked()->Message()); PostMessage(fCaptureRateMenu->FindMarked()->Message()); PostMessage(fUploadClientMenu->FindMarked()->Message()); fServerName->Invoke(); fLoginId->Invoke(); fPassword->Invoke(); fDirectory->Invoke(); fPassiveFtp->Invoke(); Unlock(); } void VideoWindow::ErrorAlert(const char* message, status_t err) { Lock(); fErrorView->SetText(message); fErrorView->MakeEditable(false); fErrorView->MakeSelectable(false); fErrorView->SetWordWrap(true); fErrorView->SetAlignment(B_ALIGN_CENTER); fErrorView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); fErrorView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); fErrorView->Show(); fVideoView->Hide(); Unlock(); printf("%s\n%s [%" B_PRIx32 "]", message, strerror(err), err); } void VideoWindow::_SetUpSettings(const char* filename, const char* dirname) { fSettings = new Settings(filename, dirname); fServerSetting = new StringValueSetting("Server", "ftp.my.server", B_TRANSLATE("server address expected")); fLoginSetting = new StringValueSetting("Login", "loginID", B_TRANSLATE("login ID expected")); fPasswordSetting = new StringValueSetting("Password", B_TRANSLATE("password"), B_TRANSLATE("password expected")); fDirectorySetting = new StringValueSetting("Directory", "web/images", B_TRANSLATE("destination directory expected")); fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1); fFilenameSetting = new StringValueSetting("StillImageFilename", "codycam.jpg", B_TRANSLATE("still image filename expected")); fImageFormatSettings = new StringValueSetting("ImageFileFormat", B_TRANSLATE("JPEG image"), B_TRANSLATE("image file format expected")); fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate", kCaptureRates[3].name, &CaptureRateAt, B_TRANSLATE("capture rate expected"), "unrecognized capture rate specified"); fUploadClientSetting = new EnumeratedStringValueSetting("UploadClient", B_TRANSLATE("FTP"), &UploadClientAt, B_TRANSLATE("upload client name expected"), B_TRANSLATE("unrecognized upload client specified")); fSettings->Add(fServerSetting); fSettings->Add(fLoginSetting); fSettings->Add(fPasswordSetting); fSettings->Add(fDirectorySetting); fSettings->Add(fPassiveFtpSetting); fSettings->Add(fFilenameSetting); fSettings->Add(fImageFormatSettings); fSettings->Add(fCaptureRateSetting); fSettings->Add(fUploadClientSetting); fSettings->TryReadingSettings(); } void VideoWindow::_UploadClientChanged() { bool enableServerControls = fFtpInfo.uploadClient < 2; fServerName->SetEnabled(enableServerControls); fLoginId->SetEnabled(enableServerControls); fPassword->SetEnabled(enableServerControls); fDirectory->SetEnabled(enableServerControls); fPassiveFtp->SetEnabled(enableServerControls); } void VideoWindow::_QuitSettings() { fServerSetting->ValueChanged(fServerName->Text()); fLoginSetting->ValueChanged(fLoginId->Text()); fPasswordSetting->ValueChanged(fFtpInfo.passwordText); fDirectorySetting->ValueChanged(fDirectory->Text()); fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value()); fFilenameSetting->ValueChanged(fFileName->Text()); fImageFormatSettings->ValueChanged(fImageFormatMenu->FindMarked()->Label()); fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label()); fUploadClientSetting->ValueChanged( fUploadClientMenu->FindMarked()->Label()); fSettings->SaveSettings(); delete fSettings; } void VideoWindow::ToggleMenuOnOff() { BMenuItem* item = fMenu->FindItem(msg_video); item->SetEnabled(!item->IsEnabled()); item = fMenu->FindItem(msg_start); item->SetEnabled(!item->IsEnabled()); item = fMenu->FindItem(msg_stop); item->SetEnabled(!item->IsEnabled()); } // #pragma mark - ControlWindow::ControlWindow(BView* controls, media_node node) : BWindow(controls->Bounds().OffsetToSelf(100, 100), B_TRANSLATE("Video settings"), B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS) { fView = controls; fNode = node; AddChild(fView); } void ControlWindow::MessageReceived(BMessage* message) { BParameterWeb* web = NULL; status_t err; switch (message->what) { case B_MEDIA_WEB_CHANGED: { // If this is a tab view, find out which tab // is selected BTabView* tabView = dynamic_cast(fView); int32 tabNum = -1; if (tabView) tabNum = tabView->Selection(); RemoveChild(fView); delete fView; err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web); if (err >= B_OK && web != NULL) { fView = BMediaTheme::ViewFor(web); AddChild(fView); // Another tab view? Restore previous selection if (tabNum > 0) { BTabView* newTabView = dynamic_cast(fView); if (newTabView) newTabView->Select(tabNum); } } break; } default: BWindow::MessageReceived(message); break; } } bool ControlWindow::QuitRequested() { be_app->PostMessage(msg_control_win); return true; } // #pragma mark - int main() { CodyCam app; app.Run(); return 0; }