1/*
2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify,
8 * merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <stdio.h>
26#include <string.h>
27#include <Window.h>
28#include <TimeSource.h>
29#include <MediaRoster.h>
30#include <BufferGroup.h>
31#include <Buffer.h>
32#include <Bitmap.h>
33#include <Locker.h>
34#include <Debug.h>
35
36#include "VideoNode.h"
37#include "VideoView.h"
38
39void
40overlay_copy(uint32 lines, void *dst, uint32 dst_bpr, const void *src,
41	uint32 src_bpr)
42{
43//	bigtime_t start = system_time();
44	int len = min_c(dst_bpr, src_bpr);
45//	int len4 = len / 4;
46	while (lines--) {
47/*
48		// this does not copy the last few bytes, if length is not aligned
49		// to 4 bytes
50		asm ("rep\n\t""movsl"
51		     :
52		     : "c" (len4), "S" (src), "D" (dst)
53		     : "eax");
54*/
55/*
56		const uint32 *s4 = (const uint32 *)src;
57		uint32 *d4 = (uint32 *)dst;
58		for (int i = 0; i < len4; i++)
59			d4[i] = s4[i];
60*/
61/*
62		const uint8 *s1 = (const uint8 *)s4;
63		uint8 *d1 = (uint8 *)d4;
64		int l1 = len1;
65		while (l1--)
66			*d1++ = *s1++;
67*/
68		memcpy(dst, src, len);
69		src = (char *)src + src_bpr;
70		dst = (char *)dst + dst_bpr;
71	}
72//	printf("overlay copy: %.06f sec\n", (system_time() - start) / 1000000.0);
73}
74
75
76VideoNode::VideoNode(const char *name, VideoView *view)
77 :	BMediaNode(name)
78 ,	BMediaEventLooper()
79 ,	BBufferConsumer(B_MEDIA_RAW_VIDEO)
80 ,	fVideoView(view)
81 ,	fInput()
82 ,	fOverlayEnabled(true)
83 ,	fOverlayActive(false)
84 ,	fDirectOverlayBuffer(false)
85 ,	fBitmap(0)
86 ,	fBitmapLocker(new BLocker("Video Node Locker"))
87{
88}
89
90
91VideoNode::~VideoNode()
92{
93	Quit();
94	DeleteBuffers();
95	delete fBitmapLocker;
96}
97
98
99BMediaAddOn	*
100VideoNode::AddOn(int32 *internal_id) const
101{
102	*internal_id = 0;
103	return NULL;
104}
105
106
107void
108VideoNode::NodeRegistered()
109{
110	fInput.node = Node();
111	fInput.source = media_source::null;
112	fInput.destination.port = ControlPort();
113	fInput.destination.id = 0;
114	fInput.format.type = B_MEDIA_RAW_VIDEO;
115	fInput.format.u.raw_video = media_raw_video_format::wildcard;
116	strcpy(fInput.name, "video in");
117
118	SetPriority(B_DISPLAY_PRIORITY);
119	Run();
120}
121
122
123void
124VideoNode::BufferReceived(BBuffer * buffer)
125{
126	if (RunState() != B_STARTED) {
127		buffer->Recycle();
128		return;
129	}
130	if (fOverlayActive && fDirectOverlayBuffer) {
131		HandleBuffer(buffer);
132	} else {
133		media_timed_event event(buffer->Header()->start_time,
134								BTimedEventQueue::B_HANDLE_BUFFER,
135								buffer,
136								BTimedEventQueue::B_RECYCLE_BUFFER);
137		EventQueue()->AddEvent(event);
138	}
139}
140
141
142status_t
143VideoNode::GetNextInput(int32 *cookie,	media_input *out_input)
144{
145	if (*cookie < 1) {
146		*out_input = fInput;
147		*cookie += 1;
148		return B_OK;
149	}
150	return B_ERROR;
151}
152
153
154void
155VideoNode::DisposeInputCookie(int32 cookie)
156{
157	// nothing to do
158}
159
160
161status_t
162VideoNode::	HandleMessage(int32 message,
163						  const void *data,
164						  size_t size)
165{
166	if (BBufferConsumer::HandleMessage(message, data, size) == B_OK
167		|| BMediaEventLooper::HandleMessage(message, data, size) == B_OK)
168		return B_OK;
169
170	return B_ERROR;
171}
172
173
174void
175VideoNode::HandleEvent(const media_timed_event *event,
176					   bigtime_t lateness,
177					   bool realTimeEvent)
178{
179	switch (event->type) {
180		case BTimedEventQueue::B_START:
181			break;
182		case BTimedEventQueue::B_STOP:
183			EventQueue()->FlushEvents(event->event_time,
184				BTimedEventQueue::B_ALWAYS, true,
185				BTimedEventQueue::B_HANDLE_BUFFER);
186			break;
187		case BTimedEventQueue::B_HANDLE_BUFFER:
188			HandleBuffer((BBuffer *)event->pointer);
189			break;
190		default:
191			printf("VideoNode::HandleEvent unknown event");
192			break;
193	}
194}
195
196
197void
198VideoNode::ProducerDataStatus(const media_destination &dst,
199							 int32 status,
200							 bigtime_t at_media_time)
201{
202	// do nothing
203}
204
205
206status_t
207VideoNode::GetLatencyFor(const media_destination &dst,
208						 bigtime_t *out_latency,
209						 media_node_id *out_id)
210{
211	if (dst != fInput.destination)
212		return B_MEDIA_BAD_DESTINATION;
213
214	*out_latency = 10000;
215	*out_id = TimeSource()->ID();
216	return B_OK;
217}
218
219status_t
220VideoNode::AcceptFormat(const media_destination &dst,
221						media_format *format)
222{
223	/* The connection process:
224	 *                BBufferProducer::FormatProposal
225	 * we are here => BBufferConsumer::AcceptFormat
226	 *                BBufferProducer::PrepareToConnect
227	 *                BBufferConsumer::Connected
228	 *                BBufferProducer::Connect
229	 */
230
231	if (dst != fInput.destination)
232		return B_MEDIA_BAD_DESTINATION;
233
234	if (format->type == B_MEDIA_NO_TYPE)
235		format->type = B_MEDIA_RAW_VIDEO;
236
237	if (format->type != B_MEDIA_RAW_VIDEO)
238		return B_MEDIA_BAD_FORMAT;
239
240
241	return B_OK;
242}
243
244
245status_t
246VideoNode::Connected(const media_source &src,
247					 const media_destination &dst,
248					 const media_format &format,
249					 media_input *out_input)
250{
251	/* The connection process:
252	 *                BBufferProducer::FormatProposal
253	 *                BBufferConsumer::AcceptFormat
254	 *                BBufferProducer::PrepareToConnect
255	 * we are here => BBufferConsumer::Connected
256	 *                BBufferProducer::Connect
257	 */
258
259	if (dst != fInput.destination)
260		return B_MEDIA_BAD_DESTINATION;
261
262	fInput.source = src;
263	fInput.format = format;
264
265	if (fInput.format.u.raw_video.field_rate < 1.0)
266		fInput.format.u.raw_video.field_rate = 25.0;
267
268	color_space colorspace = format.u.raw_video.display.format;
269	BRect		frame(0, 0, format.u.raw_video.display.line_width - 1,
270		format.u.raw_video.display.line_count - 1);
271	status_t	err;
272
273	DeleteBuffers();
274	err = CreateBuffers(frame, colorspace, fOverlayEnabled);
275	if (err) {
276		printf("VideoNode::Connected failed, fOverlayEnabled = %d\n",
277			fOverlayEnabled);
278		return err;
279	}
280
281	*out_input = fInput;
282
283	return B_OK;
284
285}
286
287
288void
289VideoNode::Disconnected(const media_source &src,
290						const media_destination &dst)
291{
292	if (src != fInput.source)
293		return;
294	if (dst != fInput.destination)
295		return;
296
297	DeleteBuffers();
298
299	// disconnect the connection
300	fInput.source = media_source::null;
301}
302
303
304status_t
305VideoNode::FormatChanged(const media_source &src,
306						 const media_destination &dst,
307						 int32 from_change_count,
308						 const media_format &format)
309{
310	printf("VideoNode::FormatChanged enter\n");
311	if (src != fInput.source)
312		return B_MEDIA_BAD_SOURCE;
313	if (dst != fInput.destination)
314		return B_MEDIA_BAD_DESTINATION;
315
316	color_space colorspace = format.u.raw_video.display.format;
317	BRect		frame(0, 0, format.u.raw_video.display.line_width - 1,
318		format.u.raw_video.display.line_count - 1);
319	status_t	err;
320
321	DeleteBuffers();
322	if (fOverlayEnabled) {
323		fVideoView->RemoveOverlay();
324		err = CreateBuffers(frame, colorspace, true); // try overlay
325		if (err) {
326			printf("VideoNode::FormatChanged creating overlay buffer "
327				"failed\n");
328			err = CreateBuffers(frame, colorspace, false); // no overlay
329		}
330	} else {
331		err = CreateBuffers(frame, colorspace, false); // no overlay
332	}
333	if (err) {
334		printf("VideoNode::FormatChanged failed (lost buffer group!)\n");
335		return B_MEDIA_BAD_FORMAT;
336	}
337
338	fInput.format = format;
339
340	printf("VideoNode::FormatChanged leave\n");
341	return B_OK;
342}
343
344
345void
346VideoNode::HandleBuffer(BBuffer *buffer)
347{
348//	printf("VideoNode::HandleBuffer\n");
349
350	LockBitmap();
351	if (fBitmap) {
352//		bigtime_t start = system_time();
353		if (fOverlayActive) {
354			if (B_OK == fBitmap->LockBits()) {
355
356//				memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength());
357
358//				fBitmap->SetBits(buffer->Data(), fBitmap->BitsLength(), 0,
359//					fInput.format.u.raw_video.display.format);
360
361				overlay_copy(fBitmap->Bounds().IntegerHeight() + 1,
362							 fBitmap->Bits(), fBitmap->BytesPerRow(),
363							 buffer->Data(),
364							 fInput.format.u.raw_video.display.bytes_per_row);
365
366
367				fBitmap->UnlockBits();
368			}
369		} else {
370//			memcpy(fBitmap->Bits(), buffer->Data(), fBitmap->BitsLength());
371
372			overlay_copy(fBitmap->Bounds().IntegerHeight() + 1,
373						 fBitmap->Bits(), fBitmap->BytesPerRow(),
374						 buffer->Data(),
375						 fInput.format.u.raw_video.display.bytes_per_row);
376		}
377//		printf("overlay copy: %lld usec\n", system_time() - start);
378	}
379	UnlockBitmap();
380
381	buffer->Recycle();
382
383	fVideoView->DrawFrame();
384}
385
386
387void
388VideoNode::SetOverlayEnabled(bool yesno)
389{
390	fOverlayEnabled = yesno;
391}
392
393
394void
395VideoNode::LockBitmap()
396{
397	fBitmapLocker->Lock();
398}
399
400
401BBitmap *
402VideoNode::Bitmap()
403{
404	return fBitmap;
405}
406
407
408void
409VideoNode::UnlockBitmap()
410{
411	fBitmapLocker->Unlock();
412}
413
414
415bool
416VideoNode::IsOverlayActive()
417{
418	return fOverlayActive;
419}
420
421
422status_t
423VideoNode::CreateBuffers(BRect frame, color_space cspace, bool overlay)
424{
425	printf("VideoNode::CreateBuffers: frame %d,%d,%d,%d colorspace 0x%08x, "
426		"overlay %d\n", int(frame.left), int(frame.top), int(frame.right),
427		int(frame.bottom), int(cspace), overlay);
428
429//	int32 bytesPerRow = B_ANY_BYTES_PER_ROW;
430//	if (cspace == B_YCbCr422)
431//		bytesPerRow = (int(frame.Width()) + 1) * 2;
432
433//	printf("overlay bitmap: requesting: bytes per row: %d\n", bytesPerRow);
434
435	LockBitmap();
436	ASSERT(fBitmap == 0);
437	uint32 flags = overlay ? (B_BITMAP_WILL_OVERLAY
438		| B_BITMAP_RESERVE_OVERLAY_CHANNEL) : 0;
439//	fBitmap = new BBitmap(frame, flags, cspace, bytesPerRow);
440	fBitmap = new BBitmap(frame, flags, cspace);
441	if (!(fBitmap && fBitmap->InitCheck() == B_OK && fBitmap->IsValid())) {
442		delete fBitmap;
443		fBitmap = 0;
444		fOverlayActive = false;
445		UnlockBitmap();
446		printf("VideoNode::CreateBuffers failed\n");
447		return B_ERROR;
448	}
449	printf("overlay bitmap: got: bytes per row: %" B_PRId32 "\n",
450		fBitmap->BytesPerRow());
451	fOverlayActive = overlay;
452	UnlockBitmap();
453	printf("VideoNode::CreateBuffers success\n");
454	return B_OK;
455}
456
457
458void
459VideoNode::DeleteBuffers()
460{
461	LockBitmap();
462	delete fBitmap;
463	fBitmap = NULL;
464	UnlockBitmap();
465}
466