1#include <fcntl.h>
2#include <malloc.h>
3#include <math.h>
4#include <stdio.h>
5#include <string.h>
6#include <sys/uio.h>
7#include <unistd.h>
8
9#include <media/Buffer.h>
10#include <media/BufferGroup.h>
11#include <media/ParameterWeb.h>
12#include <media/TimeSource.h>
13
14#include <support/Autolock.h>
15#include <support/Debug.h>
16
17#define TOUCH(x) ((void)(x))
18
19#define PRINTF(a,b) \
20		do { \
21			if (a < 2) { \
22				printf("FinePixProducer::"); \
23				printf b; \
24			} \
25		} while (0)
26
27#include "Producer.h"
28
29#define FIELD_RATE 30.f
30
31#define MAX_FRAME_SIZE 50000 // bytes (jpeg)
32#define FPIX_RGB24_WIDTH 320
33#define FPIX_RGB24_HEIGHT 240
34#define FPIX_RGB24_FRAME_SIZE FPIX_RGB24_WIDTH * FPIX_RGB24_HEIGHT * 3
35
36int32 FinePixProducer::fInstances = 0;
37
38FinePixProducer::FinePixProducer(
39		BMediaAddOn *addon, const char *name, int32 internal_id)
40  :	BMediaNode(name),
41	BMediaEventLooper(),
42	BBufferProducer(B_MEDIA_ENCODED_VIDEO),
43	BControllable()
44{
45	//status_t err;
46
47	fInitStatus = B_NO_INIT;
48
49	/* Only allow one instance of the node to exist at any time */
50	if (atomic_add(&fInstances, 1) != 0)
51		return;
52
53	fInternalID = internal_id;
54	fAddOn = addon;
55
56	fBufferGroup = NULL;
57
58	fThread = -1;
59	fFrameSync = -1;
60	fProcessingLatency = 0LL;
61
62	fRunning = false;
63	fConnected = false;
64	fEnabled = false;
65
66	fOutput.destination = media_destination::null;
67
68	AddNodeKind(B_PHYSICAL_INPUT);
69
70	fDeltaBuffer = NULL; //øyvind
71	fCam = new FinePix();
72
73	fInitStatus = B_OK;
74	return;
75}
76
77FinePixProducer::~FinePixProducer()
78{
79	if (fInitStatus == B_OK) {
80		/* Clean up after ourselves, in case the application didn't make us
81		 * do so. */
82		if (fConnected)
83			Disconnect(fOutput.source, fOutput.destination);
84		if (fRunning)
85			HandleStop();
86	}
87
88	if( fCam ) //øyvind
89	{
90		delete fCam;
91	}
92
93	atomic_add(&fInstances, -1);
94}
95
96/* BMediaNode */
97
98port_id
99FinePixProducer::ControlPort() const
100{
101	return BMediaNode::ControlPort();
102}
103
104BMediaAddOn *
105FinePixProducer::AddOn(int32 *internal_id) const
106{
107	if (internal_id)
108		*internal_id = fInternalID;
109	return fAddOn;
110}
111
112status_t
113FinePixProducer::HandleMessage(int32 message, const void *data, size_t size)
114{
115	return B_ERROR;
116}
117
118void
119FinePixProducer::Preroll()
120{
121	/* This hook may be called before the node is started to give the hardware
122	 * a chance to start. */
123}
124
125void
126FinePixProducer::SetTimeSource(BTimeSource *time_source)
127{
128	/* Tell frame generation thread to recalculate delay value */
129	release_sem(fFrameSync);
130}
131
132status_t
133FinePixProducer::RequestCompleted(const media_request_info &info)
134{
135	return BMediaNode::RequestCompleted(info);
136}
137
138/* BMediaEventLooper */
139
140void
141FinePixProducer::NodeRegistered()
142{
143	if (fInitStatus != B_OK) {
144		ReportError(B_NODE_IN_DISTRESS);
145		return;
146	}
147
148	fOutput.node = Node();
149	fOutput.source.port = ControlPort();
150	fOutput.source.id = 0;
151	fOutput.destination = media_destination::null;
152	strcpy(fOutput.name, Name());
153
154	/* Tailor these for the output of your device */
155	fOutput.format.type = B_MEDIA_RAW_VIDEO;
156	fOutput.format.u.raw_video = media_raw_video_format::wildcard;
157	fOutput.format.u.raw_video.interlace = 1;
158	fOutput.format.u.raw_video.display.format = B_RGB32;
159
160	/* Start the BMediaEventLooper control loop running */
161	Run();
162}
163
164void
165FinePixProducer::Start(bigtime_t performance_time)
166{
167	BMediaEventLooper::Start(performance_time);
168}
169
170void
171FinePixProducer::Stop(bigtime_t performance_time, bool immediate)
172{
173	BMediaEventLooper::Stop(performance_time, immediate);
174}
175
176void
177FinePixProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
178{
179	BMediaEventLooper::Seek(media_time, performance_time);
180}
181
182void
183FinePixProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
184{
185	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
186}
187
188status_t
189FinePixProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
190{
191	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
192}
193
194void
195FinePixProducer::SetRunMode(run_mode mode)
196{
197	BMediaEventLooper::SetRunMode(mode);
198}
199
200void
201FinePixProducer::HandleEvent(const media_timed_event *event,
202		bigtime_t lateness, bool realTimeEvent)
203{
204	TOUCH(lateness); TOUCH(realTimeEvent);
205
206	switch(event->type)
207	{
208		case BTimedEventQueue::B_START:
209			HandleStart(event->event_time);
210			break;
211		case BTimedEventQueue::B_STOP:
212			HandleStop();
213			break;
214		case BTimedEventQueue::B_WARP:
215			HandleTimeWarp(event->bigdata);
216			break;
217		case BTimedEventQueue::B_SEEK:
218			HandleSeek(event->bigdata);
219			break;
220		case BTimedEventQueue::B_HANDLE_BUFFER:
221		case BTimedEventQueue::B_DATA_STATUS:
222		case BTimedEventQueue::B_PARAMETER:
223		default:
224			PRINTF(-1, ("HandleEvent: Unhandled event -- %lx\n", event->type));
225			break;
226	}
227}
228
229void
230FinePixProducer::CleanUpEvent(const media_timed_event *event)
231{
232	BMediaEventLooper::CleanUpEvent(event);
233}
234
235bigtime_t
236FinePixProducer::OfflineTime()
237{
238	return BMediaEventLooper::OfflineTime();
239}
240
241void
242FinePixProducer::ControlLoop()
243{
244	BMediaEventLooper::ControlLoop();
245}
246
247status_t
248FinePixProducer::DeleteHook(BMediaNode * node)
249{
250	return BMediaEventLooper::DeleteHook(node);
251}
252
253/* BBufferProducer */
254
255status_t
256FinePixProducer::FormatSuggestionRequested(
257		media_type type, int32 quality, media_format *format)
258{
259	if (type != B_MEDIA_ENCODED_VIDEO)
260		return B_MEDIA_BAD_FORMAT;
261
262	TOUCH(quality);
263
264	*format = fOutput.format;
265	return B_OK;
266}
267
268status_t
269FinePixProducer::FormatProposal(const media_source &output, media_format *format)
270{
271	status_t err;
272
273	if (!format)
274		return B_BAD_VALUE;
275
276	if (output != fOutput.source)
277		return B_MEDIA_BAD_SOURCE;
278
279	err = format_is_compatible(*format, fOutput.format) ?
280			B_OK : B_MEDIA_BAD_FORMAT;
281	*format = fOutput.format;
282	return err;
283
284}
285
286status_t
287FinePixProducer::FormatChangeRequested(const media_source &source,
288		const media_destination &destination, media_format *io_format,
289		int32 *_deprecated_)
290{
291	TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_);
292	if (source != fOutput.source)
293		return B_MEDIA_BAD_SOURCE;
294
295	return B_ERROR;
296}
297
298status_t
299FinePixProducer::GetNextOutput(int32 *cookie, media_output *out_output)
300{
301	if (!out_output)
302		return B_BAD_VALUE;
303
304	if ((*cookie) != 0)
305		return B_BAD_INDEX;
306
307	*out_output = fOutput;
308	(*cookie)++;
309	return B_OK;
310}
311
312status_t
313FinePixProducer::DisposeOutputCookie(int32 cookie)
314{
315	TOUCH(cookie);
316
317	return B_OK;
318}
319
320status_t
321FinePixProducer::SetBufferGroup(const media_source &for_source,
322		BBufferGroup *group)
323{
324	TOUCH(for_source); TOUCH(group);
325
326	return B_ERROR;
327}
328
329status_t
330FinePixProducer::VideoClippingChanged(const media_source &for_source,
331		int16 num_shorts, int16 *clip_data,
332		const media_video_display_info &display, int32 *_deprecated_)
333{
334	TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data);
335	TOUCH(display); TOUCH(_deprecated_);
336
337	return B_ERROR;
338}
339
340status_t
341FinePixProducer::GetLatency(bigtime_t *out_latency)
342{
343	*out_latency = EventLatency() + SchedulingLatency();
344	return B_OK;
345}
346
347status_t
348FinePixProducer::PrepareToConnect(const media_source &source,
349		const media_destination &destination, media_format *format,
350		media_source *out_source, char *out_name)
351{
352	//status_t err;
353
354	PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \
355			format->u.raw_video.display.line_width, \
356			format->u.raw_video.display.line_count));
357
358	if (fConnected) {
359		PRINTF(0, ("PrepareToConnect: Already connected\n"));
360		return EALREADY;
361	}
362
363	if (source != fOutput.source)
364		return B_MEDIA_BAD_SOURCE;
365
366	if (fOutput.destination != media_destination::null)
367		return B_MEDIA_ALREADY_CONNECTED;
368
369	/* The format parameter comes in with the suggested format, and may be
370	 * specialized as desired by the node */
371	if (!format_is_compatible(*format, fOutput.format)) {
372		*format = fOutput.format;
373		return B_MEDIA_BAD_FORMAT;
374	}
375
376	if (format->u.raw_video.display.line_width == 0)
377		format->u.raw_video.display.line_width = 320;
378	if (format->u.raw_video.display.line_count == 0)
379		format->u.raw_video.display.line_count = 240;
380	if (format->u.raw_video.field_rate == 0)
381		format->u.raw_video.field_rate = 29.97f;
382
383	*out_source = fOutput.source;
384	strcpy(out_name, fOutput.name);
385
386	fOutput.destination = destination;
387
388	return B_OK;
389}
390
391void
392FinePixProducer::Connect(status_t error, const media_source &source,
393		const media_destination &destination, const media_format &format,
394		char *io_name)
395{
396	PRINTF(1, ("Connect() %ldx%ld\n", \
397			format.u.raw_video.display.line_width, \
398			format.u.raw_video.display.line_count));
399
400	if (fConnected) {
401		PRINTF(0, ("Connect: Already connected\n"));
402		return;
403	}
404
405	if (	(source != fOutput.source) || (error < B_OK) ||
406			!const_cast<media_format *>(&format)->Matches(&fOutput.format)) {
407		PRINTF(1, ("Connect: Connect error\n"));
408		return;
409	}
410
411	fOutput.destination = destination;
412	strcpy(io_name, fOutput.name);
413
414	if (fOutput.format.u.raw_video.field_rate != 0.0f) {
415		fPerformanceTimeBase = fPerformanceTimeBase +
416				(bigtime_t)
417					((fFrame - fFrameBase) *
418					(1000000 / fOutput.format.u.raw_video.field_rate));
419		fFrameBase = fFrame;
420	}
421
422	fConnectedFormat = format.u.raw_video;
423
424	fDeltaBuffer = new uint8[MAX_FRAME_SIZE]; //ø in buffer
425	tempInBuffer = new uint8[3 * fConnectedFormat.display.line_width *
426			fConnectedFormat.display.line_count]; // for 24 bit color
427	fCam->SetupCam(); //øyvind
428
429	/* get the latency */
430	bigtime_t latency = 0;
431	media_node_id tsID = 0;
432	FindLatencyFor(fOutput.destination, &latency, &tsID);
433	#define NODE_LATENCY 1000
434	SetEventLatency(latency + NODE_LATENCY);
435
436	uint8 *tmp24 = (uint8*)tempInBuffer;
437	uint8 *buffer, *dst;
438	dst = buffer = (uint8 *)malloc(4 * fConnectedFormat.display.line_count *
439			fConnectedFormat.display.line_width);
440	if (!buffer) {
441		PRINTF(0, ("Connect: Out of memory\n"));
442		return;
443	}
444	bigtime_t now = system_time();
445
446	// Get a frame from the camera
447	fCam->GetPic(fDeltaBuffer, frame_size);
448
449	// Convert from jpeg to bitmap
450	if (jpeg_check_size(fDeltaBuffer,
451		    FPIX_RGB24_WIDTH, FPIX_RGB24_HEIGHT))
452	{
453		int n = jpeg_decode(fDeltaBuffer, tmp24,
454			FPIX_RGB24_WIDTH, FPIX_RGB24_HEIGHT, 24, //32 not working
455			&decdata);
456		if (n)
457		{
458			PRINTF(-1, ("ooeps decode jpg result : %d", n));
459		}
460	} else
461	{
462		PRINTF(-1, ("ooeps check_size failed"));
463	}
464
465	// Convert from 24 bit to 32 bit
466	for (uint y=0; y<fConnectedFormat.display.line_count; y++)
467		for (uint x=0; x<fConnectedFormat.display.line_width; x++) {
468			*(dst++) = *tmp24; //red
469			tmp24++;
470			*(dst++) = *tmp24; //green
471			tmp24++;
472			*(dst++) = *tmp24; //blue
473			tmp24++;
474			dst++; //last 8 bit empty
475		}
476
477	fProcessingLatency = system_time() - now;
478	free(buffer);
479
480	/* Create the buffer group */
481	fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width *
482			fConnectedFormat.display.line_count, 8);
483	if (fBufferGroup->InitCheck() < B_OK) {
484		delete fBufferGroup;
485		fBufferGroup = NULL;
486		return;
487	}
488
489	fConnected = true;
490	fEnabled = true;
491
492	/* Tell frame generation thread to recalculate delay value */
493	release_sem(fFrameSync);
494}
495
496void
497FinePixProducer::Disconnect(const media_source &source,
498		const media_destination &destination)
499{
500	PRINTF(1, ("Disconnect()\n"));
501
502	if (!fConnected) {
503		PRINTF(0, ("Disconnect: Not connected\n"));
504		return;
505	}
506
507	if ((source != fOutput.source) || (destination != fOutput.destination)) {
508		PRINTF(0, ("Disconnect: Bad source and/or destination\n"));
509		return;
510	}
511
512	fEnabled = false;
513	fOutput.destination = media_destination::null;
514
515	fLock.Lock();
516		delete fBufferGroup;
517		fBufferGroup = NULL;
518		delete fDeltaBuffer; //ø
519		fDeltaBuffer = NULL; //ø
520		delete tempInBuffer; //Ø
521		tempInBuffer = NULL; //Ø
522	fLock.Unlock();
523
524	fConnected = false;
525}
526
527void
528FinePixProducer::LateNoticeReceived(const media_source &source,
529		bigtime_t how_much, bigtime_t performance_time)
530{
531	TOUCH(source); TOUCH(how_much); TOUCH(performance_time);
532}
533
534void
535FinePixProducer::EnableOutput(const media_source &source, bool enabled,
536		int32 *_deprecated_)
537{
538	TOUCH(_deprecated_);
539
540	if (source != fOutput.source)
541		return;
542
543	fEnabled = enabled;
544}
545
546status_t
547FinePixProducer::SetPlayRate(int32 numer, int32 denom)
548{
549	TOUCH(numer); TOUCH(denom);
550
551	return B_ERROR;
552}
553
554void
555FinePixProducer::AdditionalBufferRequested(const media_source &source,
556		media_buffer_id prev_buffer, bigtime_t prev_time,
557		const media_seek_tag *prev_tag)
558{
559	TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag);
560}
561
562void
563FinePixProducer::LatencyChanged(const media_source &source,
564		const media_destination &destination, bigtime_t new_latency,
565		uint32 flags)
566{
567	TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags);
568}
569
570/* BControllable */
571
572status_t
573FinePixProducer::GetParameterValue(
574	int32 id, bigtime_t *last_change, void *value, size_t *size)
575{
576	return B_OK;
577}
578
579void
580FinePixProducer::SetParameterValue(
581	int32 id, bigtime_t when, const void *value, size_t size)
582{
583}
584
585status_t
586FinePixProducer::StartControlPanel(BMessenger *out_messenger)
587{
588	return BControllable::StartControlPanel(out_messenger);
589}
590
591/* FinePixProducer */
592
593void
594FinePixProducer::HandleStart(bigtime_t performance_time)
595{
596	/* Start producing frames, even if the output hasn't been connected yet. */
597
598	PRINTF(1, ("HandleStart(%Ld)\n", performance_time));
599
600	if (fRunning) {
601		PRINTF(-1, ("HandleStart: Node already started\n"));
602		return;
603	}
604
605	fFrame = 0;
606	fFrameBase = 0;
607	fPerformanceTimeBase = performance_time;
608
609	fFrameSync = create_sem(0, "frame synchronization");
610	if (fFrameSync < B_OK)
611		goto err1;
612
613	fThread = spawn_thread(_frame_generator_, "frame generator",
614			B_NORMAL_PRIORITY, this);
615	if (fThread < B_OK)
616		goto err2;
617
618	resume_thread(fThread);
619
620	fRunning = true;
621	return;
622
623err2:
624	delete_sem(fFrameSync);
625err1:
626	return;
627}
628
629void
630FinePixProducer::HandleStop(void)
631{
632	PRINTF(1, ("HandleStop()\n"));
633
634	if (!fRunning) {
635		PRINTF(-1, ("HandleStop: Node isn't running\n"));
636		return;
637	}
638
639	delete_sem(fFrameSync);
640	wait_for_thread(fThread, &fThread);
641
642	fRunning = false;
643}
644
645void
646FinePixProducer::HandleTimeWarp(bigtime_t performance_time)
647{
648	fPerformanceTimeBase = performance_time;
649	fFrameBase = fFrame;
650
651	/* Tell frame generation thread to recalculate delay value */
652	release_sem(fFrameSync);
653}
654
655void
656FinePixProducer::HandleSeek(bigtime_t performance_time)
657{
658	fPerformanceTimeBase = performance_time;
659	fFrameBase = fFrame;
660
661	/* Tell frame generation thread to recalculate delay value */
662	release_sem(fFrameSync);
663}
664
665/* The following functions form the thread that generates frames. You should
666 * replace this with the code that interfaces to your hardware. */
667int32
668FinePixProducer::FrameGenerator()
669{
670	bigtime_t wait_until = system_time();
671
672	while (1) {
673		status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT,
674				wait_until);
675
676		/* The only acceptable responses are B_OK and B_TIMED_OUT. Everything
677		 * else means the thread should quit. Deleting the semaphore, as in
678		 * FinePixProducer::HandleStop(), will trigger this behavior. */
679		if ((err != B_OK) && (err != B_TIMED_OUT))
680			break;
681
682		fFrame++;
683
684		/* Recalculate the time until the thread should wake up to begin
685		 * processing the next frame. Subtract fProcessingLatency so that
686		 * the frame is sent in time. */
687		wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase, 0) +
688				(bigtime_t)
689						((fFrame - fFrameBase) *
690						(1000000 / fConnectedFormat.field_rate)) -
691				fProcessingLatency;
692
693		/* Drop frame if it's at least a frame late */
694		if (wait_until < system_time())
695			continue;
696
697		/* If the semaphore was acquired successfully, it means something
698		 * changed the timing information (see FinePixProducer::Connect()) and
699		 * so the thread should go back to sleep until the newly-calculated
700		 * wait_until time. */
701		if (err == B_OK)
702			continue;
703
704		/* Send buffers only if the node is running and the output has been
705		 * enabled */
706		if (!fRunning || !fEnabled)
707			continue;
708
709		BAutolock _(fLock);
710
711		// Get the frame from the camera
712		fCam->GetPic(fDeltaBuffer, frame_size);
713
714		/* Fetch a buffer from the buffer group */
715		BBuffer *buffer = fBufferGroup->RequestBuffer(
716						4 * fConnectedFormat.display.line_width *
717						fConnectedFormat.display.line_count, 0LL);
718		if (!buffer)
719			continue;
720
721		/* Fill out the details about this buffer. */
722		media_header *h = buffer->Header();
723		h->type = B_MEDIA_RAW_VIDEO;
724		h->time_source = TimeSource()->ID();
725		h->size_used = 4 * fConnectedFormat.display.line_width *
726						fConnectedFormat.display.line_count;
727		/* For a buffer originating from a device, you might want to calculate
728		 * this based on the PerformanceTimeFor the time your buffer arrived at
729		 * the hardware (plus any applicable adjustments).
730		h->start_time = fPerformanceTimeBase +
731						(bigtime_t)
732							((fFrame - fFrameBase) *
733							(1000000 / fConnectedFormat.field_rate));*/
734		h->start_time = TimeSource()->Now();
735		h->file_pos = 0;
736		h->orig_size = 0;
737		h->data_offset = 0;
738		h->u.raw_video.field_gamma = 1.0;
739		h->u.raw_video.field_sequence = fFrame;
740		h->u.raw_video.field_number = 0;
741		h->u.raw_video.pulldown_number = 0;
742		h->u.raw_video.first_active_line = 1;
743		h->u.raw_video.line_count = fConnectedFormat.display.line_count;
744
745		// Frame data pointers
746		uint8 *tmp24 = (uint8*)tempInBuffer;
747		uint8 *dst = (uint8*)buffer->Data();
748
749		// Convert from jpeg to bitmap
750		if (jpeg_check_size(fDeltaBuffer,
751			    FPIX_RGB24_WIDTH, FPIX_RGB24_HEIGHT))
752		{
753			int n = jpeg_decode(fDeltaBuffer, tmp24,
754				FPIX_RGB24_WIDTH, FPIX_RGB24_HEIGHT, 24, //32 not working
755				&decdata);
756			if (n)
757			{
758				PRINTF(-1, ("ooeps decode jpg result : %d", n));
759			}
760		} else
761		{
762			PRINTF(-1, ("ooeps check_size failed"));
763		}
764
765		// Convert from 24 bit to 32 bit
766		for (uint y=0; y<fConnectedFormat.display.line_count; y++)
767			for (uint x=0; x<fConnectedFormat.display.line_width; x++) {
768				*(dst++) = *tmp24; //red
769				tmp24++;
770				*(dst++) = *tmp24; //green
771				tmp24++;
772				*(dst++) = *tmp24; //blue
773				tmp24++;
774				dst++; //last 8 bit empty
775			}
776
777		/* Send the buffer on down to the consumer */
778		if (SendBuffer(buffer, fOutput.destination) < B_OK) {
779			PRINTF(-1, ("FrameGenerator: Error sending buffer\n"));
780			/* If there is a problem sending the buffer, return it to its
781			 * buffer group. */
782			buffer->Recycle();
783		}
784	}
785
786	return B_OK;
787}
788
789int32
790FinePixProducer::_frame_generator_(void *data)
791{
792	return ((FinePixProducer *)data)->FrameGenerator();
793}
794