xref: /haiku/src/apps/cortex/addons/LoggingConsumer/NodeHarnessWin.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
1 // NodeHarnessWin.cpp
2 
3 #include "NodeHarnessWin.h"
4 #include "LoggingConsumer.h"
5 #include <app/Application.h>
6 #include <interface/Button.h>
7 //#include <storage/Entry.h>
8 #include <Entry.h>
9 #include <media/MediaRoster.h>
10 #include <media/MediaAddOn.h>
11 #include <media/TimeSource.h>
12 #include <media/MediaTheme.h>
13 #include <stdio.h>
14 
15 const int32 BUTTON_CONNECT = 'Cnct';
16 const int32 BUTTON_START = 'Strt';
17 const int32 BUTTON_STOP = 'Stop';
18 
19 #define TEST_WITH_AUDIO 1
20 
21 // --------------------
22 // static utility functions
23 static void ErrorCheck(status_t err, const char* msg)
24 {
25 	if (err)
26 	{
27 		fprintf(stderr, "* FATAL ERROR (%s): %s\n", strerror(err), msg);
28 		exit(1);
29 	}
30 }
31 
32 // --------------------
33 // NodeHarnessWin implementation
34 NodeHarnessWin::NodeHarnessWin(BRect frame, const char *title)
35 	:	BWindow(frame, title, B_TITLED_WINDOW, B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS),
36 		mLogNode(NULL), mIsConnected(false), mIsRunning(false)
37 {
38 	// build the UI
39 	BRect r(10, 10, 100, 40);
40 	mConnectButton = new BButton(r, "Connect", "Connect", new BMessage(BUTTON_CONNECT));
41 	mConnectButton->SetEnabled(true);
42 	AddChild(mConnectButton);
43 	r.OffsetBy(0, 40);
44 	mStartButton = new BButton(r, "Start", "Start", new BMessage(BUTTON_START));
45 	mStartButton->SetEnabled(false);
46 	AddChild(mStartButton);
47 	r.OffsetBy(0, 40);
48 	mStopButton = new BButton(r, "Stop", "Stop", new BMessage(BUTTON_STOP));
49 	mStopButton->SetEnabled(false);
50 	AddChild(mStopButton);
51 }
52 
53 NodeHarnessWin::~NodeHarnessWin()
54 {
55 	BMediaRoster* r = BMediaRoster::Roster();
56 
57 	// tear down the node network
58 	if (mIsRunning) StopNodes();
59 	if (mIsConnected)
60 	{
61 		printf("Total late buffers: %ld\n", mLogNode->LateBuffers());
62 		r->StopNode(mConnection.consumer, 0, true);
63 		r->Disconnect(mConnection.producer.node, mConnection.source,
64 			mConnection.consumer.node, mConnection.destination);
65 		r->ReleaseNode(mConnection.producer);
66 		r->ReleaseNode(mConnection.consumer);
67 	}
68 }
69 
70 void
71 NodeHarnessWin::Quit()
72 {
73 	be_app->PostMessage(B_QUIT_REQUESTED);
74 	BWindow::Quit();
75 }
76 
77 void
78 NodeHarnessWin::MessageReceived(BMessage *msg)
79 {
80 	status_t err;
81 
82 	switch (msg->what)
83 	{
84 	case BUTTON_CONNECT:
85 		mIsConnected = true;
86 
87 		// set the button states appropriately
88 		mConnectButton->SetEnabled(false);
89 		mStartButton->SetEnabled(true);
90 
91 		// set up the node network
92 		{
93 			BMediaRoster* r = BMediaRoster::Roster();
94 
95 			// find a node that can handle an audio file
96 #if TEST_WITH_AUDIO
97 			entry_ref inRef;
98 			dormant_node_info info;
99 
100 			::get_ref_for_path("/boot/optional/sound/virtual (void)", &inRef);
101 			err = r->SniffRef(inRef, B_BUFFER_PRODUCER | B_FILE_INTERFACE, &info);
102 			ErrorCheck(err, "couldn't find file reader node\n");
103 
104 			err = r->InstantiateDormantNode(info, &mConnection.producer, B_FLAVOR_IS_LOCAL);
105 			ErrorCheck(err, "couldn't instantiate file reader node\n");
106 
107 			bigtime_t dummy_length;			// output = media length; we don't use it
108 			err = r->SetRefFor(mConnection.producer, inRef, false, &dummy_length);
109 			ErrorCheck(err, "unable to SetRefFor() to read that sound file!\n");
110 #else
111 			r->GetVideoInput(&mConnection.producer);
112 #endif
113 
114 			entry_ref logRef;
115 			::get_ref_for_path("/tmp/node_log", &logRef);
116 
117 			mLogNode = new LoggingConsumer(logRef);
118 			err = r->RegisterNode(mLogNode);
119 			ErrorCheck(err, "unable to register LoggingConsumer node!\n");
120 			// make sure the Media Roster knows that we're using the node
121 			r->GetNodeFor(mLogNode->Node().node, &mConnection.consumer);
122 
123 			// trim down the log's verbosity a touch
124 			mLogNode->SetEnabled(LOG_HANDLE_EVENT, false);
125 
126 			// fire off a window with the LoggingConsumer's controls in it
127 			BParameterWeb* web;
128 			r->GetParameterWebFor(mConnection.consumer, &web);
129 			BView* view = BMediaTheme::ViewFor(web);
130 			BWindow* win = new BWindow(BRect(250, 200, 300, 300), "Controls",
131 				B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS);
132 			win->AddChild(view);
133 			win->ResizeTo(view->Bounds().Width(), view->Bounds().Height());
134 			win->Show();
135 
136 			// set the nodes' time sources
137 			r->GetTimeSource(&mTimeSource);
138 			r->SetTimeSourceFor(mConnection.consumer.node, mTimeSource.node);
139 			r->SetTimeSourceFor(mConnection.producer.node, mTimeSource.node);
140 
141 			// got the nodes; now we find the endpoints of the connection
142 			media_input logInput;
143 			media_output soundOutput;
144 			int32 count;
145 			err = r->GetFreeOutputsFor(mConnection.producer, &soundOutput, 1, &count);
146 			ErrorCheck(err, "unable to get a free output from the producer node");
147 			err = r->GetFreeInputsFor(mConnection.consumer, &logInput, 1, &count);
148 			ErrorCheck(err, "unable to get a free input to the LoggingConsumer");
149 
150 			// fill in the rest of the Connection object
151 			mConnection.source = soundOutput.source;
152 			mConnection.destination = logInput.destination;
153 
154 			// got the endpoints; now we connect it!
155 			media_format format;
156 #if TEST_WITH_AUDIO
157 			format.type = B_MEDIA_RAW_AUDIO;			// !!! hmmm.. how to fully wildcard this?
158 			format.u.raw_audio = media_raw_audio_format::wildcard;
159 #else
160 			format.type = B_MEDIA_RAW_VIDEO;			// !!! hmmm.. how to fully wildcard this?
161 			format.u.raw_video = media_raw_video_format::wildcard;
162 #endif
163 			err = r->Connect(mConnection.source, mConnection.destination, &format, &soundOutput, &logInput);
164 			ErrorCheck(err, "unable to connect nodes");
165 			mConnection.format = format;
166 
167 			// for video input, we need to set the downstream latency for record -> playback
168 			bigtime_t latency;
169 			r->GetLatencyFor(mConnection.producer, &latency);
170 			printf("Setting producer run mode latency to %Ld\n", latency);
171 			r->SetProducerRunModeDelay(mConnection.producer, latency + 6000);
172 
173 			// preroll first, to be a good citizen
174 			r->PrerollNode(mConnection.consumer);
175 			r->PrerollNode(mConnection.producer);
176 
177 			// start the LoggingConsumer and leave it running
178 			BTimeSource* ts = r->MakeTimeSourceFor(mTimeSource);
179 			r->StartNode(mConnection.consumer, ts->Now());
180 			ts->Release();
181 		}
182 		break;
183 
184 	case BUTTON_START:
185 		mStartButton->SetEnabled(false);
186 		mStopButton->SetEnabled(true);
187 
188 		// start the consumer running
189 		{
190 			bigtime_t latency;
191 			BMediaRoster* r = BMediaRoster::Roster();
192 			BTimeSource* ts = r->MakeTimeSourceFor(mConnection.consumer);
193 			r->GetLatencyFor(mConnection.producer, &latency);
194 			r->StartNode(mConnection.producer, ts->Now() + latency);
195 			ts->Release();
196 			mIsRunning = true;
197 		}
198 		break;
199 
200 	case BUTTON_STOP:
201 		StopNodes();
202 		break;
203 
204 	default:
205 		BWindow::MessageReceived(msg);
206 		break;
207 	}
208 }
209 
210 // Private routines
211 void
212 NodeHarnessWin::StopNodes()
213 {
214 	mStartButton->SetEnabled(true);
215 	mStopButton->SetEnabled(false);
216 
217 	// stop the producer
218 	{
219 		BMediaRoster* r = BMediaRoster::Roster();
220 		r->StopNode(mConnection.producer, 0, true);		// synchronous stop
221 	}
222 }
223 
224