/////////////////////////////////////////////////////////////////////////////
// Name:        mediatrc_ffmpeg.cpp
// Purpose:     FFMPEG Media Transcoder
// Author:      Alex Thuering
// Created:     26.04.2008
// RCS-ID:      $Id: mediatrc_ffmpeg.cpp,v 1.53 2012/11/05 21:10:32 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "mediatrc_ffmpeg.h"
#include "Utils.h"
#include "Config.h"
#include <wx/wx.h>

wxFfmpegMediaTranscoder::wxFfmpegMediaTranscoder(int threadCount) {
	m_threadCount = threadCount;
	m_cmd = s_config.GetAVConvCmd();
	m_inputFileCount = 0;
}

wxFfmpegMediaTranscoder::~wxFfmpegMediaTranscoder() {
}

void wxFfmpegMediaTranscoder::Init() {
}

void wxFfmpegMediaTranscoder::AddOption(const wxString& name, const wxString& value) {
	m_cmd += wxString::Format(wxT(" -%s %s"), name.c_str(), value.c_str());
}

void wxFfmpegMediaTranscoder::AddAudioOption(const wxString& name, int streamIdx, const wxString& value) {
	m_cmd += wxString::Format(wxT(" -%s:a:%d %s"), name.c_str(), streamIdx, value.c_str());
}

void wxFfmpegMediaTranscoder::AddVideoOption(const wxString& name, int streamIdx, const wxString& value) {
	m_cmd += wxString::Format(wxT(" -%s:v:%d %s"), name.c_str(), streamIdx, value.c_str());
}

bool wxFfmpegMediaTranscoder::AddInputFile(const wxString& fileName, bool disableVideo, bool disableAudio,
		bool disableSubtitle, long tsOffset) {
	m_cmd += wxString::Format(wxT(" -i \"%s\""), fileName.c_str());
	if (tsOffset > 0)
		AddOption(wxT("itsoffset"), Time2String(tsOffset));
	
	m_inputFileCount++;
	return true;
}

bool wxFfmpegMediaTranscoder::SetOutputFile(const wxString& fileName, VideoFormat videoFormat, bool ntscFilm,
		AudioFormat audioFormat, SubtitleFormat subtitleFormat, int videoBitrate, bool vbr, int audioBitrate,
		int mapStreamIdx) {
	wxArrayInt audioFormats;
	if (audioFormat != afNONE)
		audioFormats.Add(audioFormat);
	wxArrayInt subtitleFormats;
	subtitleFormats.Add(subtitleFormat);
	return SetOutputFile(fileName, videoFormat, ntscFilm, audioFormats, subtitleFormats, videoBitrate, vbr,
			audioBitrate, mapStreamIdx);
}

