#include "StdAfx.h"
#include <Windows.h>
#include <Wingdi.h>

//#include <ShellScalingApi.h>
#include <jni.h>
#include <jawt.h>
#include <win32\jawt_md.h>
#include <dshow.h>
#include <D3d9.h>
#include <Vmr9.h>
#include <streams.h>
#include <Dvdmedia.h>
#include <Dmodshow.h>
#include <Evr.h>
#include <Mfidl.h>
#include "DirectShowVideoSystem.h"
#include "JStreamReader.h"
#include "JVideoRenderer.h"
#include "JAudioRenderer.h"
#include "DSMediaDecodingPlayer.h"

#define AS 4096

//static boolean DEBUG=false;
extern LPWSTR ToErrorMessage(HRESULT);

/*
LPWSTR ToErrorMessage(HRESULT hRes)
{
	LPWSTR errMsg = NULL;
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hRes, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),errMsg, 0, NULL);
	return errMsg;
}
*/

JNIEXPORT jobject JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_init(JNIEnv *env, jobject obj){
	JavaVM *javaVm;
	env->GetJavaVM(&javaVm);
	//  weak (can be garbage collected if object is gone) ref to Java object
	jweak objRef=env->NewWeakGlobalRef(obj);

	unsigned int nObjSize = sizeof(DSMediaDecodingPlayer);
	
	DSMediaDecodingPlayer* mdp = new DSMediaDecodingPlayer(javaVm, objRef);
	jobject thisObj = env->NewDirectByteBuffer(mdp, nObjSize);
	//jweak nativePointeref = env->NewWeakGlobalRef(thisObj);
	//mdp->setNativePointerRef(nativePointeref);
	//printf("JNI: DSMediaDecodingPlayer initialized debug version 7\n");
	//fflush(stdout);
	
	return thisObj;
}

JNIEXPORT jint JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_getApiLevel(JNIEnv *env, jobject jobj){
	return NATIVE_API_VERSION;
}


JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_setAudioRenderer(JNIEnv *env, jobject obj,jobject nativeP,jobject jAudioRendererP){

	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	JAudioRenderer* jap=(JAudioRenderer*)env->GetDirectBufferAddress(jAudioRendererP);
	mdp->setAudioRenderer(jap);
}

JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_setNativeVideoWindowHandleLong(JNIEnv *env, jobject obj, jobject nativeP, jlong nativeWindowHwnd){
	DSMediaDecodingPlayer* mdp = (DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	if (nativeWindowHwnd != 0){
		mdp->setNativeWindowHWND((HWND)nativeWindowHwnd);
		mdp->tryConnectNativeWindow();
	}
}

JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_setNativeVideoWindowPointer(JNIEnv *env, jobject obj, jobject nativeP, jobject nativeWindowHwndObj){
	DSMediaDecodingPlayer* mdp = (DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	if (nativeWindowHwndObj != NULL){
		HWND  nativeWindowHwnd = (HWND)env->GetDirectBufferAddress(nativeWindowHwndObj);
		mdp->setNativeWindowHWND(nativeWindowHwnd);
		mdp->tryConnectNativeWindow();
	}
}

JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_setAWTVideoComponent(JNIEnv *env, jobject obj, jobject nativeP, jobject awtComponent){
	DSMediaDecodingPlayer* mdp = (DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);

	if (awtComponent != NULL){
		mdp->getAWTSurface(env, awtComponent);
		mdp->tryConnectNativeWindow();
	}
	
}

JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_buildGraph(JNIEnv *env, jobject obj,jobject nativeP,jboolean nativeVideoRendering){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	
	HRESULT hr=mdp->buildGraph(nativeVideoRendering);
	if(FAILED(hr)){
		mdp->close();
		USES_CONVERSION;
		env->ThrowNew((jclass)exceptionClassRef,W2A(ToErrorMessage(hr)));
		return;
	}
}



static jobject createWarnResultJObject(JNIEnv *env, char *msg){
	jclass prCls = env->FindClass("ips/media/NativeMediaResult");
	if (prCls == NULL){
		printf("Could not find class\n");
		fflush(stdout);
		return NULL;
	}
	// Get enum Type WARNING of result class
	jclass resTypeCls = env->FindClass("ips/media/NativeMediaResult$Type");
	jfieldID warnTypeFId = env->GetStaticFieldID(resTypeCls, "WARNING", "Lips/media/NativeMediaResult$Type;");
	jobject warnType = env->GetStaticObjectField(resTypeCls, warnTypeFId);

	jstring msgStr = env->NewStringUTF(msg);
	jmethodID prCstr = env->GetMethodID(prCls, "<init>", "(Lips/media/NativeMediaResult$Type;Ljava/lang/String;)V");
	return env->NewObject(prCls, prCstr, warnType, msgStr);
}

static jobject createWarnPartialRenderResultJObject(JNIEnv *env, char *msg){
	jclass prCls = env->FindClass("ips/media/NativeMediaPartialRenderResult");
	if (prCls == NULL){
		printf("Could not find class\n");
		fflush(stdout);
		return NULL;
	}
	// Partial render result is per default a warning
	jstring msgStr = env->NewStringUTF(msg);
	jmethodID prCstr = env->GetMethodID(prCls, "<init>", "(Ljava/lang/String;)V");
	return env->NewObject(prCls, prCstr, msgStr);
}

JNIEXPORT jobject JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_open(JNIEnv *env, jobject obj, jobject nativeP, jstring mediaFilename){
	DSMediaDecodingPlayer* mdp = (DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);

	if (mdp->getRenderMethodID() == 0) {
		jclass cls = env->GetObjectClass(obj);
		if (cls == NULL) {
			env->ThrowNew((jclass)exceptionClassRef, "JNI: Could not get class of object");
			return NULL;
		}
		jmethodID mid = env->GetMethodID(cls, "render", "([B)V");
		if (mid == 0) {
			fprintf(stderr, "JNI: Could not get render method ID\n");
			fflush(stderr);
			env->ThrowNew((jclass)exceptionClassRef, "JNI: Could not get render method ID.");
			return NULL;
		}

		// causes EXCEPTION_ACCESS_VIOLATION in GC thread !!
		//jweak renderMethodRef = env->NewWeakGlobalRef(mid);
		// It seems not good practice to create a ref on the method ID 
		mdp->setRenderMethodID(mid);
	}
	LPWSTR mediaFileNameWStr = NULL;

	if (mediaFilename != NULL){
		mediaFileNameWStr = (LPWSTR)env->GetStringChars(mediaFilename, NULL);
	}

	HRESULT hr = mdp->render(mediaFileNameWStr);
	char *msg = NULL;
	jobject jres = NULL;
	if (hr != S_OK)
	{
		
		if (hr == VFW_S_AUDIO_NOT_RENDERED){
			msg = "Partial success; the audio was not rendered.";
			jres = createWarnPartialRenderResultJObject(env,msg);
		}
		else if (hr == VFW_S_DUPLICATE_NAME){
			msg = "Success; the Filter Graph Manager modified the filter name to avoid duplication.";
			jres = createWarnResultJObject(env, msg);
		}
		else if (hr == VFW_S_PARTIAL_RENDER){
			msg = "Some of the streams in this movie are in an unsupported format.";
			jres = createWarnPartialRenderResultJObject(env, msg);
		}
		else if (hr == VFW_S_VIDEO_NOT_RENDERED){
			msg = "Partial success; some of the streams in this movie are in an unsupported format.";
			jres = createWarnPartialRenderResultJObject(env, msg);
		}
		else if (hr == E_ABORT){
			msg = "Operation aborted.";
		}
		else if (hr == E_FAIL){
			msg = "Failure.";
		}
		else if (hr == E_INVALIDARG){
			msg = "Argument is invalid.";
		}
		else if (hr == E_OUTOFMEMORY){
			msg = "Insufficient memory.";
		}
		else if (hr == E_POINTER){
			msg = "NULL pointer argument.";
		}
		else if (hr == VFW_E_CANNOT_CONNECT){
			msg = "No combination of intermediate filters could be found to make the connection.";
		}
		else if (hr == VFW_E_CANNOT_LOAD_SOURCE_FILTER){
			msg = "The source filter for this file could not be loaded.";
		}
		else if (hr == VFW_E_CANNOT_RENDER){
			msg = "No combination of filters could be found to render the stream.";
		}
		else if (hr == VFW_E_INVALID_FILE_FORMAT){
			msg = "The file format is invalid.";
		}
		else if (hr == VFW_E_NOT_FOUND){
			msg = "An object or name was not found.";
		}
		else if (hr == VFW_E_UNKNOWN_FILE_TYPE){
			msg = "The media type of this file is not recognized.";
		}
		else if (hr == VFW_E_UNSUPPORTED_STREAM){
			msg = "Cannot play back the file : the format is not supported.";
		}
		else{
			msg = "Unknown error";
		}

		if (FAILED(hr)){
			env->ThrowNew((jclass)exceptionClassRef, msg);
		}
		
	}
	if (mediaFileNameWStr != NULL){
		env->ReleaseStringChars(mediaFilename, (const jchar*)mediaFileNameWStr);
	}
	if (env->ExceptionCheck()){
		mdp->close();
		return NULL;
	}
	return jres;
}

JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_waitForEvent(JNIEnv *env, jobject obj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	mdp->waitForEvent(env,obj);
}

JNIEXPORT jboolean JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_start(JNIEnv *env, jobject obj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	return mdp->start();
}
JNIEXPORT jboolean JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_stop(JNIEnv *env, jobject obj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	return mdp->stop();
}
JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_close(JNIEnv *env, jobject obj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	return mdp->close();
}
JNIEXPORT jboolean JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_rewind(JNIEnv *env, jobject obj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	return mdp->rewind();
}
JNIEXPORT jlong JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_getDuration(JNIEnv *env, jobject onj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	return mdp->getDuration();
}
JNIEXPORT jlong JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_getPosition(JNIEnv *env, jobject onj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	return mdp->getPosition();
}
JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_releaseGraph(JNIEnv *env, jobject obj, jobject nativeP){
	DSMediaDecodingPlayer* mdp = (DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	mdp->releaseGraph();
}
JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_release(JNIEnv *env, jobject obj,jobject nativeP){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
	mdp->release(env);
	mdp->~DSMediaDecodingPlayer();
}




DSMediaDecodingPlayer::DSMediaDecodingPlayer(JavaVM* inJavaVm,jobject inJRef)
{
	javaVm=inJavaVm;
	objRef=inJRef;
	renderMethodID = 0;
	pGraph=NULL;
	pControl = NULL;
	pEvent = NULL;
	hwndAWT=NULL;
	flushing=false;
	iBv=NULL;
	renderThreadID=0;
	renderEnv=NULL;
	pWc = NULL;
	pVmr = NULL;
	jvr=NULL;
	jar=NULL;
	lineBuffer = NULL;
	CoInitialize(0);
}


jmethodID DSMediaDecodingPlayer::getRenderMethodID() {
	return renderMethodID;
}
void DSMediaDecodingPlayer::setRenderMethodID(jmethodID rmID) {
	renderMethodID = rmID;
}

/*
	Try to retrieve the window handle of the AWT Canvas.  
*/
void DSMediaDecodingPlayer::getAWTSurface(JNIEnv *env, jobject awtCanvas){
	HWND hwnd = NULL;
	// the code follows examples in the JDK include files jawt.h and linux/jawt_md.h
	JAWT jawt;
	jawt.version = JAWT_VERSION_1_3;
	jboolean res = JAWT_GetAWT(env, &jawt);
	if (!res){
		fprintf(stderr, "Could not get AWT\n");
		fflush(stderr);
	}
	else if (awtCanvas != NULL){
		JAWT_DrawingSurface* jawtDs = jawt.GetDrawingSurface(env, awtCanvas);
		if (jawtDs == NULL){
			fprintf(stderr, "Could not get drawing surface\n");
			fflush(stderr);
		}
		else{
			jint lockRes = jawtDs->Lock(jawtDs);
			// Test Bit mask ((mask & FLAG) == FLAG)
#ifdef DEBUG
			if ((lockRes & JAWT_LOCK_CLIP_CHANGED) == JAWT_LOCK_CLIP_CHANGED){
				printf("JAWT surface lock clip changed\n");
				fflush(stdout);
			}
			if ((lockRes & JAWT_LOCK_BOUNDS_CHANGED) == JAWT_LOCK_BOUNDS_CHANGED){
				printf("JAWT surface bounds changed\n");
				fflush(stdout);
			}
			if ((lockRes & JAWT_LOCK_SURFACE_CHANGED) == JAWT_LOCK_SURFACE_CHANGED){
				printf("JAWT surface lock surface changed\n");
				fflush(stdout);
			}
#endif			
			if ((lockRes & JAWT_LOCK_ERROR) == JAWT_LOCK_ERROR){
				// Happens if the AWT canvas is not yet added to the AWT toolkit
				// for instance a SpeechRecorder speaker display, which is not activated (does not show) 
				fprintf(stderr, "JAWT surface lock error\n");
				fflush(stderr);
			}
			else{
				JAWT_DrawingSurfaceInfo* dsInfo = jawtDs->GetDrawingSurfaceInfo(jawtDs);
				if (dsInfo == NULL){
					fprintf(stderr, "Could not get drawing surface info\n");
					fflush(stderr);
				}
				else{
					JAWT_Win32DrawingSurfaceInfo* dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsInfo->platformInfo;
					if (dsi_win != NULL){
						// Finally we get the window handle!
						hwnd = dsi_win->hwnd;
					}
					else{
						fprintf(stderr, "Drawing surface info NULL!\n");
						fflush(stderr);
					}
					jawtDs->FreeDrawingSurfaceInfo(dsInfo);
				}
				jawtDs->Unlock(jawtDs);
			}
			jawt.FreeDrawingSurface(jawtDs);
		}
	}
	// set retrieved handle or NULL 
	hwndAWT = hwnd;
}

void DSMediaDecodingPlayer::setNativeWindowHWND(HWND nwHwnd){
	hwndAWT = nwHwnd;
}

void DSMediaDecodingPlayer::setAudioRenderer(JAudioRenderer *ar){
	jar=ar;
}

HRESULT DSMediaDecodingPlayer::buildGraph(boolean renderVideoNative)
{
	CoInitialize(0);
	HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
		CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);
	if (FAILED(hr))
	{
		return hr;
	}

	hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
	if (FAILED(hr))
	{
		return hr;
	}
	hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
	if (FAILED(hr))
	{
		return hr;
	}
	hr = pGraph->QueryInterface(IID_IMediaSeeking, (void **)&pSeek);
	if (FAILED(hr))
	{
		return hr;
	}


	//cCritSec=new CCritSec();
	/*
	strReader=new CJStreamReader(NULL,&hr,cCritSec,this);
	if (FAILED(hr))
	{
	printf("Java reader creation failed\n");
	}

	hr=pGraph->AddFilter(strReader,L"JStreamReader");
	if (FAILED(hr))
	{
	printf("Java reader adding failed\n");
	}
	*/
	if (!renderVideoNative){
#ifdef DEBUG
		printf("Using Java Videorenderer\n");
		fflush(stdout);
#endif
		//IBaseFilter* jvrI;
		//hr=CoCreateInstance(CLSID_JVideoRenderer,NULL,CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&jvrI);
		hr = JVideoRenderer::CreateInstance(NULL, &hr, this, &jvr);

		jvr->setMdp(this);
		if (FAILED(hr))
		{
			return hr;
		}
		hr = pGraph->AddFilter(jvr, L"JavaVideoRenderer");
		if (FAILED(hr))
		{
			fprintf(stderr, "Could not add JVideoRenderer");
			fflush(stderr);
			return hr;
		}
	}
	else{
		// we have a native AWT canvas window to display video on
		jvr = NULL;
		// we use EVR renderer
#ifdef DEBUG
		printf("Using EVR Video renderer\n");
		fflush(stdout);
#endif
		hr = CoCreateInstance(CLSID_EnhancedVideoRenderer, 0,
			CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pVmr);

		if (FAILED(hr))
		{
			return hr;
		}
		//hr = pGraph->AddFilter(pVmr, L"VMR9");
		hr = pGraph->AddFilter(pVmr, L"EVR");
		if (FAILED(hr))
		{
			return hr;
		}

	}
	if (jar != NULL){
		hr = pGraph->AddFilter(jar, L"JavaAudioRenderer");
		if (FAILED(hr))
		{
			return hr;
		}
	}
	pEvent->GetEventHandle((OAEVENT *)&hEvent);
	return S_OK;

}


HRESULT DSMediaDecodingPlayer::tryConnectNativeWindow(){
	if (hwndAWT != NULL && pVmr!=NULL){
		IMFGetService *getSrv = NULL;
		HRESULT hr = pVmr->QueryInterface(IID_IMFGetService, (void**)&getSrv);
		if (FAILED(hr)){
			return hr;
		}
		if (getSrv != NULL){
			getSrv->Release();
		}
		hr = getSrv->GetService(MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl, (void**)&pWc);
		if (SUCCEEDED(hr))
		{
			hr = pWc->SetVideoWindow(hwndAWT);
			if (FAILED(hr)){
				fprintf(stderr, "ERROR: Could not set AWT Window to EVR!\n");
				fflush(stderr);
				return hr;
			}
		}
		else{
			fprintf(stderr, "ERROR: Could not get EVR video display control\n");
			fflush(stderr);
			return hr;
		}
	}
	return S_FALSE;
	
}


JNIEXPORT void JNICALL Java_ips_media_jnm_impl_directshow_DSMediaDecodingPlayer_setVideoCanvasSize(JNIEnv *env, jobject obj,jobject nativeP,jint x, jint y,jint width,jint height,jboolean scaleSizeLogical){
	DSMediaDecodingPlayer* mdp=(DSMediaDecodingPlayer*)env->GetDirectBufferAddress(nativeP);
#ifdef DEBUG
	printf("Set video canvas size %d,%d %dx%d\n",x,y,width,height);
	fflush(stdout);
#endif
	mdp->setVideoCanvas(x,y,width,height,scaleSizeLogical);
}

void DSMediaDecodingPlayer::setVideoCanvas(jint x,jint y,jint width,jint height,jboolean scaleSizeLogical){
	
	//printf("setvideoCANVAS ");
	//fflush(stdout);
	if (hwndAWT == NULL){
		return;
	}
	//
	LONG trgWidth = width;
	LONG trgHeight = height;

	//if (scaleSizeLogical){

	//	// causes stackoverflow error in Swing/AWT (non JavaFX) mode
	//	HDC hdc = GetDC(hwndAWT);

	//	// Java 9 DPI awarness
	//	int logicalPixelsX = GetDeviceCaps(hdc, LOGPIXELSX);
	//	int logicalPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
	//	//printf("Logical pixels x,y: %d,%d\n", logicalPixelsX,logicalPixelsY);


	//	HMONITOR hMonitor = MonitorFromWindow(hwndAWT, MONITOR_DEFAULTTOPRIMARY);

	//	// Not available for Windows 7 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
	//	// HRESULT hr = GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);


	//	MONITORINFOEX logicalMonitorInfo;
	//	logicalMonitorInfo.cbSize = sizeof(MONITORINFOEX);
	//	GetMonitorInfo(hMonitor, &logicalMonitorInfo);

	//	LONG logicalMonitorWidth = logicalMonitorInfo.rcMonitor.right - logicalMonitorInfo.rcMonitor.left;
	//	int logicalDesktopWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
	//	//printf("Width logical m0: %u, logical desktop m0: %ld\n", logicalMonitorInfo, logicalDesktopWidth);
	//	UINT32 numPathArrayElements = AS;
	//	DISPLAYCONFIG_PATH_INFO pathInfoArray[AS];
	//	UINT32 numModeInfoArrayElements = AS;
	//	DISPLAYCONFIG_MODE_INFO modeInfoArray[AS];
	//	LONG qres = QueryDisplayConfig(QDC_ALL_PATHS, &numPathArrayElements, pathInfoArray, &numModeInfoArrayElements, modeInfoArray, NULL);
	//	if (qres == ERROR_SUCCESS){
	//		for (UINT32 i = 0; i < numModeInfoArrayElements; i++){
	//			if (modeInfoArray[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE){
	//				UINT32 dispPhysWidth = modeInfoArray[i].sourceMode.width;
	//				//printf("Phys width m0: %u\n", dispPhysWidth);
	//			}
	//		}
	//	}
	//	else if (qres == ERROR_INSUFFICIENT_BUFFER){
	//		fprintf(stderr, "Insufficient buffer\n");
	//	}
	//	fflush(stdout);
	//	fflush(stderr);

	//	trgWidth = trgWidth*logicalPixelsX / 96;
	//	trgHeight = trgHeight*logicalPixelsY / 96;
	//}
	RECT trg = {x,y,trgWidth,trgHeight}; 
	// Get the window client area.
	GetClientRect(hwndAWT, &trg);
	// Set the destination rectangle.
	SetRect(&trg, 0, 0, trg.right, trg.bottom);
	
	if (pWc != NULL){
		//pWc->GetNativeVideoSize(&src.right, &src.bottom, &arW, &arH);
		HRESULT hr = S_OK;
		MFVideoNormalizedRect srcRect;
		// normalized
		srcRect.left = 0.0;
		srcRect.right = 1.0;
		srcRect.top = 0.0;
		srcRect.bottom = 1.0;
		hr=pWc->SetVideoPosition(&srcRect, &trg);
		if (FAILED(hr)){
			fprintf(stderr, "Could not set video position!\n");
			fflush(stderr);
		}
		else{}
		pWc->RepaintVideo();
	}
	//
}

LRESULT CALLBACK CBTProc(
   int nCode,
   WPARAM wParam,
	  LPARAM lParam
  ){
	//printf("CBT callback\n");
		fflush(stdout);
	  if(nCode==HCBT_MOVESIZE){
		LPRECT wb=(LPRECT)lParam;
		//printf("new size %d %d %d %d\n",wb->bottom,wb->left,wb->right,wb->top);
		//fflush(stdout);

	  }
	  return CallNextHookEx(NULL,nCode,wParam,lParam);
}

HRESULT DSMediaDecodingPlayer::render(LPCWSTR lpwstrFile)
{
	HRESULT hr;

	if (lpwstrFile == NULL){
		hr = pGraph->Render(strReader->GetPin(0));
	}
	else{
		hr = pGraph->RenderFile(lpwstrFile, NULL);
	}
	return hr;
}



LONGLONG DSMediaDecodingPlayer::getDuration()
{

	DWORD seekCaps=0;
	HRESULT hr=pSeek->GetCapabilities(&seekCaps);
	
	if(FAILED(hr)){
		printf("Get caps from seek interface failed!\n");
		fflush(stdout);
		return -1;
	}else{
	/*	GUID prefTimeFormat;
		pSeek->QueryPreferredFormat(&prefTimeFormat);
		printf("Pref time format: ");
		if(prefTimeFormat==TIME_FORMAT_MEDIA_TIME){
			printf("media time");
		}else{
			printf("other");
		}
		printf("\n");
		fflush(stdout);
*/
		if(seekCaps & AM_SEEKING_CanGetDuration){
			
			LONGLONG duration=0;
			hr=pSeek->GetDuration(&duration);
			if(SUCCEEDED(hr)){
				//printf("Duration: %lld %lld sec\n",duration,(duration*100)/(1000*1000*1000));
				//fflush(stdout);
				return duration;
			}

		}else{
			fprintf(stderr,"Cannot get duration.\n");
			fflush(stderr);
			return -1;
		}
	}


	return 0;
}

LONGLONG DSMediaDecodingPlayer::getPosition()
{
	//DWORD seekCaps=0;
	//HRESULT hr=pSeek->GetCapabilities(&seekCaps);
	
	//if(FAILED(hr)){
	//	printf("Get caps from seek interface failed!\n");
	//	fflush(stdout);
	//}else{
	//	
	//	if(seekCaps & AM_SEEKING_CanGetCurrentPos){
			LONGLONG pos=0;
			HRESULT hr=pSeek->GetCurrentPosition(&pos);
			if(SUCCEEDED(hr)){	
				return pos;
			}
		//}else{
		//	printf("Cannot get pos.\n");
		//	fflush(stdout);
		//}
	//}
	return 0;
}

void DSMediaDecodingPlayer::displayChanged(){
	if (pWc != NULL){
		//pWc->DisplayModeChanged();
	}
}

void DSMediaDecodingPlayer::waitForEvent(JNIEnv *env, jobject obj){
	long    evCode;
	LONG_PTR param1, param2;
	if (hEvent == NULL){
		fprintf(stderr, "hEvent NULL\n");
		fflush(stderr);
		Sleep(WAIT_FOR_EVENT_TIMEOUT_MS);
		return;
	}
	DWORD ret = WaitForSingleObject(hEvent, WAIT_FOR_EVENT_TIMEOUT_MS);
	if (WAIT_OBJECT_0 == ret){ 
		while (S_OK == pEvent->GetEvent(&evCode, &param1, &param2, 0)) {
			if(evCode == EC_CLOCK_CHANGED){
			}else if(evCode==EC_PALETTE_CHANGED){
			}
			else if (evCode == EC_DISPLAY_CHANGED){
				printf("Display changed\n");
				fflush(stdout);
				displayChanged();
			}
			else if (evCode == EC_DEVICE_LOST){
				printf("Device lost\n");
				fflush(stdout);
			}
			
			//printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);
			//fflush(stdout);
			pEvent->FreeEventParams(evCode, param1, param2);
			if(EC_COMPLETE == evCode){

				// TODO cache method reference
				jclass cls=env->GetObjectClass(objRef);
				if(cls==NULL){
					fprintf(stderr,"Could not find class\n");
					fflush(stderr);
				}else{
					jmethodID mid = env->GetMethodID(cls, "endOfStream", "()V");
					if (mid == 0){
						printf("Could not find method ID\n");

					}else{

						// (This thread is generated on the java side and must not be attached)
						env->CallVoidMethod(obj,mid);
					}
				}
			} 
		}
	}else if(WAIT_ABANDONED == ret){
		printf("WAIT_ABANDONED\n");
		fflush(stdout);
	}else if(WAIT_FAILED ==ret){
		fprintf(stderr,"WAIT_FAILED\n");
		fflush(stderr);
		Sleep(WAIT_FOR_EVENT_TIMEOUT_MS);
	}else if(WAIT_TIMEOUT==ret){
		// OK normal loop timeout
		return;
	}
} 


HRESULT DSMediaDecodingPlayer::BeginFlush(){
	return S_OK;
}
HRESULT DSMediaDecodingPlayer::PinBeginFlush(){
	JNIEnv *env;
	HRESULT hr=S_OK;
	flushing=true;
	jint res=javaVm->AttachCurrentThread((void **)&env,NULL);
	if(res!=JNI_OK){
		hr=E_FAIL;
	}else{

		jclass cls=env->GetObjectClass(objRef);
		if(cls==NULL){
			printf("Could not find class\n");
			hr=E_FAIL;
		}

		jmethodID mid = env->GetMethodID(cls, "beginFlush", "()V");
		if (mid == 0){
			printf("Could not find method ID\n");
			hr=E_FAIL;
		}

		env->CallVoidMethod(objRef, mid);
		jboolean exCheck=env->ExceptionCheck();

		/*
		res=javaVm->DetachCurrentThread();
		if(res<0){
		printf("Beginflush cannot detach thread!\n");
		fflush(stdout);
		hr=E_FAIL;
		}
		*/
		if(exCheck){
			hr=E_FAIL;
		}
		javaVm->DetachCurrentThread();
	}
	//printf("BeginFlush out %ld\n",hr);
	//fflush(stdout);
	return hr;
}

HRESULT DSMediaDecodingPlayer::EndFlush(){
	return S_OK;
}
HRESULT DSMediaDecodingPlayer::PinEndFlush(){
	JNIEnv *env;
	jint res=javaVm->AttachCurrentThread((void **)&env,NULL);
	if(res==JNI_OK){

		jclass cls=env->GetObjectClass(objRef);
		if(cls==NULL){
			printf("Could not find class\n");
			return E_FAIL;
		}

		jmethodID mid = env->GetMethodID(cls, "endFlush", "()V");
		if (mid == 0){
			printf("Could not find method ID\n");
			return E_FAIL;
		}

		env->CallVoidMethod(objRef, mid);
		jboolean exCheck=env->ExceptionCheck();

		//javaVm->DetachCurrentThread();
		if(exCheck){
			return E_FAIL;
		}
		flushing=false;
		javaVm->DetachCurrentThread();
		return S_OK;

	}
	return E_FAIL;
}
HRESULT DSMediaDecodingPlayer::length(LONGLONG *pTotal,LONGLONG *pAvailable){
	JNIEnv *env;
	jint res=javaVm->AttachCurrentThread((void **)&env,NULL);
	if(res==JNI_OK){
		jclass cls=env->GetObjectClass(objRef);
		if(cls==NULL){
			printf("Could not find class\n");
			return E_FAIL;
		}
		//jmethodID mid = env->GetMethodID(cls, "readSync", "(JLjava/nio/ByteBuffer;I)I");
		jmethodID mid = env->GetMethodID(cls, "length", "()J");
		if (mid == 0){
			printf("Could not find method ID\n");
			return E_FAIL;
		}
		byteLength=env->CallLongMethod(objRef,mid);
		*pTotal=byteLength;
		if(pAvailable!=NULL){
			*pAvailable=byteLength;
		}
		javaVm->DetachCurrentThread();
		return S_OK;

	}
	return E_FAIL;
}

HRESULT DSMediaDecodingPlayer::request(IMediaSample *pMs,LONGLONG position,LONG length,BYTE *buf,DWORD_PTR dwUser){
	JNIEnv *env;
	HRESULT hr=S_OK;
	//printf("Request native\n");
	//fflush(stdout);

	if(flushing){
		//printf("Request flushing\n");
		//fflush(stdout);
		hr=VFW_E_WRONG_STATE;
	}else{
		jint res=javaVm->AttachCurrentThread((void **)&env,NULL);
		if(res==JNI_OK){
			pMs->SetActualDataLength(length);
			dwUserTest=dwUser;
			// use DirectByteBufer as Java pointer
			// (a interface has no size (?), however the size  does not matter) 
			jobject jMs=env->NewDirectByteBuffer(pMs,sizeof(IMediaSample));

			jclass cls=env->GetObjectClass(objRef);
			if(cls==NULL){
				printf("Could not find class\n");
				return E_FAIL;
			}
			//jmethodID mid = env->GetMethodID(cls, "readSync", "(JLjava/nio/ByteBuffer;I)I");
			jmethodID mid = env->GetMethodID(cls, "request", "(Ljava/nio/ByteBuffer;JLjava/nio/ByteBuffer;J)V");
			if (mid == 0){
				printf("Could not find method ID\n");
				return E_FAIL;
			}
			//jbyteArray jba=env->NewByteArray(length);
			jobject dbb=env->NewDirectByteBuffer(buf,length);
			//printf("dwUser request %ld\n",dwUser);
			env->CallVoidMethod(objRef, mid,jMs,position,dbb,(jlong)dwUser);
			jboolean exCheck=env->ExceptionCheck();

			res=javaVm->DetachCurrentThread();
			if(res<0){
				printf("Request: cannot dettach thread!\n");
				hr=E_FAIL;
			}
			if(exCheck){
				hr=E_FAIL;
			}
			//return S_OK;

		}else{
			hr=E_FAIL;
		}
	}
	//printf("Request ret %ld\n",hr);
	//		fflush(stdout);

	return hr;
}

HRESULT DSMediaDecodingPlayer::WaitForNext(DWORD dwTimeOut,IMediaSample **ppSample,DWORD_PTR *pdwUser){
	JNIEnv *env;
	//printf("waitForNext in\n");
	//fflush(stdout);
	HRESULT hr=S_OK;
	if(flushing){
		hr=VFW_E_WRONG_STATE;
	}else{
		jint res=javaVm->AttachCurrentThread((void **)&env,NULL);
		if(res==JNI_OK){
			jclass cls=env->GetObjectClass(objRef);
			if(cls==NULL){
				printf("Could not find class\n");
				return E_FAIL;
			}
			//jmethodID mid = env->GetMethodID(cls, "readSync", "(JLjava/nio/ByteBuffer;I)I");
			jmethodID mid = env->GetMethodID(cls, "waitForNext", "(J)Lde/klausj/video/impl/directshow/AsyncReadRequest;");
			if (mid == 0){
				printf("Could not find waitForNext method ID\n");
				return E_FAIL;
			}
			// blocks here
			jobject asyncReadRequest=env->CallObjectMethod(objRef, mid,(jlong)dwTimeOut);
			// OK reveived requested data
			jboolean exCheck=env->ExceptionCheck();
			if(asyncReadRequest!=NULL){
				jclass asyncReadRequestClass=env->FindClass("de/klausj/video/impl/directshow/AsyncReadRequest");
				jfieldID pIMediasampleFID=env->GetFieldID(asyncReadRequestClass,"pIMediaSample","Ljava/nio/ByteBuffer;");
				jobject pIMediaSample=env->GetObjectField(asyncReadRequest,pIMediasampleFID);
				IMediaSample *pSample=(IMediaSample*)env->GetDirectBufferAddress(pIMediaSample);
				//printf("Actual %ld\n",pSample->GetActualDataLength());
				*ppSample=pSample;
				jfieldID dwUserFID=env->GetFieldID(asyncReadRequestClass,"dwUser","J");
				jlong dwUser=env->GetLongField(asyncReadRequest,dwUserFID);
				//printf("dwUser wait %ld\n",(DWORD_PTR)dwUser);
				*pdwUser=(DWORD_PTR)dwUser;

				//*pdwUser=dwUserTest;
			}else{
				// happens if player is stopped and beginFlush is called
				*ppSample=NULL;
				hr=VFW_E_TIMEOUT;
				printf("waitForNext VF_E_TIMEOUT\n");
			}
			res=javaVm->DetachCurrentThread();
			if(res<0){
				printf("waitForNext: cannot detach thread!\n");
				hr=E_FAIL;
			}
			if(exCheck){
				hr=E_FAIL;
				printf("waitForNext E_FAIL (exception)\n");
			}
			//return S_OK;

		}else{
			hr=E_FAIL;
		}
	}
	//printf("waitForNext out %d\n",hr);
	//fflush(stdout);
	return hr;
}

HRESULT DSMediaDecodingPlayer::read(LONGLONG position,LONG length,BYTE *buf){
	JNIEnv *env;
	HRESULT hr=S_OK;
	jint res=javaVm->AttachCurrentThread((void **)&env,NULL);
	if(res==JNI_OK){
		jclass cls=env->GetObjectClass(objRef);
		if(cls==NULL){
			printf("Could not find class\n");
			hr=E_FAIL;
		}
		//jmethodID mid = env->GetMethodID(cls, "readSync", "(JLjava/nio/ByteBuffer;I)I");
		jmethodID mid = env->GetMethodID(cls, "readSync", "(J[BI)I");
		if (mid == 0){
			printf("Could not find method ID\n");
			hr=E_FAIL;
		}
		jbyteArray jba=env->NewByteArray(length);

		//jobject jBuf=env->NewDirectByteBuffer(buf,length);
		jint read=env->CallIntMethod(objRef, mid,position,jba,length);
		jboolean exCheck=env->ExceptionCheck();
		env->GetByteArrayRegion(jba,0,read,(jbyte *)buf);

		javaVm->DetachCurrentThread();
		if(exCheck){
			hr=E_FAIL;
		}
		if(read<length){
			hr= S_FALSE;
		}

	}else{
		hr=E_FAIL;
	}
	return hr;
}


void DSMediaDecodingPlayer::setVideoSize(GUID mediaSubtype,LONG w, LONG h){
	JNIEnv *env;
	jint bits=24;
	jboolean hasAlpha = JNI_FALSE;
	if(mediaSubtype == MEDIASUBTYPE_RGB24){
			bits=24;
			pixelStride=3;
	}else if(mediaSubtype == MEDIASUBTYPE_ARGB32){
			bits=32;
			pixelStride=4;
			hasAlpha = JNI_TRUE;
	}
	else if (mediaSubtype == MEDIASUBTYPE_RGB32){
		bits = 32;
		pixelStride = 4;
	}
#ifdef DEBUG
	printf("Video media size: %dx%d\n", w, h);
	fflush(stdout);
#endif
	width=w;
	height=h;
	
	jint res=javaVm->AttachCurrentThread((void **)&env,NULL);
	if(res==JNI_OK){
		jclass cls=env->GetObjectClass(objRef);
		if(cls==NULL){
			printf("Could not find class\n");
			return ;
		}
		jmethodID mid = env->GetMethodID(cls, "setVideoFormat", "(IJJZ)[B");
		if (mid == 0){
			printf("Could not find method ID\n");
			return;
		}
		jarray bufArr=(jarray)env->CallObjectMethod(objRef, mid,bits,(jlong)width,(jlong)height,hasAlpha);
		
		imgArrRef=env->NewWeakGlobalRef(bufArr);
		javaVm->DetachCurrentThread();
	}

}

void DSMediaDecodingPlayer::rendered(BYTE *imgBuf,long dataLength){
	
	CCritSec csMyLock;  // Critical section is not locked yet.
	{
		CAutoLock cObjectLock(&csMyLock);  // Lock the critical section.
		DWORD tid=GetCurrentThreadId();
	
		jint res=0;
		//if(renderThreadID!=tid){
		//	renderEnv=NULL;
		//thr_args.version = JNI_VERSION_1_4;
		//thr_args.group=NULL;
		//sprintf_s(jRenderThreadName,64,"DS-Render_%d",tid);
		//thr_args.name=jRenderThreadName;
		//renderThreadID=tid;
		////printf("Native render thread ID %d\n",renderThreadID);
		////fflush(stdout);
		//}
		JNIEnv *env;
		
		res=javaVm->AttachCurrentThread((void **)&env,&thr_args);
	
		if(res==JNI_OK){
			//printf("Env %p dataLength %d\n",env,dataLength);
			//fflush(stdout);
			/*
			jbyteArray imgArr=env->NewByteArray(dataLength);
			if(imgArr==NULL){
				printf("Could not get byte array dataLength %d\n",dataLength);
				fflush(stdout);
				return;
			}*/

			if (imgArrRef == NULL) {
				printf("Rendered call, but no image array buffer available yet. (set video size method not called yet): %p dataLength %d\n", imgBuf, dataLength);
				fflush(stdout);
			}
			if (objRef == NULL || renderMethodID==0) {
				printf("Media player java object already finalized!!\n");
				fflush(stdout);
			}

			jbyteArray imgArr=(jbyteArray)imgArrRef;
			//	printf("Width %d height %d size %d %d\n",width,height,dataLength,width*height*3);
			// line stride is always multiple of DWORD (4 bytes)
			// See docu for BITMAPINFOHEADER structure
			//int rawLineStride=width*3;
			int rawLineStride=width*pixelStride;
			int lineStrideDwords=rawLineStride / 4;
			if(rawLineStride % 4 >0){
				lineStrideDwords++;
			}
			int lineStride=lineStrideDwords*4;
			int dstPos=0;
			//printf("RawLineStride %d heigth %d =? %d\n",rawLineStride,height,dataLength);
			//if(height>0){
			//	// img is bottom up
			//	// we need top down for Java buffered image
			//	for(int lc=0;lc<height;lc++){

			//		int srcPos=(height-lc-1)*lineStride;
			//		//printf("%d %d %d\n",lineStride,srcPos,dstPos);
			//		env->SetByteArrayRegion((jbyteArray)imgArr,dstPos,rawLineStride,(jbyte *)(imgBuf+srcPos));
			//		dstPos+=rawLineStride;
			//	}
			//}else{
			//	// Hmm. copylen is half of dataLength for some mp4 videos. Why?
			//	jint copyLen=lineStride*(-height);
			//	env->SetByteArrayRegion((jbyteArray)imgArr,0,copyLen,(jbyte *)imgBuf);
			//	/*
			//	for(int lc=0;lc<(-height);lc++){

			//		int srcPos=lc*lineStride;
			//		//printf("%d %d %d\n",lineStride,srcPos,dstPos);
			//		env->SetByteArrayRegion((jbyteArray)imgArr,dstPos,rawLineStride,(jbyte *)(imgBuf+srcPos));
			//		dstPos+=rawLineStride;
			//	}
			//	*/
			//}
			

			if (imgBuf != NULL && dataLength>=0 && imgArr!=NULL && objRef!=NULL && renderMethodID!=0) {
				env->SetByteArrayRegion((jbyteArray)imgArr, dstPos, dataLength, (jbyte *)(imgBuf));
				env->CallVoidMethod(objRef, renderMethodID, imgArr);
			}
			//env->ReleaseByteArrayElements((jbyteArray)imgArr,NULL,0); // does not free the array !!

			javaVm->DetachCurrentThread();
			//free(imgBufCopy);
		}

	}
}

boolean DSMediaDecodingPlayer::rewind(){
	LONGLONG current=0;
	HRESULT hr=pSeek->SetPositions(&current,AM_SEEKING_AbsolutePositioning,NULL,AM_SEEKING_NoPositioning);
	return SUCCEEDED(hr);
}


boolean DSMediaDecodingPlayer::start()
{
	HRESULT hr=pControl->Run();

	// S_FALSE means it is  started but not yet running
	return (hr==S_OK || hr==S_FALSE);
	//return (hr==S_OK);
}

boolean DSMediaDecodingPlayer::stop()
{
	HRESULT hr=pControl->Stop();
	return (hr==S_OK || hr==S_FALSE);
	//	return (hr==S_OK);
}

void DSMediaDecodingPlayer::close()
{
	HRESULT hr;
	if(pControl!=NULL){
		hr=pControl->Stop();
		if (hr != S_OK){
			fprintf(stderr, "Stop returned %d\n",hr);
			fflush(stderr);
		}
		OAFilterState state;
		do{
			pControl->GetState(100, &state);
			if (State_Stopped != state){
				printf("Wait for stopped: %d\n", state);
				fflush(stdout);
			}
		} while (State_Stopped != state);
#ifdef DEBUG
		printf("Graph stopped.\n");
		fflush(stdout);
#endif
	}
}

void DSMediaDecodingPlayer::releaseGraph()
{
	HRESULT hr;
	
	if (pGraph != NULL){

		if (jvr != NULL){
			hr = pGraph->RemoveFilter(jvr);
			if (hr != S_OK){
				fprintf(stderr, "Failed to remove videorender filter\n");
				fflush(stderr);
			}
			else{
#ifdef DEBUG
				printf("Video renderer filter removed.\n");
				fflush(stdout);
#endif
			}

		}

		if (jar != NULL){
			hr = pGraph->RemoveFilter(jar);
			if (hr != S_OK){
				fprintf(stderr, "Failed to remove audio render filter\n");
				fflush(stderr);
			}
			else{
#ifdef DEBUG
				printf("Removed audio renderer filter.\n");
				fflush(stdout);
#endif
			}
		}
		if (pVmr != NULL){
			hr = pGraph->RemoveFilter(pVmr);
			if (hr != S_OK){
				fprintf(stderr, "Failed to remove EVR filter\n");
				fflush(stderr);
			}
			else{
#ifdef DEBUG
				printf("Removed EVR filter.\n");
				fflush(stdout);
#endif
			}
		}

		hr = pGraph->Abort();
#ifdef DEBUG
		printf("Graph aborted.\n");
		fflush(stdout);
#endif

	}

	if (pWc != NULL){
		pWc->Release();
		pWc = NULL;
#ifdef DEBUG
		printf("Released video display control.\n");
		fflush(stdout);
#endif
	}


	if (iBv != NULL){
		ULONG iBvRefCnt = iBv->Release();
		//printf("iBv release %d\n", iBvRefCnt);
		iBv = NULL;
#ifdef DEBUG
		printf("Released basic video filter.\n");
		fflush(stdout);
#endif
	}

	if (pEvent != NULL){
		pEvent->Release();
		pEvent = NULL;
	}
	if (pSeek != NULL){
		pSeek->Release();
		pSeek = NULL;
	}
	if (pControl != NULL){
		pControl->Release();
		pControl = NULL;
	}
	if (pVmr != NULL){
		ULONG vmrRefCnt = pVmr->Release();
		//printf("VMR9 release: %d\n", vmrRefCnt);
		//fflush(stdout);
		pVmr = NULL;
#ifdef DEBUG
		printf("Released EVR renderer.\n");
		fflush(stdout);
#endif
	}

	if (pGraph != NULL){
		ULONG iGrRefCnt = pGraph->Release();
		//printf("IGraphBuilder release: %d\n", iGrRefCnt);
		//fflush(stdout);
#ifdef DEBUG
		printf("Released graph builder.\n");
		fflush(stdout);
#endif
		if (iGrRefCnt == 0){
			pGraph = NULL;
		}
		else{
			// try to reuse on next open
			// otherwise we have a huge memory leak
		}
	}

	if (jvr != NULL){
		jvr->Release();
		jvr = NULL;
#ifdef DEBUG
		printf("Released Java video renderer.\n");
		fflush(stdout);
#endif
	}
	if (jar != NULL){
		jar->Release();
		jar = NULL;
#ifdef DEBUG
		printf("Released Java audio renderer\n");
		fflush(stdout);
#endif
	}
}


void DSMediaDecodingPlayer::release(JNIEnv *env)
{
	
	// release JNI references
	if (objRef != NULL) {
		env->DeleteWeakGlobalRef(objRef);
	}
	if (imgArrRef != NULL) {
		env->DeleteWeakGlobalRef(imgArrRef);
	}
	
}
DSMediaDecodingPlayer::~DSMediaDecodingPlayer()
{
	if(lineBuffer!=NULL)free(lineBuffer);
}
