Exemple #1
0
void Project::LoadVideo(agi::fs::path path) {
	if (path.empty()) return;
	if (!DoLoadVideo(path)) return;
	if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file && video_provider->HasAudio())
		DoLoadAudio(video_file, true);

	double dar = video_provider->GetDAR();
	if (dar > 0)
		context->videoController->SetAspectRatio(dar);
	else
		context->videoController->SetAspectRatio(AspectRatio::Default);
	context->videoController->JumpToFrame(0);
}
void VideoContext::LoadKeyframes(agi::fs::path const& filename) {
	if (filename == keyframes_filename || filename.empty()) return;
	try {
		keyframes = agi::keyframe::Load(filename);
		keyframes_filename = filename;
		KeyframesOpen(keyframes);
		config::mru->Add("Keyframes", filename);
	}
	catch (agi::keyframe::Error const& err) {
		wxMessageBox(to_wx(err.GetMessage()), "Error opening keyframes file", wxOK | wxICON_ERROR | wxCENTER, context->parent);
		config::mru->Remove("Keyframes", filename);
	}
	catch (agi::fs::FileSystemError const& err) {
		wxMessageBox(to_wx(err.GetMessage()), "Error opening keyframes file", wxOK | wxICON_ERROR | wxCENTER, context->parent);
		config::mru->Remove("Keyframes", filename);
	}
}
void AudioController::OpenAudio(agi::fs::path const& url)
{
	if (url.empty())
		throw agi::InternalError("AudioController::OpenAudio() was passed an empty string. This must not happen.", 0);

	std::unique_ptr<AudioProvider> new_provider;
	try {
		new_provider = AudioProviderFactory::GetProvider(url);
		config::path->SetToken("?audio", url);
	}
	catch (agi::UserCancelException const&) {
		throw;
	}
	catch (...) {
		config::mru->Remove("Audio", url);
		throw;
	}

	CloseAudio();
	provider = std::move(new_provider);

	try
	{
		player = AudioPlayerFactory::GetAudioPlayer(provider.get());
	}
	catch (...)
	{
		provider.reset();
		throw;
	}

	audio_url = url;

	config::mru->Add("Audio", url);

	try
	{
		AnnounceAudioOpen(provider.get());
	}
	catch (...)
	{
		CloseAudio();
		throw;
	}
}
void VideoContext::LoadTimecodes(agi::fs::path const& filename) {
	if (filename == timecodes_filename || filename.empty()) return;
	try {
		ovr_fps = agi::vfr::Framerate(filename);
		timecodes_filename = filename;
		config::mru->Add("Timecodes", filename);
		OnSubtitlesCommit(0, std::set<const AssEntry*>());
		TimecodesOpen(ovr_fps);
	}
	catch (agi::fs::FileSystemError const& err) {
		wxMessageBox(to_wx(err.GetMessage()), "Error opening timecodes file", wxOK | wxICON_ERROR | wxCENTER, context->parent);
		config::mru->Remove("Timecodes", filename);
	}
	catch (const agi::vfr::Error& e) {
		wxLogError("Timecode file parse error: %s", to_wx(e.GetMessage()));
		config::mru->Remove("Timecodes", filename);
	}
}
void AudioController::SaveClip(agi::fs::path const& filename, TimeRange const& range) const
{
	int64_t start_sample = SamplesFromMilliseconds(range.begin());
	int64_t end_sample = SamplesFromMilliseconds(range.end());
	if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return;

	agi::io::Save outfile(filename, true);
	std::ofstream& out(outfile.Get());

	size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();
	size_t bufsize = (end_sample - start_sample) * bytes_per_sample;

	int intval;
	short shortval;

	out << "RIFF";
	out.write((char*)&(intval=bufsize+36),4);
	out<< "WAVEfmt ";
	out.write((char*)&(intval=16),4);
	out.write((char*)&(shortval=1),2);
	out.write((char*)&(shortval=provider->GetChannels()),2);
	out.write((char*)&(intval=provider->GetSampleRate()),4);
	out.write((char*)&(intval=provider->GetSampleRate()*provider->GetChannels()*provider->GetBytesPerSample()),4);
	out.write((char*)&(intval=provider->GetChannels()*provider->GetBytesPerSample()),2);
	out.write((char*)&(shortval=provider->GetBytesPerSample()<<3),2);
	out << "data";
	out.write((char*)&bufsize,4);

	//samples per read
	size_t spr = 65536 / bytes_per_sample;
	std::vector<char> buf(bufsize);
	for(int64_t i = start_sample; i < end_sample; i += spr) {
		size_t len = std::min<size_t>(spr, end_sample - i);
		provider->GetAudio(&buf[0], i, len);
		out.write(&buf[0], len * bytes_per_sample);
	}
}
void VideoContext::SetVideo(const agi::fs::path &filename) {
	Reset();
	if (filename.empty()) {
		VideoOpen();
		return;
	}

	bool commit_subs = false;
	try {
		provider.reset(new ThreadedFrameSource(filename, context->ass->GetScriptInfo("YCbCr Matrix"), this));
		video_provider = provider->GetVideoProvider();
		video_filename = filename;

		// Check that the script resolution matches the video resolution
		int sx = context->ass->GetScriptInfoAsInt("PlayResX");
		int sy = context->ass->GetScriptInfoAsInt("PlayResY");
		int vx = GetWidth();
		int vy = GetHeight();

		// If the script resolution hasn't been set at all just force it to the
		// video resolution
		if (sx == 0 && sy == 0) {
			context->ass->SetScriptInfo("PlayResX", std::to_string(vx));
			context->ass->SetScriptInfo("PlayResY", std::to_string(vy));
			commit_subs = true;
		}
		// If it has been set to something other than a multiple of the video
		// resolution, ask the user if they want it to be fixed
		else if (sx % vx != 0 || sy % vy != 0) {
			switch (OPT_GET("Video/Check Script Res")->GetInt()) {
			case 1: // Ask to change on mismatch
				if (wxYES != wxMessageBox(
					wxString::Format(_("The resolution of the loaded video and the resolution specified for the subtitles don't match.\n\nVideo resolution:\t%d x %d\nScript resolution:\t%d x %d\n\nChange subtitles resolution to match video?"), vx, vy, sx, sy),
					_("Resolution mismatch"),
					wxYES_NO | wxCENTER,
					context->parent))

					break;
				// Fallthrough to case 2
			case 2: // Always change script res
				context->ass->SetScriptInfo("PlayResX", std::to_string(vx));
				context->ass->SetScriptInfo("PlayResY", std::to_string(vy));
				commit_subs = true;
				break;
			default: // Never change
				break;
			}
		}

		keyframes = video_provider->GetKeyFrames();

		// Set frame rate
		video_fps = video_provider->GetFPS();
		if (ovr_fps.IsLoaded()) {
			int ovr = wxMessageBox(_("You already have timecodes loaded. Would you like to replace them with timecodes from the video file?"), _("Replace timecodes?"), wxYES_NO | wxICON_QUESTION);
			if (ovr == wxYES) {
				ovr_fps = agi::vfr::Framerate();
				timecodes_filename.clear();
			}
		}

		// Set aspect ratio
		double dar = video_provider->GetDAR();
		if (dar > 0)
			SetAspectRatio(dar);

		// Set filename
		config::mru->Add("Video", filename);
		config::path->SetToken("?video", filename);

		// Show warning
		std::string warning = video_provider->GetWarning();
		if (!warning.empty())
			wxMessageBox(to_wx(warning), "Warning", wxICON_WARNING | wxOK);

		has_subtitles = false;
		if (agi::fs::HasExtension(filename, "mkv"))
			has_subtitles = MatroskaWrapper::HasSubtitles(filename);

		provider->LoadSubtitles(context->ass);
		VideoOpen();
		KeyframesOpen(keyframes);
		TimecodesOpen(FPS());
	}
	catch (agi::UserCancelException const&) { }
	catch (agi::fs::FileSystemError const& err) {
		config::mru->Remove("Video", filename);
		wxMessageBox(to_wx(err.GetMessage()), "Error setting video", wxOK | wxICON_ERROR | wxCENTER);
	}
	catch (VideoProviderError const& err) {
		wxMessageBox(to_wx(err.GetMessage()), "Error setting video", wxOK | wxICON_ERROR | wxCENTER);
	}

	if (commit_subs)
		context->ass->Commit(_("change script resolution"), AssFile::COMMIT_SCRIPTINFO);
	else
		JumpToFrame(0);
}