bool wxFfmpegMediaTranscoder::SetOutputFile(const wxString& fileName, VideoFormat videoFormat, bool ntscFilm,
		wxArrayInt audioFormats, wxArrayInt subtitleFormats, int videoBitrate, bool vbr, int audioBitrate,
		int mapStreamIdx) {
	if (m_outputFormat.length() > 0)
		AddOption(wxT("f"), m_outputFormat);
	else if (audioFormats.GetCount() == 0)
		AddOption(wxT("f"), wxT("mpeg2video"));
	else if (videoFormat == vfNONE)
		AddOption(wxT("f"), audioFormats.size() && audioFormats[0] == afMP2 ? wxT("mp2")
				: audioFormats[0] == afAC3 ? wxT("ac3") : wxT("s16be"));
	else
		AddOption(wxT("f"), wxT("dvd"));
	
	if (videoFormat == vfCOPY) {
		AddVideoOption(wxT("c"), 0, wxT("copy"));
	} else if (videoFormat != vfNONE) {
		AddVideoOption(wxT("c"), 0, wxT("mpeg2video"));
		
		wxSize frameSize = GetFrameSize(videoFormat);
		AddOption(wxT("s"), wxString::Format(wxT("%dx%d"), frameSize.x, frameSize.y));
		
		if (!isNTSC(videoFormat)) {
			AddOption(wxT("r"), wxT("25"));
			AddOption(wxT("g"), wxT("15"));
		} else {
			AddOption(wxT("r"), ntscFilm ? wxT("24000/1001") : wxT("30000/1001"));
			AddOption(wxT("g"), wxT("18"));
		}

		AddVideoOption(wxT("b"), 0, wxString::Format(wxT("%d"), videoBitrate * 1000));
		AddVideoOption(wxT("maxrate"), 0, wxString::Format(wxT("%d"), vbr ? 9000000 : videoBitrate * 1000));
		AddVideoOption(wxT("minrate"), 0, wxString::Format(wxT("%d"), vbr ? 0 : videoBitrate * 1000));
		AddVideoOption(wxT("bufsize"), 0, wxT("1835008")); // 224*1024*8;

		AddOption(wxT("packetsize"), wxT("2048"));  // from www.mpucoder.com: DVD sectors contain 2048 bytes of data, this is also the size of one pack.
		AddOption(wxT("muxrate"), wxT("10080000")); // from mplex project: data_rate = 1260000. mux_rate = data_rate * 8
		
		if (m_videoFilters.length())
			AddOption(wxT("vf"), m_videoFilters);
	}

	AddOption(wxT("b:a"), wxString::Format(wxT("%d"), audioBitrate * 1000));
	AddOption(wxT("ar"), wxT("48000"));
	
	int outputStreamIdx = 0;
	for (unsigned int i = 0; i < audioFormats.size(); i++) {
		if (audioFormats[i] != afNONE) {
			AddAudioOption(wxT("c"), outputStreamIdx, audioFormats[i] == afCOPY ? wxT("copy") : audioFormats[i] == afMP2
						? wxT("mp2") : audioFormats[i] == afAC3 ? wxT("ac3") : wxT("pcm_s16be"));
			if (audioFormats[i] >= afAC3 && m_channelNumber.find(i) != m_channelNumber.end())
				AddAudioOption(wxT("af"), outputStreamIdx, wxString::Format(wxT("aformat=channel_layouts\\=%d"), m_channelNumber[i]));
			outputStreamIdx++;
		}
	}
	for(map<int,int>::const_iterator i = m_audioVolume.begin(); i != m_audioVolume.end(); ++i) {
		if (i->second != 256) {
			AddOption(wxT("vol"), wxString::Format(wxT("%d"), i->second));
			break;
		}
	}
	
	if (mapStreamIdx >= 0) {
		AddOption(wxT("map"), wxString::Format(wxT("%d"), mapStreamIdx));
	} else {
		// map all input files into one
		for (int i = 0; i < m_inputFileCount; i++)
			AddOption(wxT("map"), wxString::Format(wxT("%d"), i));
		// disable video streams if videoFormat is "none"
		if (videoFormat == vfNONE)
			for (int i = 0; i < m_inputFileCount; i++)
				AddOption(wxT("map"), wxString::Format(wxT("-%d:v"), i));
		// disable data streams
		for (int i = 0; i < m_inputFileCount; i++)
			AddOption(wxT("map"), wxString::Format(wxT("-%d:d"), i));
		// disable streams with format 'none'
		for (unsigned int i = 0; i < audioFormats.size(); i++) {
			if (audioFormats[i] == afNONE)
				AddOption(wxT("map"), wxString::Format(wxT("-0:a:%d"), i));
		}
		for (unsigned int i = 0; i < subtitleFormats.size(); i++) {
			if (subtitleFormats[i] == sfNONE)
				AddOption(wxT("map"), wxString::Format(wxT("-0:s:%d"), i));
		}
	}
	
	m_cmd += wxString::Format(wxT(" \"%s\""), fileName.c_str());
	return true;
}

/** Sets interlaced encoding flag */
void wxFfmpegMediaTranscoder::SetInterlaced(bool value) {
	if (value)
		AddVideoOption(wxT("flags"), 0, wxT("+ilme+ildct"));
}

/** Sets field first flag (Auto, TFF, BFF) */
void wxFfmpegMediaTranscoder::SetFirstField(FirstField firstField) {
	if (firstField != ffAUTO)
		AddVideoOption(wxT("top"), 0, wxString::Format(wxT("%d"), firstField));
}

/** Sets start time */
void wxFfmpegMediaTranscoder::SetStartTime(double startTime) {
	if (startTime != 0)
		AddOption(wxT("ss"), wxString::Format(wxT("%f"), startTime));
}

/** Sets recording time */
void wxFfmpegMediaTranscoder::SetRecordingTime(double recordingTime) {
	if (recordingTime != 0)
		AddOption(wxT("t"), wxString::Format(wxT("%f"), recordingTime));
}

/** Starts transcoding. */
bool wxFfmpegMediaTranscoder::Run(bool& canceled) {
	End();
	return true;
}

void wxFfmpegMediaTranscoder::End() {
	m_cmd = s_config.GetAVConvCmd();
}
