/********************************************************

	Video decoder implementation
	Copyright 2000 Eugene Kuznetsov  (divx@euro.ru)

*********************************************************/

#include <default.h>
#include <fourcc.h>
#include <cpuinfo.h>
#include <except.h>

//#include <registry.h>
//#include <wine/winreg.h>

#include "VideoDecoder.h"
#include "VideoEncoder.h"
#include "VideoCodec.h"
#include "../DirectShow/DS_Filter.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#include <cstdio>
#include <iostream>

#define __MODULE__ "VideoDecoder"

#define ICDECOMPRESS_HURRYUP		0x80000000	/* don't draw just buffer (hurry up!) */
#define ICDECOMPRESS_UPDATE		0x40000000	/* don't draw just update screen */
#define ICDECOMPRESS_PREROL		0x20000000	/* this frame is before real start */
#define ICDECOMPRESS_NOTKEYFRAME	0x08000000	/* this frame is not a key frame */

using namespace std;

//IVideoDecoder::IVideoDecoder
VideoDecoder::VideoDecoder(const CodecInfo& info, const BITMAPINFOHEADER& format, int flip)
    :IVideoDecoder(info, format), m_pCodec(0)
{
    try
    {
#warning NOT SURE
//
// MPEG-4 format has changed during development. Package
// binaries.zip includes mpg4c32.dll - old MPEG-4 codec
// with unlocked compression - and divxc32.dll,
// which differs from current MPEG-4 only by FOURCC's.
// The problem is to identify which is needed to us,
// because 'old' codec doesn't work with 'new' clips and
// vice versa. Unfortunately, they may simulate correct
// work until the moment of actual decompression. Then,
// you'll receive garbled picture and lots of warnings about
// hr=-100.
//

// I've got old video with fccHandler mp43 and biCompression MPG4.
// Dirk Vornheider sent me old video with both values MP42.


	m_pCodec = control.Create(m_bh->biCompression, info, Module::Decompress);

	if (!m_pCodec)
	    throw FATAL("ICOpen failed");

	HRESULT h = m_pCodec->DecompressGetFormat(m_bh, (BITMAPINFOHEADER*)&m_obh);
        if (h != 0)
	{
	    printf("Declined format dump:\n");
            BitmapInfo(*m_bh).Print();
	    m_obh.Print();
	    printf(" Error %d\n", int(h));
	    throw FATAL("ICDecompressGetFormat failed");
	}

	setDecoder(m_obh);
	BitmapInfo mbi(m_obh);

	m_Caps = CAP_NONE;
	switch (info.fourcc_array[0])
	{
	case fccDIV3:
	case fccDIV4:
	case fccDIV5:
	case fccDIV6:
	case fccIV50:
	case fccMJPG: // YV12 works but incorrectly
	    // these codecs natively supports YUY2
	    m_Caps = CAP_YUY2; //planar formats broken, others untested
	    //m_Caps = (CAPS)0xff;
	    break;
	case fccHFYU:
	    if (m_bh->biSize == 232)
                m_Caps = CAP_YUY2;
		break;
            // fall through
	default:
	    struct ct {
		fourcc_t fcc;
		CAPS cap;
	    } check[] = {
		{ fccIYUV, CAP_IYUV },
		{ fccUYVY, CAP_UYVY },
		{ fccYV12, CAP_YV12 },
		{ fccYVYU, CAP_YVYU },
		{ fccYUY2, CAP_YUY2 },
		//{ fccI420, CAP_I420 },
		{ 0, CAP_NONE }
	    };

	    for (ct* c = check; c->fcc; c++)
	    {
		mbi.SetSpace(c->fcc);
		//mbi.Print();
		h = m_pCodec->DecompressQuery(m_bh, (BITMAPINFOHEADER*)&mbi);
		if (h == 0)
		    m_Caps = (CAPS)(m_Caps | c->cap);
	    }
	}
	if (m_Caps)
	    cout << "Decoder is capable of YUV output ( flags 0x"<<hex<<(int)m_Caps<<dec<<" )"<<endl;
    }
    catch(FatalError& error)
    {
        delete m_pCodec;
	throw;
    }
}

VideoDecoder::~VideoDecoder()
{
    Stop();
    delete m_pCodec;
}

