xref: /haiku/src/apps/cortex/RouteApp/ConnectionIO.cpp (revision 95c9effd68127df2dce202d5e254a7c86560010a)
1 /*
2  * Copyright (c) 1999-2000, Eric Moon.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions, and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 // ConnectionIO.cpp
33 
34 #include "ConnectionIO.h"
35 #include "LiveNodeIO.h"
36 #include "NodeManager.h"
37 #include "NodeSetIOContext.h"
38 
39 #include "MediaFormatIO.h"
40 #include "route_app_io.h"
41 
42 #include <vector>
43 #include <Debug.h>
44 
45 using namespace std;
46 
47 __USE_CORTEX_NAMESPACE
48 
49 // -------------------------------------------------------- //
50 // *** ctor/dtor
51 // -------------------------------------------------------- //
52 
53 ConnectionIO::~ConnectionIO() {
54 	if(m_inputNodeIO) delete m_inputNodeIO;
55 	if(m_outputNodeIO) delete m_outputNodeIO;
56 }
57 
58 // initialize for import
59 ConnectionIO::ConnectionIO() :
60 	m_inputNodeIO(0),
61 	m_outputNodeIO(0),
62 	m_flags(0),
63 	m_exportValid(false),
64 	m_importState(IMPORT_NONE) {
65 
66 	m_outputFormat.type = B_MEDIA_NO_TYPE;
67 	m_inputFormat.type = B_MEDIA_NO_TYPE;
68 	m_requestedFormat.type = B_MEDIA_NO_TYPE;
69 }
70 
71 // initialize for export
72 ConnectionIO::ConnectionIO(
73 	const Connection*				con,
74 	const NodeManager*			manager,
75 	const NodeSetIOContext*	context) :
76 
77 	m_inputNodeIO(0),
78 	m_outputNodeIO(0),
79 	m_exportValid(false),
80 	m_importState(IMPORT_NONE) {
81 
82 	ASSERT(con);
83 	ASSERT(manager);
84 	ASSERT(context);
85 
86 	if(!con->isValid()) {
87 		PRINT((
88 			"!!! ConnectionIO(): invalid connection\n"));
89 		return;
90 	}
91 
92 	m_outputNodeIO = new LiveNodeIO(
93 		manager,
94 		context,
95 		con->sourceNode());
96 
97 	// fetch output (connection-point) description
98 	const char* name;
99 	if(con->getOutputHint(
100 		&name,
101 		&m_outputFormat) == B_OK)
102 		m_outputName = name;
103 	else {
104 		m_outputName = con->outputName();
105 	}
106 
107 	m_inputNodeIO = new LiveNodeIO(
108 		manager,
109 		context,
110 		con->destinationNode());
111 
112 	// fetch input (connection-point) description
113 	if(con->getInputHint(
114 		&name,
115 		&m_inputFormat) == B_OK)
116 		m_inputName = name;
117 
118 	else {
119 		m_inputName = con->inputName();
120 	}
121 
122 	m_requestedFormat = con->requestedFormat();
123 	m_flags = con->flags();
124 
125 	m_exportValid = true;
126 }
127 
128 // -------------------------------------------------------- //
129 // *** operations
130 // -------------------------------------------------------- //
131 
132 // call when object imported to create the described
133 // connection
134 
135 // +++++ to do
136 // smarter input/output matching -- if no name/format provided,
137 // pick the first available endpoint.  otherwise, make two passes:
138 // 1) match all nodes w/ given name (pass wildcards through to roster)
139 // 2) filter by format
140 
141 status_t ConnectionIO::instantiate(
142 	NodeManager*						manager,
143 	const NodeSetIOContext*	context,
144 	Connection*							outCon) {
145 
146 	// sanity checks
147 	ASSERT(manager);
148 	if(!m_inputNodeIO || !m_outputNodeIO)
149 		return B_NOT_ALLOWED;
150 
151 	status_t err;
152 	media_node_id node;
153 
154 	// find output node
155 	NodeRef* outputRef;
156 	err = m_outputNodeIO->getNode(manager, context, &node);
157 	if(err < B_OK)
158 		return err;
159 	err = manager->getNodeRef(
160 		node,
161 		&outputRef);
162 	if(err < B_OK)
163 		return err;
164 
165 	// find output +++++ currently matches by name only
166 	const int32 outputBufferSize = 16;
167 	media_output outputs[outputBufferSize];
168 	int32 count = outputBufferSize;
169 
170 	//vector<media_output> outputs;
171 //	err = outputRef->getFreeOutputs(
172 //		outputs/*,
173 //		m_outputFormat.type*/);
174 
175 	err = outputRef->getFreeOutputs(
176 		outputs,
177 		outputBufferSize,
178 		&count);
179 	if(err < B_OK)
180 		return err;
181 
182 	media_output output;
183 	bool found = false;
184 	for(int n = 0; n < count; ++n) {
185 		if(m_outputName == outputs[n].name) {
186 			output = outputs[n];
187 			found = true;
188 			break;
189 		}
190 	}
191 	if(!found) {
192 		PRINT(("!!! output '%s' of node '%s' not found\n",
193 			m_outputName.String(),
194 			outputRef->name()));
195 		return B_NAME_NOT_FOUND;
196 	}
197 
198 	// find input node
199 	NodeRef* inputRef;
200 	err = m_inputNodeIO->getNode(manager, context, &node);
201 	if(err < B_OK)
202 		return err;
203 	err = manager->getNodeRef(
204 		node,
205 		&inputRef);
206 	if(err < B_OK)
207 		return err;
208 
209 	// find input +++++ currently matches by name only
210 	vector<media_input> inputs;
211 	err = inputRef->getFreeInputs(
212 		inputs /*,
213 		m_inputFormat.type*/);
214 	if(err < B_OK)
215 		return err;
216 
217 	media_input input;
218 	found = false;
219 	for(unsigned int n = 0; n < inputs.size(); ++n) {
220 		if(m_inputName == inputs[n].name) {
221 			input = inputs[n];
222 			found = true;
223 			break;
224 		}
225 	}
226 	if(!found) {
227 		PRINT(("!!! input '%s' of node '%s' not found\n",
228 			m_inputName.String(),
229 			inputRef->name()));
230 		return B_NAME_NOT_FOUND;
231 	}
232 
233 	// connect
234 	Connection con;
235 	if(m_requestedFormat.type != B_MEDIA_NO_TYPE)
236 		err = manager->connect(
237 			output,
238 			input,
239 			m_requestedFormat,
240 			&con);
241 	else
242 		err = manager->connect(
243 			output,
244 			input,
245 			&con);
246 
247 	if(err < B_OK)
248 		return err;
249 
250 	if(outCon)
251 		*outCon = con;
252 	return B_OK;
253 }
254 
255 // -------------------------------------------------------- //
256 // *** document-type setup
257 // -------------------------------------------------------- //
258 
259 /*static*/
260 void ConnectionIO::AddTo(
261 	XML::DocumentType*			docType) {
262 
263 	// map self
264 	docType->addMapping(new Mapping<ConnectionIO>(_CONNECTION_ELEMENT));
265 
266 	// map simple (content-only) elements
267 	// +++++ should these be added at a higher level, since they're
268 	//       shared?  no harm is done if one is added more than once,
269 	//       since they'll always map to StringContent -- but it's way
270 	//       messy!
271 	// +++++
272 	//docType->addMapping(new Mapping<StringContent>(_LIVE_NODE_ELEMENT));
273 //	docType->addMapping(new Mapping<StringContent>(_NAME_ELEMENT));
274 //	docType->addMapping(new Mapping<StringContent>(_KIND_ELEMENT));
275 //	docType->addMapping(new Mapping<StringContent>(_FLAG_ELEMENT));
276 }
277 
278 // -------------------------------------------------------- //
279 // *** IPersistent
280 // -------------------------------------------------------- //
281 
282 // -------------------------------------------------------- //
283 // EXPORT:
284 // -------------------------------------------------------- //
285 
286 
287 // -------------------------------------------------------- //
288 void ConnectionIO::xmlExportBegin(
289 	ExportContext&						context) const {
290 
291 	if(!m_exportValid) {
292 		context.reportError(
293 			"ConnectionIO::xmlExportBegin():\n"
294 			"*** invalid ***\n");
295 		return;
296 	}
297 
298 	context.beginElement(_CONNECTION_ELEMENT);
299 }
300 
301 void ConnectionIO::xmlExportAttributes(
302 	ExportContext&						context) const { TOUCH(context); }
303 
304 void ConnectionIO::xmlExportContent(
305 	ExportContext&						context) const {
306 
307 	context.beginContent();
308 
309 	// write output
310 	{
311 		context.beginElement(_OUTPUT_ELEMENT);
312 		context.beginContent();
313 
314 		// describe the node
315 //		LiveNodeIO nodeIO(
316 //			m_manager,
317 //			dynamic_cast<NodeSetIOContext*>(&context),
318 //			m_outputNode);
319 		context.writeObject(m_outputNodeIO);
320 
321 //		context.beginElement(_LIVE_NODE_ELEMENT);
322 //		if(m_outputNodeKey.Length()) {
323 //			context.writeAttr("key", m_outputNodeKey);
324 //		}
325 //		else {
326 //			_write_simple("name", m_outputNodeName.String(), context);
327 //			_write_node_kinds(m_outputNodeKind, context);
328 //		}
329 //		context.endElement(); // _LIVE_NODE_ELEMENT
330 
331 		// describe the output
332 
333 		_write_simple("name", m_outputName.String(), context);
334 
335 		if(m_outputFormat.type > B_MEDIA_UNKNOWN_TYPE) {
336 			MediaFormatIO io(m_outputFormat);
337 			context.writeObject(&io);
338 		}
339 
340 		context.endElement(); // _OUTPUT_ELEMENT
341 	}
342 
343 	// write input
344 	{
345 		context.beginElement(_INPUT_ELEMENT);
346 		context.beginContent();
347 
348 		// describe the node
349 //		LiveNodeIO nodeIO(
350 //			m_manager,
351 //			dynamic_cast<NodeSetIOContext*>(&context),
352 //			m_inputNode);
353 		context.writeObject(m_inputNodeIO);
354 
355 //		context.beginElement(_LIVE_NODE_ELEMENT);
356 //		if(m_inputNodeKey.Length()) {
357 //			context.writeAttr("key", m_inputNodeKey);
358 //		}
359 //		else {
360 //			_write_simple("name", m_inputNodeName.String(), context);
361 //			_write_node_kinds(m_inputNodeKind, context);
362 //		}
363 //		context.endElement(); // _LIVE_NODE_ELEMENT
364 
365 		// describe the input
366 
367 		_write_simple("name", m_inputName.String(), context);
368 
369 		if(m_inputFormat.type > B_MEDIA_UNKNOWN_TYPE) {
370 			MediaFormatIO io(m_inputFormat);
371 			context.writeObject(&io);
372 		}
373 
374 		context.endElement(); // _INPUT_ELEMENT
375 	}
376 
377 	// write requested format
378 	if(m_requestedFormat.type > B_MEDIA_UNKNOWN_TYPE) {
379 		MediaFormatIO io(m_requestedFormat);
380 		BString comment = "\n";
381 		comment << context.indentString();
382 		comment << "<!-- initial requested format -->";
383 		context.writeString(comment);
384 		context.writeObject(&io);
385 	}
386 }
387 
388 void ConnectionIO::xmlExportEnd(
389 	ExportContext&						context) const {
390 
391 	context.endElement(); // _CONNECTION_ELEMENT
392 }
393 
394 // -------------------------------------------------------- //
395 // IMPORT:
396 // -------------------------------------------------------- //
397 
398 void ConnectionIO::xmlImportBegin(
399 	ImportContext&						context) { TOUCH(context); }
400 
401 void ConnectionIO::xmlImportAttribute(
402 	const char*								key,
403 	const char*								value,
404 	ImportContext&						context) { TOUCH(key); TOUCH(value); TOUCH(context); }
405 
406 void ConnectionIO::xmlImportContent(
407 	const char*								data,
408 	uint32										length,
409 	ImportContext&						context) { TOUCH(data); TOUCH(length); TOUCH(context); }
410 
411 void ConnectionIO::xmlImportChild(
412 	IPersistent*							child,
413 	ImportContext&						context) {
414 
415 	status_t err;
416 
417 	if(!strcmp(context.element(), _LIVE_NODE_ELEMENT)) {
418 		LiveNodeIO* nodeIO = dynamic_cast<LiveNodeIO*>(child);
419 		ASSERT(nodeIO);
420 
421 		// store the LiveNodeIO for now; it will be used in
422 		// instantiate()
423 
424 		switch(m_importState) {
425 			case IMPORT_OUTPUT:
426 				m_outputNodeIO = nodeIO;
427 				child = 0; // don't delete child object
428 				break;
429 
430 			case IMPORT_INPUT:
431 				m_inputNodeIO = nodeIO;
432 				child = 0; // don't delete child object
433 				break;
434 
435 			case IMPORT_NONE:
436 				context.reportError("Unexpected node description.\n");
437 				delete child;
438 				return;
439 		}
440 	}
441 	else if(!strcmp(context.element(), _NAME_ELEMENT)) {
442 		StringContent* c = dynamic_cast<StringContent*>(child);
443 		ASSERT(c);
444 
445 		switch(m_importState) {
446 			case IMPORT_OUTPUT:
447 				m_outputName = c->content;
448 				break;
449 
450 			case IMPORT_INPUT:
451 				m_inputName = c->content;
452 				break;
453 
454 			case IMPORT_NONE:
455 				context.reportError("Unexpected node name.\n");
456 				delete child;
457 				return;
458 		}
459 	}
460 	else {
461 		MediaFormatIO* io = dynamic_cast<MediaFormatIO*>(child);
462 		if(!io) {
463 			context.reportError("Unexpected element.\n");
464 			delete child;
465 			return;
466 		}
467 
468 		media_format f;
469 		err = io->getFormat(f);
470 		if(err < B_OK) {
471 			context.reportError("Malformed format.\n");
472 			delete child;
473 			return;
474 		}
475 
476 		switch(m_importState) {
477 			case IMPORT_OUTPUT:
478 				m_outputFormat = f;
479 				break;
480 
481 			case IMPORT_INPUT:
482 				m_inputFormat = f;
483 				break;
484 
485 			case IMPORT_NONE:
486 				m_requestedFormat = f;
487 				break;
488 		}
489 	}
490 
491 	if(child)
492 		delete child;
493 }
494 
495 void ConnectionIO::xmlImportComplete(
496 	ImportContext&						context) {
497 
498 	// +++++
499 }
500 
501 void ConnectionIO::xmlImportChildBegin(
502 	const char*								name,
503 	ImportContext&						context) {
504 
505 	if(!strcmp(name, "input")) {
506 		if(m_importState != IMPORT_NONE) {
507 			context.reportError("ConnectionIO: unexpected nested child element\n");
508 			return;
509 		}
510 		m_importState = IMPORT_INPUT;
511 	}
512 	else if(!strcmp(name, "output")) {
513 		if(m_importState != IMPORT_NONE) {
514 			context.reportError("ConnectionIO: unexpected nested child element\n");
515 			return;
516 		}
517 		m_importState = IMPORT_OUTPUT;
518 	}
519 	else
520 		context.reportError("ConnectionIO: unexpected child element\n");
521 }
522 
523 void ConnectionIO::xmlImportChildComplete(
524 	const char*								name,
525 	ImportContext&						context) {
526 	TOUCH(name); TOUCH(context);
527 
528 	m_importState = IMPORT_NONE;
529 }
530 
531 // END -- ConnectionIO.cpp --
532