1 /* 2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de> 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without restriction, 7 * including without limitation the rights to use, copy, modify, 8 * merge, publish, distribute, sublicense, and/or sell copies of 9 * the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 #include <stdio.h> 26 #include <string.h> 27 #include <Debug.h> 28 #include <ParameterWeb.h> 29 #include <TimeSource.h> 30 31 #include "Controller.h" 32 #include "DeviceRoster.h" 33 #include "VideoView.h" 34 #include "VideoNode.h" 35 36 extern bool gOverlayDisabled; 37 38 status_t MediaRoster_Disconnect(const media_output &output, const media_input &input); 39 40 41 42 media_node dvb_node; 43 media_node audio_mixer_node; 44 media_node video_window_node; 45 media_node time_node; 46 47 media_input audio_input; 48 media_output audio_output; 49 media_input video_input; 50 media_output video_output; 51 52 BMediaRoster *gMediaRoster; 53 54 void 55 HandleError(const char *text, status_t err) 56 { 57 if (err != B_OK) { 58 printf("%s. error 0x%08x (%s)\n",text, (int)err, strerror(err)); 59 fflush(NULL); 60 exit(1); 61 } 62 } 63 64 65 Controller::Controller() 66 : fCurrentInterface(-1) 67 , fCurrentChannel(-1) 68 , fVideoView(NULL) 69 , fVideoNode(NULL) 70 , fWeb(NULL) 71 , fChannelParam(NULL) 72 , fConnected(false) 73 , fInput() 74 , fOutput() 75 { 76 gMediaRoster = BMediaRoster::Roster(); 77 } 78 79 80 Controller::~Controller() 81 { 82 delete fWeb; 83 } 84 85 86 void 87 Controller::SetVideoView(VideoView *view) 88 { 89 fVideoView = view; 90 } 91 92 93 void 94 Controller::SetVideoNode(VideoNode *node) 95 { 96 fVideoNode = node; 97 } 98 99 100 void 101 Controller::DisconnectInterface() 102 { 103 DisconnectNodes(); 104 fCurrentInterface = -1; 105 fCurrentChannel = -1; 106 delete fWeb; 107 fWeb = 0; 108 fChannelParam = 0; 109 } 110 111 112 status_t 113 Controller::ConnectInterface(int i) 114 { 115 if (i < 0) { 116 printf("Controller::ConnectInterface: wrong index\n"); 117 return B_ERROR; 118 } 119 if (fCurrentInterface != -1) { 120 printf("Controller::ConnectInterface: already connected\n"); 121 return B_ERROR; 122 } 123 124 BParameterWeb *web; 125 status_t err; 126 127 err = gDeviceRoster->MediaRoster()->GetParameterWebFor(gDeviceRoster->DeviceNode(i), &web); 128 if (err != B_OK) { 129 printf("Controller::ConnectInterface: can't get parameter web\n"); 130 return B_ERROR; 131 } 132 133 delete fWeb; 134 fWeb = web; 135 fCurrentInterface = i; 136 137 // XXX we may need to monitor for parameter web changes 138 // and reassing fWeb and fChannelParam on demand. 139 140 // find the channel control and assign it to fChannelParam 141 fChannelParam = NULL; 142 int count = fWeb->CountParameters(); 143 for (int i = 0; i < count; i++) { 144 BParameter *parameter = fWeb->ParameterAt(i); 145 146 printf("parameter %d\n", i); 147 printf(" name '%s'\n", parameter->Name()); 148 printf(" kind '%s'\n", parameter->Kind()); 149 printf(" unit '%s'\n", parameter->Unit()); 150 printf(" flags 0x%08" B_PRIx32 "\n", parameter->Flags()); 151 152 // XXX TODO: matching on Name is weak 153 if (strcmp(parameter->Name(), "Channel") == 0 || strcmp(parameter->Kind(), B_TUNER_CHANNEL) == 0) { 154 fChannelParam = dynamic_cast<BDiscreteParameter *>(parameter); 155 if (fChannelParam) 156 break; 157 } 158 } 159 if (!fChannelParam) { 160 printf("Controller::ConnectInterface: can't find channel parameter control\n"); 161 fCurrentChannel = -1; 162 } else { 163 if (fChannelParam->CountItems() == 0) { 164 fCurrentChannel = -1; 165 printf("Controller::ConnectInterface: channel control has 0 items\n"); 166 } else { 167 int32 index; 168 size_t size; 169 status_t err; 170 bigtime_t when; 171 size = sizeof(index); 172 err = fChannelParam->GetValue(&index, &size, &when); 173 if (err == B_OK && size == sizeof(index)) { 174 fCurrentChannel = index; 175 printf("Controller::ConnectInterface: selected channel is %d\n", fCurrentChannel); 176 } else { 177 fCurrentChannel = -1; 178 printf("Controller::ConnectInterface: can't get channel control value\n"); 179 } 180 } 181 } 182 183 ConnectNodes(); 184 185 return B_OK; 186 } 187 188 189 bool 190 Controller::IsInterfaceAvailable(int i) 191 { 192 return gDeviceRoster->IsRawAudioOutputFree(i) && gDeviceRoster->IsRawVideoOutputFree(i); 193 } 194 195 196 int 197 Controller::CurrentInterface() 198 { 199 return fCurrentInterface; 200 } 201 202 203 int 204 Controller::CurrentChannel() 205 { 206 return fCurrentChannel; 207 } 208 209 210 status_t 211 Controller::SelectChannel(int i) 212 { 213 if (!fChannelParam) 214 return B_ERROR; 215 216 int32 index; 217 status_t err; 218 index = i; 219 err = fChannelParam->SetValue(&index, sizeof(index), 0); 220 if (err != B_OK) { 221 printf("Controller::SelectChannel %d failed\n", i); 222 return err; 223 } 224 225 fCurrentChannel = i; 226 return B_OK; 227 } 228 229 230 int 231 Controller::ChannelCount() 232 { 233 if (fCurrentInterface == -1) 234 return 0; 235 236 if (!fChannelParam) 237 return 0; 238 239 return fChannelParam->CountItems(); 240 } 241 242 243 const char * 244 Controller::ChannelName(int i) 245 { 246 if (fCurrentInterface == -1) 247 return NULL; 248 249 if (!fChannelParam) 250 return NULL; 251 252 return fChannelParam->ItemNameAt(i); 253 } 254 255 256 void 257 Controller::VolumeUp() 258 { 259 } 260 261 262 void 263 Controller::VolumeDown() 264 { 265 } 266 267 268 status_t 269 Controller::ConnectNodes() 270 { 271 status_t err; 272 273 // dvb_node = gDeviceRoster->DeviceNode(fCurrentInterface); 274 275 err = gMediaRoster->GetNodeFor(gDeviceRoster->DeviceNode(fCurrentInterface).node, &dvb_node); 276 HandleError("GetNodeFor failed", err); 277 278 video_window_node = fVideoNode->Node(); 279 280 err = gMediaRoster->GetAudioMixer(&audio_mixer_node); 281 HandleError("GetAudioMixer failed", err); 282 283 media_input input; 284 media_output output; 285 media_format fmt; 286 int32 count; 287 288 // Connect audio 289 290 err = gMediaRoster->GetFreeOutputsFor(dvb_node, &output, 1, &count, B_MEDIA_RAW_AUDIO); 291 HandleError("Can't find free audio output", err); 292 if (count < 1) 293 HandleError("No free audio output", -1); 294 295 err = gMediaRoster->GetFreeInputsFor(audio_mixer_node, &input, 1, &count, B_MEDIA_RAW_AUDIO); 296 HandleError("Can't find free audio input", err); 297 if (count < 1) 298 HandleError("No free audio input", -1); 299 300 memset(&fmt, 0, sizeof(fmt)); 301 err = gMediaRoster->Connect(output.source, input.destination, &fmt, &audio_output, &audio_input); 302 HandleError("Can't connect audio", err); 303 304 // Connect video 305 306 err = gMediaRoster->GetFreeOutputsFor(dvb_node, &output, 1, &count, B_MEDIA_RAW_VIDEO); 307 HandleError("Can't find free video output", err); 308 if (count < 1) 309 HandleError("No free video output", -1); 310 311 err = gMediaRoster->GetFreeInputsFor(video_window_node, &input, 1, &count, B_MEDIA_RAW_VIDEO); 312 HandleError("Can't find free video input", err); 313 if (count < 1) 314 HandleError("No free video input", -1); 315 316 color_space cspaces_overlay[] = { B_YCbCr422, B_RGB32, B_NO_COLOR_SPACE }; 317 color_space cspaces_bitmap[] = { B_RGB32, B_NO_COLOR_SPACE }; 318 319 if (gOverlayDisabled) { 320 err = B_ERROR; 321 } else { 322 fVideoNode->SetOverlayEnabled(true); 323 for (int i = 0; cspaces_overlay[i] != B_NO_COLOR_SPACE; i++) { 324 printf("trying connect with colorspace 0x%08x\n", cspaces_overlay[i]); 325 memset(&fmt, 0, sizeof(fmt)); 326 fmt.type = B_MEDIA_RAW_VIDEO; 327 fmt.u.raw_video.display.format = cspaces_overlay[i]; 328 err = gMediaRoster->Connect(output.source, input.destination, &fmt, &video_output, &video_input); 329 if (err == B_OK) 330 break; 331 } 332 } 333 if (err) { 334 fVideoNode->SetOverlayEnabled(false); 335 for (int i = 0; cspaces_bitmap[i] != B_NO_COLOR_SPACE; i++) { 336 printf("trying connect with colorspace 0x%08x\n", cspaces_bitmap[i]); 337 memset(&fmt, 0, sizeof(fmt)); 338 fmt.type = B_MEDIA_RAW_VIDEO; 339 fmt.u.raw_video.display.format = cspaces_bitmap[i]; 340 err = gMediaRoster->Connect(output.source, input.destination, &fmt, &video_output, &video_input); 341 if (err == B_OK) 342 break; 343 } 344 } 345 HandleError("Can't connect video", err); 346 347 // set time sources 348 349 err = gMediaRoster->GetTimeSource(&time_node); 350 HandleError("Can't get time source", err); 351 352 BTimeSource *ts = gMediaRoster->MakeTimeSourceFor(time_node); 353 354 err = gMediaRoster->SetTimeSourceFor(dvb_node.node, time_node.node); 355 HandleError("Can't set dvb time source", err); 356 357 err = gMediaRoster->SetTimeSourceFor(audio_mixer_node.node, time_node.node); 358 HandleError("Can't set audio mixer time source", err); 359 360 err = gMediaRoster->SetTimeSourceFor(video_window_node.node, time_node.node); 361 HandleError("Can't set video window time source", err); 362 363 // Add a delay of (2 video frames) to the buffers send by the DVB node, 364 // because as a capture device in B_RECORDING run mode it's supposed to 365 // deliver buffers that were captured in the past (and does so). 366 // It is important that the audio buffer size used by the connection with 367 // the DVB node is smaller than this, optimum is the same length as one 368 // video frame (40 ms). However, this is not yet guaranteed. 369 err = gMediaRoster->SetProducerRunModeDelay(dvb_node, 80000); 370 HandleError("Can't set DVB producer delay", err); 371 372 bigtime_t start_time = ts->Now() + 50000; 373 374 ts->Release(); 375 376 // start nodes 377 378 err = gMediaRoster->StartNode(dvb_node, start_time); 379 HandleError("Can't start dvb node", err); 380 381 err = gMediaRoster->StartNode(audio_mixer_node, start_time); 382 HandleError("Can't start audio mixer node", err); 383 384 err = gMediaRoster->StartNode(video_window_node, start_time); 385 HandleError("Can't start video window node", err); 386 387 printf("running...\n"); 388 389 fConnected = true; 390 391 return B_OK; 392 } 393 394 395 status_t 396 Controller::DisconnectNodes() 397 { 398 printf("stopping...\n"); 399 400 if (!fConnected) 401 return B_OK; 402 403 status_t err; 404 405 // stop nodes 406 407 err = gMediaRoster->StopNode(dvb_node, 0, true); 408 HandleError("Can't stop dvb node", err); 409 410 err = gMediaRoster->StopNode(audio_mixer_node, 0, true); 411 HandleError("Can't stop audio mixer node", err); 412 413 err = gMediaRoster->StopNode(video_window_node, 0, true); 414 HandleError("Can't stop video window node", err); 415 416 // disconnect nodes 417 418 err = MediaRoster_Disconnect(video_output, video_input); 419 HandleError("Can't disconnect video", err); 420 421 err = MediaRoster_Disconnect(audio_output, audio_input); 422 HandleError("Can't disconnect audio", err); 423 424 // disable overlay, or erase image 425 426 fVideoView->RemoveVideoDisplay(); 427 428 // release other nodes 429 430 err = gMediaRoster->ReleaseNode(audio_mixer_node); 431 HandleError("Can't release audio mixer node", err); 432 433 // err = gMediaRoster->ReleaseNode(video_window_node); 434 // HandleError("Can't release video window node", err); 435 436 // err = gMediaRoster->ReleaseNode(time_node); 437 // HandleError("Can't release time source node", err); 438 439 // release dvb 440 441 err = gMediaRoster->ReleaseNode(dvb_node); 442 HandleError("Can't release DVB node", err); 443 444 fConnected = false; 445 446 return B_OK; 447 } 448 449 450 status_t 451 MediaRoster_Disconnect(const media_output &output, const media_input &input) 452 { 453 if (output.node.node <= 0) { 454 printf("MediaRoster_Disconnect: output.node.node %d invalid\n", 455 (int)output.node.node); 456 return B_MEDIA_BAD_NODE; 457 } 458 if (input.node.node <= 0) { 459 printf("MediaRoster_Disconnect: input.node.node %d invalid\n", 460 (int)input.node.node); 461 return B_MEDIA_BAD_NODE; 462 } 463 if (!(output.node.kind & B_BUFFER_PRODUCER)) { 464 printf("MediaRoster_Disconnect: output.node.kind 0x%x is no B_BUFFER_PRODUCER\n", 465 (int)output.node.kind); 466 return B_MEDIA_BAD_NODE; 467 } 468 if (!(input.node.kind & B_BUFFER_CONSUMER)) { 469 printf("MediaRoster_Disconnect: input.node.kind 0x%x is no B_BUFFER_PRODUCER\n", 470 (int)input.node.kind); 471 return B_MEDIA_BAD_NODE; 472 } 473 if (input.source.port != output.source.port) { 474 printf("MediaRoster_Disconnect: input.source.port %d doesn't match output.source.port %d\n", 475 (int)input.source.port, (int)output.source.port); 476 return B_MEDIA_BAD_NODE; 477 } 478 if (input.source.id != output.source.id) { 479 printf("MediaRoster_Disconnect: input.source.id %d doesn't match output.source.id %d\n", 480 (int)input.source.id, (int)output.source.id); 481 return B_MEDIA_BAD_NODE; 482 } 483 if (input.destination.port != output.destination.port) { 484 printf("MediaRoster_Disconnect: input.destination.port %d doesn't match output.destination.port %d\n", 485 (int)input.destination.port, (int)output.destination.port); 486 return B_MEDIA_BAD_NODE; 487 } 488 if (input.destination.id != output.destination.id) { 489 printf("MediaRoster_Disconnect: input.destination.id %d doesn't match output.destination.id %d\n", 490 (int)input.destination.id, (int)output.destination.id); 491 return B_MEDIA_BAD_NODE; 492 } 493 return BMediaRoster::Roster()->Disconnect(output.node.node, output.source, input.node.node, input.destination); 494 } 495