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
45using namespace std;
46
47__USE_CORTEX_NAMESPACE
48
49// -------------------------------------------------------- //
50// *** ctor/dtor
51// -------------------------------------------------------- //
52
53ConnectionIO::~ConnectionIO() {
54	if(m_inputNodeIO) delete m_inputNodeIO;
55	if(m_outputNodeIO) delete m_outputNodeIO;
56}
57
58// initialize for import
59ConnectionIO::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
72ConnectionIO::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
141status_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*/
260void 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// -------------------------------------------------------- //
288void 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
301void ConnectionIO::xmlExportAttributes(
302	ExportContext&						context) const { TOUCH(context); }
303
304void 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
388void ConnectionIO::xmlExportEnd(
389	ExportContext&						context) const {
390
391	context.endElement(); // _CONNECTION_ELEMENT
392}
393
394// -------------------------------------------------------- //
395// IMPORT:
396// -------------------------------------------------------- //
397
398void ConnectionIO::xmlImportBegin(
399	ImportContext&						context) { TOUCH(context); }
400
401void ConnectionIO::xmlImportAttribute(
402	const char*								key,
403	const char*								value,
404	ImportContext&						context) { TOUCH(key); TOUCH(value); TOUCH(context); }
405
406void ConnectionIO::xmlImportContent(
407	const char*								data,
408	uint32										length,
409	ImportContext&						context) { TOUCH(data); TOUCH(length); TOUCH(context); }
410
411void 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
495void ConnectionIO::xmlImportComplete(
496	ImportContext&						context) {
497
498	// +++++
499}
500
501void 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
523void 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