#!/usr/bin/env python
# -*- coding: utf-8 -*-
#---------------------------------------------------------------------------------------
# Player for LiveStream(WebCam etc) with BGM
# use ffmpeg(muxer) + liveStreamer (video stream)
#                   + audio_cat (Audio stream)
#---------------------------------------------------------------------------------------
import os, sys, time
import subprocess
import win32pipe,win32file
import threading
from multiprocessing import Process, Lock, Pipe

sys.path.append('regzamod')
from Common.RzCmnSubs import dbg,dbg2,dbg0,error,setdbg
from Common.RzCmnSubs import isAudio,isImage,isVideo,isWebVideo,isWebContents,conv_encoding
from Common.RzCmnSubs import gs_encode_sys,gs_debug,gs_verbose,gs_ff_loglev
from Common.RzCmnSubs import gs_python,gs_prog_ytdl,gs_prog_ytdl_s,gs_prog_streamer,gs_streamer_conf


#--- globals
g_ReConnect=0  			# >0: Retry connect when session closed in time(g_MaxDurationTimeForReconnect)
g_ReConnect_MaxTime=300 # max of session_duration_time_sec for retry connect
g_ReConnect_MinTime=10  # min of session_duration_time_sec for retry connect
g_read_ttl=0

#python="C:/Python27/python.exe"
python=sys.executable   #path of python.exe

LivePlayer="regzamod/RzWEBplayer/RzLivePlayer.py"

def exec_prog (args,stdi,stdo,stde,sh):
	#print >>sys.stderr,"rzLivePlayer_m: exec_prog: args=%s" % args
	dbg("rzLivePlayer_m: exec_prog: args=%s" % args)
	p=None
	try:
		cwd="./"
		p = subprocess.Popen(args, shell=sh, bufsize=-1, stdin=stdi, stdout=stdo,
			stderr=stde, close_fds=False)
	except OSError:
		dbg("rzLivePlayer: exec_prog: Failed to exec %s" % args)

	#ret=p.wait()
	#return ret
	return p

def named_pipe(name,suffix):
	millis = int(round(time.time() * 1000))
	#pname="\\\\.\\pipe\\%s(%d)" % (name,millis)
	#pname=r"\\.\pipe\%s%d" % (name,millis)  # r prefix indicates literal(ignore any escape-sequence)
	pname="\\\\.\\pipe\\%s%d" % (name,millis)
	if(suffix is not None):
		pname=pname+suffix
	dbg("pname=%s" % pname)
	
	p=win32pipe.CreateNamedPipe(
		pname,
		win32pipe.PIPE_ACCESS_DUPLEX,	# pipe mode
		#win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_WAIT,	# pipe mode
		win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,	# pipe mode, 
		1,			# max instances
		65000,	# out buffer size
		65000,	# in buffer size
		300,		# timeout 
		None)
	#ph=p.handle
	
	if(False): #blocked until peer open??
		dbg("named_pipe: Start ConnectNamedPipe")
		win32pipe.ConnectNamedPipe(p,None) 
		dbg("named_pipe: End ConnectNamedPipe")
	return p,pname

def pipe_win(fi,fo):
	global g_read_ttl
	dbg("pipe_win: Start")
	
	bufsz=500000
	connected=False
	try:
		win32pipe.ConnectNamedPipe(fi,None) 
		connected=True
		while True:
			rc,buf=win32file.ReadFile(fi,bufsz)
			if(rc==0):
				fo.write(buf)
			if(rc!=0 or len(buf) <=0):
				break
			g_read_ttl+=len(buf)
	except Exception, e:
		dbg("pipe_win: IOError in pipe read/write err=%r" % e)
	
	try:
		if(connected):
			win32pipe.DisconnectNamedPipe(fi)
	except Exception, e:
		dbg("pipe_win: DisconnectNamedPipe error=%r" % e)
	
	dbg("pipe_win: End")

#---- watchdog
def watchdog_pipe(pr,ppid,pid1,pid2):
	import time  #need here
	time1=time.time()
	dbg("rzLivePlayer_m: watchdog_pipe: Start, target pid1=%r pid2=%r" % (pid1,pid2))

	try:
		#read pipe in blocking mode
		#i.e. wait untill parent die, or send good end
		msg=pr.recv()
		dbg("rzLivePlayer_m: watchdog_pipe: received pipe_msg=%s :seems good end: will exit withot child kills" % msg)
		# good end
		os._exit(0)
	except EOFError:
		dbg("rzLivePlayer_m: watchdog_pipe: EOFError on pipe.recv")
		#parent seems to be killed
		pass	#Noop
	except IOError:
		dbg("rzLivePlayer_m: watchdog_pipe: IOError on pipe.recv")
		pass	#Noop
	except:
		dbg("rzLivePlayer_m: watchdog_pipe: Unknown Exception")
		pass	#Noop
	
	dbg("rzLivePlayer_m: watchdog_pipe: seems bad end, kill pid=%r,%r" % (pid1,pid2))
	try:
		if(pid1 is not None):
			os.kill(pid1,9)
		if(pid2 is not None):
			os.kill(pid2,9)
	except WindowsError:
		dbg("rzLivePlayer_m: watchdog_pipe: WindowsError: pid=%r,%r may not exists" % (pid1,pid2))

	time2=time.time()
	time=time2-time1
	dbg("rzLivePlayer_m: watchdog_pipe: play time=%f" % time)
	os._exit(0)

