841 lines
27 KiB
C++
841 lines
27 KiB
C++
#include "pch.h"
|
|
#include "app.h"
|
|
#include "action.h"
|
|
#include "node_dialog_open.h"
|
|
#include "node_dialog_browse.h"
|
|
#include "node_dialog_resize.h"
|
|
#include "node_dialog_cloud.h"
|
|
#include "node_about.h"
|
|
#include "node_changelog.h"
|
|
#include "node_usermanual.h"
|
|
#include "node_dialog_export_ppbr.h"
|
|
|
|
#include <codec_api.h>
|
|
#define MP4V2_NO_STDINT_DEFS
|
|
#include <mp4v2/mp4v2.h>
|
|
|
|
#ifdef __QUEST__
|
|
#include "oculus_vr.h"
|
|
#elif __WEB__
|
|
void webgl_pick_file(std::function<void(std::string)> callback);
|
|
void webgl_pick_file_save(const std::string& path,
|
|
const std::string& name, std::function<void(bool)> callback);
|
|
void webgl_sync();
|
|
#endif
|
|
|
|
std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title, int total /*= 0*/)
|
|
{
|
|
auto pb = std::make_shared<NodeProgressBar>();
|
|
pb->set_manager(&layout);
|
|
pb->init();
|
|
pb->create();
|
|
pb->loaded();
|
|
pb->m_progress->SetWidthP(0);
|
|
pb->m_title->set_text(title.c_str());
|
|
pb->m_total = total;
|
|
pb->m_count = 0;
|
|
layout[main_id]->add_child(pb);
|
|
return pb;
|
|
}
|
|
|
|
std::shared_ptr<NodeMessageBox> App::message_box(const std::string &title, const std::string& text, bool cancel_button)
|
|
{
|
|
auto m = std::make_shared<NodeMessageBox>();
|
|
m->set_manager(&layout);
|
|
m->init();
|
|
m->create();
|
|
m->loaded();
|
|
m->m_title->set_text(title.c_str());
|
|
m->m_message->set_text(text.c_str());
|
|
m->btn_ok->m_text->set_text("Ok");
|
|
if (!cancel_button)
|
|
m->btn_cancel->destroy();
|
|
layout[main_id]->add_child(m);
|
|
return m;
|
|
}
|
|
|
|
std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
|
const std::string& field_name, const std::string& ok_caption /*= "Ok"*/)
|
|
{
|
|
auto m = std::make_shared<NodeInputBox>();
|
|
m->set_manager(&layout);
|
|
m->init();
|
|
m->create();
|
|
m->loaded();
|
|
m->m_title->set_text(title.c_str());
|
|
m->m_field_name->set_text(field_name.c_str());
|
|
m->btn_ok->m_text->set_text(ok_caption.c_str());
|
|
layout[main_id]->add_child(m);
|
|
return m;
|
|
}
|
|
|
|
void App::dialog_usermanual()
|
|
{
|
|
auto dialog = std::make_shared<NodeUserManual>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
}
|
|
|
|
void App::dialog_changelog()
|
|
{
|
|
auto dialog = std::make_shared<NodeChangelog>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
}
|
|
|
|
void App::dialog_about()
|
|
{
|
|
auto dialog = std::make_shared<NodeAbout>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
}
|
|
|
|
void App::dialog_newdoc()
|
|
{
|
|
auto show_dialog = [this] {
|
|
auto dialog = std::make_shared<NodeDialogNewDoc>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
dialog->input->set_text("name");
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
|
|
App::I->showKeyboard();
|
|
|
|
dialog->btn_ok->on_click = [this, dialog](Node*)
|
|
{
|
|
std::string name = dialog->input->m_text;
|
|
std::string path = work_path + "/" + name + ".ppi";
|
|
|
|
if (name.empty())
|
|
{
|
|
message_box("Warning", "You need to specify a name to file.");
|
|
return;
|
|
}
|
|
|
|
auto action = [this, dialog, name, path] {
|
|
std::array<int, 6> resolutions{ 512, 1024, 1536, 2048, 4096, 8192 };
|
|
int res = resolutions[dialog->m_resolution->m_current_index];
|
|
doc_name = name;
|
|
doc_path = path;
|
|
doc_filename = name + ".ppi";
|
|
doc_dir = work_path;
|
|
|
|
layers->clear();
|
|
canvas->m_canvas->m_layers.clear();
|
|
canvas->m_canvas->resize(res, res);
|
|
canvas->reset_camera();
|
|
ActionManager::clear();
|
|
|
|
layers->add_layer("Default", false, true);
|
|
|
|
canvas->m_canvas->m_unsaved = true;
|
|
canvas->m_canvas->m_newdoc = false;
|
|
title_update();
|
|
|
|
dialog->destroy();
|
|
App::I->hideKeyboard();
|
|
};
|
|
|
|
if (Asset::exist(path))
|
|
{
|
|
// ask confirm is file already exist
|
|
auto msgbox = new NodeMessageBox();
|
|
msgbox->set_manager(&layout);
|
|
msgbox->init();
|
|
msgbox->m_title->set_text("Warning");
|
|
msgbox->m_message->set_text("A document with this name already exists, continue?");
|
|
msgbox->btn_ok->on_click = [this, msgbox, action](Node*) {
|
|
action();
|
|
msgbox->destroy();
|
|
};
|
|
layout[main_id]->add_child(msgbox);
|
|
}
|
|
else
|
|
{
|
|
action();
|
|
}
|
|
|
|
};
|
|
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
|
{
|
|
dialog->destroy();
|
|
App::I->hideKeyboard();
|
|
};
|
|
};
|
|
|
|
if (canvas)
|
|
{
|
|
if (Canvas::I->m_unsaved)
|
|
{
|
|
auto m = layout[main_id]->add_child<NodeMessageBox>();
|
|
m->m_title->set_text("Unsaved document");
|
|
m->m_message->set_text("Would you like to save this document before closing?");
|
|
m->btn_ok->m_text->set_text("Yes");
|
|
m->btn_cancel->m_text->set_text("No");
|
|
m->btn_ok->on_click = [this, m, show_dialog](Node*) {
|
|
Canvas::I->project_save([this, m, show_dialog](bool success){
|
|
if (success)
|
|
show_dialog();
|
|
else
|
|
message_box("Saving Error", "There was a problem saving the document");
|
|
});
|
|
m->destroy();
|
|
};
|
|
m->btn_cancel->on_click = [this, m, show_dialog](Node*) {
|
|
show_dialog();
|
|
m->destroy();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
show_dialog();
|
|
}
|
|
}
|
|
}
|
|
|
|
// DEPRECATED
|
|
void App::dialog_open()
|
|
{
|
|
auto show_dialog = [this] {
|
|
// load thumbnail test
|
|
auto dialog = std::make_shared<NodeDialogOpen>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
|
|
dialog->btn_ok->on_click = [this, dialog](Node*)
|
|
{
|
|
// canvas->reset_camera();
|
|
// layers->clear();
|
|
// doc_name = dialog->selected_name;
|
|
// canvas->m_canvas->project_open(dialog->selected_path, [this](bool success) {
|
|
// // on complete
|
|
// async_start();
|
|
// title_update();
|
|
// for (auto& i : canvas->m_canvas->m_order)
|
|
// layers->add_layer(canvas->m_canvas->m_layers[i]->m_name.c_str());
|
|
// async_end();
|
|
// });
|
|
// dialog->destroy();
|
|
// ActionManager::clear();
|
|
};
|
|
};
|
|
|
|
if (canvas)
|
|
{
|
|
if (Canvas::I->m_unsaved)
|
|
{
|
|
auto m = layout[main_id]->add_child<NodeMessageBox>();
|
|
m->m_title->set_text("Unsaved document");
|
|
m->m_message->set_text("Would you like to save this document before closing?");
|
|
m->btn_ok->m_text->set_text("Yes");
|
|
m->btn_cancel->m_text->set_text("No");
|
|
m->btn_ok->on_click = [this,m,show_dialog](Node*){
|
|
Canvas::I->project_save([this,m,show_dialog](bool success){
|
|
if (success)
|
|
show_dialog();
|
|
else
|
|
message_box("Saving Error", "There was a problem saving the document");
|
|
});
|
|
m->destroy();
|
|
};
|
|
m->btn_cancel->on_click = [this,m,show_dialog](Node*) {
|
|
show_dialog();
|
|
m->destroy();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
show_dialog();
|
|
}
|
|
}
|
|
}
|
|
|
|
void App::dialog_browse()
|
|
{
|
|
auto show_dialog = [this] {
|
|
// load thumbnail test
|
|
auto dialog = std::make_shared<NodeDialogBrowse>();
|
|
dialog->set_manager(&layout);
|
|
#ifdef __IOS__
|
|
dialog->search_paths = {work_path, data_path + "/Inbox"};
|
|
#else
|
|
dialog->search_paths = {work_path};
|
|
#endif
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
|
|
dialog->btn_ok->on_click = [this, dialog](Node*)
|
|
{
|
|
if (dialog->is_selected())
|
|
{
|
|
open_document(dialog->selected_path);
|
|
dialog->destroy();
|
|
}
|
|
};
|
|
};
|
|
|
|
if (canvas)
|
|
{
|
|
if (Canvas::I->m_unsaved)
|
|
{
|
|
auto m = layout[main_id]->add_child<NodeMessageBox>();
|
|
m->m_title->set_text("Unsaved document");
|
|
m->m_message->set_text("Would you like to save this document before closing?");
|
|
m->btn_ok->m_text->set_text("Yes");
|
|
m->btn_cancel->m_text->set_text("No");
|
|
m->btn_ok->on_click = [this, m, show_dialog](Node*) {
|
|
Canvas::I->project_save([this, m, show_dialog](bool success){
|
|
if (success)
|
|
show_dialog();
|
|
else
|
|
message_box("Saving Error", "There was a problem saving the document");
|
|
});
|
|
m->destroy();
|
|
};
|
|
m->btn_cancel->on_click = [this, m, show_dialog](Node*) {
|
|
show_dialog();
|
|
m->destroy();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
show_dialog();
|
|
}
|
|
}
|
|
}
|
|
|
|
void App::dialog_save_ver()
|
|
{
|
|
if (!check_license())
|
|
{
|
|
message_box("License", "This function is disabled in demo mode.");
|
|
return;
|
|
}
|
|
|
|
int current = 0;
|
|
std::string next = doc_name + ".01";
|
|
std::string base = doc_name;
|
|
|
|
std::regex r(R"((.*)\.(\w{2})$)");
|
|
std::smatch m;
|
|
if (std::regex_search(doc_name, m, r))
|
|
{
|
|
base = m[1].str();
|
|
current = atoi(m[2].str().c_str());
|
|
}
|
|
|
|
for (int i = current + 1; i < 99; i++)
|
|
{
|
|
static char tmp_name[256];
|
|
sprintf(tmp_name, "%s.%02d", base.c_str(), i);
|
|
next = tmp_name;
|
|
if (Asset::exist(doc_dir + "/" + next + ".ppi"))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
doc_name = next;
|
|
doc_path = doc_dir + "/" + next + ".ppi";
|
|
canvas->m_canvas->m_unsaved = true;
|
|
title_update();
|
|
canvas->m_canvas->project_save(doc_path);
|
|
}
|
|
|
|
void App::dialog_save()
|
|
{
|
|
if (!check_license())
|
|
{
|
|
message_box("License", "This function is disabled in demo mode.");
|
|
return;
|
|
}
|
|
|
|
if (canvas)
|
|
{
|
|
auto dialog = std::make_shared<NodeDialogSave>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
dialog->input->set_text(doc_name);
|
|
|
|
App::I->showKeyboard();
|
|
|
|
dialog->btn_ok->on_click = [this, dialog](Node*)
|
|
{
|
|
std::string name = dialog->input->m_text;
|
|
std::string path = work_path + "/" + name + ".ppi";
|
|
|
|
if (name.empty())
|
|
{
|
|
message_box("Warning", "You need to specify a name to file.");
|
|
return;
|
|
}
|
|
|
|
auto action = [this, dialog, name, path] {
|
|
canvas->m_canvas->project_save(path);
|
|
doc_name = name;
|
|
doc_path = path;
|
|
doc_dir = work_path;
|
|
title_update();
|
|
dialog->destroy();
|
|
App::I->hideKeyboard();
|
|
};
|
|
|
|
if (Asset::exist(path))
|
|
{
|
|
// ask confirm is file already exist
|
|
auto msgbox = new NodeMessageBox();
|
|
msgbox->set_manager(&layout);
|
|
msgbox->init();
|
|
msgbox->m_title->set_text("Warning");
|
|
msgbox->m_message->set_text(("Are you sure you want to overwrite " + name + "?").c_str());
|
|
msgbox->btn_ok->on_click = [this, msgbox, action](Node*) {
|
|
action();
|
|
msgbox->destroy();
|
|
};
|
|
layout[main_id]->add_child(msgbox);
|
|
}
|
|
else
|
|
{
|
|
action();
|
|
}
|
|
};
|
|
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
|
{
|
|
dialog->destroy();
|
|
App::I->hideKeyboard();
|
|
};
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
}
|
|
}
|
|
|
|
void App::dialog_export(std::string ext)
|
|
{
|
|
if (!check_license())
|
|
{
|
|
message_box("License", "This function is disabled in demo mode.");
|
|
return;
|
|
}
|
|
|
|
if (canvas)
|
|
{
|
|
// TODO: use picker
|
|
auto path = work_path + "/" + doc_name + ext;
|
|
auto name = doc_name + ext;
|
|
canvas->m_canvas->export_equirectangular(path, [this, path, name]{
|
|
#if defined(__IOS__)
|
|
message_box("Export Equirectangular", "Image exported to Photos");
|
|
#elif defined(__OSX__)
|
|
message_box("Export Equirectangular", "Image exported to Pictures/PanoPainter folder");
|
|
#elif defined(_WIN32)
|
|
message_box("Export Equirectangular", "Image exported to " + work_path);
|
|
#elif defined(__QUEST__)
|
|
//auto result = ovr_Media_ShareToFacebook("Sharing from PanoPainter on Oculus Quest", path.c_str(), ovrMediaContentType_Photo);
|
|
#elif __WEB__
|
|
ui_task([=]{
|
|
webgl_pick_file_save(path, name, [](bool success){ });
|
|
});
|
|
#endif
|
|
});
|
|
}
|
|
}
|
|
|
|
void App::dialog_export_layers()
|
|
{
|
|
if (!check_license())
|
|
{
|
|
message_box("License", "This function is disabled in demo mode.");
|
|
return;
|
|
}
|
|
|
|
if (canvas)
|
|
{
|
|
#if defined(__IOS__)
|
|
auto dir = work_path + "/" + doc_name + "_layers";
|
|
if (Asset::create_dir(dir))
|
|
{
|
|
auto p = dir + "/" + doc_name;
|
|
canvas->m_canvas->export_layers(p, [this, p] {
|
|
message_box("Export Layers", "Image layers exported to Files/PanoPainter");
|
|
});
|
|
}
|
|
#else
|
|
pick_dir([this](std::string path) {
|
|
auto p = path + "/" + doc_name;
|
|
canvas->m_canvas->export_layers(p, [this, p] {
|
|
message_box("Export Layers", "Layers exported to: " + p);
|
|
});
|
|
});
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void App::dialog_export_depth()
|
|
{
|
|
if (!check_license())
|
|
{
|
|
message_box("License", "This function is disabled in demo mode.");
|
|
return;
|
|
}
|
|
|
|
if (canvas)
|
|
{
|
|
// TODO: use picker
|
|
canvas->m_canvas->export_depth(doc_name, [this] {
|
|
#if defined(__IOS__)
|
|
message_box("Export 3D View + Depth", "Image and depth exported to Files/PanoPainter");
|
|
#elif defined(__OSX__)
|
|
message_box("Export 3D View + Depth", "Image and depth exported to Pictures/PanoPainter folder");
|
|
#elif defined(_WIN32)
|
|
message_box("Export 3D View + Depth", "Image and depth exported to " + work_path);
|
|
#endif
|
|
});
|
|
}
|
|
}
|
|
|
|
void App::dialog_resize()
|
|
{
|
|
auto dialog = std::make_shared<NodeDialogResize>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
|
|
dialog->btn_ok->on_click = [this,dialog](Node*)
|
|
{
|
|
int res = dialog->get_resolution();
|
|
if (canvas)
|
|
canvas->m_canvas->resize(res, res);
|
|
App::I->title_update();
|
|
ActionManager::clear();
|
|
dialog->destroy();
|
|
};
|
|
}
|
|
|
|
void App::dialog_export_cube_faces()
|
|
{
|
|
if (canvas)
|
|
{
|
|
canvas->m_canvas->export_cube_faces(doc_name, [this] {
|
|
#if defined(__IOS__)
|
|
message_box("Export Cube Faces", "Image and depth exported to Files/PanoPainter");
|
|
#elif defined(__OSX__)
|
|
message_box("Export Cube Faces", "Image and depth exported to Pictures/PanoPainter folder");
|
|
#elif defined(_WIN32)
|
|
message_box("Export Cube Faces", "Image and depth exported to " + work_path);
|
|
#endif
|
|
});
|
|
}
|
|
}
|
|
|
|
void App::dialog_layer_rename()
|
|
{
|
|
auto dialog = std::make_shared<NodeDialogLayerRename>();
|
|
dialog->set_manager(&layout);
|
|
dialog->init();
|
|
dialog->create();
|
|
dialog->loaded();
|
|
dialog->input->set_text(layers->m_current_layer->m_label_text);
|
|
|
|
App::I->showKeyboard();
|
|
|
|
layout[main_id]->add_child(dialog);
|
|
|
|
dialog->btn_ok->on_click = [this,dialog](Node*)
|
|
{
|
|
struct ActionLayerRename : public Action
|
|
{
|
|
std::string m_old_name;
|
|
std::string m_new_name;
|
|
bool m_unsaved;
|
|
Layer* m_layer;
|
|
std::shared_ptr<NodeLayer> m_layer_node;
|
|
ActionLayerRename(std::string old_name, std::string new_name, std::shared_ptr<NodeLayer> layer_node, Layer* layer) :
|
|
m_old_name(old_name), m_new_name(new_name), m_layer_node(layer_node), m_layer(layer) { }
|
|
virtual void run() override { }
|
|
virtual size_t memory() override { return 0; }
|
|
virtual void undo() override
|
|
{
|
|
m_layer_node->set_name(m_old_name.c_str());
|
|
m_layer->m_name = m_old_name;
|
|
}
|
|
virtual Action* get_redo() override
|
|
{
|
|
return new ActionLayerRename(m_new_name, m_old_name, m_layer_node, m_layer);
|
|
}
|
|
};
|
|
auto layer_node = std::static_pointer_cast<NodeLayer>(layers->m_current_layer->shared_from_this());
|
|
auto* layer = canvas->m_canvas->m_layers[canvas->m_canvas->m_current_layer_idx].get();
|
|
ActionManager::add(new ActionLayerRename(layers->m_current_layer->m_label_text, dialog->get_name(), layer_node, layer));
|
|
layer_node->set_name(dialog->get_name().c_str());
|
|
layer->m_name = dialog->get_name();
|
|
dialog->destroy();
|
|
App::I->hideKeyboard();
|
|
};
|
|
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
|
{
|
|
dialog->destroy();
|
|
App::I->hideKeyboard();
|
|
};
|
|
}
|
|
|
|
void App::dialog_preset_download()
|
|
{
|
|
|
|
}
|
|
|
|
void App::dialog_ppbr_export()
|
|
{
|
|
auto root = layout[main_id];
|
|
auto dialog = root->add_child_ref<NodeDialogExportPPBR>();
|
|
dialog->btn_ok->on_click = [this, dialog] (Node*) {
|
|
NodePanelBrushPreset::PPBRInfo info;
|
|
info.author = dialog->txt_author->m_text;
|
|
info.url = dialog->txt_url->m_text;
|
|
info.email = dialog->txt_email->m_text;
|
|
info.descr = dialog->txt_descr->m_text;
|
|
info.header_image = dialog->m_header_image;
|
|
info.dest_path = dialog->m_dest_path;
|
|
if (dialog->export_check)
|
|
info.export_data = dialog->export_check->checked;
|
|
#if __IOS__ || __WEB__
|
|
App::I->pick_file_save("ppbr", "exported-brushes",
|
|
[this, dialog, info] (std::string path) {
|
|
presets->export_ppbr(path, info);
|
|
},
|
|
[dialog] (const std::string& path, bool saved) {
|
|
if (saved)
|
|
dialog->destroy();
|
|
}
|
|
);
|
|
#else
|
|
App::I->pick_file_save({ "ppbr" }, [this, dialog, info] (std::string path) {
|
|
std::thread([this, path, dialog, info] {
|
|
BT_SetTerminate();
|
|
presets->export_ppbr(path, info);
|
|
dialog->destroy();
|
|
App::I->message_box("Export PPBR", "Brushes exported to:\n" + path);
|
|
}).detach();
|
|
});
|
|
#endif
|
|
};
|
|
}
|
|
|
|
void App::dialog_timelapse_export()
|
|
{
|
|
#if __IOS__ || __WEB__
|
|
pick_file_save("mp4", doc_name + "-timelapse",
|
|
[this](std::string path) {
|
|
rec_export(path);
|
|
},
|
|
[this](const std::string& path, bool saved) {
|
|
message_box("Export Timelapse", "Timelapse exported succesfully.");
|
|
}
|
|
);
|
|
#else
|
|
pick_file_save({ "mp4" }, [this](std::string path) {
|
|
std::thread([this, path] {
|
|
BT_SetTerminate();
|
|
rec_export(path);
|
|
message_box("Export Timelapse", "Timelapse exported to: " + path);
|
|
}).detach();
|
|
});
|
|
#endif
|
|
}
|
|
|
|
void App::dialog_export_mp4()
|
|
{
|
|
std::thread([this] {
|
|
const int video_width = 1024;
|
|
const int video_height = 512;
|
|
const int video_frames = 500;
|
|
|
|
auto pb = show_progress("Export MP4", video_frames);
|
|
|
|
ISVCEncoder* encoder;
|
|
int rv = WelsCreateSVCEncoder(&encoder);
|
|
|
|
//SEncParamBase param;
|
|
//memset(¶m, 0, sizeof(SEncParamBase));
|
|
//param.iUsageType = EUsageType::CAMERA_VIDEO_REAL_TIME; //from EUsageType enum
|
|
//param.fMaxFrameRate = 25;
|
|
//param.iPicWidth = 400;
|
|
//param.iPicHeight = 400;
|
|
//param.iTargetBitrate = 5000000;
|
|
//param.iRCMode = RC_TIMESTAMP_MODE;
|
|
//encoder->Initialize(¶m);
|
|
|
|
//Encoder params
|
|
SEncParamExt param;
|
|
encoder->GetDefaultParams(¶m);
|
|
param.iUsageType = CAMERA_VIDEO_REAL_TIME;
|
|
param.fMaxFrameRate = 25.f;
|
|
param.iLtrMarkPeriod = 75;
|
|
param.iPicWidth = 1024;
|
|
param.iPicHeight = 512;
|
|
param.iTargetBitrate = 1000 << 10;
|
|
param.bEnableDenoise = false;
|
|
param.iSpatialLayerNum = 1;
|
|
param.bUseLoadBalancing = false;
|
|
param.bEnableSceneChangeDetect = false;
|
|
param.bEnableBackgroundDetection = false;
|
|
param.bEnableAdaptiveQuant = false;
|
|
param.bEnableFrameSkip = false;
|
|
param.iMultipleThreadIdc = 0;
|
|
//param.uiIntraPeriod = 10;
|
|
|
|
for (int i = 0; i < param.iSpatialLayerNum; i++)
|
|
{
|
|
param.sSpatialLayers[i].iVideoWidth = param.iPicWidth >> (param.iSpatialLayerNum - 1 - i);
|
|
param.sSpatialLayers[i].iVideoHeight = param.iPicHeight >> (param.iSpatialLayerNum - 1 - i);
|
|
param.sSpatialLayers[i].fFrameRate = 25.f;
|
|
param.sSpatialLayers[i].iSpatialBitrate = param.iTargetBitrate;
|
|
param.sSpatialLayers[i].uiProfileIdc = PRO_BASELINE;
|
|
param.sSpatialLayers[i].uiLevelIdc = LEVEL_4_2;
|
|
param.sSpatialLayers[i].iDLayerQp = 42;
|
|
|
|
//SSliceArgument sliceArg;
|
|
//sliceArg.uiSliceMode = SM_FIXEDSLCNUM_SLICE;
|
|
//sliceArg.uiSliceNum = 0;
|
|
//param.sSpatialLayers[i].sSliceArgument = sliceArg;
|
|
}
|
|
|
|
param.uiMaxNalSize = 1500;
|
|
param.iTargetBitrate *= param.iSpatialLayerNum;
|
|
encoder->InitializeExt(¶m);
|
|
|
|
|
|
int trace_level = WELS_LOG_ERROR;
|
|
encoder->SetOption(ENCODER_OPTION_TRACE_LEVEL, &trace_level);
|
|
int videoFormat = videoFormatI420;
|
|
encoder->SetOption(ENCODER_OPTION_DATAFORMAT, &videoFormat);
|
|
|
|
int frameSize = param.iPicWidth * param.iPicHeight * 3 / 2;
|
|
std::vector<uint8_t> buf(frameSize);
|
|
|
|
SFrameBSInfo info;
|
|
memset(&info, 0, sizeof(SFrameBSInfo));
|
|
SSourcePicture pic;
|
|
memset(&pic, 0, sizeof(SSourcePicture));
|
|
pic.iPicWidth = param.iPicWidth;
|
|
pic.iPicHeight = param.iPicHeight;
|
|
pic.iColorFormat = videoFormatI420;
|
|
pic.iStride[0] = pic.iPicWidth;
|
|
pic.iStride[1] = pic.iStride[2] = pic.iPicWidth >> 1;
|
|
pic.pData[0] = buf.data();
|
|
pic.pData[1] = pic.pData[0] + (param.iPicWidth * param.iPicHeight);
|
|
pic.pData[2] = pic.pData[1] + (param.iPicWidth * param.iPicHeight >> 2);
|
|
|
|
std::string mp4_path = data_path + "/out.mp4";
|
|
|
|
MP4FileHandle mp4 = MP4Create(mp4_path.c_str());
|
|
MP4TrackId mp4_track = -1;
|
|
MP4SetTimeScale(mp4, 90000);
|
|
|
|
const int frames = 500;
|
|
//std::ofstream f("out.h264", std::ios::binary);
|
|
for (int num = 0; num < frames; num++)
|
|
{
|
|
pb->increment();
|
|
printf("encoding %.2f%%\r", (float)num / (float)(frames - 1) * 100.f);
|
|
|
|
float value = sinf((float)num / (float)frames * 10.f);
|
|
for (int y = 0; y < param.iPicHeight; y++)
|
|
for (int x = 0; x < param.iPicWidth; x++)
|
|
buf[y * param.iPicWidth + x] = (((y + num / 4) / 10) % 2) * (255.f * value);
|
|
|
|
//prepare input data
|
|
rv = encoder->EncodeFrame(&pic, &info);
|
|
//pic.uiTimeStamp += (1.f/25.f) * 1000.f;
|
|
//assert (rv == cmResultSuccess);
|
|
if (info.eFrameType != videoFrameTypeSkip)
|
|
{
|
|
//output bitstream handling
|
|
for (int layer = 0; layer < info.iLayerNum; layer++)
|
|
{
|
|
size_t bs_size = 0;
|
|
for (int nal = 0; nal < info.sLayerInfo[layer].iNalCount; nal++)
|
|
{
|
|
std::array<uint8_t, 5> nalu_bytes;
|
|
for (int i = 0; i < nalu_bytes.size(); i++)
|
|
nalu_bytes[i] = info.sLayerInfo[layer].pBsBuf[bs_size + i];
|
|
if (nalu_bytes[4] == 0x67) // SPS
|
|
{
|
|
uint8_t avc_profile = info.sLayerInfo[layer].pBsBuf[bs_size + 5];
|
|
uint8_t avc_profile_compat = info.sLayerInfo[layer].pBsBuf[bs_size + 6];
|
|
uint8_t avc_level = info.sLayerInfo[layer].pBsBuf[bs_size + 7];
|
|
if (mp4_track == -1)
|
|
{
|
|
mp4_track = MP4AddH264VideoTrack(mp4, 90000, 90000 / 25,
|
|
param.iPicWidth, param.iPicHeight,
|
|
avc_profile, avc_profile_compat, avc_level, 3);
|
|
MP4SetVideoProfileLevel(mp4, 1);
|
|
}
|
|
MP4AddH264SequenceParameterSet(mp4, mp4_track, info.sLayerInfo[layer].pBsBuf + bs_size + 4,
|
|
info.sLayerInfo[layer].pNalLengthInByte[nal] - 4);
|
|
}
|
|
else if (nalu_bytes[4] == 0x68) // PPS
|
|
{
|
|
MP4AddH264PictureParameterSet(mp4, mp4_track, info.sLayerInfo[layer].pBsBuf + bs_size + 4,
|
|
info.sLayerInfo[layer].pNalLengthInByte[nal] - 4);
|
|
}
|
|
else
|
|
{
|
|
int nalu_sz = info.sLayerInfo[layer].pNalLengthInByte[nal];
|
|
uint8_t* data = info.sLayerInfo[layer].pBsBuf + bs_size;
|
|
*(uint32_t*)data = BinaryStream::htonx(nalu_sz - 4);
|
|
bool sync = false;
|
|
if (nalu_bytes[4] == 0x65) // I-frame
|
|
sync = true;
|
|
else // 0x61 P-frame
|
|
sync = false;
|
|
MP4WriteSample(mp4, mp4_track, data, nalu_sz, MP4_INVALID_DURATION, 0, sync);
|
|
}
|
|
//printf("nalu %x\n", nalu_bytes[4]);
|
|
bs_size += info.sLayerInfo[layer].pNalLengthInByte[nal];
|
|
}
|
|
//f.write((const char*)info.sLayerInfo[layer].pBsBuf, bs_size);
|
|
}
|
|
}
|
|
}
|
|
//f.close();
|
|
|
|
printf("\n");
|
|
|
|
MP4Close(mp4);
|
|
|
|
if (encoder)
|
|
{
|
|
encoder->Uninitialize();
|
|
WelsDestroySVCEncoder(encoder);
|
|
}
|
|
|
|
pb->destroy();
|
|
}).detach();
|
|
}
|