xref: /haiku/src/kits/media/MediaRecorder.cpp (revision fcf7cbe79e26d0c2d9d339c2dc826db755bc7323)
1739fd34cSBarrett /*
2683cf2ffSHamish Morrison  * Copyright 2015, Hamish Morrison <hamishm53@gmail.com>
3c1ad6121SDario Casalinuovo  * Copyright 2014-2016, Dario Casalinuovo
4683cf2ffSHamish Morrison  * Copyright 1999, Be Incorporated
5739fd34cSBarrett  * All Rights Reserved.
6739fd34cSBarrett  * This file may be used under the terms of the Be Sample Code License.
7739fd34cSBarrett  */
8739fd34cSBarrett 
9739fd34cSBarrett 
10683cf2ffSHamish Morrison #include <MediaRecorder.h>
11739fd34cSBarrett 
12739fd34cSBarrett #include <MediaAddOn.h>
13739fd34cSBarrett #include <MediaRoster.h>
14739fd34cSBarrett #include <TimeSource.h>
15739fd34cSBarrett 
163ef4b200SDario Casalinuovo #include "MediaDebug.h"
173ef4b200SDario Casalinuovo #include "MediaRecorderNode.h"
18739fd34cSBarrett 
19739fd34cSBarrett 
BMediaRecorder(const char * name,media_type type)20739fd34cSBarrett BMediaRecorder::BMediaRecorder(const char* name, media_type type)
21739fd34cSBarrett 	:
22739fd34cSBarrett 	fInitErr(B_OK),
23739fd34cSBarrett 	fConnected(false),
24739fd34cSBarrett 	fRunning(false),
25683cf2ffSHamish Morrison 	fReleaseOutputNode(false),
26739fd34cSBarrett 	fRecordHook(NULL),
27739fd34cSBarrett 	fNotifyHook(NULL),
28739fd34cSBarrett 	fNode(NULL),
29739fd34cSBarrett 	fBufferCookie(NULL)
30739fd34cSBarrett {
31739fd34cSBarrett 	CALLED();
32739fd34cSBarrett 
33739fd34cSBarrett 	BMediaRoster::Roster(&fInitErr);
34739fd34cSBarrett 
35739fd34cSBarrett 	if (fInitErr == B_OK) {
36683cf2ffSHamish Morrison 		fNode = new(std::nothrow) BMediaRecorderNode(name, this, type);
37683cf2ffSHamish Morrison 		if (fNode == NULL)
38683cf2ffSHamish Morrison 			fInitErr = B_NO_MEMORY;
39683cf2ffSHamish Morrison 
40739fd34cSBarrett 		fInitErr = BMediaRoster::CurrentRoster()->RegisterNode(fNode);
41739fd34cSBarrett 	}
42739fd34cSBarrett }
43739fd34cSBarrett 
44739fd34cSBarrett 
~BMediaRecorder()45739fd34cSBarrett BMediaRecorder::~BMediaRecorder()
46739fd34cSBarrett {
47739fd34cSBarrett 	CALLED();
48739fd34cSBarrett 
49739fd34cSBarrett 	if (fNode != NULL) {
50739fd34cSBarrett 		Stop();
51739fd34cSBarrett 		Disconnect();
52739fd34cSBarrett 		fNode->Release();
53739fd34cSBarrett 	}
54739fd34cSBarrett }
55739fd34cSBarrett 
56739fd34cSBarrett 
57739fd34cSBarrett status_t
InitCheck() const58739fd34cSBarrett BMediaRecorder::InitCheck() const
59739fd34cSBarrett {
60739fd34cSBarrett 	CALLED();
61739fd34cSBarrett 
62739fd34cSBarrett 	return fInitErr;
63739fd34cSBarrett }
64739fd34cSBarrett 
65739fd34cSBarrett 
66739fd34cSBarrett void
SetAcceptedFormat(const media_format & format)67739fd34cSBarrett BMediaRecorder::SetAcceptedFormat(const media_format& format)
68739fd34cSBarrett {
69739fd34cSBarrett 	CALLED();
70739fd34cSBarrett 
71739fd34cSBarrett 	fNode->SetAcceptedFormat(format);
72739fd34cSBarrett }
73739fd34cSBarrett 
74739fd34cSBarrett 
752a2e352aSDario Casalinuovo const media_format&
AcceptedFormat() const762a2e352aSDario Casalinuovo BMediaRecorder::AcceptedFormat() const
772a2e352aSDario Casalinuovo {
782a2e352aSDario Casalinuovo 	CALLED();
792a2e352aSDario Casalinuovo 
802a2e352aSDario Casalinuovo 	return fNode->AcceptedFormat();
812a2e352aSDario Casalinuovo }
822a2e352aSDario Casalinuovo 
832a2e352aSDario Casalinuovo 
84739fd34cSBarrett status_t
SetHooks(ProcessFunc recordFunc,NotifyFunc notifyFunc,void * cookie)85739fd34cSBarrett BMediaRecorder::SetHooks(ProcessFunc recordFunc, NotifyFunc notifyFunc,
86739fd34cSBarrett 	void* cookie)
87739fd34cSBarrett {
88739fd34cSBarrett 	CALLED();
89739fd34cSBarrett 
90739fd34cSBarrett 	fRecordHook = recordFunc;
91739fd34cSBarrett 	fNotifyHook = notifyFunc;
92739fd34cSBarrett 	fBufferCookie = cookie;
93739fd34cSBarrett 
94739fd34cSBarrett 	return B_OK;
95739fd34cSBarrett }
96739fd34cSBarrett 
97739fd34cSBarrett 
98739fd34cSBarrett void
BufferReceived(void * buffer,size_t size,const media_header & header)99739fd34cSBarrett BMediaRecorder::BufferReceived(void* buffer, size_t size,
100739fd34cSBarrett 	const media_header& header)
101739fd34cSBarrett {
102739fd34cSBarrett 	CALLED();
103739fd34cSBarrett 
104739fd34cSBarrett 	if (fRecordHook) {
105739fd34cSBarrett 		(*fRecordHook)(fBufferCookie, header.start_time,
106739fd34cSBarrett 			buffer, size, Format());
107739fd34cSBarrett 	}
108739fd34cSBarrett }
109739fd34cSBarrett 
110739fd34cSBarrett 
111739fd34cSBarrett status_t
Connect(const media_format & format)112683cf2ffSHamish Morrison BMediaRecorder::Connect(const media_format& format)
113739fd34cSBarrett {
114739fd34cSBarrett 	CALLED();
115739fd34cSBarrett 
116739fd34cSBarrett 	if (fInitErr != B_OK)
117739fd34cSBarrett 		return fInitErr;
118739fd34cSBarrett 
119739fd34cSBarrett 	if (fConnected)
120739fd34cSBarrett 		return B_MEDIA_ALREADY_CONNECTED;
121739fd34cSBarrett 
122683cf2ffSHamish Morrison 	status_t err = B_OK;
123683cf2ffSHamish Morrison 	media_node node;
124683cf2ffSHamish Morrison 
125683cf2ffSHamish Morrison 	switch (format.type) {
126683cf2ffSHamish Morrison 		// switch on format for default
127683cf2ffSHamish Morrison 		case B_MEDIA_RAW_AUDIO:
128683cf2ffSHamish Morrison 			err = BMediaRoster::Roster()->GetAudioMixer(&node);
129683cf2ffSHamish Morrison 			break;
130683cf2ffSHamish Morrison 		case B_MEDIA_RAW_VIDEO:
131683cf2ffSHamish Morrison 		case B_MEDIA_ENCODED_VIDEO:
132683cf2ffSHamish Morrison 			err = BMediaRoster::Roster()->GetVideoInput(&node);
133683cf2ffSHamish Morrison 			break;
134683cf2ffSHamish Morrison 		// give up?
135683cf2ffSHamish Morrison 		default:
136683cf2ffSHamish Morrison 			return B_MEDIA_BAD_FORMAT;
137683cf2ffSHamish Morrison 	}
138683cf2ffSHamish Morrison 
139683cf2ffSHamish Morrison 	if (err != B_OK)
140683cf2ffSHamish Morrison 		return err;
141683cf2ffSHamish Morrison 
142683cf2ffSHamish Morrison 	fReleaseOutputNode = true;
143683cf2ffSHamish Morrison 
144683cf2ffSHamish Morrison 	err = _Connect(node, NULL, format);
145683cf2ffSHamish Morrison 
146683cf2ffSHamish Morrison 	if (err != B_OK) {
147683cf2ffSHamish Morrison 		BMediaRoster::Roster()->ReleaseNode(node);
148683cf2ffSHamish Morrison 		fReleaseOutputNode = false;
149683cf2ffSHamish Morrison 	}
150683cf2ffSHamish Morrison 
151683cf2ffSHamish Morrison 	return err;
152739fd34cSBarrett }
153739fd34cSBarrett 
154739fd34cSBarrett 
155739fd34cSBarrett status_t
Connect(const dormant_node_info & dormantNode,const media_format & format)156683cf2ffSHamish Morrison BMediaRecorder::Connect(const dormant_node_info& dormantNode,
157683cf2ffSHamish Morrison 	const media_format& format)
158739fd34cSBarrett {
159739fd34cSBarrett 	CALLED();
160739fd34cSBarrett 
161739fd34cSBarrett 	if (fInitErr != B_OK)
162739fd34cSBarrett 		return fInitErr;
163739fd34cSBarrett 
164739fd34cSBarrett 	if (fConnected)
165739fd34cSBarrett 		return B_MEDIA_ALREADY_CONNECTED;
166739fd34cSBarrett 
167683cf2ffSHamish Morrison 	media_node node;
168683cf2ffSHamish Morrison 	status_t err = BMediaRoster::Roster()->InstantiateDormantNode(dormantNode,
169683cf2ffSHamish Morrison 		&node, B_FLAVOR_IS_GLOBAL);
170683cf2ffSHamish Morrison 
171683cf2ffSHamish Morrison 	if (err != B_OK)
172683cf2ffSHamish Morrison 		return err;
173683cf2ffSHamish Morrison 
174683cf2ffSHamish Morrison 	fReleaseOutputNode = true;
175683cf2ffSHamish Morrison 
176683cf2ffSHamish Morrison 	err = _Connect(node, NULL, format);
177683cf2ffSHamish Morrison 
178683cf2ffSHamish Morrison 	if (err != B_OK) {
179683cf2ffSHamish Morrison 		BMediaRoster::Roster()->ReleaseNode(node);
180683cf2ffSHamish Morrison 		fReleaseOutputNode = false;
181683cf2ffSHamish Morrison 	}
182683cf2ffSHamish Morrison 
183683cf2ffSHamish Morrison 	return err;
184739fd34cSBarrett }
185739fd34cSBarrett 
186739fd34cSBarrett 
187739fd34cSBarrett status_t
Connect(const media_node & node,const media_output * output,const media_format * format)188739fd34cSBarrett BMediaRecorder::Connect(const media_node& node,
189683cf2ffSHamish Morrison 	const media_output* output, const media_format* format)
190739fd34cSBarrett {
191739fd34cSBarrett 	CALLED();
192739fd34cSBarrett 
193739fd34cSBarrett 	if (fInitErr != B_OK)
194739fd34cSBarrett 		return fInitErr;
195739fd34cSBarrett 
196739fd34cSBarrett 	if (fConnected)
197739fd34cSBarrett 		return B_MEDIA_ALREADY_CONNECTED;
198739fd34cSBarrett 
199683cf2ffSHamish Morrison 	if (format == NULL && output != NULL)
200683cf2ffSHamish Morrison 		format = &output->format;
201683cf2ffSHamish Morrison 
202683cf2ffSHamish Morrison 	return _Connect(node, output, *format);
203739fd34cSBarrett }
204739fd34cSBarrett 
205739fd34cSBarrett 
206739fd34cSBarrett status_t
Disconnect()207739fd34cSBarrett BMediaRecorder::Disconnect()
208739fd34cSBarrett {
209739fd34cSBarrett 	CALLED();
210739fd34cSBarrett 
211739fd34cSBarrett 	status_t err = B_OK;
212739fd34cSBarrett 
213739fd34cSBarrett 	if (fInitErr != B_OK)
214739fd34cSBarrett 		return fInitErr;
215739fd34cSBarrett 
216739fd34cSBarrett 	if (!fConnected)
217739fd34cSBarrett 		return B_MEDIA_NOT_CONNECTED;
218739fd34cSBarrett 
219739fd34cSBarrett 	if (!fNode)
220739fd34cSBarrett 		return B_ERROR;
221739fd34cSBarrett 
222739fd34cSBarrett 	if (fRunning)
223739fd34cSBarrett 		err = Stop();
224739fd34cSBarrett 
225739fd34cSBarrett 	if (err != B_OK)
226739fd34cSBarrett 		return err;
227739fd34cSBarrett 
2282a2e352aSDario Casalinuovo 	media_input ourInput;
2292a2e352aSDario Casalinuovo 	fNode->GetInput(&ourInput);
2302a2e352aSDario Casalinuovo 
231739fd34cSBarrett 	// do the disconnect
232739fd34cSBarrett 	err = BMediaRoster::CurrentRoster()->Disconnect(
2332a2e352aSDario Casalinuovo 		fOutputNode.node, fOutputSource,
2342a2e352aSDario Casalinuovo 			fNode->Node().node, ourInput.destination);
235739fd34cSBarrett 
236683cf2ffSHamish Morrison 	if (fReleaseOutputNode) {
237739fd34cSBarrett 		BMediaRoster::Roster()->ReleaseNode(fOutputNode);
238683cf2ffSHamish Morrison 		fReleaseOutputNode = false;
239739fd34cSBarrett 	}
240739fd34cSBarrett 
241739fd34cSBarrett 	fConnected = false;
242739fd34cSBarrett 	fRunning = false;
243739fd34cSBarrett 
244739fd34cSBarrett 	return err;
245739fd34cSBarrett }
246739fd34cSBarrett 
247739fd34cSBarrett 
248739fd34cSBarrett status_t
Start(bool force)249739fd34cSBarrett BMediaRecorder::Start(bool force)
250739fd34cSBarrett {
251739fd34cSBarrett 	CALLED();
252739fd34cSBarrett 
253739fd34cSBarrett 	if (fInitErr != B_OK)
254739fd34cSBarrett 		return fInitErr;
255739fd34cSBarrett 
256739fd34cSBarrett 	if (!fConnected)
257739fd34cSBarrett 		return B_MEDIA_NOT_CONNECTED;
258739fd34cSBarrett 
259739fd34cSBarrett 	if (fRunning && !force)
260739fd34cSBarrett 		return EALREADY;
261739fd34cSBarrett 
262739fd34cSBarrett 	if (!fNode)
263739fd34cSBarrett 		return B_ERROR;
264739fd34cSBarrett 
265739fd34cSBarrett 	// start node here
266739fd34cSBarrett 	status_t err = B_OK;
267739fd34cSBarrett 
268683cf2ffSHamish Morrison 	if ((fOutputNode.kind & B_TIME_SOURCE) != 0)
269739fd34cSBarrett 		err = BMediaRoster::CurrentRoster()->StartTimeSource(
270739fd34cSBarrett 			fOutputNode, BTimeSource::RealTime());
271739fd34cSBarrett 	else
272739fd34cSBarrett 		err = BMediaRoster::CurrentRoster()->StartNode(
273683cf2ffSHamish Morrison 			fOutputNode, fNode->TimeSource()->Now());
274739fd34cSBarrett 
275739fd34cSBarrett 	// then un-mute it
276739fd34cSBarrett 	if (err == B_OK) {
277739fd34cSBarrett 		fNode->SetDataEnabled(true);
278739fd34cSBarrett 		fRunning = true;
279739fd34cSBarrett 	} else
280739fd34cSBarrett 		fRunning = false;
281739fd34cSBarrett 
282739fd34cSBarrett 	return err;
283739fd34cSBarrett }
284739fd34cSBarrett 
285739fd34cSBarrett 
286739fd34cSBarrett status_t
Stop(bool force)287739fd34cSBarrett BMediaRecorder::Stop(bool force)
288739fd34cSBarrett {
289739fd34cSBarrett 	CALLED();
290739fd34cSBarrett 
291739fd34cSBarrett 	if (fInitErr != B_OK)
292739fd34cSBarrett 		return fInitErr;
293739fd34cSBarrett 
294739fd34cSBarrett 	if (!fRunning && !force)
295739fd34cSBarrett 		return EALREADY;
296739fd34cSBarrett 
297739fd34cSBarrett 	if (!fNode)
298739fd34cSBarrett 		return B_ERROR;
299739fd34cSBarrett 
300739fd34cSBarrett 	// should have the Node mute the output here
301739fd34cSBarrett 	fNode->SetDataEnabled(false);
302739fd34cSBarrett 
303739fd34cSBarrett 	fRunning = false;
304739fd34cSBarrett 
305739fd34cSBarrett 	return BMediaRoster::CurrentRoster()->StopNode(fNode->Node(), 0);
306739fd34cSBarrett }
307739fd34cSBarrett 
308739fd34cSBarrett 
309739fd34cSBarrett bool
IsRunning() const310739fd34cSBarrett BMediaRecorder::IsRunning() const
311739fd34cSBarrett {
312739fd34cSBarrett 	CALLED();
313739fd34cSBarrett 
314739fd34cSBarrett 	return fRunning;
315739fd34cSBarrett }
316739fd34cSBarrett 
317739fd34cSBarrett 
318739fd34cSBarrett bool
IsConnected() const319739fd34cSBarrett BMediaRecorder::IsConnected() const
320739fd34cSBarrett {
321739fd34cSBarrett 	CALLED();
322739fd34cSBarrett 
323739fd34cSBarrett 	return fConnected;
324739fd34cSBarrett }
325739fd34cSBarrett 
326739fd34cSBarrett 
3272a2e352aSDario Casalinuovo const media_source&
MediaSource() const3282a2e352aSDario Casalinuovo BMediaRecorder::MediaSource() const
329739fd34cSBarrett {
330739fd34cSBarrett 	CALLED();
331739fd34cSBarrett 
3322a2e352aSDario Casalinuovo 	return fOutputSource;
333739fd34cSBarrett }
334739fd34cSBarrett 
335739fd34cSBarrett 
336*fcf7cbe7SAdrien Destugues const media_input
MediaInput() const337739fd34cSBarrett BMediaRecorder::MediaInput() const
338739fd34cSBarrett {
339739fd34cSBarrett 	CALLED();
340739fd34cSBarrett 
341*fcf7cbe7SAdrien Destugues 	media_input input;
342*fcf7cbe7SAdrien Destugues 	fNode->GetInput(&input);
343*fcf7cbe7SAdrien Destugues 	return input;
344739fd34cSBarrett }
345739fd34cSBarrett 
346739fd34cSBarrett 
347739fd34cSBarrett const media_format&
Format() const348739fd34cSBarrett BMediaRecorder::Format() const
349739fd34cSBarrett {
350739fd34cSBarrett 	CALLED();
351739fd34cSBarrett 
3522a2e352aSDario Casalinuovo 	return fNode->AcceptedFormat();
3532a2e352aSDario Casalinuovo }
3542a2e352aSDario Casalinuovo 
3552a2e352aSDario Casalinuovo 
3562a2e352aSDario Casalinuovo status_t
SetUpConnection(media_source outputSource)357cc0d365eSDario Casalinuovo BMediaRecorder::SetUpConnection(media_source outputSource)
3582a2e352aSDario Casalinuovo {
3592a2e352aSDario Casalinuovo 	fOutputSource = outputSource;
3602a2e352aSDario Casalinuovo 
3612a2e352aSDario Casalinuovo 	// Perform the connection
3622a2e352aSDario Casalinuovo 	media_node timeSource;
3632a2e352aSDario Casalinuovo 	if ((fOutputNode.kind & B_TIME_SOURCE) != 0)
3642a2e352aSDario Casalinuovo 		timeSource = fOutputNode;
3652a2e352aSDario Casalinuovo 	else
3662a2e352aSDario Casalinuovo 		BMediaRoster::Roster()->GetTimeSource(&timeSource);
3672a2e352aSDario Casalinuovo 
3682a2e352aSDario Casalinuovo 	// Set time source
3692a2e352aSDario Casalinuovo 	return BMediaRoster::Roster()->SetTimeSourceFor(fNode->Node().node,
3702a2e352aSDario Casalinuovo 		timeSource.node);
371739fd34cSBarrett }
372739fd34cSBarrett 
373739fd34cSBarrett 
374739fd34cSBarrett status_t
_Connect(const media_node & node,const media_output * output,const media_format & format)375683cf2ffSHamish Morrison BMediaRecorder::_Connect(const media_node& node,
376683cf2ffSHamish Morrison 	const media_output* output, const media_format& format)
377739fd34cSBarrett {
378739fd34cSBarrett 	CALLED();
379739fd34cSBarrett 
380739fd34cSBarrett 	status_t err = B_OK;
381683cf2ffSHamish Morrison 	media_format ourFormat = format;
382739fd34cSBarrett 	media_output ourOutput;
383739fd34cSBarrett 
384739fd34cSBarrett 	if (fNode == NULL)
385739fd34cSBarrett 		return B_ERROR;
386739fd34cSBarrett 
387739fd34cSBarrett 	fNode->SetAcceptedFormat(ourFormat);
388739fd34cSBarrett 
389739fd34cSBarrett 	fOutputNode = node;
390739fd34cSBarrett 
391c1ad6121SDario Casalinuovo 	// Figure out the output provided
392739fd34cSBarrett 	if (output != NULL) {
393739fd34cSBarrett 		ourOutput = *output;
394739fd34cSBarrett 	} else if (err == B_OK) {
395739fd34cSBarrett 		media_output outputs[10];
396739fd34cSBarrett 		int32 count = 10;
397739fd34cSBarrett 
398683cf2ffSHamish Morrison 		err = BMediaRoster::Roster()->GetFreeOutputsFor(fOutputNode,
399739fd34cSBarrett 			outputs, count, &count, ourFormat.type);
400739fd34cSBarrett 
401739fd34cSBarrett 		if (err != B_OK)
402739fd34cSBarrett 			return err;
403739fd34cSBarrett 
404739fd34cSBarrett 		for (int i = 0; i < count; i++) {
405739fd34cSBarrett 			if (format_is_compatible(outputs[i].format, ourFormat)) {
406739fd34cSBarrett 				ourOutput = outputs[i];
407739fd34cSBarrett 				ourFormat = outputs[i].format;
408739fd34cSBarrett 				break;
409739fd34cSBarrett 			}
410739fd34cSBarrett 		}
411739fd34cSBarrett 	}
412739fd34cSBarrett 
413739fd34cSBarrett 	if (ourOutput.source == media_source::null)
414739fd34cSBarrett 		return B_MEDIA_BAD_SOURCE;
415739fd34cSBarrett 
416c1ad6121SDario Casalinuovo 	// Find our Node's free input
417739fd34cSBarrett 	media_input ourInput;
4182a2e352aSDario Casalinuovo 	fNode->GetInput(&ourInput);
419739fd34cSBarrett 
420c1ad6121SDario Casalinuovo 	// Acknowledge the node that we already know
421c1ad6121SDario Casalinuovo 	// who is our producer node.
422c1ad6121SDario Casalinuovo 	fNode->ActivateInternalConnect(false);
423c1ad6121SDario Casalinuovo 
424d23913f2SDario Casalinuovo 	return BMediaRoster::CurrentRoster()->Connect(ourOutput.source,
4252a2e352aSDario Casalinuovo 		ourInput.destination, &ourFormat, &ourOutput, &ourInput,
426739fd34cSBarrett 		BMediaRoster::B_CONNECT_MUTED);
427739fd34cSBarrett }
428683cf2ffSHamish Morrison 
429683cf2ffSHamish Morrison 
_ReservedMediaRecorder0()430683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder0() { }
_ReservedMediaRecorder1()431683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder1() { }
_ReservedMediaRecorder2()432683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder2() { }
_ReservedMediaRecorder3()433683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder3() { }
_ReservedMediaRecorder4()434683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder4() { }
_ReservedMediaRecorder5()435683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder5() { }
_ReservedMediaRecorder6()436683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder6() { }
_ReservedMediaRecorder7()437683cf2ffSHamish Morrison void BMediaRecorder::_ReservedMediaRecorder7() { }
438683cf2ffSHamish Morrison 
439