package com.jisco.dynamicweight.dynamicbasecamera.factories;

import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.AsyncContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * javacv转包装<br/>
 * 无须转码，更低的资源消耗，更低的延迟<br/>
 * 确保流来源视频H264格式,音频AAC格式
 * 
 * @author hzj
 */
public class ConverterFactories extends Thread implements Converter {
	private static Logger logger	= LoggerFactory.getLogger( ConverterFactories.class );

	public volatile boolean runing = true;
	/**
	 * 读流器
	 */
	private FFmpegFrameGrabber grabber;
	/**
	 * 转码器
	 */
	private FFmpegFrameRecorder recorder;

	/**
	 * 转FLV格式的头信息<br/>
	 * 如果有第二个客户端播放首先要返回头信息
	 */
	private byte[] headers;
	/**
	 * 保存转换好的流
	 */
	private ByteArrayOutputStream stream;
	/**
	 * 流地址，h264,aac
	 */
	private String url;
	/**
	 * 流输出
	 */
	private List<AsyncContext> outEntitys;

	/**
	 * key用于表示这个转换器
	 */
	private String key;

	/**
	 * 转换队列
	 */
	private Map<String, Converter> factories;

	private String videoId;

	public ConverterFactories(String url, String key, Map<String, Converter> factories, List<AsyncContext> outEntitys,String videoId) {
		this.url = url;
		this.key = key;
		this.factories = factories;
		this.outEntitys = outEntitys;
		this.videoId = videoId;
	}

	@Override
	public void run() {
		boolean isCloseGrabberAndResponse = true;
		try {
			//抓取
			grabber = new FFmpegFrameGrabber(url);
			if ("rtsp".equals(url.substring(0, 4))) {
				grabber.setOption("rtsp_transport", "tcp");
				grabber.setOption("stimeout", "5000000");
			}
			grabber.start();
			//end
			if (avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec()
					&& (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec())) {
				logger.info("this url:{} converterFactories start", url);
				// 来源视频H264格式,音频AAC格式
				// 无须转码，更低的资源消耗，更低的延迟
				stream = new ByteArrayOutputStream();
				recorder = new FFmpegFrameRecorder(stream, grabber.getImageWidth(), grabber.getImageHeight(),
						grabber.getAudioChannels());
			 	recorder.setInterleaved(true);
				recorder.setVideoOption("preset", "ultrafast");
				recorder.setVideoOption("tune", "zerolatency");
				recorder.setVideoOption("crf", "25");
				recorder.setFrameRate(grabber.getFrameRate());
				recorder.setSampleRate(grabber.getSampleRate());
				if (grabber.getAudioChannels() > 0) {
					recorder.setAudioChannels(grabber.getAudioChannels());
					recorder.setAudioBitrate(grabber.getAudioBitrate());
					recorder.setAudioCodec(grabber.getAudioCodec());
				}
				recorder.setFormat("flv");



				recorder.setVideoBitrate(grabber.getVideoBitrate());
			    recorder.setVideoCodec(grabber.getVideoCodec());
				recorder.start(grabber.getFormatContext());//这个不报警告，但是不能加水印
				//recorder.start();//这个能加水印，但是又警告
				if (headers == null) {
					headers = stream.toByteArray();
					stream.reset();
					writeResponse(headers);
				}
				while (runing) {
				 	 AVPacket k = grabber.grabPacket();
					if (k != null) {
						try {
							recorder.recordPacket(k);
						} catch (Exception e) {
						}
						if (stream.size() > 0) {
							byte[] b = stream.toByteArray();
							stream.reset();
							writeResponse(b);
							if (outEntitys.isEmpty()) {
								logger.info("没有输出退出");
								break;
							}
						}
						avcodec.av_packet_unref(k);
					}
					Thread.sleep(5);
				}
			} else {
				////system.out.println("为什么会走到这？");
				isCloseGrabberAndResponse = false;
				// 需要转码为视频H264格式,音频AAC格式
				ConverterTranFactories c = new ConverterTranFactories(url, key, factories, outEntitys,grabber,videoId);
				factories.put(key, c);
				c.start();
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			logger.info("播放失败");

		} finally {
			closeConverter(isCloseGrabberAndResponse);
			completeResponse(isCloseGrabberAndResponse);
			logger.info("this url:{} converterFactories exit", url);

		}
	}

	private boolean add = false;
	/**
	 * 输出FLV视频流
	 * 
	 * @param b
	 */
	public void writeResponse(byte[] b) {
		Iterator<AsyncContext> it = outEntitys.iterator();
		while (it.hasNext()) {
			AsyncContext o = it.next();
			try {
				o.getResponse().getOutputStream().write(b);
			} catch (Exception e) {
				logger.info("移除一个输出");
			 	o.complete();
				it.remove();
			}
		}
	}

	/**
	 * 退出转换
	 */
	public void closeConverter(boolean isCloseGrabberAndResponse) {
		try{
		 if (isCloseGrabberAndResponse) {
			 grabber.close();
			factories.remove(this.key);
		 }
			recorder.close();
			stream.close();
		}catch (Exception e){}
	}

	/**
	 * 关闭异步响应
	 * 
	 * @param isCloseGrabberAndResponse
	 */
	public void completeResponse(boolean isCloseGrabberAndResponse) {
		//system.out.println("isCloseGrabberAndResponse="+isCloseGrabberAndResponse);
		if (isCloseGrabberAndResponse) {
			Iterator<AsyncContext> it = outEntitys.iterator();
			while (it.hasNext()) {
				AsyncContext o = it.next();
				//system.out.println("o="+o);
				//o.getResponse().setContentType("application/json;charset=utf-8");
				try{
					o.getResponse().flushBuffer();
				}catch (Exception e){}

				o.complete();
				it.remove();
			}
		}
	}

	@Override
	public String getKey() {
		return this.key;
	}

	@Override
	public String getUrl() {
		return this.url;
	}

	@Override
	public void addOutputStreamEntity(String key, AsyncContext entity) throws IOException {
		if (headers == null) {
			outEntitys.add(entity);
		} else {
			entity.getResponse().getOutputStream().write(headers);
			entity.getResponse().getOutputStream().flush();
			outEntitys.add(entity);
		}
	}

	@Override
	public void exit() {
		this.runing = false;
		try {
			this.join();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

}
