1/*
2 * XvidDecoder.cpp - XviD plugin for the Haiku Operating System
3 *
4 * Copyright (C) 2007 Stephan A��mus <superstippi@gmx.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 *
19 */
20#define DEBUG 0
21#define PROPER_SEEKING 0
22#define PRINT_FOURCC 0
23
24#define MIN_USEFUL_BYTES 1
25
26#include "XvidDecoder.h"
27
28//#include <fenv.h>
29#include <new>
30#include <setjmp.h>
31#include <signal.h>
32#include <string.h>
33
34#include <ByteOrder.h>
35#include <Debug.h>
36#include <MediaTrack.h>
37#include <OS.h>
38
39#include <MediaDefs.h>
40#include <File.h>
41#include <Bitmap.h>
42
43#include "supported_codecs.h"
44
45
46using std::nothrow;
47
48
49#if DEBUG
50static const char*
51media_type_name(int type)
52{
53	switch (type) {
54		case B_MEDIA_NO_TYPE:
55			return "B_MEDIA_NO_TYPE";
56		case B_MEDIA_RAW_AUDIO:
57			return "B_MEDIA_RAW_AUDIO";
58		case B_MEDIA_RAW_VIDEO:
59			return "B_MEDIA_RAW_VIDEO";
60		case B_MEDIA_VBL:
61			return "B_MEDIA_VBL";
62		case B_MEDIA_TIMECODE:
63			return "B_MEDIA_TIMECODE";
64		case B_MEDIA_MIDI:
65			return "B_MEDIA_MIDI";
66		case B_MEDIA_TEXT:
67			return "B_MEDIA_TEXT";
68		case B_MEDIA_HTML:
69			return "B_MEDIA_HTML";
70		case B_MEDIA_MULTISTREAM:
71			return "B_MEDIA_MULTISTREAM";
72		case B_MEDIA_PARAMETERS:
73			return "B_MEDIA_PARAMETERS";
74		case B_MEDIA_ENCODED_AUDIO:
75			return "B_MEDIA_ENCODED_AUDIO";
76		case B_MEDIA_ENCODED_VIDEO:
77			return "B_MEDIA_ENCODED_VIDEO";
78
79		case B_MEDIA_UNKNOWN_TYPE:
80		default:
81			return "B_MEDIA_UNKNOWN_TYPE";
82	}
83}
84
85static char
86make_printable_char(uchar c)
87{
88	if (c >= 0x20 && c < 0x7F)
89		return c;
90	return '.';
91}
92
93static void
94print_hex(unsigned char* buff, int len)
95{
96	int i, j;
97	for(i=0; i<len+7;i+=8) {
98		for (j=0; j<8; j++) {
99			if (i+j < len)
100				printf("%02X", buff[i+j]);
101			else
102				printf("  ");
103			if (j==3)
104				printf(" ");
105		}
106		printf("\t");
107		for (j=0; j<8; j++) {
108			if (i+j < len)
109				printf("%c", make_printable_char(buff[i+j]));
110			else
111				printf(" ");
112		}
113		printf("\n");
114	}
115}
116
117static void
118print_media_header(media_header* mh)
119{
120	printf("media_header {%s, size_used: %ld, start_time: %lld (%02d:%02d.%02d), "
121		"field_sequence=%lu, user_data_type: .4s, file_pos: %ld, orig_size: %ld, "
122		"data_offset: %ld}\n",
123		media_type_name(mh->type), mh->size_used, mh->start_time,
124		int((mh->start_time / 60000000) % 60),
125		int((mh->start_time / 1000000) % 60),
126		int((mh->start_time / 10000) % 100),
127		(long)mh->u.raw_video.field_sequence,
128		//&(mh->user_data_type),
129		(long)mh->file_pos,
130		(long)mh->orig_size,
131		mh->data_offset);
132}
133
134static void
135print_media_decode_info(media_decode_info *info)
136{
137	if (info) {
138		printf("media_decode_info {time_to_decode: %lld, "
139			"file_format_data_size: %ld, codec_data_size: %ld}\n",
140			info->time_to_decode, info->file_format_data_size,
141			info->codec_data_size);
142	} else
143		printf("media_decode_info (null)\n");
144}
145#endif // DEBUG
146
147
148// #pragma mark -
149
150
151XvidDecoder::XvidDecoder()
152	: Decoder()
153	, fInputFormat()
154	, fOutputVideoFormat()
155
156	, fXvidDecoderHandle(NULL)
157	, fXvidColorspace(0)
158
159	, fFrame(0)
160	, fIndexInCodecTable(-1)
161
162	, fChunkBuffer(NULL)
163	, fWrappedChunkBuffer(NULL)
164	, fChunkBufferHandle(NULL)
165	, fChunkBufferSize(0)
166	, fLeftInChunkBuffer(0)
167	, fDiscontinuity(false)
168{
169}
170
171
172XvidDecoder::~XvidDecoder()
173{
174	_XvidUninit();
175	delete[] fWrappedChunkBuffer;
176}
177
178
179void
180XvidDecoder::GetCodecInfo(media_codec_info* mci)
181{
182	PRINT(("XvidDecoder::GetCodecInfo()\n"));
183
184	if (mci == NULL)
185		return;// B_BAD_VALUE;
186
187	if (fIndexInCodecTable < 0)
188		return;// B_NO_INIT;
189
190	sprintf(mci->short_name, "xvid");
191	sprintf(mci->pretty_name, "xvid - %s",
192		gCodecTable[fIndexInCodecTable].prettyName);
193
194	mci->id = 0;
195	mci->sub_id = 0;
196
197	return;// B_OK;
198}
199
200
201status_t
202XvidDecoder::Setup(media_format* inputFormat, const void* inInfo,
203	size_t inSize)
204{
205	if (inputFormat == NULL)
206		return B_BAD_VALUE;
207
208	if (inputFormat->type != B_MEDIA_ENCODED_VIDEO)
209		return B_BAD_VALUE;
210
211//	PRINT(("%p->XvidDecoder::Setup()\n", this));
212
213//#if DEBUG
214//	char buffer[1024];
215//	string_for_format(*inputFormat, buffer, sizeof(buffer));
216//	PRINT(("   inputFormat=%s\n", buffer));
217//	PRINT(("   inSize=%d\n", inSize));
218//	print_hex((uchar*)inInfo, inSize);
219//	PRINT(("   user_data_type=%08lx\n", (int)inputFormat->user_data_type));
220//	print_hex((uchar*)inputFormat->user_data, 48);
221//#endif
222
223	uint32 codecID = 0;
224	media_format_family familyID = B_ANY_FORMAT_FAMILY;
225
226	// hacky... get the exact 4CC from there if it's in, to help xvid
227	// handle broken files
228//	if ((inputFormat->user_data_type == B_CODEC_TYPE_INFO)
229//	 && !memcmp(inputFormat->user_data, "AVI ", 4)) {
230//		codecID = ((uint32*)inputFormat->user_data)[1];
231//		familyID = B_AVI_FORMAT_FAMILY;
232//		PRINT(("XvidDecoder::Setup() - AVI 4CC: %4s\n",
233//			inputFormat->user_data + 4));
234//	}
235
236	if (codecID == 0) {
237		BMediaFormats formats;
238		media_format_description descr;
239		if (formats.GetCodeFor(*inputFormat, B_QUICKTIME_FORMAT_FAMILY,
240				&descr) == B_OK) {
241			codecID = descr.u.quicktime.codec;
242			familyID = B_QUICKTIME_FORMAT_FAMILY;
243
244			#if PRINT_FOURCC
245			uint32 bigEndianID = B_HOST_TO_BENDIAN_INT32(codecID);
246			printf("%p->XvidDecoder::Setup() - QT 4CC: %.4s\n", this,
247				(const char*)&bigEndianID);
248			#endif
249		} else if (formats.GetCodeFor(*inputFormat, B_AVI_FORMAT_FAMILY,
250				&descr) == B_OK) {
251			codecID = descr.u.avi.codec;
252			familyID = B_AVI_FORMAT_FAMILY;
253
254			#if PRINT_FOURCC
255			uint32 bigEndianID = B_HOST_TO_BENDIAN_INT32(codecID);
256			printf("%p->XvidDecoder::Setup() - AVI 4CC: %.4s\n", this,
257				(const char*)&bigEndianID);
258			#endif
259		} else if (formats.GetCodeFor(*inputFormat, B_MPEG_FORMAT_FAMILY,
260				&descr) == B_OK) {
261			codecID = descr.u.mpeg.id;
262			familyID = B_MPEG_FORMAT_FAMILY;
263
264			#if PRINT_FOURCC
265			printf("%p->XvidDecoder::Setup() - MPEG ID: %ld\n", this, codecID);
266			#endif
267		}
268	}
269
270	if (codecID == 0)
271		return B_ERROR;
272
273	for (int32 i = 0; i < gSupportedCodecsCount; i++) {
274		if (gCodecTable[i].family == familyID
275			&& gCodecTable[i].fourcc == codecID) {
276			PRINT(("%p->XvidDecoder::Setup() - found codec in the table "
277				"at %ld.\n", this, i));
278			fIndexInCodecTable = i;
279			fInputFormat = *inputFormat;
280			return B_OK;
281		}
282	}
283
284	#if PRINT_FOURCC
285	printf("%p->XvidDecoder::Setup() - no matching codec found in the "
286		"table.\n", this);
287	#endif
288
289	return B_ERROR;
290}
291
292
293status_t
294XvidDecoder::NegotiateOutputFormat(media_format* _inoutFormat)
295{
296	PRINT(("%p->XvidDecoder::NegotiateOutputFormat()\n", this));
297
298	if (_inoutFormat == NULL)
299		return B_BAD_VALUE;
300
301#if DEBUG
302	char buffer[1024];
303	buffer[0] = 0;
304	if (string_for_format(*_inoutFormat, buffer, sizeof(buffer)) == B_OK)
305		PRINT(("  _inoutFormat = %s\n", buffer));
306#endif
307
308	// init our own output format from the sniffed values
309	fOutputVideoFormat = fInputFormat.u.encoded_video.output;
310
311	// suggest a default colorspace
312	color_space askedFormat = _inoutFormat->u.raw_video.display.format;
313	color_space supportedFormat;
314	uint32 bpr;
315	switch (askedFormat) {
316		case B_NO_COLOR_SPACE:
317			// suggest preferred format
318		case B_YCbCr422:
319			supportedFormat = B_YCbCr422;
320			fXvidColorspace = XVID_CSP_YUY2;
321			bpr = fOutputVideoFormat.display.line_width * 2;
322			break;
323		case B_YCbCr420:
324			supportedFormat = askedFormat;
325			fXvidColorspace = XVID_CSP_I420;
326			bpr = fOutputVideoFormat.display.line_width
327				+ (fOutputVideoFormat.display.line_width + 1) / 2;
328			break;
329		case B_RGB32_BIG:
330			supportedFormat = askedFormat;
331			fXvidColorspace = XVID_CSP_RGBA;
332			bpr = fOutputVideoFormat.display.line_width * 4;
333			break;
334		case B_RGB24:
335			supportedFormat = askedFormat;
336			fXvidColorspace = XVID_CSP_BGR;
337			bpr = fOutputVideoFormat.display.line_width * 3;
338			break;
339		case B_RGB16:
340			supportedFormat = askedFormat;
341			fXvidColorspace = XVID_CSP_RGB565;
342			bpr = fOutputVideoFormat.display.line_width * 2;
343			break;
344		case B_RGB15:
345			supportedFormat = askedFormat;
346			fXvidColorspace = XVID_CSP_RGB555;
347			bpr = fOutputVideoFormat.display.line_width * 2;
348			break;
349
350		default:
351			fprintf(stderr, "XvidDecoder::NegotiateOutputFormat() - Application "
352				"asked for unsupported colorspace, using fallback "
353				"of B_RGB32.\n");
354		case B_RGB32:
355			supportedFormat = B_RGB32;
356			fXvidColorspace = XVID_CSP_BGRA;
357			bpr = fOutputVideoFormat.display.line_width * 4;
358			break;
359	}
360
361	// enforce the colorspace we support
362	fOutputVideoFormat.display.format = supportedFormat;
363	_inoutFormat->u.raw_video.display.format = supportedFormat;
364
365	// enforce supported bytes per row
366	bpr = max_c(_inoutFormat->u.raw_video.display.bytes_per_row,
367			((bpr + 3) / 4) * 4);
368	fOutputVideoFormat.display.bytes_per_row = bpr;
369	_inoutFormat->u.raw_video.display.bytes_per_row = bpr;
370
371	_inoutFormat->type = B_MEDIA_RAW_VIDEO;
372	_inoutFormat->u.raw_video = fOutputVideoFormat;
373	_inoutFormat->require_flags = 0;
374	_inoutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
375
376#if DEBUG
377	if (string_for_format(*_inoutFormat, buffer, sizeof(buffer)) == B_OK)
378		PRINT(("%p->XvidDecoder: out_format=%s\n", this, buffer));
379#endif
380
381	return _XvidInit() == 0 ? B_OK : B_ERROR;
382}
383
384
385status_t
386XvidDecoder::Decode(void* outBuffer, int64* outFrameCount, media_header* mh,
387	media_decode_info* info)
388{
389	if (outBuffer == NULL || outFrameCount == NULL || mh == NULL)
390		return B_BAD_VALUE;
391
392//	PRINT((%p->"XvidDecoder::Decode()\n", this));
393
394	*outFrameCount = 0;
395
396	// are we in a hurry ?
397	bool hurryUp = (!info || (info->time_to_decode > 0)) ? false : true;
398
399	mh->type = B_MEDIA_UNKNOWN_TYPE;
400	mh->start_time = 0;
401	mh->size_used = 0;
402	mh->file_pos = 0;
403	mh->orig_size = 0;
404	mh->data_offset = 0;
405	mh->u.raw_video.field_gamma = 1.0;
406	mh->u.raw_video.field_sequence = 0;
407	mh->u.raw_video.field_number = 0;
408	mh->u.raw_video.pulldown_number = 0;
409	mh->u.raw_video.first_active_line = 1;
410	mh->u.raw_video.line_count = 0;
411
412	#if DEBUG
413	int32 chunk = 0;
414	#endif
415
416	status_t ret = B_OK;
417
418	do {
419		if (fLeftInChunkBuffer <= MIN_USEFUL_BYTES) {
420			uint8* leftOverBuffer = NULL;
421			size_t leftOverBufferSize = 0;
422			if (fLeftInChunkBuffer > 0) {
423				// we need to wrap the chunk buffer
424				// create a temporary buffer to hold the remaining data
425				leftOverBufferSize = fLeftInChunkBuffer;
426				leftOverBuffer = new (nothrow) uint8[leftOverBufferSize];
427				if (!leftOverBuffer)
428					ret = B_NO_MEMORY;
429				else {
430					memcpy(leftOverBuffer, fChunkBufferHandle,
431						leftOverBufferSize);
432				}
433			}
434			// we don't need the previous wrapped buffer anymore in
435			// case we had it
436			delete[] fWrappedChunkBuffer;
437			fWrappedChunkBuffer = NULL;
438
439			// read new data
440			if (ret >= B_OK)
441				ret = GetNextChunk(&fChunkBuffer, &fChunkBufferSize, mh);
442			if (ret >= B_OK) {
443				if (leftOverBuffer) {
444					fWrappedChunkBuffer = new (nothrow) uint8[fChunkBufferSize
445						+ leftOverBufferSize];
446					if (!fWrappedChunkBuffer)
447						ret = B_NO_MEMORY;
448					else {
449						// copy the left over buffer to the beginning of the
450						// wrapped chunk buffer
451						memcpy(fWrappedChunkBuffer, leftOverBuffer,
452							leftOverBufferSize);
453						// copy the new chunk buffer after that
454						memcpy(fWrappedChunkBuffer + leftOverBufferSize,
455							fChunkBuffer, fChunkBufferSize);
456
457						fChunkBufferSize += leftOverBufferSize;
458						fLeftInChunkBuffer = fChunkBufferSize;
459						fChunkBufferHandle = (const char*)fWrappedChunkBuffer;
460					}
461				} else {
462					fLeftInChunkBuffer = fChunkBufferSize;
463					fChunkBufferHandle = (const char*)fChunkBuffer;
464				}
465			}
466			if (ret < B_OK) {
467				fChunkBufferSize = 0;
468				fChunkBuffer = NULL;
469				fChunkBufferHandle = NULL;
470			}
471			delete[] leftOverBuffer;
472		}
473
474		// Check if there is a negative number of useful bytes left in buffer
475		// This means we went too far
476		if (!fChunkBufferHandle || fLeftInChunkBuffer < 0) {
477			break;
478		}
479
480		xvid_dec_stats_t xvidDecoderStats;
481
482		// This loop is needed to handle VOL/NVOP reading
483		do {
484			// Decode frame
485			int usedBytes = _XvidDecode((uchar*)fChunkBufferHandle,
486				(uchar*)outBuffer, fLeftInChunkBuffer, &xvidDecoderStats,
487				hurryUp);
488
489			// Resize image buffer if needed
490			if (xvidDecoderStats.type == XVID_TYPE_VOL) {
491
492				// Check if old buffer is smaller
493				if ((int)(fOutputVideoFormat.display.line_width
494					* fOutputVideoFormat.display.line_count)
495					< xvidDecoderStats.data.vol.width
496					* xvidDecoderStats.data.vol.height) {
497
498					fprintf(stderr, "XvidDecoder::Decode() - image size "
499						"changed: %dx%d\n",
500						xvidDecoderStats.data.vol.width,
501						xvidDecoderStats.data.vol.height);
502
503					return B_ERROR;
504				}
505			}
506
507			// Update buffer pointers
508			if (usedBytes > 0) {
509				fChunkBufferHandle += usedBytes;
510				fLeftInChunkBuffer -= usedBytes;
511			}
512
513//			PRINT((%p->"XvidDecoder::Decode() - chunk %d: %d bytes consumed, "
514//				"%d bytes in buffer\n", this, chunk++, usedBytes,
515//				fLeftInChunkBuffer));
516
517		} while (xvidDecoderStats.type <= 0
518			&& fLeftInChunkBuffer > MIN_USEFUL_BYTES);
519
520		if (xvidDecoderStats.type > XVID_TYPE_NOTHING) {
521			// got a full frame
522			mh->size_used = fOutputVideoFormat.display.line_count
523				* fOutputVideoFormat.display.bytes_per_row;
524			break;
525		}
526
527	} while (fLeftInChunkBuffer > MIN_USEFUL_BYTES || ret >= B_OK);
528
529	if (ret != B_OK) {
530		PRINT(("%p->XvidDecoder::Decode() - error: %s\n", this,
531			strerror(ret)));
532		return ret;
533	}
534
535	mh->type = B_MEDIA_RAW_VIDEO;
536	mh->start_time = (bigtime_t) (1000000.0 * fFrame
537		/ fOutputVideoFormat.field_rate);
538	mh->u.raw_video.field_sequence = fFrame;
539	mh->u.raw_video.first_active_line = 1;
540	mh->u.raw_video.line_count = fOutputVideoFormat.display.line_count;
541
542	*outFrameCount = 1;
543
544	fFrame++;
545
546	PRINT(("%p->XvidDecoder::Decode() - start_time=%02d:%02d.%02d "
547		"field_sequence=%u\n", this,
548		int((mh->start_time / 60000000) % 60),
549		int((mh->start_time / 1000000) % 60),
550		int((mh->start_time / 10000) % 100),
551		mh->u.raw_video.field_sequence));
552
553//#if DEBUG
554//	print_media_header(mh);
555//	print_media_decode_info(info);
556//#endif
557
558	return B_OK;
559}
560
561
562status_t
563XvidDecoder::SeekedTo(int64 frame, bigtime_t time)
564{
565	PRINT(("%p->XvidDecoder::SeekedTo(frame=%lld, time=%lld)\n",
566		this, frame, time));
567
568	fFrame = frame;
569
570	fChunkBuffer = NULL;
571	fChunkBufferHandle = NULL;
572	fChunkBufferSize = 0;
573	fLeftInChunkBuffer = 0;
574
575	// this will cause the xvid core to discard any cached stuff
576	fDiscontinuity = true;
577
578	return B_OK;
579}
580
581
582// #pragma mark -
583
584
585static bool
586init_xvid(bool useAssembler, int debugLevel)
587{
588	// XviD core initialization
589	xvid_gbl_init_t xvidGlobalInit;
590
591	// reset the structure with zeros
592	memset(&xvidGlobalInit, 0, sizeof(xvid_gbl_init_t));
593
594	// version
595	xvidGlobalInit.version = XVID_VERSION;
596
597	// assembly usage setting
598	if (useAssembler)
599		xvidGlobalInit.cpu_flags = 0;
600	else
601		xvidGlobalInit.cpu_flags = XVID_CPU_FORCE;
602
603	// debug level
604	xvidGlobalInit.debug = debugLevel;
605
606	xvid_global(NULL, 0, &xvidGlobalInit, NULL);
607
608	return true;
609}
610
611
612static bool sXvidInitialized = init_xvid(true, 0);
613
614
615// _XvidInit
616int
617XvidDecoder::_XvidInit()
618{
619	if (fXvidDecoderHandle != NULL)
620		return 0;
621
622	// XviD decoder initialization
623	xvid_dec_create_t xvidDecoderCreate;
624
625	// reset the structure with zeros
626	memset(&xvidDecoderCreate, 0, sizeof(xvid_dec_create_t));
627
628	// version
629	xvidDecoderCreate.version = XVID_VERSION;
630
631	// bitmap dimensions -- xvidcore will resize when needed
632	xvidDecoderCreate.width = 0;
633	xvidDecoderCreate.height = 0;
634
635	int ret = xvid_decore(NULL, XVID_DEC_CREATE, &xvidDecoderCreate, NULL);
636
637	if (ret == 0)
638		fXvidDecoderHandle = xvidDecoderCreate.handle;
639
640	return ret;
641}
642
643// _XvidUninit
644int
645XvidDecoder::_XvidUninit()
646{
647	if (fXvidDecoderHandle == NULL)
648		return 0;
649
650	int ret = xvid_decore(fXvidDecoderHandle, XVID_DEC_DESTROY, NULL, NULL);
651	if (ret == 0) {
652		fXvidDecoderHandle = NULL;
653		fXvidColorspace = 0;
654	}
655	return ret;
656}
657
658// handle_fp_exeption
659static void
660handle_fp_exeption(int sig, void* opaque)
661{
662	printf("_XvidDecode(): WARNING: Xvid decoder raised SIGFPE exception.\n");
663
664// TODO: enable when fenv.h is available
665//	feclearexcept(FE_ALL_EXCEPT);
666
667	//jump back before xvid_decore() and take the other branch
668	siglongjmp((sigjmp_buf)opaque, 1);
669}
670
671// _XvidDecode
672int
673XvidDecoder::_XvidDecode(uchar *inStream, uchar *outStream, int inStreamSize,
674	xvid_dec_stats_t* xvidDecoderStats, bool hurryUp)
675{
676	PRINT(("%p->XvidDecoder::_XvidDecode(%p, %p, %d)\n", this, inStream,
677		outStream, inStreamSize));
678
679	if (inStream == NULL || inStreamSize == 0)
680		return -1;
681
682	xvid_dec_frame_t xvid_dec_frame;
683
684	// reset all structures
685	memset(&xvid_dec_frame, 0, sizeof(xvid_dec_frame_t));
686	memset(xvidDecoderStats, 0, sizeof(xvid_dec_stats_t));
687
688	// set version
689	xvid_dec_frame.version = XVID_VERSION;
690	xvidDecoderStats->version = XVID_VERSION;
691
692	// no general flags to set
693	xvid_dec_frame.general = 0; //XVID_DEBLOCKY | XVID_DEBLOCKUV;
694	if (hurryUp)
695		xvid_dec_frame.general |= XVID_LOWDELAY;
696	if (fDiscontinuity) {
697		xvid_dec_frame.general |= XVID_DISCONTINUITY;
698		fDiscontinuity = false;
699	}
700
701	// input stream
702	xvid_dec_frame.bitstream = inStream;
703	xvid_dec_frame.length = inStreamSize;
704
705	// output frame structure
706	xvid_dec_frame.output.plane[0] = outStream;
707	xvid_dec_frame.output.stride[0] = outStream ?
708		fOutputVideoFormat.display.bytes_per_row : 0;
709	xvid_dec_frame.output.csp = fXvidColorspace;
710
711	// prepare for possible floating point exception
712	sigjmp_buf preDecoreEnv;
713
714    struct sigaction action;
715    struct sigaction oldAction;
716    memset(&action, 0, sizeof(struct sigaction));
717    action.sa_flags = 0;
718    action.sa_handler = (__signal_func_ptr)handle_fp_exeption;
719    action.sa_userdata = preDecoreEnv;
720
721    if (sigaction(SIGFPE, &action, &oldAction) != 0)
722    	return -1;
723
724	int usedBytes;
725	if (sigsetjmp(preDecoreEnv, 1) == 0) {
726		// decode
727		usedBytes = xvid_decore(fXvidDecoderHandle, XVID_DEC_DECODE,
728			&xvid_dec_frame, xvidDecoderStats);
729	} else {
730		// make sure the state changes before calling xvid_decore again
731		// or we'll cause the same exception
732		usedBytes = 1;
733	}
734
735	if (sigaction(SIGFPE, &oldAction, &action) != 0)
736		return -1;
737
738	return usedBytes;
739}
740
741// #pragma mark -
742
743
744status_t
745XvidPlugin::GetSupportedFormats(media_format** _mediaFormatArray, size_t *_count)
746{
747	PRINT(("XvidDecoder::register_decoder()\n"));
748
749	static bool codecsRegistered = false;
750	if (codecsRegistered)
751		return B_OK;
752	codecsRegistered = true;
753
754	PRINT(("XvidDecoder: registering %d codecs\n", gSupportedCodecsCount));
755
756	media_format_description descr[gSupportedCodecsCount];
757
758	for (int i = 0; i < gSupportedCodecsCount; i++) {
759		descr[i].family = gCodecTable[i].family;
760		switch(descr[i].family) {
761			case B_AVI_FORMAT_FAMILY:
762				descr[i].u.avi.codec = gCodecTable[i].fourcc;
763				break;
764			case B_MPEG_FORMAT_FAMILY:
765				descr[i].u.mpeg.id = gCodecTable[i].fourcc;
766				break;
767			case B_QUICKTIME_FORMAT_FAMILY:
768				descr[i].u.quicktime.codec = gCodecTable[i].fourcc;
769				break;
770			default:
771				break;
772		}
773	}
774
775	BMediaFormats formats;
776	for (int i = 0; i < gSupportedCodecsCount; i++) {
777		media_format format;
778		format.type = B_MEDIA_ENCODED_VIDEO;
779		format.u.encoded_video = media_encoded_video_format::wildcard;
780		format.require_flags = 0;
781		format.deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
782
783		status_t err = formats.MakeFormatFor(&descr[i], 1, &format);
784
785		if (err < B_OK) {
786			fprintf(stderr, "XvidDecoder: BMediaFormats::MakeFormatFor: "
787				"error %s\n", strerror(err));
788			continue;
789		}
790
791		gXvidFormats[i] = format;
792	}
793
794	*_mediaFormatArray = gXvidFormats;
795	*_count = gSupportedCodecsCount;
796
797	return B_OK;
798}
799
800
801Decoder*
802XvidPlugin::NewDecoder(uint index)
803{
804	return new (nothrow) XvidDecoder();
805}
806
807
808MediaPlugin *instantiate_plugin()
809{
810	return new (nothrow) XvidPlugin;
811}
812
813
814