xref: /haiku/src/kits/media/MediaRecorder.cpp (revision 4d8811742fa447ec05b4993a16a0931bc29aafab)
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 
20 BMediaRecorder::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 
45 BMediaRecorder::~BMediaRecorder()
46 {
47 	CALLED();
48 
49 	if (fNode != NULL) {
50 		Stop();
51 		Disconnect();
52 		fNode->Release();
53 	}
54 }
55 
56 
57 status_t
58 BMediaRecorder::InitCheck() const
59 {
60 	CALLED();
61 
62 	return fInitErr;
63 }
64 
65 
66 void
67 BMediaRecorder::SetAcceptedFormat(const media_format& format)
68 {
69 	CALLED();
70 
71 	fNode->SetAcceptedFormat(format);
72 }
73 
74 
75 const media_format&
76 BMediaRecorder::AcceptedFormat() const
77 {
78 	CALLED();
79 
80 	return fNode->AcceptedFormat();
81 }
82 
83 
84 status_t
85 BMediaRecorder::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 
98 void
99 BMediaRecorder::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 
111 status_t
112 BMediaRecorder::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 
155 status_t
156 BMediaRecorder::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 
187 status_t
188 BMediaRecorder::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 
206 status_t
207 BMediaRecorder::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 
248 status_t
249 BMediaRecorder::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 
286 status_t
287 BMediaRecorder::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 
309 bool
310 BMediaRecorder::IsRunning() const
311 {
312 	CALLED();
313 
314 	return fRunning;
315 }
316 
317 
318 bool
319 BMediaRecorder::IsConnected() const
320 {
321 	CALLED();
322 
323 	return fConnected;
324 }
325 
326 
327 const media_source&
328 BMediaRecorder::MediaSource() const
329 {
330 	CALLED();
331 
332 	return fOutputSource;
333 }
334 
335 
336 const media_input&
337 BMediaRecorder::MediaInput() const
338 {
339 	CALLED();
340 
341 	media_input* input = NULL;
342 	fNode->GetInput(input);
343 	return *input;
344 }
345 
346 
347 const media_format&
348 BMediaRecorder::Format() const
349 {
350 	CALLED();
351 
352 	return fNode->AcceptedFormat();
353 }
354 
355 
356 status_t
357 BMediaRecorder::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 
374 status_t
375 BMediaRecorder::_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 
430 void BMediaRecorder::_ReservedMediaRecorder0() { }
431 void BMediaRecorder::_ReservedMediaRecorder1() { }
432 void BMediaRecorder::_ReservedMediaRecorder2() { }
433 void BMediaRecorder::_ReservedMediaRecorder3() { }
434 void BMediaRecorder::_ReservedMediaRecorder4() { }
435 void BMediaRecorder::_ReservedMediaRecorder5() { }
436 void BMediaRecorder::_ReservedMediaRecorder6() { }
437 void BMediaRecorder::_ReservedMediaRecorder7() { }
438 
439