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
CRadeonPlug(CRadeonAddOn * aaddon,const BPath & adev_path,int aid)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
getSettingsPath()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
writeSettings(BMessage * new_settings)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
readSettings()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
CRadeonAddOn(image_id imid)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
~CRadeonAddOn()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
InitCheck(const char ** out_failure_text)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
settings_writer(void * param)207 CRadeonAddOn::settings_writer( void *param )
208 {
209 ((CRadeonAddOn *)param)->settingsWriter();
210 return B_OK;
211 }
212
213 void
writeSettings()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
settingsWriter()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
UnregisterNode(BMediaNode * node,BMessage * settings)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
CountFlavors()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
GetFlavorAt(int32 n,const flavor_info ** out_info)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 *
InstantiateNodeFor(const flavor_info * info,BMessage * config,status_t * out_error)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
GetConfigurationFor(BMediaNode * your_node,BMessage * into_message)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
RecursiveScan(const char * rootPath,BEntry * rootEntry=NULL)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 *
make_media_addon(image_id imid)428 make_media_addon(image_id imid)
429 {
430 return new CRadeonAddOn(imid);
431 }
432