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
BMediaRecorder(const char * name,media_type type)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
~BMediaRecorder()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
InitCheck() const58 BMediaRecorder::InitCheck() const
59 {
60 CALLED();
61
62 return fInitErr;
63 }
64
65
66 void
SetAcceptedFormat(const media_format & format)67 BMediaRecorder::SetAcceptedFormat(const media_format& format)
68 {
69 CALLED();
70
71 fNode->SetAcceptedFormat(format);
72 }
73
74
75 const media_format&
AcceptedFormat() const76 BMediaRecorder::AcceptedFormat() const
77 {
78 CALLED();
79
80 return fNode->AcceptedFormat();
81 }
82
83
84 status_t
SetHooks(ProcessFunc recordFunc,NotifyFunc notifyFunc,void * cookie)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
BufferReceived(void * buffer,size_t size,const media_header & header)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
Connect(const media_format & format)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
Connect(const dormant_node_info & dormantNode,const media_format & format)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
Connect(const media_node & node,const media_output * output,const media_format * format)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
Disconnect()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
Start(bool force)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
Stop(bool force)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
IsRunning() const310 BMediaRecorder::IsRunning() const
311 {
312 CALLED();
313
314 return fRunning;
315 }
316
317
318 bool
IsConnected() const319 BMediaRecorder::IsConnected() const
320 {
321 CALLED();
322
323 return fConnected;
324 }
325
326
327 const media_source&
MediaSource() const328 BMediaRecorder::MediaSource() const
329 {
330 CALLED();
331
332 return fOutputSource;
333 }
334
335
336 const media_input
MediaInput() const337 BMediaRecorder::MediaInput() const
338 {
339 CALLED();
340
341 media_input input;
342 fNode->GetInput(&input);
343 return input;
344 }
345
346
347 const media_format&
Format() const348 BMediaRecorder::Format() const
349 {
350 CALLED();
351
352 return fNode->AcceptedFormat();
353 }
354
355
356 status_t
SetUpConnection(media_source outputSource)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
_Connect(const media_node & node,const media_output * output,const media_format & format)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
_ReservedMediaRecorder0()430 void BMediaRecorder::_ReservedMediaRecorder0() { }
_ReservedMediaRecorder1()431 void BMediaRecorder::_ReservedMediaRecorder1() { }
_ReservedMediaRecorder2()432 void BMediaRecorder::_ReservedMediaRecorder2() { }
_ReservedMediaRecorder3()433 void BMediaRecorder::_ReservedMediaRecorder3() { }
_ReservedMediaRecorder4()434 void BMediaRecorder::_ReservedMediaRecorder4() { }
_ReservedMediaRecorder5()435 void BMediaRecorder::_ReservedMediaRecorder5() { }
_ReservedMediaRecorder6()436 void BMediaRecorder::_ReservedMediaRecorder6() { }
_ReservedMediaRecorder7()437 void BMediaRecorder::_ReservedMediaRecorder7() { }
438
439