xref: /haiku/src/kits/media/MediaRecorder.cpp (revision 6d2f2ec177bf615a117a7428d71be4330545b320)
1 /*
2  * Copyright 2015, Hamish Morrison <hamishm53@gmail.com>
3  * Copyright 2014, 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 status_t
76 BMediaRecorder::SetHooks(ProcessFunc recordFunc, NotifyFunc notifyFunc,
77 	void* cookie)
78 {
79 	CALLED();
80 
81 	fRecordHook = recordFunc;
82 	fNotifyHook = notifyFunc;
83 	fBufferCookie = cookie;
84 
85 	return B_OK;
86 }
87 
88 
89 void
90 BMediaRecorder::BufferReceived(void* buffer, size_t size,
91 	const media_header& header)
92 {
93 	CALLED();
94 
95 	if (fRecordHook) {
96 		(*fRecordHook)(fBufferCookie, header.start_time,
97 			buffer, size, Format());
98 	}
99 }
100 
101 
102 status_t
103 BMediaRecorder::Connect(const media_format& format)
104 {
105 	CALLED();
106 
107 	if (fInitErr != B_OK)
108 		return fInitErr;
109 
110 	if (fConnected)
111 		return B_MEDIA_ALREADY_CONNECTED;
112 
113 	status_t err = B_OK;
114 	media_node node;
115 
116 	switch (format.type) {
117 		// switch on format for default
118 		case B_MEDIA_RAW_AUDIO:
119 			err = BMediaRoster::Roster()->GetAudioMixer(&node);
120 			break;
121 		case B_MEDIA_RAW_VIDEO:
122 		case B_MEDIA_ENCODED_VIDEO:
123 			err = BMediaRoster::Roster()->GetVideoInput(&node);
124 			break;
125 		// give up?
126 		default:
127 			return B_MEDIA_BAD_FORMAT;
128 	}
129 
130 	if (err != B_OK)
131 		return err;
132 
133 	fReleaseOutputNode = true;
134 
135 	err = _Connect(node, NULL, format);
136 
137 	if (err != B_OK) {
138 		BMediaRoster::Roster()->ReleaseNode(node);
139 		fReleaseOutputNode = false;
140 	}
141 
142 	return err;
143 }
144 
145 
146 status_t
147 BMediaRecorder::Connect(const dormant_node_info& dormantNode,
148 	const media_format& format)
149 {
150 	CALLED();
151 
152 	if (fInitErr != B_OK)
153 		return fInitErr;
154 
155 	if (fConnected)
156 		return B_MEDIA_ALREADY_CONNECTED;
157 
158 	media_node node;
159 	status_t err = BMediaRoster::Roster()->InstantiateDormantNode(dormantNode,
160 		&node, B_FLAVOR_IS_GLOBAL);
161 
162 	if (err != B_OK)
163 		return err;
164 
165 	fReleaseOutputNode = true;
166 
167 	err = _Connect(node, NULL, format);
168 
169 	if (err != B_OK) {
170 		BMediaRoster::Roster()->ReleaseNode(node);
171 		fReleaseOutputNode = false;
172 	}
173 
174 	return err;
175 }
176 
177 
178 status_t
179 BMediaRecorder::Connect(const media_node& node,
180 	const media_output* output, const media_format* format)
181 {
182 	CALLED();
183 
184 	if (fInitErr != B_OK)
185 		return fInitErr;
186 
187 	if (fConnected)
188 		return B_MEDIA_ALREADY_CONNECTED;
189 
190 	if (format == NULL && output != NULL)
191 		format = &output->format;
192 
193 	return _Connect(node, output, *format);
194 }
195 
196 
197 status_t
198 BMediaRecorder::Disconnect()
199 {
200 	CALLED();
201 
202 	status_t err = B_OK;
203 
204 	if (fInitErr != B_OK)
205 		return fInitErr;
206 
207 	if (!fConnected)
208 		return B_MEDIA_NOT_CONNECTED;
209 
210 	if (!fNode)
211 		return B_ERROR;
212 
213 	if (fRunning)
214 		err = Stop();
215 
216 	if (err != B_OK)
217 		return err;
218 
219 	// do the disconnect
220 	err = BMediaRoster::CurrentRoster()->Disconnect(
221 		fOutputNode.node, fOutput.source,
222 			fNode->Node().node, fInput.destination);
223 
224 	if (fReleaseOutputNode) {
225 		BMediaRoster::Roster()->ReleaseNode(fOutputNode);
226 		fReleaseOutputNode = false;
227 	}
228 
229 	fConnected = false;
230 	fRunning = false;
231 
232 	return err;
233 }
234 
235 
236 status_t
237 BMediaRecorder::Start(bool force)
238 {
239 	CALLED();
240 
241 	if (fInitErr != B_OK)
242 		return fInitErr;
243 
244 	if (!fConnected)
245 		return B_MEDIA_NOT_CONNECTED;
246 
247 	if (fRunning && !force)
248 		return EALREADY;
249 
250 	if (!fNode)
251 		return B_ERROR;
252 
253 	// start node here
254 	status_t err = B_OK;
255 
256 	if ((fOutputNode.kind & B_TIME_SOURCE) != 0)
257 		err = BMediaRoster::CurrentRoster()->StartTimeSource(
258 			fOutputNode, BTimeSource::RealTime());
259 	else
260 		err = BMediaRoster::CurrentRoster()->StartNode(
261 			fOutputNode, fNode->TimeSource()->Now());
262 
263 	// then un-mute it
264 	if (err == B_OK) {
265 		fNode->SetDataEnabled(true);
266 		fRunning = true;
267 	} else
268 		fRunning = false;
269 
270 	return err;
271 }
272 
273 
274 status_t
275 BMediaRecorder::Stop(bool force)
276 {
277 	CALLED();
278 
279 	if (fInitErr != B_OK)
280 		return fInitErr;
281 
282 	if (!fRunning && !force)
283 		return EALREADY;
284 
285 	if (!fNode)
286 		return B_ERROR;
287 
288 	// should have the Node mute the output here
289 	fNode->SetDataEnabled(false);
290 
291 	fRunning = false;
292 
293 	return BMediaRoster::CurrentRoster()->StopNode(fNode->Node(), 0);
294 }
295 
296 
297 bool
298 BMediaRecorder::IsRunning() const
299 {
300 	CALLED();
301 
302 	return fRunning;
303 }
304 
305 
306 bool
307 BMediaRecorder::IsConnected() const
308 {
309 	CALLED();
310 
311 	return fConnected;
312 }
313 
314 
315 const media_output&
316 BMediaRecorder::MediaOutput() const
317 {
318 	CALLED();
319 
320 	return fOutput;
321 }
322 
323 
324 const media_input&
325 BMediaRecorder::MediaInput() const
326 {
327 	CALLED();
328 
329 	return fInput;
330 }
331 
332 
333 const media_format&
334 BMediaRecorder::Format() const
335 {
336 	CALLED();
337 
338 	return fOutput.format;
339 }
340 
341 
342 status_t
343 BMediaRecorder::_Connect(const media_node& node,
344 	const media_output* output, const media_format& format)
345 {
346 	CALLED();
347 
348 	status_t err = B_OK;
349 	media_format ourFormat = format;
350 	media_output ourOutput;
351 
352 	if (fNode == NULL)
353 		return B_ERROR;
354 
355 	fNode->SetAcceptedFormat(ourFormat);
356 
357 	fOutputNode = node;
358 
359 	// figure out the output provided
360 	if (output != NULL) {
361 		ourOutput = *output;
362 	} else if (err == B_OK) {
363 		media_output outputs[10];
364 		int32 count = 10;
365 
366 		err = BMediaRoster::Roster()->GetFreeOutputsFor(fOutputNode,
367 			outputs, count, &count, ourFormat.type);
368 
369 		if (err != B_OK)
370 			return err;
371 
372 		for (int i = 0; i < count; i++) {
373 			if (format_is_compatible(outputs[i].format, ourFormat)) {
374 				ourOutput = outputs[i];
375 				ourFormat = outputs[i].format;
376 				break;
377 			}
378 		}
379 	}
380 
381 	if (ourOutput.source == media_source::null)
382 		return B_MEDIA_BAD_SOURCE;
383 
384 	// find our Node's free input
385 	media_input ourInput;
386 	err = fNode->GetInput(&ourInput);
387 	if (err != B_OK)
388 		return err;
389 
390 	media_node timeSource;
391 	if ((node.kind & B_TIME_SOURCE) != 0)
392 		timeSource = node;
393 	else
394 		BMediaRoster::Roster()->GetTimeSource(&timeSource);
395 
396 	// set time source
397 	err = BMediaRoster::Roster()->SetTimeSourceFor(fNode->Node().node,
398 		timeSource.node);
399 
400 	if (err != B_OK)
401 		return err;
402 
403 	// start the recorder node (it's always running)
404 	err = BMediaRoster::CurrentRoster()->StartNode(fOutputNode,
405 		fNode->TimeSource()->Now());
406 
407 	if (err != B_OK)
408 		return err;
409 
410 	// perform the connection
411 	fOutput = ourOutput;
412 	fInput = ourInput;
413 
414 	err = BMediaRoster::CurrentRoster()->Connect(fOutput.source,
415 		fInput.destination, &ourFormat, &fOutput, &fInput,
416 		BMediaRoster::B_CONNECT_MUTED);
417 
418 	if (err != B_OK)
419 		return err;
420 
421 	fConnected = true;
422 	return B_OK;
423 }
424 
425 
426 void BMediaRecorder::_ReservedMediaRecorder0() { }
427 void BMediaRecorder::_ReservedMediaRecorder1() { }
428 void BMediaRecorder::_ReservedMediaRecorder2() { }
429 void BMediaRecorder::_ReservedMediaRecorder3() { }
430 void BMediaRecorder::_ReservedMediaRecorder4() { }
431 void BMediaRecorder::_ReservedMediaRecorder5() { }
432 void BMediaRecorder::_ReservedMediaRecorder6() { }
433 void BMediaRecorder::_ReservedMediaRecorder7() { }
434 
435