#----------------------------------------------------------------------------------
# main
#----------------------------------------------------------------------------------
if __name__ == '__main__':
	
	if(gs_verbose):
		dbg("==== rzLivePlayer_m: Main Start")
		dbg("rzLivePlayer_m: arg_cnt=%d args=%r" % (len(sys.argv),sys.argv))

	#ws_setdbg(gs_debug)
	#---- get main args 
	if(len(sys.argv)<3):
		dbg0("rzLivePlayer_m: Usage: rzLivePlayer_m.py {video_in(URL)} {video_out} [options]")
		sys.exit(1)
	
	myprog=sys.argv[0]
	url=sys.argv[1]
	video_out=sys.argv[2]
	
	#---- init settings 
	#---- judge for re-connect  
	reconnect=0
	str_arg=sys.argv[3]
	str_val=sys.argv[4]
	if(str_arg.find("--reconnect")>=0):
		if(str_val.find("1")>=0):
			reconnect=1
		elif(str_val.find("2")>=0):
			reconnect=2
	dbg("rzLivePlayer_m: reconnect=%d" % reconnect)
	
	if(reconnect>1):
		g_ReConnect=1
	elif(reconnect>0 or url.find("http://www.filmon.com")>=0):
		#g_ReConnect doesn't work well
		g_ReConnect=1
		#pass
	
	#---- kick livePlayer body
	pr,pw=Pipe()  #pipe for watchdog_pipe (for my death)
	# to retain open after iav_trans once finished
	if(g_ReConnect>0):
		vo=open(video_out,"wb+") 
		fdh,fdn= named_pipe("pipe_win",".mpg")  # pipe 
		vi=None
		#vi=open(fdn,"rb")
		sys.argv[2]=fdn
	
	sys.argv[0]=LivePlayer
	args0=[python]+sys.argv
	
	dbg("rzLivePlayer_m: start exec loop g_ReConnect=%d" % g_ReConnect)
	prc=0
	connected=False
	g_read_ttl=0
	tim0=time.time()
	retry=0
	while(True):
		tim1=time.time()
		if(g_ReConnect>0):
			args=args0+["--retry_cnt",("%d" % retry)]
			p=exec_prog(args,sys.stdin,sys.stdout,sys.stderr,False)
			dbg("rzLivePlayer_m: kick Child end")
			
			p3=Process(target=watchdog_pipe,args=(pr,os.getpid(),p.pid,None))
			p3.start()
			dbg("rzLivePlayer_m: kick watchdog_pipe end")
			
			if(True):
				t2=threading.Thread(target=pipe_win, name="pipe_win", args=(fdh,vo))
				t2.start()
			else:
				#vi=open(fdn,"rb")
				if(True or connected==False):
					win32pipe.ConnectNamedPipe(fdh,None) 
					connected=True
				dbg("rzLivePlayer_m: ConnectNamedPipe end")
				
				bufsize=50000
				try:
					dbg("rzLivePlayer_m: ---- read named_pipe Start")
					while (True):
						if(True or vi==None):
							#dbg("rzLivePlayer_m: ReadFile Start")
							rc,buf=win32file.ReadFile(fdh,bufsize)
							#dbg("rzLivePlayer_m: ReadFile End rc=%d, readlen=%d" % (rc,len(buf)))
							if(rc==0):
								vo.write(buf)
							if(rc!=0 or len(buf) <=0):
								break
							g_read_ttl+=len(buf)
						else:
							buf=vi.read(bufsize)
							if(buf):
								vo.write(buf)
							else:
								break
				except Exception, e:
					dbg("rzLivePlayer_m: IOError in pipe read/write err=%r" % e)
					#don't break
				
				dbg("rzLivePlayer_m: ---- read named_pipe End")
				win32pipe.DisconnectNamedPipe(fdh)
				#connected=False
		else:
			p=exec_prog(args0,sys.stdin,sys.stdout,sys.stderr,False)
			p3=Process(target=watchdog_pipe,args=(pr,os.getpid(),p.pid,None))
			p3.start()
		
		#if(connected):
		#	win32pipe.DisconnectNamedPipe(fdh)

		#dbg("rzLivePlayer_m: wait child process")
		prc=p.wait()
		pw.send("OK") # send terminate message to watchdog_pipe

		tim2=time.time()
		dur=tim2-tim1
		dur_ttl=tim2-tim0
		dbg("rzLivePlayer_m: child process terminated rc=%r LastElapsed(sec)=%.2f, TotalElapsed(sec)=%.2f" % (prc,dur,dur_ttl))

		if(g_ReConnect>0 and g_read_ttl>0 and dur>g_ReConnect_MinTime and dur<g_ReConnect_MaxTime):
			# retry when closed in short_time (after once connected)
			dbg0("rzLivePlayer_m: process ended, part_timesec(%.2f), total_timesec(%.2f) : terminated too short, exec re-connect" % (dur,dur_ttl))
			time.sleep(1)
			retry+=1
			pass
		else:
			dbg0("rzLivePlayer_m: process ended, part_timesec(%.2f), total_timesec(%.2f) : seems terminated normally, will exit " % (dur,dur_ttl))
			break
			
	dbg("rzLivePlayer_m: exit")
	os._exit(prc)
