diff --git a/PanoPainter.vcxproj b/PanoPainter.vcxproj index c2ee88a..93c66de 100644 --- a/PanoPainter.vcxproj +++ b/PanoPainter.vcxproj @@ -77,8 +77,8 @@ true - libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;libs\bugtrap-client\include;libs\poly2tri\poly2tri;libs\base64;libs\sqlite3;libs\openvr\headers;libs\nanort;libs\hash-library;libs\fmt\include;libs\glad\include;$(IncludePath) - libs\curl-win\lib\dll-$(Configuration)-$(PlatformShortName);libs\glew-2.0.0\lib\Release\$(Platform);libs\bugtrap-client\lib;libs\openvr\lib\win64;$(LibraryPath) + libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;libs\bugtrap-client\include;libs\poly2tri\poly2tri;libs\base64;libs\sqlite3;libs\openvr\headers;libs\nanort;libs\hash-library;libs\fmt\include;libs\glad\include;libs\openh264\include;libs\mp4v2\include;$(IncludePath) + libs\curl-win\lib\dll-$(Configuration)-$(PlatformShortName);libs\glew-2.0.0\lib\Release\$(Platform);libs\bugtrap-client\lib;libs\openvr\lib\win64;libs\openh264\lib;libs\mp4v2\lib\win;$(LibraryPath) false @@ -87,8 +87,8 @@ false - libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;libs\bugtrap-client\include;libs\poly2tri\poly2tri;libs\base64;libs\sqlite3;libs\openvr\headers;libs\nanort;libs\hash-library;libs\fmt\include;libs\glad\include;$(IncludePath) - libs\curl-win\lib\dll-$(Configuration)-$(PlatformShortName);libs\glew-2.0.0\lib\Release\$(Platform);libs\bugtrap-client\lib;libs\openvr\lib\win64;$(LibraryPath) + libs\glm;libs\glew-2.0.0\include;libs\stb;libs\tinyxml2;libs\yoga;libs\curl-win\include;libs\jpeg;libs\wacom;libs\bugtrap-client\include;libs\poly2tri\poly2tri;libs\base64;libs\sqlite3;libs\openvr\headers;libs\nanort;libs\hash-library;libs\fmt\include;libs\glad\include;libs\openh264\include;libs\mp4v2\include;$(IncludePath) + libs\curl-win\lib\dll-$(Configuration)-$(PlatformShortName);libs\glew-2.0.0\lib\Release\$(Platform);libs\bugtrap-client\lib;libs\openvr\lib\win64;libs\openh264\lib;libs\mp4v2\lib\win;$(LibraryPath) diff --git a/data/layout.xml b/data/layout.xml index b7628c9..62befa7 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -249,6 +249,10 @@ + + + + diff --git a/src/app.h b/src/app.h index a111121..3b41cfb 100644 --- a/src/app.h +++ b/src/app.h @@ -255,6 +255,7 @@ public: void dialog_resize(); void dialog_preset_download(); void dialog_ppbr_export(); + void dialog_export_mp4(); void cloud_upload(); void cloud_upload_all(); diff --git a/src/app_dialogs.cpp b/src/app_dialogs.cpp index 3c2233c..e8bf1ea 100644 --- a/src/app_dialogs.cpp +++ b/src/app_dialogs.cpp @@ -10,6 +10,10 @@ #include "node_usermanual.h" #include "node_dialog_export_ppbr.h" +#include +#define MP4V2_NO_STDINT_DEFS +#include + #ifdef __QUEST__ #include "oculus_vr.h" #elif __WEB__ @@ -641,3 +645,177 @@ void App::dialog_ppbr_export() #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 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 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; + data[0] = (nalu_sz - 4) >> 24; + data[1] = (nalu_sz - 4) >> 16; + data[2] = (nalu_sz - 4) >> 8; + data[3] = (nalu_sz - 4) & 0xff; + 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(); +} \ No newline at end of file diff --git a/src/app_layout.cpp b/src/app_layout.cpp index d0f3cc1..0ab3a4b 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -1037,6 +1037,12 @@ void App::init_menu_tools() popup_exp->destroy(); }; + popup_exp->find("mp4test")->on_click = [this, popup_exp](Node*) { + dialog_export_mp4(); + popup_exp->mouse_release(); + popup_exp->destroy(); + }; + #if __IOS__ popup_exp->find("sonarpen")->on_click = [this, popup_exp](Node*) { [ios_app sonarpen_start]; diff --git a/src/pch.cpp b/src/pch.cpp index ed69b62..b863eb7 100644 --- a/src/pch.cpp +++ b/src/pch.cpp @@ -21,4 +21,6 @@ #pragma comment(lib, "Shlwapi.lib") //#pragma comment(lib, "Shcore.lib") #pragma comment(lib, "openvr_api.lib") + #pragma comment(lib, "openh264-2.0.0-win64.lib") + #pragma comment(lib, "libmp4v2.lib") #endif // _WIN32