1/*
2 * Copyright 2015, Hamish Morrison <hamishm53@gmail.com>
3 * Copyright 2014-2016, Dario Casalinuovo
4 * Copyright 1999, Be Incorporated
5 * All Rights Reserved.
6 * This file may be used under the terms of the Be Sample Code License.
7 */
8
9
10#include <MediaRecorder.h>
11
12#include <MediaAddOn.h>
13#include <MediaRoster.h>
14#include <TimeSource.h>
15
16#include "MediaDebug.h"
17#include "MediaRecorderNode.h"
18
19
20BMediaRecorder::BMediaRecorder(const char* name, media_type type)
21	:
22	fInitErr(B_OK),
23	fConnected(false),
24	fRunning(false),
25	fReleaseOutputNode(false),
26	fRecordHook(NULL),
27	fNotifyHook(NULL),
28	fNode(NULL),
29	fBufferCookie(NULL)
30{
31	CALLED();
32
33	BMediaRoster::Roster(&fInitErr);
34
35	if (fInitErr == B_OK) {
36		fNode = new(std::nothrow) BMediaRecorderNode(name, this, type);
37		if (fNode == NULL)
38			fInitErr = B_NO_MEMORY;
39
40		fInitErr = BMediaRoster::CurrentRoster()->RegisterNode(fNode);
41	}
42}
43
44
45BMediaRecorder::~BMediaRecorder()
46{
47	CALLED();
48
49	if (fNode != NULL) {
50		Stop();
51		Disconnect();
52		fNode->Release();
53	}
54}
55
56
57status_t
58BMediaRecorder::InitCheck() const
59{
60	CALLED();
61
62	return fInitErr;
63}
64
65
66void
67BMediaRecorder::SetAcceptedFormat(const media_format& format)
68{
69	CALLED();
70
71	fNode->SetAcceptedFormat(format);
72}
73
74
75const media_format&
76BMediaRecorder::AcceptedFormat() const
77{
78	CALLED();
79
80	return fNode->AcceptedFormat();
81}
82
83
84status_t
85BMediaRecorder::SetHooks(ProcessFunc recordFunc, NotifyFunc notifyFunc,
86	void* cookie)
87{
88	CALLED();
89
90	fRecordHook = recordFunc;
91	fNotifyHook = notifyFunc;
92	fBufferCookie = cookie;
93
94	return B_OK;
95}
96
97
98void
99BMediaRecorder::BufferReceived(void* buffer, size_t size,
100	const media_header& header)
101{
102	CALLED();
103
104	if (fRecordHook) {
105		(*fRecordHook)(fBufferCookie, header.start_time,
106			buffer, size, Format());
107	}
108}
109
110
111status_t
112BMediaRecorder::Connect(const media_format& format)
113{
114	CALLED();
115
116	if (fInitErr != B_OK)
117		return fInitErr;
118
119	if (fConnected)
120		return B_MEDIA_ALREADY_CONNECTED;
121
122	status_t err = B_OK;
123	media_node node;
124
125	switch (format.type) {
126		// switch on format for default
127		case B_MEDIA_RAW_AUDIO:
128			err = BMediaRoster::Roster()->GetAudioMixer(&node);
129			break;
130		case B_MEDIA_RAW_VIDEO:
131		case B_MEDIA_ENCODED_VIDEO:
132			err = BMediaRoster::Roster()->GetVideoInput(&node);
133			break;
134		// give up?
135		default:
136			return B_MEDIA_BAD_FORMAT;
137	}
138
139	if (err != B_OK)
140		return err;
141
142	fReleaseOutputNode = true;
143
144	err = _Connect(node, NULL, format);
145
146	if (err != B_OK) {
147		BMediaRoster::Roster()->ReleaseNode(node);
148		fReleaseOutputNode = false;
149	}
150
151	return err;
152}
153
154
155status_t
156BMediaRecorder::Connect(const dormant_node_info& dormantNode,
157	const media_format& format)
158{
159	CALLED();
160
161	if (fInitErr != B_OK)
162		return fInitErr;
163
164	if (fConnected)
165		return B_MEDIA_ALREADY_CONNECTED;
166
167	media_node node;
168	status_t err = BMediaRoster::Roster()->InstantiateDormantNode(dormantNode,
169		&node, B_FLAVOR_IS_GLOBAL);
170
171	if (err != B_OK)
172		return err;
173
174	fReleaseOutputNode = true;
175
176	err = _Connect(node, NULL, format);
177
178	if (err != B_OK) {
179		BMediaRoster::Roster()->ReleaseNode(node);
180		fReleaseOutputNode = false;
181	}
182
183	return err;
184}
185
186
187status_t
188BMediaRecorder::Connect(const media_node& node,
189	const media_output* output, const media_format* format)
190{
191	CALLED();
192
193	if (fInitErr != B_OK)
194		return fInitErr;
195
196	if (fConnected)
197		return B_MEDIA_ALREADY_CONNECTED;
198
199	if (format == NULL && output != NULL)
200		format = &output->format;
201
202	return _Connect(node, output, *format);
203}
204
205
206status_t
207BMediaRecorder::Disconnect()
208{
209	CALLED();
210
211	status_t err = B_OK;
212
213	if (fInitErr != B_OK)
214		return fInitErr;
215
216	if (!fConnected)
217		return B_MEDIA_NOT_CONNECTED;
218
219	if (!fNode)
220		return B_ERROR;
221
222	if (fRunning)
223		err = Stop();
224
225	if (err != B_OK)
226		return err;
227
228	media_input ourInput;
229	fNode->GetInput(&ourInput);
230
231	// do the disconnect
232	err = BMediaRoster::CurrentRoster()->Disconnect(
233		fOutputNode.node, fOutputSource,
234			fNode->Node().node, ourInput.destination);
235
236	if (fReleaseOutputNode) {
237		BMediaRoster::Roster()->ReleaseNode(fOutputNode);
238		fReleaseOutputNode = false;
239	}
240
241	fConnected = false;
242	fRunning = false;
243
244	return err;
245}
246
247
248status_t
249BMediaRecorder::Start(bool force)
250{
251	CALLED();
252
253	if (fInitErr != B_OK)
254		return fInitErr;
255
256	if (!fConnected)
257		return B_MEDIA_NOT_CONNECTED;
258
259	if (fRunning && !force)
260		return EALREADY;
261
262	if (!fNode)
263		return B_ERROR;
264
265	// start node here
266	status_t err = B_OK;
267
268	if ((fOutputNode.kind & B_TIME_SOURCE) != 0)
269		err = BMediaRoster::CurrentRoster()->StartTimeSource(
270			fOutputNode, BTimeSource::RealTime());
271	else
272		err = BMediaRoster::CurrentRoster()->StartNode(
273			fOutputNode, fNode->TimeSource()->Now());
274
275	// then un-mute it
276	if (err == B_OK) {
277		fNode->SetDataEnabled(true);
278		fRunning = true;
279	} else
280		fRunning = false;
281
282	return err;
283}
284
285
286status_t
287BMediaRecorder::Stop(bool force)
288{
289	CALLED();
290
291	if (fInitErr != B_OK)
292		return fInitErr;
293
294	if (!fRunning && !force)
295		return EALREADY;
296
297	if (!fNode)
298		return B_ERROR;
299
300	// should have the Node mute the output here
301	fNode->SetDataEnabled(false);
302
303	fRunning = false;
304
305	return BMediaRoster::CurrentRoster()->StopNode(fNode->Node(), 0);
306}
307
308
309bool
310BMediaRecorder::IsRunning() const
311{
312	CALLED();
313
314	return fRunning;
315}
316
317
318bool
319BMediaRecorder::IsConnected() const
320{
321	CALLED();
322
323	return fConnected;
324}
325
326
327const media_source&
328BMediaRecorder::MediaSource() const
329{
330	CALLED();
331
332	return fOutputSource;
333}
334
335
336const media_input
337BMediaRecorder::MediaInput() const
338{
339	CALLED();
340
341	media_input input;
342	fNode->GetInput(&input);
343	return input;
344}
345
346
347const media_format&
348BMediaRecorder::Format() const
349{
350	CALLED();
351
352	return fNode->AcceptedFormat();
353}
354
355
356status_t
357BMediaRecorder::SetUpConnection(media_source outputSource)
358{
359	fOutputSource = outputSource;
360
361	// Perform the connection
362	media_node timeSource;
363	if ((fOutputNode.kind & B_TIME_SOURCE) != 0)
364		timeSource = fOutputNode;
365	else
366		BMediaRoster::Roster()->GetTimeSource(&timeSource);
367
368	// Set time source
369	return BMediaRoster::Roster()->SetTimeSourceFor(fNode->Node().node,
370		timeSource.node);
371}
372
373
374status_t
375BMediaRecorder::_Connect(const media_node& node,
376	const media_output* output, const media_format& format)
377{
378	CALLED();
379
380	status_t err = B_OK;
381	media_format ourFormat = format;
382	media_output ourOutput;
383
384	if (fNode == NULL)
385		return B_ERROR;
386
387	fNode->SetAcceptedFormat(ourFormat);
388
389	fOutputNode = node;
390
391	// Figure out the output provided
392	if (output != NULL) {
393		ourOutput = *output;
394	} else if (err == B_OK) {
395		media_output outputs[10];
396		int32 count = 10;
397
398		err = BMediaRoster::Roster()->GetFreeOutputsFor(fOutputNode,
399			outputs, count, &count, ourFormat.type);
400
401		if (err != B_OK)
402			return err;
403
404		for (int i = 0; i < count; i++) {
405			if (format_is_compatible(outputs[i].format, ourFormat)) {
406				ourOutput = outputs[i];
407				ourFormat = outputs[i].format;
408				break;
409			}
410		}
411	}
412
413	if (ourOutput.source == media_source::null)
414		return B_MEDIA_BAD_SOURCE;
415
416	// Find our Node's free input
417	media_input ourInput;
418	fNode->GetInput(&ourInput);
419
420	// Acknowledge the node that we already know
421	// who is our producer node.
422	fNode->ActivateInternalConnect(false);
423
424	return BMediaRoster::CurrentRoster()->Connect(ourOutput.source,
425		ourInput.destination, &ourFormat, &ourOutput, &ourInput,
426		BMediaRoster::B_CONNECT_MUTED);
427}
428
429
430void BMediaRecorder::_ReservedMediaRecorder0() { }
431void BMediaRecorder::_ReservedMediaRecorder1() { }
432void BMediaRecorder::_ReservedMediaRecorder2() { }
433void BMediaRecorder::_ReservedMediaRecorder3() { }
434void BMediaRecorder::_ReservedMediaRecorder4() { }
435void BMediaRecorder::_ReservedMediaRecorder5() { }
436void BMediaRecorder::_ReservedMediaRecorder6() { }
437void BMediaRecorder::_ReservedMediaRecorder7() { }
438
439