#include "filereader.h"

#ifdef __cplusplus
extern "C"
{
#endif

#include "libavformat/avformat.h"

#ifdef __cplusplus
};
#endif

#define LogWrite
#define INF

CFileReader::CFileReader()
{
	av_register_all();
	avformat_network_init();

	// 设置编解码库日志回调函数
	av_log_set_callback(NULL);

	resetReader();
}
CFileReader::~CFileReader()
{}
void CFileReader::resetReader()
{
	m_bNetFile = false;
	m_bEof = false;

	m_nVideoIndex = -1;
	m_nAudioIndex = -1;

	m_inVideoStream = NULL;
	m_inAudioStream = NULL;

	m_inFormatContext = NULL;
}

int CFileReader::OpenFile(std::string filePath, std::string key/* = ""*/, int nTimeOutValue)
{
	unsigned char *tmp;

	resetReader();

	const char *p = filePath.c_str();

	if (filePath.length() > 4 && (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p'))
	{
		m_bNetFile = true;
	}

	aes_128_key_value_ = key;

	m_inFormatContext = avformat_alloc_context();
	if(!m_inFormatContext)
	{
        //LogWrite("OpenFile failed::LastError = %d, filePath = %s", CUtilityMethod::GetErrorNo(), filePath.c_str());
		return -1;
	}

	//支持hls加密文件的播放
	const AVClass  *c;
	const AVOption *o = NULL;

	AVDictionary *format_opts = NULL;
	if (aes_128_key_value_.length() > 0)
	{
		av_dict_set(&format_opts, "allowed_extensions", "ALL", AV_DICT_MATCH_CASE);
		av_dict_set_int(&format_opts, "cryptkey_extend_len", 16, AV_DICT_MATCH_CASE);
		av_dict_set(&format_opts, "cryptkey_extend", aes_128_key_value_.c_str(), AV_DICT_MATCH_CASE);
	}
	//禁用 HLS 使用多个链接拉取ts文件逻辑，多链接拉取有bug例如，正在播放第二个文件的中间时 取拉取第四个文件，拉取失败时会导致第二个文件剩下的数据无法播放
	av_dict_set(&format_opts, "http_multiple", "0", 0);
	int nRet = avformat_open_input(&m_inFormatContext, filePath.c_str(), 0, &format_opts);
	if (nRet!=0)
	{
		char szBuffer[256] = { 0 };
		av_make_error_string(szBuffer, 256, nRet);
		//LogWrite("OpenFile failed:avformat_open_input return = %d, LastError = %d, filePath = %s", nRet, CUtilityMethod::GetErrorNo(), filePath.c_str());
		goto err;
	}

	nRet = avformat_find_stream_info(m_inFormatContext, 0);
	if (nRet < 0)
	{
		//LogWrite("OpenFile failed:avformat_find_stream_info return = %d, LastError = %d, filePath = %s", nRet, CUtilityMethod::GetErrorNo(), filePath.c_str());
		goto err;
	}

	for(int i = 0; i < m_inFormatContext->nb_streams; i++)
	{
		if((m_inFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
			(m_inFormatContext->streams[i]->codec->codec_id == AV_CODEC_ID_H264))
		{
			m_nVideoIndex = i;
			m_inVideoStream = m_inFormatContext->streams[i];
		}
		if(m_inFormatContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
		{
			m_nAudioIndex = i;
			m_inAudioStream = m_inFormatContext->streams[i];
		}
	}
	//必须包含音频
	if(m_nVideoIndex < 0 && m_nAudioIndex < 0)
	{
		//LogWrite("OpenFile failed:videoindex = %d, audioindex = %d", m_nVideoIndex, m_nAudioIndex);
		goto err;
	}

	return 0;

err:
	CloseFile();
	return -1;
}
int CFileReader::CloseFile()
{
	if(m_inFormatContext)
	{
		avformat_close_input(&m_inFormatContext);
		avformat_free_context(m_inFormatContext);
		m_inFormatContext = NULL;
	}

	return 0;
}
int CFileReader::ReadFile(AVPacket &pkt)
{
	int nRet = av_read_frame(m_inFormatContext, &pkt);
	if(nRet == AVERROR_EOF)
	{
		//LogWrite(INF, "ReadFile failed, return value = %d, LastError = %d", nRet, CUtilityMethod::GetErrorNo());
		return -1;
	}

	return nRet;
}
int CFileReader::PauseRead(bool bPause)
{
	int nRet = 0;

	if (!IsNetFile()) return 0;

	if (bPause)
	{
		nRet = av_read_pause(m_inFormatContext);
	}
	else
	{
		nRet = av_read_play(m_inFormatContext);
	}
	return nRet;
}
double CFileReader::GetFileTimeLength()
{
	if(m_inFormatContext)
	{
		return (m_inFormatContext->duration)/1000000;
	}
	return 0;
}
int	CFileReader::GetVideoSize(int &nWidth, int &nHeight)
{
	if (m_nVideoIndex >= 0 && m_inVideoStream->codec)
	{
		nWidth	= m_inVideoStream->codec->width;
		nHeight = m_inVideoStream->codec->height;
	}
	else
	{
		nWidth = 0;
		nHeight = 0;
	}
	return 0;
}
float CFileReader::GetVideoFrameRate()
{
	if (m_nVideoIndex >= 0)
	{
		float num = m_inVideoStream->avg_frame_rate.num;
		float den = m_inVideoStream->avg_frame_rate.den;
		float tmp = num / den + 0.5;

		return num / den + 0.5;
	}
	return 0;
}

int CFileReader::SeekFile(int streamIndex, long long minTs, long long timestamp_us, long long maxTs, int flags)
{
	avformat_flush(m_inFormatContext);

	return avformat_seek_file(m_inFormatContext, streamIndex, minTs, timestamp_us, maxTs, flags);
}

AVCodecID CFileReader::GetVideoCodecID()
{
	if(m_nVideoIndex >= 0 && m_inVideoStream && m_inVideoStream->codec)
	{
		return m_inVideoStream->codec->codec_id;
	}
	return AV_CODEC_ID_NONE;
}
AVCodecID CFileReader::GetAudioCodecID()
{
	if(m_inAudioStream && m_inAudioStream->codec)
	{
		return m_inAudioStream->codec->codec_id;
	}
	return AV_CODEC_ID_NONE;
}
int CFileReader::GetAudioRawFrameSize()
{
	return 0;
}
int	CFileReader::GetVideoRawFrameSize(int &width, int &height)
{
	width = m_inVideoStream->codec->width;
	height = m_inVideoStream->codec->height;

	return 0;
}
bool CFileReader::IsAnnexb4Video()
{
	//m3u8的ts中, extradata = NULL
	if (!m_inVideoStream->codec->extradata || m_inVideoStream->codec->extradata[0] == 0)
	{
		return true;
	}
	return false;
}
bool CFileReader::IsVideoPacketNeedDecode(char *ptr)
{
	char nType = GetNaluType(ptr, IsAnnexb4Video());

	//m3u8的ts中, extradata = NULL， sps和pps跟随数据一起发来，所以需要解码这种类型的sps和pps
	/*if ((nType == 7 || nType == 8) && m_inVideoStream->codec->extradata)
	{
		return false;
	}

	if (nType == 9)
	{
		return false;
	}*/

	if (nType == 6)
	{
		char subType = GetSeiSubType(ptr, IsAnnexb4Video());
		if (subType != 5)
		{
			return false;
		}
	}
	return true;
}
char CFileReader::GetNaluType(char *ptr, bool isAnnexb)
{
	if (!isAnnexb)
	{
		return (*(ptr + 4) & 0x1f);
	}

	if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 1)    //寻找0x000001
	{
		return (*(ptr + 3) & 0x1f);
	}
	else if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 1)   //寻找0x00000001
	{
		return (*(ptr + 4) & 0x1f);
	}
	return 0;
}
char CFileReader::GetSeiSubType(char *ptr, bool isAnnexb)
{
	if (!isAnnexb)
	{
		return *(ptr + 5);
	}

	if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 1)    //寻找0x000001
	{
		return *(ptr + 4);
	}
	else if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 1)   //寻找0x00000001
	{
		return *(ptr + 5);
	}
	return 0;
}
void CFileReader::SetEof(bool bEof)
{
	m_bEof = bEof;
}
bool CFileReader::GetEof()
{
	return m_bEof;
}