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