1 #include "CodyCam.h" 2 3 #include <stdio.h> 4 #include <string.h> 5 #include <unistd.h> 6 7 #include <Alert.h> 8 #include <Button.h> 9 #include <GridLayout.h> 10 #include <GroupLayout.h> 11 #include <GroupLayoutBuilder.h> 12 #include <MediaDefs.h> 13 #include <MediaNode.h> 14 #include <MediaRoster.h> 15 #include <MediaTheme.h> 16 #include <Menu.h> 17 #include <MenuBar.h> 18 #include <MenuItem.h> 19 #include <PopUpMenu.h> 20 #include <scheduler.h> 21 #include <SpaceLayoutItem.h> 22 #include <TabView.h> 23 #include <TextControl.h> 24 #include <TimeSource.h> 25 #include <TranslationKit.h> 26 27 28 #undef B_TRANSLATE_CONTEXT 29 #define B_TRANSLATE_CONTEXT "CodyCam" 30 31 #define VIDEO_SIZE_X 320 32 #define VIDEO_SIZE_Y 240 33 34 #define WINDOW_SIZE_X (VIDEO_SIZE_X + 80) 35 #define WINDOW_SIZE_Y (VIDEO_SIZE_Y + 230) 36 37 #define WINDOW_OFFSET_X 28 38 #define WINDOW_OFFSET_Y 28 39 40 const int32 kBtnHeight = 20; 41 const int32 kBtnWidth = 60; 42 const int32 kBtnBuffer = 25; 43 const int32 kXBuffer = 10; 44 const int32 kYBuffer = 10; 45 const int32 kMenuHeight = 15; 46 const int32 kButtonHeight = 15; 47 const int32 kSliderViewRectHeight = 40; 48 49 static void ErrorAlert(const char* message, status_t err, BWindow *window); 50 static status_t AddTranslationItems(BMenu* intoMenu, uint32 fromType); 51 52 #define CALL printf 53 #define ERROR printf 54 #define FTPINFO printf 55 #define INFO printf 56 57 58 // Utility functions 59 60 static void 61 ErrorAlert(const char* message, status_t err, BWindow *window = NULL) 62 { 63 BAlert *alert = new BAlert("", message, B_TRANSLATE("OK")); 64 if (window != NULL) 65 alert->CenterIn(window->Frame()); 66 alert->Go(); 67 68 printf("%s\n%s [%lx]", message, strerror(err), err); 69 // be_app->PostMessage(B_QUIT_REQUESTED); 70 } 71 72 73 status_t 74 AddTranslationItems(BMenu* intoMenu, uint32 fromType) 75 { 76 77 BTranslatorRoster* use; 78 char* translatorTypeName; 79 const char* translatorIdName; 80 81 use = BTranslatorRoster::Default(); 82 translatorIdName = "be:translator"; 83 translatorTypeName = (char *)"be:type"; 84 translator_id* ids = NULL; 85 int32 count = 0; 86 87 status_t err = use->GetAllTranslators(&ids, &count); 88 if (err < B_OK) 89 return err; 90 91 for (int tix = 0; tix < count; tix++) { 92 const translation_format* formats = NULL; 93 int32 num_formats = 0; 94 bool ok = false; 95 err = use->GetInputFormats(ids[tix], &formats, &num_formats); 96 if (err == B_OK) 97 for (int iix = 0; iix < num_formats; iix++) { 98 if (formats[iix].type == fromType) { 99 ok = true; 100 break; 101 } 102 } 103 104 if (!ok) 105 continue; 106 107 err = use->GetOutputFormats(ids[tix], &formats, &num_formats); 108 if (err == B_OK) 109 for (int oix = 0; oix < num_formats; oix++) { 110 if (formats[oix].type != fromType) { 111 BMessage* itemmsg; 112 itemmsg = new BMessage(msg_translate); 113 itemmsg->AddInt32(translatorIdName, ids[tix]); 114 itemmsg->AddInt32(translatorTypeName, formats[oix].type); 115 intoMenu->AddItem(new BMenuItem(formats[oix].name, itemmsg)); 116 } 117 } 118 } 119 delete[] ids; 120 return B_OK; 121 } 122 123 124 // #pragma mark - 125 126 127 CodyCam::CodyCam() 128 : BApplication("application/x-vnd.Haiku.CodyCam"), 129 fMediaRoster(NULL), 130 fVideoConsumer(NULL), 131 fWindow(NULL), 132 fPort(0), 133 fVideoControlWindow(NULL), 134 fAppCatalog(NULL) 135 { 136 be_locale->GetAppCatalog(&fAppCatalog); 137 138 int32 index = 0; 139 kCaptureRate[index++] = B_TRANSLATE("Every 15 seconds"); 140 kCaptureRate[index++] = B_TRANSLATE("Every 30 seconds"); 141 kCaptureRate[index++] = B_TRANSLATE("Every minute"); 142 kCaptureRate[index++] = B_TRANSLATE("Every 5 minutes"); 143 kCaptureRate[index++] = B_TRANSLATE("Every 10 minutes"); 144 kCaptureRate[index++] = B_TRANSLATE("Every 15 minutes"); 145 kCaptureRate[index++] = B_TRANSLATE("Every 30 minutes"); 146 kCaptureRate[index++] = B_TRANSLATE("Every hour"); 147 kCaptureRate[index++] = B_TRANSLATE("Every 2 hours"); 148 kCaptureRate[index++] = B_TRANSLATE("Every 4 hours"); 149 kCaptureRate[index++] = B_TRANSLATE("Every 8 hours"); 150 kCaptureRate[index++] = B_TRANSLATE("Every 24 hours"); 151 kCaptureRate[index++] = B_TRANSLATE("Never"); 152 153 index = 0; 154 kUploadClient[index++] = B_TRANSLATE("FTP"); 155 kUploadClient[index++] = B_TRANSLATE("SFTP"); 156 kUploadClient[index++] = B_TRANSLATE("Local"); 157 158 chdir("/boot/home"); 159 } 160 161 162 CodyCam::~CodyCam() 163 { 164 CALL("CodyCam::~CodyCam\n"); 165 166 // release the video consumer node 167 // the consumer node cleans up the window 168 if (fVideoConsumer) { 169 fVideoConsumer->Release(); 170 fVideoConsumer = NULL; 171 } 172 173 CALL("CodyCam::~CodyCam - EXIT\n"); 174 } 175 176 177 void 178 CodyCam::ReadyToRun() 179 { 180 fWindow = new VideoWindow(BRect(28, 28, 28, 28), 181 (const char*) B_TRANSLATE("CodyCam"), B_TITLED_WINDOW, 182 B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS, &fPort); 183 184 _SetUpNodes(); 185 186 ((VideoWindow*)fWindow)->ApplyControls(); 187 } 188 189 190 191 bool 192 CodyCam::QuitRequested() 193 { 194 _TearDownNodes(); 195 snooze(100000); 196 197 return true; 198 } 199 200 201 void 202 CodyCam::MessageReceived(BMessage *message) 203 { 204 switch (message->what) { 205 case msg_start: 206 { 207 BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor( 208 fTimeSourceNode); 209 bigtime_t real = BTimeSource::RealTime(); 210 bigtime_t perf = timeSource->PerformanceTimeFor(real) + 10000; 211 status_t status = fMediaRoster->StartNode(fProducerNode, perf); 212 if (status != B_OK) 213 ERROR("error starting producer!"); 214 timeSource->Release(); 215 break; 216 } 217 218 case msg_stop: 219 fMediaRoster->StopNode(fProducerNode, 0, true); 220 break; 221 222 case msg_video: 223 { 224 if (fVideoControlWindow) { 225 fVideoControlWindow->Activate(); 226 break; 227 } 228 BParameterWeb* web = NULL; 229 BView* view = NULL; 230 media_node node = fProducerNode; 231 status_t err = fMediaRoster->GetParameterWebFor(node, &web); 232 if (err >= B_OK && web != NULL) { 233 view = BMediaTheme::ViewFor(web); 234 fVideoControlWindow = new ControlWindow( 235 BRect(2 * WINDOW_OFFSET_X + WINDOW_SIZE_X, WINDOW_OFFSET_Y, 236 2 * WINDOW_OFFSET_X + WINDOW_SIZE_X + view->Bounds().right, 237 WINDOW_OFFSET_Y + view->Bounds().bottom), view, node); 238 fMediaRoster->StartWatching(BMessenger(NULL, 239 fVideoControlWindow), node, B_MEDIA_WEB_CHANGED); 240 fVideoControlWindow->Show(); 241 } 242 break; 243 } 244 245 case msg_about: 246 (new BAlert(B_TRANSLATE("About CodyCam"), B_TRANSLATE("CodyCam\n\n" 247 "The Original BeOS webcam"), B_TRANSLATE("Close")))->Go(); 248 break; 249 250 case msg_control_win: 251 // our control window is being asked to go away 252 // set our pointer to NULL 253 fVideoControlWindow = NULL; 254 break; 255 256 default: 257 BApplication::MessageReceived(message); 258 break; 259 } 260 } 261 262 263 status_t 264 CodyCam::_SetUpNodes() 265 { 266 status_t status = B_OK; 267 268 /* find the media roster */ 269 fMediaRoster = BMediaRoster::Roster(&status); 270 if (status != B_OK) { 271 ErrorAlert(B_TRANSLATE("Cannot find the media roster"), status, 272 fWindow); 273 return status; 274 } 275 276 /* find the time source */ 277 status = fMediaRoster->GetTimeSource(&fTimeSourceNode); 278 if (status != B_OK) { 279 ErrorAlert(B_TRANSLATE("Cannot get a time source"), status, fWindow); 280 return status; 281 } 282 283 /* find a video producer node */ 284 INFO("CodyCam acquiring VideoInput node\n"); 285 status = fMediaRoster->GetVideoInput(&fProducerNode); 286 if (status != B_OK) { 287 ErrorAlert(B_TRANSLATE("Cannot find a video source. You need a webcam " 288 "to use CodyCam."), status, fWindow); 289 return status; 290 } 291 292 /* create the video consumer node */ 293 fVideoConsumer = new VideoConsumer("CodyCam", 294 ((VideoWindow*)fWindow)->VideoView(), 295 ((VideoWindow*)fWindow)->StatusLine(), NULL, 0); 296 if (!fVideoConsumer) { 297 ErrorAlert(B_TRANSLATE("Cannot create a video window"), B_ERROR, 298 fWindow); 299 return B_ERROR; 300 } 301 302 /* register the node */ 303 status = fMediaRoster->RegisterNode(fVideoConsumer); 304 if (status != B_OK) { 305 ErrorAlert(B_TRANSLATE("Cannot register the video window"), status, 306 fWindow); 307 return status; 308 } 309 fPort = fVideoConsumer->ControlPort(); 310 311 /* find free producer output */ 312 int32 cnt = 0; 313 status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1, 314 &cnt, B_MEDIA_RAW_VIDEO); 315 if (status != B_OK || cnt < 1) { 316 status = B_RESOURCE_UNAVAILABLE; 317 ErrorAlert(B_TRANSLATE("Cannot find an available video stream"), 318 status, fWindow); 319 return status; 320 } 321 322 /* find free consumer input */ 323 cnt = 0; 324 status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(), 325 &fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO); 326 if (status != B_OK || cnt < 1) { 327 status = B_RESOURCE_UNAVAILABLE; 328 ErrorAlert(B_TRANSLATE("Can't find an available connection to the " 329 "video window"), status, fWindow); 330 return status; 331 } 332 333 /* Connect The Nodes!!! */ 334 media_format format; 335 format.type = B_MEDIA_RAW_VIDEO; 336 media_raw_video_format vid_format = {0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT, 337 1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X * 4, 0, 0}}; 338 format.u.raw_video = vid_format; 339 340 /* connect producer to consumer */ 341 status = fMediaRoster->Connect(fProducerOut.source, 342 fConsumerIn.destination, &format, &fProducerOut, &fConsumerIn); 343 if (status != B_OK) { 344 ErrorAlert(B_TRANSLATE("Cannot connect the video source to the video " 345 "window"), status); 346 return status; 347 } 348 349 350 /* set time sources */ 351 status = fMediaRoster->SetTimeSourceFor(fProducerNode.node, 352 fTimeSourceNode.node); 353 if (status != B_OK) { 354 ErrorAlert(B_TRANSLATE("Cannot set the time source for the video " 355 "source"), status); 356 return status; 357 } 358 359 status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(), 360 fTimeSourceNode.node); 361 if (status != B_OK) { 362 ErrorAlert(B_TRANSLATE("Cannot set the time source for the video " 363 "window"), status); 364 return status; 365 } 366 367 /* figure out what recording delay to use */ 368 bigtime_t latency = 0; 369 status = fMediaRoster->GetLatencyFor(fProducerNode, &latency); 370 status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency); 371 372 /* start the nodes */ 373 bigtime_t initLatency = 0; 374 status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency); 375 if (status < B_OK) { 376 ErrorAlert(B_TRANSLATE("Error getting initial latency for the capture " 377 "node"), status); 378 return status; 379 } 380 381 initLatency += estimate_max_scheduling_latency(); 382 383 BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode); 384 bool running = timeSource->IsRunning(); 385 386 /* workaround for people without sound cards */ 387 /* because the system time source won't be running */ 388 bigtime_t real = BTimeSource::RealTime(); 389 if (!running) { 390 status = fMediaRoster->StartTimeSource(fTimeSourceNode, real); 391 if (status != B_OK) { 392 timeSource->Release(); 393 ErrorAlert(B_TRANSLATE("Cannot start time source!"), status); 394 return status; 395 } 396 status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real); 397 if (status != B_OK) { 398 timeSource->Release(); 399 ErrorAlert(B_TRANSLATE("Cannot seek time source!"), status); 400 return status; 401 } 402 } 403 404 bigtime_t perf = timeSource->PerformanceTimeFor(real + latency 405 + initLatency); 406 timeSource->Release(); 407 408 /* start the nodes */ 409 status = fMediaRoster->StartNode(fProducerNode, perf); 410 if (status != B_OK) { 411 ErrorAlert(B_TRANSLATE("Cannot start the video source"), status); 412 return status; 413 } 414 status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf); 415 if (status != B_OK) { 416 ErrorAlert(B_TRANSLATE("Cannot start the video window"), status); 417 return status; 418 } 419 420 return status; 421 } 422 423 424 void 425 CodyCam::_TearDownNodes() 426 { 427 CALL("CodyCam::_TearDownNodes\n"); 428 if (!fMediaRoster) 429 return; 430 431 if (fVideoConsumer) { 432 /* stop */ 433 INFO("stopping nodes!\n"); 434 // fMediaRoster->StopNode(fProducerNode, 0, true); 435 fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true); 436 437 /* disconnect */ 438 fMediaRoster->Disconnect(fProducerOut.node.node, fProducerOut.source, 439 fConsumerIn.node.node, fConsumerIn.destination); 440 441 if (fProducerNode != media_node::null) { 442 INFO("CodyCam releasing fProducerNode\n"); 443 fMediaRoster->ReleaseNode(fProducerNode); 444 fProducerNode = media_node::null; 445 } 446 fMediaRoster->ReleaseNode(fVideoConsumer->Node()); 447 fVideoConsumer = NULL; 448 } 449 } 450 451 452 // #pragma mark - Video Window Class 453 454 455 VideoWindow::VideoWindow(BRect frame, const char* title, window_type type, 456 uint32 flags, port_id* consumerPort) 457 : 458 BWindow(frame, title, type, flags), 459 fPortPtr(consumerPort), 460 fView(NULL), 461 fVideoView(NULL) 462 { 463 fFtpInfo.port = 0; 464 fFtpInfo.rate = 0x7fffffff; 465 fFtpInfo.imageFormat = 0; 466 fFtpInfo.translator = 0; 467 fFtpInfo.passiveFtp = true; 468 fFtpInfo.uploadClient = 0; 469 strcpy(fFtpInfo.fileNameText, "filename"); 470 strcpy(fFtpInfo.serverText, "server"); 471 strcpy(fFtpInfo.loginText, "login"); 472 strcpy(fFtpInfo.passwordText, "password"); 473 strcpy(fFtpInfo.directoryText, "directory"); 474 475 _SetUpSettings("codycam", ""); 476 477 BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), "menu bar"); 478 479 BMenuItem* menuItem; 480 BMenu* menu = new BMenu(B_TRANSLATE("File")); 481 482 menuItem = new BMenuItem(B_TRANSLATE("Video settings"), 483 new BMessage(msg_video), 'P'); 484 menuItem->SetTarget(be_app); 485 menu->AddItem(menuItem); 486 487 menu->AddSeparatorItem(); 488 489 menuItem = new BMenuItem(B_TRANSLATE("Start video"), 490 new BMessage(msg_start), 'A'); 491 menuItem->SetTarget(be_app); 492 menu->AddItem(menuItem); 493 494 menuItem = new BMenuItem(B_TRANSLATE("Stop video"), 495 new BMessage(msg_stop), 'O'); 496 menuItem->SetTarget(be_app); 497 menu->AddItem(menuItem); 498 499 menu->AddSeparatorItem(); 500 501 menuItem = new BMenuItem(B_TRANSLATE("About Codycam" B_UTF8_ELLIPSIS), 502 new BMessage(msg_about), 'B'); 503 menuItem->SetTarget(be_app); 504 menu->AddItem(menuItem); 505 506 menu->AddSeparatorItem(); 507 508 menuItem = new BMenuItem(B_TRANSLATE("Quit"), 509 new BMessage(B_QUIT_REQUESTED), 'Q'); 510 menuItem->SetTarget(be_app); 511 menu->AddItem(menuItem); 512 513 menuBar->AddItem(menu); 514 515 /* give it a gray background view */ 516 fView = new BView(B_TRANSLATE("Background View"), B_WILL_DRAW); 517 fView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 518 519 /* add some controls */ 520 _BuildCaptureControls(fView); 521 522 SetLayout(new BGroupLayout(B_VERTICAL)); 523 AddChild(menuBar); 524 AddChild(fView); 525 526 Show(); 527 } 528 529 530 531 VideoWindow::~VideoWindow() 532 { 533 _QuitSettings(); 534 } 535 536 537 538 bool 539 VideoWindow::QuitRequested() 540 { 541 be_app->PostMessage(B_QUIT_REQUESTED); 542 return false; 543 } 544 545 546 void 547 VideoWindow::MessageReceived(BMessage* message) 548 { 549 BControl* control = NULL; 550 message->FindPointer((const char*)"source", (void **)&control); 551 552 switch (message->what) { 553 case msg_filename: 554 if (control != NULL) { 555 strncpy(fFtpInfo.fileNameText, ((BTextControl*)control)->Text(), 63); 556 FTPINFO("file is '%s'\n", fFtpInfo.fileNameText); 557 } 558 break; 559 560 case msg_rate_15s: 561 FTPINFO("fifteen seconds\n"); 562 fFtpInfo.rate = (bigtime_t)(15 * 1000000); 563 break; 564 565 case msg_rate_30s: 566 FTPINFO("thirty seconds\n"); 567 fFtpInfo.rate = (bigtime_t)(30 * 1000000); 568 break; 569 570 case msg_rate_1m: 571 FTPINFO("one minute\n"); 572 fFtpInfo.rate = (bigtime_t)(1 * 60 * 1000000); 573 break; 574 575 case msg_rate_5m: 576 FTPINFO("five minute\n"); 577 fFtpInfo.rate = (bigtime_t)(5 * 60 * 1000000); 578 break; 579 580 case msg_rate_10m: 581 FTPINFO("ten minute\n"); 582 fFtpInfo.rate = (bigtime_t)(10 * 60 * 1000000); 583 break; 584 585 case msg_rate_15m: 586 FTPINFO("fifteen minute\n"); 587 fFtpInfo.rate = (bigtime_t)(15 * 60 * 1000000); 588 break; 589 590 case msg_rate_30m: 591 FTPINFO("thirty minute\n"); 592 fFtpInfo.rate = (bigtime_t)(30 * 60 * 1000000); 593 break; 594 595 case msg_rate_1h: 596 FTPINFO("one hour\n"); 597 fFtpInfo.rate = (bigtime_t)(60LL * 60LL * 1000000LL); 598 break; 599 600 case msg_rate_2h: 601 FTPINFO("two hour\n"); 602 fFtpInfo.rate = (bigtime_t)(2LL * 60LL * 60LL * 1000000LL); 603 break; 604 605 case msg_rate_4h: 606 FTPINFO("four hour\n"); 607 fFtpInfo.rate = (bigtime_t)(4LL * 60LL * 60LL * 1000000LL); 608 break; 609 610 case msg_rate_8h: 611 FTPINFO("eight hour\n"); 612 fFtpInfo.rate = (bigtime_t)(8LL * 60LL * 60LL * 1000000LL); 613 break; 614 615 case msg_rate_24h: 616 FTPINFO("24 hour\n"); 617 fFtpInfo.rate = (bigtime_t)(24LL * 60LL * 60LL * 1000000LL); 618 break; 619 620 case msg_rate_never: 621 FTPINFO("never\n"); 622 fFtpInfo.rate = (bigtime_t)(B_INFINITE_TIMEOUT); 623 break; 624 625 case msg_translate: 626 message->FindInt32("be:type", (int32*)&(fFtpInfo.imageFormat)); 627 message->FindInt32("be:translator", &(fFtpInfo.translator)); 628 break; 629 630 case msg_upl_client: 631 message->FindInt32("client", &(fFtpInfo.uploadClient)); 632 FTPINFO("upl client = %ld\n", fFtpInfo.uploadClient); 633 _UploadClientChanged(); 634 break; 635 636 case msg_server: 637 if (control != NULL) { 638 strncpy(fFtpInfo.serverText, ((BTextControl*)control)->Text(), 64); 639 FTPINFO("server = '%s'\n", fFtpInfo.serverText); 640 } 641 break; 642 643 case msg_login: 644 if (control != NULL) { 645 strncpy(fFtpInfo.loginText, ((BTextControl*)control)->Text(), 64); 646 FTPINFO("login = '%s'\n", fFtpInfo.loginText); 647 } 648 break; 649 650 case msg_password: 651 if (control != NULL) { 652 strncpy(fFtpInfo.passwordText, ((BTextControl*)control)->Text(), 64); 653 FTPINFO("password = '%s'\n", fFtpInfo.passwordText); 654 } 655 break; 656 657 case msg_directory: 658 if (control != NULL) { 659 strncpy(fFtpInfo.directoryText, ((BTextControl*)control)->Text(), 64); 660 FTPINFO("directory = '%s'\n", fFtpInfo.directoryText); 661 } 662 break; 663 664 case msg_passiveftp: 665 if (control != NULL) { 666 fFtpInfo.passiveFtp = ((BCheckBox*)control)->Value(); 667 if (fFtpInfo.passiveFtp) 668 FTPINFO("using passive ftp\n"); 669 } 670 break; 671 672 default: 673 BWindow::MessageReceived(message); 674 return; 675 } 676 677 if (*fPortPtr) 678 write_port(*fPortPtr, FTP_INFO, (void*)&fFtpInfo, sizeof(ftp_msg_info)); 679 680 } 681 682 683 BView* 684 VideoWindow::VideoView() 685 { 686 return fVideoView; 687 } 688 689 690 BStringView* 691 VideoWindow::StatusLine() 692 { 693 return fStatusLine; 694 } 695 696 697 void 698 VideoWindow::_BuildCaptureControls(BView* theView) 699 { 700 // a view to hold the video image 701 fVideoView = new BView("Video View", B_WILL_DRAW); 702 fVideoView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); 703 fVideoView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y)); 704 705 // Capture controls 706 fCaptureSetupBox = new BBox("Capture Controls", B_WILL_DRAW); 707 fCaptureSetupBox->SetLabel(B_TRANSLATE("Capture controls")); 708 709 BGridLayout *controlsLayout = new BGridLayout(kXBuffer, 0); 710 controlsLayout->SetInsets(10, 15, 5, 5); 711 fCaptureSetupBox->SetLayout(controlsLayout); 712 713 fFileName = new BTextControl("File Name", B_TRANSLATE("File name:"), 714 fFilenameSetting->Value(), new BMessage(msg_filename)); 715 fFileName->SetTarget(BMessenger(NULL, this)); 716 717 fImageFormatMenu = new BPopUpMenu(B_TRANSLATE("Image Format Menu")); 718 AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP); 719 fImageFormatMenu->SetTargetForItems(this); 720 721 if (fImageFormatSettings->Value() 722 && fImageFormatMenu->FindItem(fImageFormatSettings->Value()) != NULL) { 723 fImageFormatMenu->FindItem( 724 fImageFormatSettings->Value())->SetMarked(true); 725 } else if (fImageFormatMenu->FindItem("JPEG image") != NULL) 726 fImageFormatMenu->FindItem("JPEG image")->SetMarked(true); 727 else 728 fImageFormatMenu->ItemAt(0)->SetMarked(true); 729 730 fImageFormatSelector = new BMenuField("Format", B_TRANSLATE("Format:"), 731 fImageFormatMenu, NULL); 732 733 fCaptureRateMenu = new BPopUpMenu(B_TRANSLATE("Capture Rate Menu")); 734 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[0], 735 new BMessage(msg_rate_15s))); 736 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[1], 737 new BMessage(msg_rate_30s))); 738 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[2], 739 new BMessage(msg_rate_1m))); 740 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[3], 741 new BMessage(msg_rate_5m))); 742 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[4], 743 new BMessage(msg_rate_10m))); 744 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[5], 745 new BMessage(msg_rate_15m))); 746 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[6], 747 new BMessage(msg_rate_30m))); 748 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[7], 749 new BMessage(msg_rate_1h))); 750 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[8], 751 new BMessage(msg_rate_2h))); 752 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[9], 753 new BMessage(msg_rate_4h))); 754 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[10], 755 new BMessage(msg_rate_8h))); 756 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[11], 757 new BMessage(msg_rate_24h))); 758 fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRate[12], 759 new BMessage(msg_rate_never))); 760 fCaptureRateMenu->SetTargetForItems(this); 761 fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true); 762 fCaptureRateSelector = new BMenuField("Rate", B_TRANSLATE("Rate:"), 763 fCaptureRateMenu, NULL); 764 765 controlsLayout->AddItem(fFileName->CreateLabelLayoutItem(), 0, 0); 766 controlsLayout->AddItem(fFileName->CreateTextViewLayoutItem(), 1, 0); 767 controlsLayout->AddItem(fImageFormatSelector->CreateLabelLayoutItem(), 0, 768 1); 769 controlsLayout->AddItem(fImageFormatSelector->CreateMenuBarLayoutItem(), 1, 770 1); 771 controlsLayout->AddItem(fCaptureRateSelector->CreateLabelLayoutItem(), 0, 772 2); 773 controlsLayout->AddItem(fCaptureRateSelector->CreateMenuBarLayoutItem(), 1, 774 2); 775 controlsLayout->AddItem(BSpaceLayoutItem::CreateGlue(), 0, 3, 2); 776 777 // FTP setup box 778 fFtpSetupBox = new BBox(B_TRANSLATE("FTP Setup"), B_WILL_DRAW); 779 780 fUploadClientMenu = new BPopUpMenu(B_TRANSLATE("Send to" B_UTF8_ELLIPSIS)); 781 for (int i = 0; kUploadClient[i]; i++) { 782 BMessage *m = new BMessage(msg_upl_client); 783 m->AddInt32("client", i); 784 fUploadClientMenu->AddItem(new BMenuItem(kUploadClient[i], m)); 785 } 786 fUploadClientMenu->SetTargetForItems(this); 787 fUploadClientMenu->FindItem(fUploadClientSetting->Value())->SetMarked(true); 788 fUploadClientSelector = new BMenuField("UploadClient", NULL, 789 fUploadClientMenu, NULL); 790 791 fFtpSetupBox->SetLabel(B_TRANSLATE("Output")); 792 // this doesn't work with the layout manager 793 // fFtpSetupBox->SetLabel(fUploadClientSelector); 794 fUploadClientSelector->SetLabel(B_TRANSLATE("Type:")); 795 796 BGridLayout *ftpLayout = new BGridLayout(kXBuffer, 0); 797 ftpLayout->SetInsets(10, 15, 5, 5); 798 fFtpSetupBox->SetLayout(ftpLayout); 799 800 fServerName = new BTextControl("Server", B_TRANSLATE("Server:"), 801 fServerSetting->Value(), new BMessage(msg_server)); 802 fServerName->SetTarget(this); 803 804 fLoginId = new BTextControl("Login", B_TRANSLATE("Login:"), 805 fLoginSetting->Value(), new BMessage(msg_login)); 806 fLoginId->SetTarget(this); 807 808 fPassword = new BTextControl("Password", B_TRANSLATE("Password:"), 809 fPasswordSetting->Value(), new BMessage(msg_password)); 810 fPassword->SetTarget(this); 811 fPassword->TextView()->HideTyping(true); 812 // BeOS HideTyping() seems broken, it empties the text 813 fPassword->SetText(fPasswordSetting->Value()); 814 815 fDirectory = new BTextControl("Directory", B_TRANSLATE("Directory:"), 816 fDirectorySetting->Value(), new BMessage(msg_directory)); 817 fDirectory->SetTarget(this); 818 819 fPassiveFtp = new BCheckBox("Passive FTP", B_TRANSLATE("Passive FTP"), 820 new BMessage(msg_passiveftp)); 821 fPassiveFtp->SetTarget(this); 822 fPassiveFtp->SetValue(fPassiveFtpSetting->Value()); 823 824 ftpLayout->AddItem(fUploadClientSelector->CreateLabelLayoutItem(), 0, 0); 825 ftpLayout->AddItem(fUploadClientSelector->CreateMenuBarLayoutItem(), 1, 0); 826 ftpLayout->AddItem(fServerName->CreateLabelLayoutItem(), 0, 1); 827 ftpLayout->AddItem(fServerName->CreateTextViewLayoutItem(), 1, 1); 828 ftpLayout->AddItem(fLoginId->CreateLabelLayoutItem(), 0, 2); 829 ftpLayout->AddItem(fLoginId->CreateTextViewLayoutItem(), 1, 2); 830 ftpLayout->AddItem(fPassword->CreateLabelLayoutItem(), 0, 3); 831 ftpLayout->AddItem(fPassword->CreateTextViewLayoutItem(), 1, 3); 832 ftpLayout->AddItem(fDirectory->CreateLabelLayoutItem(), 0, 4); 833 ftpLayout->AddItem(fDirectory->CreateTextViewLayoutItem(), 1, 4); 834 ftpLayout->AddView(fPassiveFtp, 0, 5, 2); 835 836 fStatusLine = new BStringView("Status Line", B_TRANSLATE("Waiting" B_UTF8_ELLIPSIS)); 837 838 BGroupLayout *groupLayout = new BGroupLayout(B_VERTICAL); 839 groupLayout->SetInsets(kXBuffer, kYBuffer, kXBuffer, kYBuffer); 840 841 theView->SetLayout(groupLayout); 842 843 theView->AddChild(BSpaceLayoutItem::CreateVerticalStrut(kYBuffer)); 844 theView->AddChild(BGroupLayoutBuilder(B_HORIZONTAL) 845 .Add(BSpaceLayoutItem::CreateHorizontalStrut(0.0)) 846 .Add(fVideoView) 847 .Add(BSpaceLayoutItem::CreateHorizontalStrut(0.0)) 848 ); 849 theView->AddChild(BSpaceLayoutItem::CreateVerticalStrut(kYBuffer)); 850 theView->AddChild(BGroupLayoutBuilder(B_HORIZONTAL, kXBuffer) 851 .Add(fCaptureSetupBox) 852 .Add(fFtpSetupBox) 853 ); 854 theView->AddChild(BSpaceLayoutItem::CreateVerticalStrut(kYBuffer)); 855 theView->AddChild(fStatusLine); 856 } 857 858 859 void 860 VideoWindow::ApplyControls() 861 { 862 if (!Lock()) 863 return; 864 865 // apply controls 866 fFileName->Invoke(); 867 PostMessage(fImageFormatMenu->FindMarked()->Message()); 868 PostMessage(fCaptureRateMenu->FindMarked()->Message()); 869 PostMessage(fUploadClientMenu->FindMarked()->Message()); 870 fServerName->Invoke(); 871 fLoginId->Invoke(); 872 fPassword->Invoke(); 873 fDirectory->Invoke(); 874 fPassiveFtp->Invoke(); 875 876 Unlock(); 877 } 878 879 880 void 881 VideoWindow::_SetUpSettings(const char* filename, const char* dirname) 882 { 883 fSettings = new Settings(filename, dirname); 884 885 fServerSetting = new StringValueSetting("Server", "ftp.my.server", 886 B_TRANSLATE("server address expected"), ""); 887 fLoginSetting = new StringValueSetting("Login", "loginID", 888 B_TRANSLATE("login ID expected"), ""); 889 fPasswordSetting = new StringValueSetting("Password", 890 B_TRANSLATE("password"), B_TRANSLATE("password expected"), ""); 891 fDirectorySetting = new StringValueSetting("Directory", "web/images", 892 B_TRANSLATE("destination directory expected"), ""); 893 fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1); 894 fFilenameSetting = new StringValueSetting("StillImageFilename", 895 "codycam.jpg", B_TRANSLATE("still image filename expected"), ""); 896 fImageFormatSettings = new StringValueSetting("ImageFileFormat", 897 B_TRANSLATE("JPEG image"), B_TRANSLATE("image file format expected"), 898 ""); 899 fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate", 900 kCaptureRate[3], kCaptureRate, B_TRANSLATE("capture rate expected"), 901 "unrecognized capture rate specified"); 902 fUploadClientSetting = new EnumeratedStringValueSetting("UploadClient", 903 B_TRANSLATE("FTP"), kUploadClient, 904 B_TRANSLATE("upload client name expected"), 905 B_TRANSLATE("unrecognized upload client specified")); 906 907 fSettings->Add(fServerSetting); 908 fSettings->Add(fLoginSetting); 909 fSettings->Add(fPasswordSetting); 910 fSettings->Add(fDirectorySetting); 911 fSettings->Add(fPassiveFtpSetting); 912 fSettings->Add(fFilenameSetting); 913 fSettings->Add(fImageFormatSettings); 914 fSettings->Add(fCaptureRateSetting); 915 fSettings->Add(fUploadClientSetting); 916 917 fSettings->TryReadingSettings(); 918 } 919 920 921 void 922 VideoWindow::_UploadClientChanged() 923 { 924 bool enableServerControls = fFtpInfo.uploadClient < 2; 925 fServerName->SetEnabled(enableServerControls); 926 fLoginId->SetEnabled(enableServerControls); 927 fPassword->SetEnabled(enableServerControls); 928 fDirectory->SetEnabled(enableServerControls); 929 fPassiveFtp->SetEnabled(enableServerControls); 930 } 931 932 933 void 934 VideoWindow::_QuitSettings() 935 { 936 fServerSetting->ValueChanged(fServerName->Text()); 937 fLoginSetting->ValueChanged(fLoginId->Text()); 938 fPasswordSetting->ValueChanged(fFtpInfo.passwordText); 939 fDirectorySetting->ValueChanged(fDirectory->Text()); 940 fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value()); 941 fFilenameSetting->ValueChanged(fFileName->Text()); 942 fImageFormatSettings->ValueChanged(fImageFormatMenu->FindMarked()->Label()); 943 fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label()); 944 fUploadClientSetting->ValueChanged(fUploadClientMenu->FindMarked()->Label()); 945 946 fSettings->SaveSettings(); 947 delete fSettings; 948 } 949 950 951 // #pragma mark - 952 953 954 ControlWindow::ControlWindow(const BRect& frame, BView* controls, 955 media_node node) 956 : BWindow(frame, B_TRANSLATE("Video settings"), B_TITLED_WINDOW, 957 B_ASYNCHRONOUS_CONTROLS) 958 { 959 fView = controls; 960 fNode = node; 961 962 AddChild(fView); 963 } 964 965 966 void 967 ControlWindow::MessageReceived(BMessage* message) 968 { 969 BParameterWeb* web = NULL; 970 status_t err; 971 972 switch (message->what) { 973 case B_MEDIA_WEB_CHANGED: 974 { 975 // If this is a tab view, find out which tab 976 // is selected 977 BTabView* tabView = dynamic_cast<BTabView*>(fView); 978 int32 tabNum = -1; 979 if (tabView) 980 tabNum = tabView->Selection(); 981 982 RemoveChild(fView); 983 delete fView; 984 985 err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web); 986 987 if (err >= B_OK && web != NULL) { 988 fView = BMediaTheme::ViewFor(web); 989 AddChild(fView); 990 991 // Another tab view? Restore previous selection 992 if (tabNum > 0) { 993 BTabView* newTabView = dynamic_cast<BTabView*>(fView); 994 if (newTabView) 995 newTabView->Select(tabNum); 996 } 997 } 998 break; 999 } 1000 1001 default: 1002 BWindow::MessageReceived(message); 1003 } 1004 } 1005 1006 1007 bool 1008 ControlWindow::QuitRequested() 1009 { 1010 be_app->PostMessage(msg_control_win); 1011 return true; 1012 } 1013 1014 1015 // #pragma mark - 1016 1017 1018 int main() { 1019 CodyCam app; 1020 app.Run(); 1021 return 0; 1022 } 1023 1024