package com.jisco.dynamicweight.dynamicbasecamera.factories;

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/>
 * 流来源不是视频H264格式,音频AAC格式 转码为视频H264格式,音频AAC格式
 * 
 * @author hzj
 */
public class ConverterTranFactories extends Thread implements Converter {
	private static Logger logger	= LoggerFactory.getLogger( ConverterTranFactories.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 ConverterTranFactories(String url, String key, Map<String, Converter> factories,
                                  List<AsyncContext> outEntitys, FFmpegFrameGrabber grabber,String videoId) {
		this.url = url;
		this.key = key;
		this.factories = factories;
		this.outEntitys = outEntitys;
		this.grabber = grabber;
		this.videoId = videoId;
	}

	@Override
	public void run() {
		try {
			logger.info("this url:{} converterTranFactories start", url);
			grabber.setFrameRate(25);
			if (grabber.getImageWidth() > 1920) {
				grabber.setImageWidth(1920);
			}
			if (grabber.getImageHeight() > 1080) {
				grabber.setImageHeight(1080);
			}
			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.setGopSize(50);
			recorder.setFrameRate(25);
			recorder.setSampleRate(grabber.getSampleRate());
			if (grabber.getAudioChannels() > 0) {
				recorder.setAudioChannels(grabber.getAudioChannels());
				recorder.setAudioBitrate(grabber.getAudioBitrate());
				recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
			}
			recorder.setFormat("flv");
			recorder.setVideoBitrate(grabber.getVideoBitrate());
			recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
			recorder.start();
			if (headers == null) {
				headers = stream.toByteArray();
				stream.reset();
				writeResponse(headers);
			}
			while (runing) {
				// 抓取一帧
				Frame f = grabber.grab();
				if (f != null) {
					try {
						// 转码
						recorder.record(f);
					} catch (Exception e) {
					}
					if (stream.size() > 0) {
						byte[] b = stream.toByteArray();
						stream.reset();
						writeResponse(b);
						if (outEntitys.isEmpty()) {
							logger.info("没有输出退出");
							break;
						}
					}
				}
				Thread.sleep(5);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);

		} finally {
			closeConverter();
			completeResponse();
			logger.info("this url:{} converterTranFactories exit", url);
			factories.remove(this.key);
		}
	}

	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("移除一个输出",e);
				o.complete();
				it.remove();
			}
		}

	}

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

	}

	/**
	 * 关闭异步响应
	 */
	public void completeResponse() {
		Iterator<AsyncContext> it = outEntitys.iterator();
		while (it.hasNext()) {
			AsyncContext o = it.next();
			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);
		}
	}



}
