1 /****************************************************************************** 2 / 3 / File: RadeonAddOn.cpp 4 / 5 / Description: ATI Radeon Video Capture Media AddOn for BeOS. 6 / 7 / Copyright 2001, Carlos Hasan 8 / 9 *******************************************************************************/ 10 11 #include <support/Autolock.h> 12 #include <media/MediaFormats.h> 13 #include <Directory.h> 14 #include <Entry.h> 15 #include <Debug.h> 16 #include <File.h> 17 18 #include <stdio.h> 19 #include <string.h> 20 #include <unistd.h> 21 22 #include <storage/FindDirectory.h> 23 #include <String.h> 24 25 #include "RadeonAddOn.h" 26 #include "RadeonProducer.h" 27 28 #define DPRINT(args) { PRINT(("\x1b[0;30;35m")); PRINT(args); PRINT(("\x1b[0;30;47m")); } 29 30 CRadeonPlug::CRadeonPlug( CRadeonAddOn *aaddon, const BPath &adev_path, int aid ) 31 : addon( aaddon ), dev_path( adev_path ), id( aid ), node( NULL ) 32 { 33 fFlavorInfo.name = const_cast<char *>("Radeon In"); 34 fFlavorInfo.info = const_cast<char *>("Radeon Video In Media Node"); 35 fFlavorInfo.kinds = B_BUFFER_PRODUCER | B_CONTROLLABLE | B_PHYSICAL_INPUT; 36 fFlavorInfo.flavor_flags = 0; 37 fFlavorInfo.internal_id = aid; 38 fFlavorInfo.possible_count = 1; 39 40 fFlavorInfo.in_format_count = 0; 41 fFlavorInfo.in_format_flags = 0; 42 fFlavorInfo.in_formats = NULL; 43 44 fFlavorInfo.out_format_count = 4; 45 //fFlavorInfo.out_format_count = 1; 46 fFlavorInfo.out_format_flags = 0; 47 48 fMediaFormat[0].type = B_MEDIA_RAW_VIDEO; 49 fMediaFormat[0].u.raw_video = media_raw_video_format::wildcard; 50 fMediaFormat[0].u.raw_video.interlace = 1; 51 fMediaFormat[0].u.raw_video.display.format = B_RGB32; 52 53 fMediaFormat[1].type = B_MEDIA_RAW_VIDEO; 54 fMediaFormat[1].u.raw_video = media_raw_video_format::wildcard; 55 fMediaFormat[1].u.raw_video.interlace = 1; 56 fMediaFormat[1].u.raw_video.display.format = B_RGB16; 57 58 fMediaFormat[2].type = B_MEDIA_RAW_VIDEO; 59 fMediaFormat[2].u.raw_video = media_raw_video_format::wildcard; 60 fMediaFormat[2].u.raw_video.interlace = 1; 61 fMediaFormat[2].u.raw_video.display.format = B_RGB15; 62 63 fMediaFormat[3].type = B_MEDIA_RAW_VIDEO; 64 fMediaFormat[3].u.raw_video = media_raw_video_format::wildcard; 65 fMediaFormat[3].u.raw_video.interlace = 1; 66 fMediaFormat[3].u.raw_video.display.format = B_YCbCr422; 67 68 /*fMediaFormat[0].type = B_MEDIA_RAW_VIDEO; 69 fMediaFormat[0].u.raw_video = media_raw_video_format::wildcard; 70 fMediaFormat[0].u.raw_video.interlace = 1; 71 fMediaFormat[0].u.raw_video.display.format = B_YCbCr422;*/ 72 73 fFlavorInfo.out_formats = fMediaFormat; 74 75 readSettings(); 76 } 77 78 BPath 79 CRadeonPlug::getSettingsPath() 80 { 81 BPath path; 82 83 if( find_directory( B_USER_CONFIG_DIRECTORY, &path ) != B_OK ) 84 return BPath(); 85 86 path.Append( "settings/Media/RadeonIn" ); 87 88 create_directory( path.Path(), 755 ); 89 90 BString id_string; 91 92 id_string << "settings" << id; 93 94 path.Append( id_string.String() ); 95 96 return path; 97 } 98 99 void 100 CRadeonPlug::writeSettings( BMessage *new_settings ) 101 { 102 BMessage cur_settings; 103 104 // if new_settings are provided, use them; else, ask node for settings 105 // (needed during shutdown where node cannot reply anymore) 106 if( new_settings != NULL ) { 107 cur_settings = *new_settings; 108 109 } else { 110 if( node == NULL ) 111 return; 112 113 if( addon->GetConfigurationFor( node, &cur_settings ) != B_OK ) 114 return; 115 } 116 117 BMallocIO old_settings_flat, new_settings_flat; 118 119 settings.Flatten( &old_settings_flat ); 120 cur_settings.Flatten( &new_settings_flat ); 121 122 if( old_settings_flat.BufferLength() == new_settings_flat.BufferLength() && 123 memcmp( old_settings_flat.Buffer(), new_settings_flat.Buffer(), old_settings_flat.BufferLength() ) == 0 ) 124 return; 125 126 settings = cur_settings; 127 128 BPath settings_path = getSettingsPath(); 129 130 BFile file( settings_path.Path(), B_WRITE_ONLY | B_CREATE_FILE ); 131 132 settings.Flatten( &file ); 133 } 134 135 void 136 CRadeonPlug::readSettings() 137 { 138 BPath settings_path = getSettingsPath(); 139 140 BFile file( settings_path.Path(), B_READ_ONLY ); 141 142 settings.Unflatten( &file ); 143 } 144 145 146 CRadeonAddOn::CRadeonAddOn(image_id imid) 147 : BMediaAddOn(imid), 148 settings_thread( -1 ), settings_thread_sem( -1 ) 149 { 150 DPRINT(("CRadeonAddOn::CRadeonAddOn()\n")); 151 152 fInitStatus = B_NO_INIT; 153 154 if( RecursiveScan( "/dev/video/radeon" ) != B_OK ) 155 return; 156 157 if( (settings_thread_sem = create_sem( 0, "Radeon In settings" )) < 0 ) 158 return; 159 160 if( INIT_BEN( plug_lock, "Radeon device list" ) < 0 ) 161 return; 162 163 if( (settings_thread = spawn_thread( 164 settings_writer, "Radeon In settings", B_LOW_PRIORITY, this )) < 0 ) 165 return; 166 167 resume_thread( settings_thread ); 168 169 fInitStatus = B_OK; 170 } 171 172 CRadeonAddOn::~CRadeonAddOn() 173 { 174 status_t dummy; 175 176 DPRINT(("CRadeonAddOn::~CRadeonAddOn()\n")); 177 178 release_sem( settings_thread_sem ); 179 wait_for_thread( settings_thread, &dummy ); 180 181 delete_sem( settings_thread_sem ); 182 183 for( int32 i = 0; i < fDevices.CountItems(); ++i ) { 184 CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i); 185 186 delete plug; 187 } 188 189 DELETE_BEN( plug_lock ); 190 } 191 192 193 status_t 194 CRadeonAddOn::InitCheck(const char **out_failure_text) 195 { 196 DPRINT(("CRadeonAddOn::InitCheck()\n")); 197 198 if (fInitStatus < B_OK) { 199 *out_failure_text = "Unknown error"; 200 return fInitStatus; 201 } 202 203 return B_OK; 204 } 205 206 int32 207 CRadeonAddOn::settings_writer( void *param ) 208 { 209 ((CRadeonAddOn *)param)->settingsWriter(); 210 return B_OK; 211 } 212 213 void 214 CRadeonAddOn::writeSettings() 215 { 216 ACQUIRE_BEN( plug_lock ); 217 218 for( int32 i = 0; i < fDevices.CountItems(); ++i ) { 219 CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i); 220 221 plug->writeSettings( NULL ); 222 } 223 224 RELEASE_BEN( plug_lock ); 225 } 226 227 void 228 CRadeonAddOn::settingsWriter() 229 { 230 while( acquire_sem_etc( settings_thread_sem, 1, B_RELATIVE_TIMEOUT, 10000000 ) == B_TIMED_OUT ) { 231 writeSettings(); 232 } 233 } 234 235 void 236 CRadeonAddOn::UnregisterNode( BMediaNode *node, BMessage *settings ) 237 { 238 ACQUIRE_BEN( plug_lock ); 239 240 for( int32 i = 0; i < fDevices.CountItems(); ++i ) { 241 CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt(i); 242 243 if( plug->getNode() == node ) { 244 // write last settings, so they don't get lost 245 plug->writeSettings( settings ); 246 plug->setNode( NULL ); 247 break; 248 } 249 } 250 251 RELEASE_BEN( plug_lock ); 252 } 253 254 int32 255 CRadeonAddOn::CountFlavors() 256 { 257 DPRINT(("CRadeonAddOn::CountFlavors()\n")); 258 259 if (fInitStatus < B_OK) 260 return fInitStatus; 261 262 return fDevices.CountItems(); 263 } 264 265 /* 266 * The pointer to the flavor received only needs to be valid between 267 * successive calls to BCRadeonAddOn::GetFlavorAt(). 268 */ 269 status_t 270 CRadeonAddOn::GetFlavorAt(int32 n, const flavor_info **out_info) 271 { 272 DPRINT(("CRadeonAddOn::GetFlavorAt()\n")); 273 274 if (fInitStatus < B_OK) 275 return fInitStatus; 276 277 if (n < 0 || n >= fDevices.CountItems() ) 278 return B_BAD_INDEX; 279 280 /* Return the flavor defined in the constructor */ 281 *out_info = ((CRadeonPlug *)fDevices.ItemAt(n))->getFlavorInfo(); 282 return B_OK; 283 } 284 285 BMediaNode * 286 CRadeonAddOn::InstantiateNodeFor( 287 const flavor_info *info, BMessage *config, status_t *out_error) 288 { 289 DPRINT(("CRadeonAddOn::InstantiateNodeFor()\n")); 290 291 CRadeonProducer *node; 292 293 if (fInitStatus < B_OK) 294 return NULL; 295 296 if (info->internal_id < 0 || info->internal_id >= fDevices.CountItems()) 297 return NULL; 298 299 CRadeonPlug *plug = (CRadeonPlug *)fDevices.ItemAt( info->internal_id ); 300 301 ACQUIRE_BEN( plug_lock ); 302 303 if( plug->getNode() != NULL ) { 304 *out_error = B_BUSY; 305 node = NULL; 306 307 } else { 308 BMessage single_settings; 309 310 // under R5, configuration is always an empty message, so we need to 311 // get our own configuration 312 if( config == NULL || 1 ) 313 config = plug->getSettings(); 314 315 node = new CRadeonProducer( this, plug->getName(), 316 plug->getDeviceName(), info->internal_id, config ); 317 318 if (node && (node->InitCheck() < B_OK)) { 319 *out_error = node->InitCheck(); 320 delete node; 321 node = NULL; 322 } 323 } 324 325 plug->setNode( node ); 326 327 RELEASE_BEN( plug_lock ); 328 329 return node; 330 } 331 332 status_t CRadeonAddOn::GetConfigurationFor( 333 BMediaNode *your_node, 334 BMessage *into_message ) 335 { 336 port_id reply_port; 337 CRadeonProducer::configuration_msg msg; 338 status_t res; 339 340 reply_port = create_port( 1, "GetConfiguration Reply" ); 341 if( reply_port < 0 ) 342 return reply_port; 343 344 msg.reply_port = reply_port; 345 346 res = write_port_etc( your_node->ControlPort(), CRadeonProducer::C_GET_CONFIGURATION, 347 &msg, sizeof( msg ), B_TIMEOUT, 10000000 ); 348 if( res == B_OK ) { 349 ssize_t reply_size; 350 CRadeonProducer::configuration_msg_reply *reply; 351 int32 code; 352 353 reply_size = port_buffer_size_etc( reply_port, B_TIMEOUT, 10000000 ); 354 if( reply_size < 0 ) 355 res = reply_size; 356 else { 357 reply = (CRadeonProducer::configuration_msg_reply *)malloc( reply_size ); 358 359 res = read_port( reply_port, &code, reply, reply_size ); 360 if( res == reply_size ) { 361 if( code != CRadeonProducer::C_GET_CONFIGURATION_REPLY ) 362 res = B_ERROR; 363 else { 364 res = reply->res; 365 366 if( res == B_OK ) 367 res = into_message->Unflatten( &reply->config ); 368 } 369 } 370 371 free( reply ); 372 } 373 } 374 375 delete_port( reply_port ); 376 377 return res; 378 } 379 380 status_t 381 CRadeonAddOn::RecursiveScan(const char* rootPath, BEntry *rootEntry = NULL) 382 { 383 BDirectory root; 384 385 if( rootEntry != NULL ) 386 root.SetTo( rootEntry ); 387 else if( rootPath != NULL ) { 388 root.SetTo( rootPath ); 389 } else { 390 PRINT(("Error in MultiAudioAddOn::RecursiveScan null params\n")); 391 return B_ERROR; 392 } 393 394 BEntry entry; 395 int cur_id = 0; 396 397 while( root.GetNextEntry( &entry ) > B_ERROR ) { 398 if(entry.IsDirectory()) { 399 RecursiveScan( rootPath, &entry ); 400 401 } else { 402 BPath path; 403 404 entry.GetPath(&path); 405 406 CRadeon device( path.Path() ); 407 408 if( device.InitCheck() != B_OK) 409 continue; 410 411 CVIPPort vip_port( device ); 412 413 // if there is a Rage Theatre, then there should be Video-In 414 if( vip_port.InitCheck() == B_OK && 415 ((vip_port.FindVIPDevice( C_THEATER100_VIP_DEVICE_ID ) >= 0) 416 || (vip_port.FindVIPDevice( C_THEATER200_VIP_DEVICE_ID ) >= 0))) 417 { 418 fDevices.AddItem( new CRadeonPlug( this, path, cur_id++ )); 419 } 420 } 421 } 422 423 return B_OK; 424 } 425 426 427 BMediaAddOn * 428 make_media_addon(image_id imid) 429 { 430 return new CRadeonAddOn(imid); 431 } 432