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// DormantNodeIO.cpp
33
34#include "DormantNodeIO.h"
35#include "ImportContext.h"
36#include "ExportContext.h"
37#include "StringContent.h"
38
39#include "NodeManager.h"
40
41#include <Debug.h>
42#include <MediaAddOn.h>
43#include <MediaDefs.h>
44#include <MediaRoster.h>
45#include <Path.h>
46
47#include <cstdlib>
48
49#include "route_app_io.h"
50
51__USE_CORTEX_NAMESPACE
52
53// -------------------------------------------------------- //
54// *** ctor/dtor
55// -------------------------------------------------------- //
56
57DormantNodeIO::~DormantNodeIO() {}
58
59// initialize for import (to defaults)
60DormantNodeIO::DormantNodeIO() :
61	m_kinds(0LL),
62	m_flavorID(0),
63	m_flags(0),
64	m_runMode(0),
65	m_recordingDelay(0),
66	m_cycle(false),
67	m_exportValid(false) {}
68
69// initialize for export
70DormantNodeIO::DormantNodeIO(
71	NodeRef*								ref,
72	const char*							nodeKey) :
73	m_exportValid(false) {
74
75	ASSERT(ref);
76	ASSERT(nodeKey);
77	status_t err;
78
79	m_nodeKey = nodeKey;
80
81	// * extract dormant-node info
82	dormant_node_info info;
83	err = ref->getDormantNodeInfo(&info);
84	if(err < B_OK) {
85		PRINT((
86			"!!! DormantNodeIO(): getDormantNodeInfo() failed:\n"
87			"    %s\n",
88			strerror(err)));
89		return;
90	}
91
92	dormant_flavor_info flavorInfo;
93	err = BMediaRoster::Roster()->GetDormantFlavorInfoFor(
94		info, &flavorInfo);
95	if(err < B_OK) {
96		PRINT((
97			"!!! DormantNodeIO(): GetDormantFlavorInfoFor() failed:\n"
98			"    %s\n",
99			strerror(err)));
100		return;
101	}
102
103	m_dormantName = flavorInfo.name;
104	m_flavorID = info.flavor_id;
105	m_kinds = flavorInfo.kinds;
106
107	m_flags = ref->flags();
108	entry_ref file;
109	if(ref->getFile(&file) == B_OK)
110		m_entry.SetTo(&file);
111
112	m_runMode = ref->runMode();
113	m_recordingDelay = ref->recordingDelay();
114	m_cycle = ref->isCycling();
115
116	// done extracting node info; ready for export
117	m_exportValid = true;
118}
119
120// -------------------------------------------------------- //
121// *** document-type setup
122// -------------------------------------------------------- //
123
124/*static*/
125void DormantNodeIO::AddTo(
126	XML::DocumentType*			docType) {
127
128	// map self
129	docType->addMapping(new Mapping<DormantNodeIO>(_DORMANT_NODE_ELEMENT));
130
131//	// map simple (content-only) elements
132//	// +++++ should these be added at a higher level, since they're
133//	//       shared?  no harm is done if one is added more than once,
134//	//       since they'll always map to StringContent.
135//	// +++++
136//	docType->addMapping(new Mapping<StringContent>(_NAME_ELEMENT));
137//	docType->addMapping(new Mapping<StringContent>(_KIND_ELEMENT));
138//	docType->addMapping(new Mapping<StringContent>(_FLAVOR_ID_ELEMENT));
139//	docType->addMapping(new Mapping<StringContent>(_FLAG_ELEMENT));
140//	docType->addMapping(new Mapping<StringContent>(_RUN_MODE_ELEMENT));
141//	docType->addMapping(new Mapping<StringContent>(_RECORDING_DELAY_ELEMENT));
142//	docType->addMapping(new Mapping<StringContent>(_CYCLE_ELEMENT));
143//	docType->addMapping(new Mapping<StringContent>(_REF_ELEMENT));
144}
145
146// -------------------------------------------------------- //
147// *** operations
148// -------------------------------------------------------- //
149
150// call when object imported to create the described node
151
152// +++++
153
154status_t DormantNodeIO::instantiate(
155	NodeManager*						manager,
156	NodeRef**								outRef) {
157
158	status_t err;
159	BPath p;
160	if(m_entry.InitCheck() == B_OK)
161		m_entry.GetPath(&p);
162
163//	PRINT((
164//		"DormantNodeIO:\n"
165//		"  key:       %s\n"
166//		"  name:      %s\n"
167//		"  flavor:    %ld\n"
168//		"  kinds:     %Lx\n"
169//		"  flags:     %lx\n"
170//		"  runMode:   %ld\n"
171//		"  recDelay:  %lld\n"
172//		"  cycle:     %s\n"
173//		"  entry:     %s\n\n",
174//		m_nodeKey.String(),
175//		m_dormantName.String(),
176//		m_flavorID,
177//		m_kinds,
178//		m_flags,
179//		m_runMode,
180//		m_recordingDelay,
181//		m_cycle ? "true" : "false",
182//		p.Path()));
183
184	// find matching dormant node
185	dormant_node_info info;
186	err = _matchDormantNode(&info);
187	if(err < B_OK) {
188		PRINT((
189			"!!! _matchDormantNode() failed: %s\n", strerror(err)));
190		return err;
191	}
192
193	// instantiate node
194	err = manager->instantiate(
195		info,
196		outRef,
197		B_INFINITE_TIMEOUT,
198		m_flags);
199	if(err < B_OK) {
200		PRINT((
201			"!!! instantiate() failed: %s\n", strerror(err)));
202		return err;
203	}
204
205	entry_ref mediaRef;
206	if(m_entry.InitCheck() == B_OK && m_entry.GetRef(&mediaRef) == B_OK) {
207		// set ref
208		err = (*outRef)->setFile(mediaRef);
209		if(err < B_OK) {
210			PRINT((
211				"!!! WARNING: setFile() failed: %s\n", strerror(err)));
212		}
213	}
214
215	// set run mode
216	if(m_runMode)
217		(*outRef)->setRunMode(m_runMode, m_recordingDelay);
218
219	// set cycle state
220	if(m_cycle)
221		(*outRef)->setCycling(true);
222
223	return B_OK;
224}
225
226status_t DormantNodeIO::_matchDormantNode(
227	dormant_node_info*			outInfo) {
228
229	status_t err;
230
231	// fetch all dormant nodes matching the signature
232	const int32 bufferSize = 32;
233	dormant_node_info	buffer[bufferSize];
234	int32 count = bufferSize;
235	err = BMediaRoster::Roster()->GetDormantNodes(
236		buffer,
237		&count,
238		0,
239		0,
240		m_dormantName.String(),
241		m_kinds,
242		0 /*~m_kinds*/);
243	if(err < B_OK)
244		return err;
245
246	if(!count)
247		return B_NAME_NOT_FOUND;
248
249	for(int32 n = 0; n < count; ++n) {
250		if(buffer[n].flavor_id == m_flavorID) {
251			*outInfo = buffer[n];
252			return B_OK;
253		}
254	}
255
256	// didn't match flavor id
257	return B_BAD_INDEX;
258}
259
260
261// -------------------------------------------------------- //
262// *** IPersistent
263// -------------------------------------------------------- //
264
265// -------------------------------------------------------- //
266// EXPORT:
267// -------------------------------------------------------- //
268
269//inline void _write_simple(
270//	const char* element,
271//	const char* value,
272//	ExportContext& context) {
273//
274//	context.beginElement(element);
275//	context.beginContent();
276//	context.writeString(value);
277//	context.endElement();
278//}
279
280// -------------------------------------------------------- //
281
282void DormantNodeIO::xmlExportBegin(
283	ExportContext&						context) const {
284
285	if(!m_exportValid) {
286		context.reportError(
287			"DormantNodeIO::xmlExportBegin():\n"
288			"*** invalid ***\n");
289		return;
290	}
291
292	context.beginElement(_DORMANT_NODE_ELEMENT);
293}
294
295void DormantNodeIO::xmlExportAttributes(
296	ExportContext&						context) const {
297
298	context.writeAttr("key", m_nodeKey);
299}
300
301void DormantNodeIO::xmlExportContent(
302	ExportContext&						context) const {
303
304	context.beginContent();
305	BString buffer;
306
307	// write dormant-node description
308	context.beginElement(_NAME_ELEMENT);
309	context.beginContent();
310	context.writeString(m_dormantName);
311	context.endElement();
312
313	if(m_flavorID > 0) {
314		buffer = "";
315		buffer << m_flavorID;
316		context.beginElement(_FLAVOR_ID_ELEMENT);
317		context.beginContent();
318		context.writeString(buffer);
319		context.endElement();
320	}
321
322	_write_node_kinds(m_kinds, context);
323//	if(m_kinds & B_BUFFER_PRODUCER)
324//		_write_simple(_KIND_ELEMENT, "B_BUFFER_PRODUCER", context);
325//	if(m_kinds & B_BUFFER_CONSUMER)
326//		_write_simple(_KIND_ELEMENT, "B_BUFFER_CONSUMER", context);
327//	if(m_kinds & B_TIME_SOURCE)
328//		_write_simple(_KIND_ELEMENT, "B_TIME_SOURCE", context);
329//	if(m_kinds & B_CONTROLLABLE)
330//		_write_simple(_KIND_ELEMENT, "B_CONTROLLABLE", context);
331//	if(m_kinds & B_FILE_INTERFACE)
332//		_write_simple(_KIND_ELEMENT, "B_FILE_INTERFACE", context);
333//	if(m_kinds & B_ENTITY_INTERFACE)
334//		_write_simple(_KIND_ELEMENT, "B_ENTITY_INTERFACE", context);
335//	if(m_kinds & B_PHYSICAL_INPUT)
336//		_write_simple(_KIND_ELEMENT, "B_PHYSICAL_INPUT", context);
337//	if(m_kinds & B_PHYSICAL_OUTPUT)
338//		_write_simple(_KIND_ELEMENT, "B_PHYSICAL_OUTPUT", context);
339//	if(m_kinds & B_SYSTEM_MIXER)
340//		_write_simple(_KIND_ELEMENT, "B_SYSTEM_MIXER", context);
341
342	// write NodeRef flags
343	if(m_flags & NodeRef::NO_START_STOP)
344		_write_simple(_FLAG_ELEMENT, "NO_START_STOP", context);
345	if(m_flags & NodeRef::NO_SEEK)
346		_write_simple(_FLAG_ELEMENT, "NO_SEEK", context);
347	if(m_flags & NodeRef::NO_PREROLL)
348		_write_simple(_FLAG_ELEMENT, "NO_PREROLL", context);
349	if(m_flags & NodeRef::NO_STOP)
350		_write_simple(_FLAG_ELEMENT, "NO_STOP", context);
351	if(m_flags & NodeRef::NO_ROSTER_WATCH)
352		_write_simple(_FLAG_ELEMENT, "NO_ROSTER_WATCH", context);
353	if(m_flags & NodeRef::NO_POSITION_REPORTING)
354		_write_simple(_FLAG_ELEMENT, "NO_POSITION_REPORTING", context);
355
356	// write transport settings
357	if(m_runMode > 0) {
358		switch(m_runMode) {
359			case BMediaNode::B_OFFLINE:
360				_write_simple(_RUN_MODE_ELEMENT, "B_OFFLINE", context);
361				break;
362			case BMediaNode::B_DECREASE_PRECISION:
363				_write_simple(_RUN_MODE_ELEMENT, "B_DECREASE_PRECISION", context);
364				break;
365			case BMediaNode::B_INCREASE_LATENCY:
366				_write_simple(_RUN_MODE_ELEMENT, "B_INCREASE_LATENCY", context);
367				break;
368			case BMediaNode::B_DROP_DATA:
369				_write_simple(_RUN_MODE_ELEMENT, "B_DROP_DATA", context);
370				break;
371			case BMediaNode::B_RECORDING:
372				_write_simple(_RUN_MODE_ELEMENT, "B_RECORDING", context);
373				buffer = "";
374				buffer << m_recordingDelay;
375				_write_simple(_RECORDING_DELAY_ELEMENT, buffer.String(), context);
376				break;
377			default:
378				buffer = "";
379				buffer << m_runMode;
380				_write_simple(_RUN_MODE_ELEMENT, buffer.String(), context);
381		}
382	}
383
384	if(m_cycle) {
385		context.beginElement(_CYCLE_ELEMENT);
386		context.endElement();
387	}
388
389	BPath p;
390	if(
391		m_entry.InitCheck() == B_OK &&
392		m_entry.GetPath(&p) == B_OK)
393		_write_simple(_REF_ELEMENT, p.Path(), context);
394
395}
396
397void DormantNodeIO::xmlExportEnd(
398	ExportContext&						context) const {
399
400	// finish
401	context.endElement();
402}
403
404
405// -------------------------------------------------------- //
406// IMPORT:
407// -------------------------------------------------------- //
408
409//inline void _read_node_kind(
410//	int64& ioKind,
411//	const char* data,
412//	ImportContext& context) {
413//
414//	if(!strcmp(data, "B_BUFFER_PRODUCER"))
415//		ioKind |= B_BUFFER_PRODUCER;
416//	else if(!strcmp(data, "B_BUFFER_CONSUMER"))
417//		ioKind |= B_BUFFER_CONSUMER;
418//	else if(!strcmp(data, "B_TIME_SOURCE"))
419//		ioKind |= B_TIME_SOURCE;
420//	else if(!strcmp(data, "B_CONTROLLABLE"))
421//		ioKind |= B_CONTROLLABLE;
422//	else if(!strcmp(data, "B_FILE_INTERFACE"))
423//		ioKind |= B_FILE_INTERFACE;
424//	else if(!strcmp(data, "B_ENTITY_INTERFACE"))
425//		ioKind |= B_ENTITY_INTERFACE;
426//	else if(!strcmp(data, "B_PHYSICAL_INPUT"))
427//		ioKind |= B_PHYSICAL_INPUT;
428//	else if(!strcmp(data, "B_PHYSICAL_OUTPUT"))
429//		ioKind |= B_PHYSICAL_OUTPUT;
430//	else if(!strcmp(data, "B_SYSTEM_MIXER"))
431//		ioKind |= B_SYSTEM_MIXER;
432//	else {
433//		BString err;
434//		err << "_read_noderef_kind(): unknown node kind '" << data << "'\n";
435//		context.reportWarning(err.String());
436//	}
437//}
438
439inline void _read_noderef_flag(
440	int32& ioFlags,
441	const char* data,
442	ImportContext& context) {
443
444	if(!strcmp(data, "NO_START_STOP"))
445		ioFlags |= NodeRef::NO_START_STOP;
446	else if(!strcmp(data, "NO_SEEK"))
447		ioFlags |= NodeRef::NO_SEEK;
448	else if(!strcmp(data, "NO_PREROLL"))
449		ioFlags |= NodeRef::NO_PREROLL;
450	else if(!strcmp(data, "NO_STOP"))
451		ioFlags |= NodeRef::NO_STOP;
452	else if(!strcmp(data, "NO_ROSTER_WATCH"))
453		ioFlags |= NodeRef::NO_ROSTER_WATCH;
454	else if(!strcmp(data, "NO_POSITION_REPORTING"))
455		ioFlags |= NodeRef::NO_POSITION_REPORTING;
456	else {
457		BString err;
458		err << "_read_noderef_flag(): unknown node flag '" << data << "'\n";
459		context.reportWarning(err.String());
460	}
461}
462
463inline void _read_run_mode(
464	int32& runMode,
465	const char* data,
466	ImportContext& context) {
467
468	if(!strcmp(data, "B_OFFLINE"))
469		runMode = BMediaNode::B_OFFLINE;
470	else if(!strcmp(data, "B_DECREASE_PRECISION"))
471		runMode = BMediaNode::B_DECREASE_PRECISION;
472	else if(!strcmp(data, "B_INCREASE_LATENCY"))
473		runMode = BMediaNode::B_INCREASE_LATENCY;
474	else if(!strcmp(data, "B_DROP_DATA"))
475		runMode = BMediaNode::B_DROP_DATA;
476	else if(!strcmp(data, "B_RECORDING"))
477		runMode = BMediaNode::B_RECORDING;
478	else {
479		BString err;
480		err << "_read_run_mode(): unknown run mode '" << data << "'\n";
481		context.reportWarning(err.String());
482	}
483}
484
485inline void _read_entry(
486	BEntry& entry,
487	const char* data,
488	ImportContext& context) {
489
490	entry_ref r;
491	status_t err = get_ref_for_path(data, &r);
492	if(err < B_OK) {
493		BString text;
494		text << "_read_entry_ref(): get_ref_for_path('" << data << "') failed:\n"
495			"   " << strerror(err) << "\n";
496		context.reportWarning(text.String());
497	}
498
499	entry.SetTo(&r);
500}
501
502
503void DormantNodeIO::xmlImportBegin(
504	ImportContext&						context) { TOUCH(context); }
505
506void DormantNodeIO::xmlImportAttribute(
507	const char*								key,
508	const char*								value,
509	ImportContext&						context) {
510
511	if(!strcmp(key, "key")) {
512		m_nodeKey = value;
513	}
514	else {
515		BString err;
516		err << "DormantNodeIO: unknown attribute '" << key << "'\n";
517		context.reportError(err.String());
518	}
519}
520
521void DormantNodeIO::xmlImportContent(
522	const char*								data,
523	uint32										length,
524	ImportContext&						context) { TOUCH(data); TOUCH(length); TOUCH(context); }
525
526void DormantNodeIO::xmlImportChild(
527	IPersistent*							child,
528	ImportContext&						context) {
529
530	StringContent* obj = dynamic_cast<StringContent*>(child);
531	if(!obj) {
532		BString err;
533		err << "DormantNodeIO: unexpected element '" <<
534			context.element() << "'\n";
535		context.reportError(err.String());
536		return;
537	}
538
539	if(!strcmp(context.element(), _NAME_ELEMENT))
540		m_dormantName = obj->content;
541	else if(!strcmp(context.element(), _KIND_ELEMENT))
542		_read_node_kind(m_kinds, obj->content.String(), context);
543	else if(!strcmp(context.element(), _FLAVOR_ID_ELEMENT))
544		m_flavorID = atol(obj->content.String());
545	else if(!strcmp(context.element(), _FLAG_ELEMENT))
546		_read_noderef_flag(m_flags, obj->content.String(), context);
547	else if(!strcmp(context.element(), _RUN_MODE_ELEMENT))
548		_read_run_mode(m_runMode, obj->content.String(), context);
549	else if(!strcmp(context.element(), _RECORDING_DELAY_ELEMENT))
550		m_recordingDelay = strtoll(obj->content.String(), 0, 10);
551	else if(!strcmp(context.element(), _CYCLE_ELEMENT))
552		m_cycle = true;
553	else if(!strcmp(context.element(), _REF_ELEMENT))
554		_read_entry(m_entry, obj->content.String(), context);
555	else {
556		BString err;
557		err << "DormantNodeIO: unexpected element '" <<
558			context.element() << "'\n";
559		context.reportError(err.String());
560	}
561
562	delete child;
563}
564
565void DormantNodeIO::xmlImportComplete(
566	ImportContext&						context) { TOUCH(context); } //nyi; +++++ final checks?
567
568
569// END -- DormantNodeIO.cpp --
570
571