xref: /haiku/src/add-ons/media/media-add-ons/finepix_webcam/FinePixProducer/Producer.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 
36 int32 FinePixProducer::fInstances = 0;
37 
38 FinePixProducer::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 
77 FinePixProducer::~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 
98 port_id
99 FinePixProducer::ControlPort() const
100 {
101 	return BMediaNode::ControlPort();
102 }
103 
104 BMediaAddOn *
105 FinePixProducer::AddOn(int32 *internal_id) const
106 {
107 	if (internal_id)
108 		*internal_id = fInternalID;
109 	return fAddOn;
110 }
111 
112 status_t
113 FinePixProducer::HandleMessage(int32 message, const void *data, size_t size)
114 {
115 	return B_ERROR;
116 }
117 
118 void
119 FinePixProducer::Preroll()
120 {
121 	/* This hook may be called before the node is started to give the hardware
122 	 * a chance to start. */
123 }
124 
125 void
126 FinePixProducer::SetTimeSource(BTimeSource *time_source)
127 {
128 	/* Tell frame generation thread to recalculate delay value */
129 	release_sem(fFrameSync);
130 }
131 
132 status_t
133 FinePixProducer::RequestCompleted(const media_request_info &info)
134 {
135 	return BMediaNode::RequestCompleted(info);
136 }
137 
138 /* BMediaEventLooper */
139 
140 void
141 FinePixProducer::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 
164 void
165 FinePixProducer::Start(bigtime_t performance_time)
166 {
167 	BMediaEventLooper::Start(performance_time);
168 }
169 
170 void
171 FinePixProducer::Stop(bigtime_t performance_time, bool immediate)
172 {
173 	BMediaEventLooper::Stop(performance_time, immediate);
174 }
175 
176 void
177 FinePixProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
178 {
179 	BMediaEventLooper::Seek(media_time, performance_time);
180 }
181 
182 void
183 FinePixProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
184 {
185 	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
186 }
187 
188 status_t
189 FinePixProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
190 {
191 	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
192 }
193 
194 void
195 FinePixProducer::SetRunMode(run_mode mode)
196 {
197 	BMediaEventLooper::SetRunMode(mode);
198 }
199 
200 void
201 FinePixProducer::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 
229 void
230 FinePixProducer::CleanUpEvent(const media_timed_event *event)
231 {
232 	BMediaEventLooper::CleanUpEvent(event);
233 }
234 
235 bigtime_t
236 FinePixProducer::OfflineTime()
237 {
238 	return BMediaEventLooper::OfflineTime();
239 }
240 
241 void
242 FinePixProducer::ControlLoop()
243 {
244 	BMediaEventLooper::ControlLoop();
245 }
246 
247 status_t
248 FinePixProducer::DeleteHook(BMediaNode * node)
249 {
250 	return BMediaEventLooper::DeleteHook(node);
251 }
252 
253 /* BBufferProducer */
254 
255 status_t
256 FinePixProducer::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 
268 status_t
269 FinePixProducer::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 
286 status_t
287 FinePixProducer::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 
298 status_t
299 FinePixProducer::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 
312 status_t
313 FinePixProducer::DisposeOutputCookie(int32 cookie)
314 {
315 	TOUCH(cookie);
316 
317 	return B_OK;
318 }
319 
320 status_t
321 FinePixProducer::SetBufferGroup(const media_source &for_source,
322 		BBufferGroup *group)
323 {
324 	TOUCH(for_source); TOUCH(group);
325 
326 	return B_ERROR;
327 }
328 
329 status_t
330 FinePixProducer::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 
340 status_t
341 FinePixProducer::GetLatency(bigtime_t *out_latency)
342 {
343 	*out_latency = EventLatency() + SchedulingLatency();
344 	return B_OK;
345 }
346 
347 status_t
348 FinePixProducer::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 
391 void
392 FinePixProducer::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 
496 void
497 FinePixProducer::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 
527 void
528 FinePixProducer::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 
534 void
535 FinePixProducer::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 
546 status_t
547 FinePixProducer::SetPlayRate(int32 numer, int32 denom)
548 {
549 	TOUCH(numer); TOUCH(denom);
550 
551 	return B_ERROR;
552 }
553 
554 void
555 FinePixProducer::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 
562 void
563 FinePixProducer::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 
572 status_t
573 FinePixProducer::GetParameterValue(
574 	int32 id, bigtime_t *last_change, void *value, size_t *size)
575 {
576 	return B_OK;
577 }
578 
579 void
580 FinePixProducer::SetParameterValue(
581 	int32 id, bigtime_t when, const void *value, size_t size)
582 {
583 }
584 
585 status_t
586 FinePixProducer::StartControlPanel(BMessenger *out_messenger)
587 {
588 	return BControllable::StartControlPanel(out_messenger);
589 }
590 
591 /* FinePixProducer */
592 
593 void
594 FinePixProducer::HandleStart(bigtime_t performance_time)
595 {
596 	/* Start producing frames, even if the output hasn't been connected yet. */
597 
598 	PRINTF(1, ("HandleStart(%lld)\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 
623 err2:
624 	delete_sem(fFrameSync);
625 err1:
626 	return;
627 }
628 
629 void
630 FinePixProducer::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 
645 void
646 FinePixProducer::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 
655 void
656 FinePixProducer::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. */
667 int32
668 FinePixProducer::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 
789 int32
790 FinePixProducer::_frame_generator_(void *data)
791 {
792 	return ((FinePixProducer *)data)->FrameGenerator();
793 }
794