void VideoDecoder::StartInternal()
{
#if 0
    printf("Starting decompression, format: \n");
    BitmapInfo(m_obh).Print();
    printf("Dest fmt:\n");
    BitmapInfo(m_decoder).Print();
#endif

    HRESULT hr = m_pCodec->DecompressBegin(m_bh, (BITMAPINFOHEADER*)&m_bitrick);
    //cout << "SZ " << hr << "  " << m_decoder.biSize << endl;
    //printf("DECODER COMPRESSSION  %.4s\n", (char*)&m_decoder.biCompression);
    if (hr != 0 && (hr != -2 || (m_bh->biCompression != fccMJPG
				 && m_bh->biCompression != fccmjpg)))
    {
	// FIXME  MJPG decoder (m3jpeg32.dll) returns -2
        // if anyone knows why - please fix it....
	BitmapInfo(*m_bh).Print();
	printf("Dest fmt:\n");
	BitmapInfo(m_decoder).Print();
	cerr << "WARNING: ICDecompressBegin() failed ( shouldn't happen ), error code "
	    << hr << "  (" << ((hr == ICERR_BADFORMAT) ? "Bad Format)" : "?)") << endl;
    }
}

void VideoDecoder::StopInternal()
{
    HRESULT hr = m_pCodec->DecompressEnd();
    if (hr != 0)
    {
	cerr<<"WARNING: ICDecompressEnd() failed ( shouldn't happen ), error code "<<hr<<endl;
    }
}

int VideoDecoder::DecodeInternal(void* src, size_t size, int is_keyframe, CImage* pImage)
{
    // BITMAPINFOHEADER o = *(BITMAPINFOHEADER*)(pImage->GetFmt());
    // if(o.biBitCount == 12)
    //     o.biBitCount = 16;

    //pImage->GetFmt()->Print();
    int hr = m_pCodec->Decompress(((is_keyframe) ? 0 : ICDECOMPRESS_NOTKEYFRAME)
				  | ((pImage) ? 0 : ICDECOMPRESS_HURRYUP|ICDECOMPRESS_PREROL),
				  m_bh, src, &m_bitrick,
				  (pImage) ? pImage->Data() : 0);
    if (hr && pImage)
	cerr << "VideoDecoder: warning: hr=" << hr << endl;

    return hr;
}

int VideoDecoder::SetDestFmt(int bits, fourcc_t csp)
{
    if (!CImage::Supported(csp, bits))
	return -1;

    //printf("SETDESTFMT %d   %.4s\n", bits, (char*)&csp);

    if (csp == 0)
    {
	switch (bits)
	{
	case 15:
	case 16:
	case 24:
	case 32:
	    // 16bit ??? if (record.fourcc_array[0] == fccMJPG)
	    //     m_obh.biSize=0x28;
	    m_obh.SetBits(bits);
	    switch (record.fourcc)
	    {
	    case fccASV1:
	    case fccASV2:
		// these codecs produces upside down RGB images
		break;
	    default:
		if (m_obh.biHeight > 0)
		    m_obh.biHeight *= -1;
	    }
	    break;
	default:
            return 1;
        }
    } else
	m_obh.SetSpace(csp);

    int tmp = m_obh.biHeight;
    m_obh.biHeight = labs(m_obh.biHeight);
    HRESULT h = m_pCodec->DecompressQuery(m_bh, (BITMAPINFOHEADER*)&m_obh);
    m_obh.biHeight = tmp;

    if (h != 0)
    {
	if (csp)
	    printf("Win32: WARNING: Unsupported color space 0x%x  (%.4s)\n",
		   csp, (char*)&csp);
	else
	    cout << "Win32: WARNING; Unsupported bit depth: " << bits << endl;

	return 1;
    }

    //m_bh->biBitCount = bits; // don't remember what is this for
    //cout << "SetDecoder OK " << endl;
    setDecoder(m_obh);
    Restart();

    return 0;
}

void VideoDecoder::setDecoder(BitmapInfo& bi)
{
    m_decoder = bi;
    m_bitrick = bi;
    switch (record.fourcc)
    {
    case fccHFYU:
        // huffyuv refuses to decode images if heigh is <0
	m_bitrick.biHeight = labs(m_bitrick.biHeight);
    }
}

/*
vim: tabstop=8
*/
