diff --git a/bin/tlbake/main.cpp b/bin/tlbake/main.cpp index 757ce9d6d..570ffb1f8 100644 --- a/bin/tlbake/main.cpp +++ b/bin/tlbake/main.cpp @@ -8,24 +8,19 @@ #include -int main(int argc, char* argv[]) +TLRENDER_MAIN() { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); tl::timeline::init(context); - auto app = tl::bake::App::create(argc, argv, context); - if (0 == app->getExit()) - { - app->run(); - r = app->getExit(); - } + auto app = tl::bake::App::create(tl::app::convert(argc, argv), context); + r = app->run(); } catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/bin/tlplay-gl/CMakeLists.txt b/bin/tlplay-gl/CMakeLists.txt index 4e3777e36..6e1e0b675 100644 --- a/bin/tlplay-gl/CMakeLists.txt +++ b/bin/tlplay-gl/CMakeLists.txt @@ -1,7 +1,19 @@ -add_executable(tlplay-gl main.cpp) +add_executable(tlplay-gl WIN32 main.cpp) target_link_libraries(tlplay-gl tlPlayGLApp) set_target_properties(tlplay-gl PROPERTIES FOLDER bin) install( TARGETS tlplay-gl RUNTIME DESTINATION bin) + +if(WIN32) + add_executable(tlplay-gl. main.cpp) + target_link_libraries(tlplay-gl. tlPlayGLApp) + set_target_properties( + tlplay-gl. PROPERTIES + SUFFIX com + FOLDER bin) + install( + TARGETS tlplay-gl. + RUNTIME DESTINATION bin) +endif() diff --git a/bin/tlplay-gl/main.cpp b/bin/tlplay-gl/main.cpp index fae32df66..188186c77 100644 --- a/bin/tlplay-gl/main.cpp +++ b/bin/tlplay-gl/main.cpp @@ -8,24 +8,34 @@ #include -int main(int argc, char* argv[]) +#if defined(_WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN +#include +#include +#endif // _WINDOWS + +TLRENDER_MAIN() { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); tl::timelineui::init(context); - auto app = tl::play_gl::App::create(argc, argv, context); - if (0 == app->getExit()) - { - app->run(); - r = app->getExit(); - } + auto app = tl::play_gl::App::create(tl::app::convert(argc, argv), context); + r = app->run(); } catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } + +#if defined(_WINDOWS) +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int) +{ + return wmain(__argc, __wargv); +} +#endif // _WINDOWS diff --git a/bin/tlplay-qt/main.cpp b/bin/tlplay-qt/main.cpp index c12f83fc6..adf3e707f 100644 --- a/bin/tlplay-qt/main.cpp +++ b/bin/tlplay-qt/main.cpp @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); @@ -29,7 +29,6 @@ int main(int argc, char* argv[]) catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/bin/tlresource/main.cpp b/bin/tlresource/main.cpp index 8f2cb6264..bd5fcc2c5 100644 --- a/bin/tlresource/main.cpp +++ b/bin/tlresource/main.cpp @@ -8,24 +8,19 @@ #include -int main(int argc, char* argv[]) +TLRENDER_MAIN() { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); tl::io::init(context); - auto app = tl::resource::App::create(argc, argv, context); - if (0 == app->getExit()) - { - app->run(); - r = app->getExit(); - } + auto app = tl::resource::App::create(tl::app::convert(argc, argv), context); + r = app->run(); } catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/filmstrip-qtwidget/App.cpp b/examples/filmstrip-qtwidget/App.cpp index 94e435854..2e25d00dd 100644 --- a/examples/filmstrip-qtwidget/App.cpp +++ b/examples/filmstrip-qtwidget/App.cpp @@ -21,8 +21,7 @@ namespace tl QApplication(argc, argv) { IApp::_init( - argc, - argv, + app::convert(argc, argv), context, "filmstrip-qwidget", "Example using the filmstrip widget.", diff --git a/examples/filmstrip-qtwidget/main.cpp b/examples/filmstrip-qtwidget/main.cpp index 0a891e4bc..27045497a 100644 --- a/examples/filmstrip-qtwidget/main.cpp +++ b/examples/filmstrip-qtwidget/main.cpp @@ -12,7 +12,7 @@ int main(int argc, char* argv[]) { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); @@ -31,7 +31,6 @@ int main(int argc, char* argv[]) catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/panorama-qtwidget/main.cpp b/examples/panorama-qtwidget/main.cpp index 67a227e47..846a673af 100644 --- a/examples/panorama-qtwidget/main.cpp +++ b/examples/panorama-qtwidget/main.cpp @@ -31,7 +31,7 @@ int main(int argc, char* argv[]) return 1; } - int r = 0; + int r = 1; try { // Create the Qt application. @@ -67,7 +67,6 @@ int main(int argc, char* argv[]) catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/render-gl/App.cpp b/examples/render-gl/App.cpp index 54ef033c5..e3f695d43 100644 --- a/examples/render-gl/App.cpp +++ b/examples/render-gl/App.cpp @@ -26,12 +26,10 @@ namespace tl namespace render_gl { void App::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { IApp::_init( - argc, argv, context, "render-gl", @@ -112,100 +110,98 @@ namespace tl {} std::shared_ptr App::create( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { auto out = std::shared_ptr(new App); - out->_init(argc, argv, context); + out->_init(argv, context); return out; } - void App::run() + int App::run() { - if (_exit != 0) + if (0 == _exit) { - return; - } - - // Read the timelines. - auto timeline = timeline::Timeline::create( - file::Path(_input), - _context); - auto player = timeline::Player::create(timeline, _context); - _players.push_back(player); - auto ioInfo = player->getIOInfo(); - if (!ioInfo.video.empty()) - { - _videoSizes.push_back(ioInfo.video[0].size); - } - _videoData.push_back(timeline::VideoData()); - if (!_options.compareFileName.empty()) - { - timeline = timeline::Timeline::create( - file::Path(_options.compareFileName), + // Read the timelines. + auto timeline = timeline::Timeline::create( + file::Path(_input), _context); - player = timeline::Player::create(timeline, _context); - player->setExternalTime(_players[0]); + auto player = timeline::Player::create(timeline, _context); _players.push_back(player); - ioInfo = player->getIOInfo(); + auto ioInfo = player->getIOInfo(); if (!ioInfo.video.empty()) { _videoSizes.push_back(ioInfo.video[0].size); } _videoData.push_back(timeline::VideoData()); - } + if (!_options.compareFileName.empty()) + { + timeline = timeline::Timeline::create( + file::Path(_options.compareFileName), + _context); + player = timeline::Player::create(timeline, _context); + player->setExternalTime(_players[0]); + _players.push_back(player); + ioInfo = player->getIOInfo(); + if (!ioInfo.video.empty()) + { + _videoSizes.push_back(ioInfo.video[0].size); + } + _videoData.push_back(timeline::VideoData()); + } - // Create the window. - _window = gl::GLFWWindow::create( - "render-gl", - _options.windowSize, - _context); - _frameBufferSize = _window->getFrameBufferSize(); - _contentScale = _window->getContentScale(); - _window->setFullScreen(_options.fullscreen); - _window->setFrameBufferSizeCallback( - [this](const math::Size2i& value) + // Create the window. + _window = gl::GLFWWindow::create( + "render-gl", + _options.windowSize, + _context); + _frameBufferSize = _window->getFrameBufferSize(); + _contentScale = _window->getContentScale(); + _window->setFullScreen(_options.fullscreen); + _window->setFrameBufferSizeCallback( + [this](const math::Size2i& value) + { + _frameBufferSize = value; + _renderDirty = true; + }); + _window->setContentScaleCallback( + [this](const math::Vector2f& value) + { + _contentScale = value; + _renderDirty = true; + }); + _window->setKeyCallback( + [this](int key, int scanCode, int action, int mods) + { + _keyCallback(key, scanCode, action, mods); + }); + + // Create the renderer. + _render = timeline::GLRender::create(_context); + + // Print the shortcuts help. + _printShortcutsHelp(); + + // Start the main loop. + _hud = _options.hud; + if (time::isValid(_options.inOutRange)) { - _frameBufferSize = value; - _renderDirty = true; - }); - _window->setContentScaleCallback( - [this](const math::Vector2f& value) + _players[0]->setInOutRange(_options.inOutRange); + _players[0]->seek(_options.inOutRange.start_time()); + } + if (time::isValid(_options.seek)) { - _contentScale = value; - _renderDirty = true; - }); - _window->setKeyCallback( - [this](int key, int scanCode, int action, int mods) + _players[0]->seek(_options.seek); + } + _players[0]->setPlayback(_options.playback); + _startTime = std::chrono::steady_clock::now(); + while (_running && !_window->shouldClose()) { - _keyCallback(key, scanCode, action, mods); - }); - - // Create the renderer. - _render = timeline::GLRender::create(_context); - - // Print the shortcuts help. - _printShortcutsHelp(); - - // Start the main loop. - _hud = _options.hud; - if (time::isValid(_options.inOutRange)) - { - _players[0]->setInOutRange(_options.inOutRange); - _players[0]->seek(_options.inOutRange.start_time()); - } - if (time::isValid(_options.seek)) - { - _players[0]->seek(_options.seek); - } - _players[0]->setPlayback(_options.playback); - _startTime = std::chrono::steady_clock::now(); - while (_running && !_window->shouldClose()) - { - glfwPollEvents(); - _tick(); + glfwPollEvents(); + _tick(); + } } + return _exit; } void App::exit() diff --git a/examples/render-gl/App.h b/examples/render-gl/App.h index d4b51c648..ed579b30a 100644 --- a/examples/render-gl/App.h +++ b/examples/render-gl/App.h @@ -40,8 +40,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); App(); @@ -51,12 +50,11 @@ namespace tl //! Create a new application. static std::shared_ptr create( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); //! Run the application. - void run(); + int run(); //! Exit the application. void exit(); diff --git a/examples/render-gl/main.cpp b/examples/render-gl/main.cpp index 87b370e2f..3e47efb3b 100644 --- a/examples/render-gl/main.cpp +++ b/examples/render-gl/main.cpp @@ -8,24 +8,19 @@ #include -int main(int argc, char* argv[]) +TLRENDER_MAIN() { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); tl::timeline::init(context); - auto app = tl::examples::render_gl::App::create(argc, argv, context); - if (0 == app->getExit()) - { - app->run(); - r = app->getExit(); - } + auto app = tl::examples::render_gl::App::create(tl::app::convert(argc, argv), context); + r = app->run(); } catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/simple-qtquick/App.cpp b/examples/simple-qtquick/App.cpp index 5ec343247..937fa0c5b 100644 --- a/examples/simple-qtquick/App.cpp +++ b/examples/simple-qtquick/App.cpp @@ -26,8 +26,7 @@ namespace tl QGuiApplication(argc, argv) { IApp::_init( - argc, - argv, + app::convert(argc, argv), context, "simple-qtquick", "Example Qt Quick playback application.", diff --git a/examples/simple-qtquick/main.cpp b/examples/simple-qtquick/main.cpp index d06ad37d8..b302191a1 100644 --- a/examples/simple-qtquick/main.cpp +++ b/examples/simple-qtquick/main.cpp @@ -12,7 +12,7 @@ int main(int argc, char* argv[]) { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); @@ -31,7 +31,6 @@ int main(int argc, char* argv[]) catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/simple-qtwidget/simple-qtwidget.cpp b/examples/simple-qtwidget/simple-qtwidget.cpp index f51c8cc13..de3d06841 100644 --- a/examples/simple-qtwidget/simple-qtwidget.cpp +++ b/examples/simple-qtwidget/simple-qtwidget.cpp @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) return 1; } - int r = 0; + int r = 1; try { // Create the Qt application. @@ -65,7 +65,6 @@ int main(int argc, char* argv[]) catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/test-patterns/App.cpp b/examples/test-patterns/App.cpp index 0879a68b4..a29f85c35 100644 --- a/examples/test-patterns/App.cpp +++ b/examples/test-patterns/App.cpp @@ -30,12 +30,10 @@ namespace tl namespace test_patterns { void App::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { IApp::_init( - argc, argv, context, "test-patterns", @@ -56,126 +54,124 @@ namespace tl {} std::shared_ptr App::create( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { auto out = std::shared_ptr(new App); - out->_init(argc, argv, context); + out->_init(argv, context); return out; } - void App::run() + int App::run() { - if (_exit != 0) + if (0 == _exit) { - return; - } - - for (const auto& size : { - math::Size2i(1920, 1080), - math::Size2i(3840, 2160), - math::Size2i(4096, 2160) - }) - { - otio::SerializableObject::Retainer otioTimeline(new otio::Timeline); - otio::SerializableObject::Retainer otioTrack(new otio::Track); - otioTimeline->tracks()->append_child(otioTrack); - - for (const auto& name : { - CountTestPattern::getClassName(), - SwatchesTestPattern::getClassName(), - GridTestPattern::getClassName() + for (const auto& size : { + math::Size2i(1920, 1080), + math::Size2i(3840, 2160), + math::Size2i(4096, 2160) }) { - //const std::string output = string::Format("{0}_{1}_pattern.mp4").arg(name).arg(size); - const std::string output = string::Format("{0}_{1}.0.dpx").arg(name).arg(size); - std::cout << "Output: " << output << std::endl; - otio::SerializableObject::Retainer otioClip(new otio::Clip); - //otio::SerializableObject::Retainer mediaReference( - // new otio::ExternalReference(string::Format("{0}").arg(output))); - otio::SerializableObject::Retainer mediaReference( - new otio::ImageSequenceReference( - "file://", - file::Path(output).getBaseName(), - file::Path(output).getExtension(), - 0, - 1, - 24)); - const otime::TimeRange timeRange( - otime::RationalTime(0.0, 24.0), - otime::RationalTime(24 * 3, 24.0)); - mediaReference->set_available_range(timeRange); - otioClip->set_media_reference(mediaReference); - otioTrack->append_child(otioClip); - - // Create the I/O plugin. - auto writerPlugin = _context->getSystem()->getPlugin(file::Path(output)); - if (!writerPlugin) - { - throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); - } - image::Info info; - info.size.w = size.w; - info.size.h = size.h; - info.pixelType = image::PixelType::RGB_U10; - info = writerPlugin->getWriteInfo(info); - if (image::PixelType::None == info.pixelType) - { - throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); - } - io::Info ioInfo; - ioInfo.video.push_back(info); - ioInfo.videoTime = timeRange; - auto writer = writerPlugin->write(file::Path(output), ioInfo); - if (!writer) + otio::SerializableObject::Retainer otioTimeline(new otio::Timeline); + otio::SerializableObject::Retainer otioTrack(new otio::Track); + otioTimeline->tracks()->append_child(otioTrack); + + for (const auto& name : { + CountTestPattern::getClassName(), + SwatchesTestPattern::getClassName(), + GridTestPattern::getClassName() + }) { - throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); - } + //const std::string output = string::Format("{0}_{1}_pattern.mp4").arg(name).arg(size); + const std::string output = string::Format("{0}_{1}.0.dpx").arg(name).arg(size); + std::cout << "Output: " << output << std::endl; + otio::SerializableObject::Retainer otioClip(new otio::Clip); + //otio::SerializableObject::Retainer mediaReference( + // new otio::ExternalReference(string::Format("{0}").arg(output))); + otio::SerializableObject::Retainer mediaReference( + new otio::ImageSequenceReference( + "file://", + file::Path(output).getBaseName(), + file::Path(output).getExtension(), + 0, + 1, + 24)); + const otime::TimeRange timeRange( + otime::RationalTime(0.0, 24.0), + otime::RationalTime(24 * 3, 24.0)); + mediaReference->set_available_range(timeRange); + otioClip->set_media_reference(mediaReference); + otioTrack->append_child(otioClip); + + // Create the I/O plugin. + auto writerPlugin = _context->getSystem()->getPlugin(file::Path(output)); + if (!writerPlugin) + { + throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); + } + image::Info info; + info.size.w = size.w; + info.size.h = size.h; + info.pixelType = image::PixelType::RGB_U10; + info = writerPlugin->getWriteInfo(info); + if (image::PixelType::None == info.pixelType) + { + throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); + } + io::Info ioInfo; + ioInfo.video.push_back(info); + ioInfo.videoTime = timeRange; + auto writer = writerPlugin->write(file::Path(output), ioInfo); + if (!writer) + { + throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); + } - // Create the offscreen buffer. - gl::OffscreenBufferOptions offscreenBufferOptions; - offscreenBufferOptions.colorType = image::PixelType::RGBA_F32; - auto buffer = gl::OffscreenBuffer::create(size, offscreenBufferOptions); - gl::OffscreenBufferBinding binding(buffer); - auto image = image::Image::create(info); - - // Render the test pattern. - auto render = timeline::GLRender::create(_context); - auto pattern = TestPatternFactory::create(name, size, _context); - for (double i = ioInfo.videoTime.start_time().value(); i < ioInfo.videoTime.duration().value(); i = i + 1.0) - { - const otime::RationalTime time(i, 24.0); + // Create the offscreen buffer. + gl::OffscreenBufferOptions offscreenBufferOptions; + offscreenBufferOptions.colorType = image::PixelType::RGBA_F32; + auto buffer = gl::OffscreenBuffer::create(size, offscreenBufferOptions); + gl::OffscreenBufferBinding binding(buffer); + auto image = image::Image::create(info); + + // Render the test pattern. + auto render = timeline::GLRender::create(_context); + auto pattern = TestPatternFactory::create(name, size, _context); + for (double i = ioInfo.videoTime.start_time().value(); i < ioInfo.videoTime.duration().value(); i = i + 1.0) + { + const otime::RationalTime time(i, 24.0); - render->begin(size); - pattern->render(render, time); - render->end(); + render->begin(size); + pattern->render(render, time); + render->end(); - // Write the image. - glPixelStorei(GL_PACK_ALIGNMENT, info.layout.alignment); + // Write the image. + glPixelStorei(GL_PACK_ALIGNMENT, info.layout.alignment); #if defined(TLRENDER_API_GL_4_1) - glPixelStorei(GL_PACK_SWAP_BYTES, info.layout.endian != memory::getEndian()); + glPixelStorei(GL_PACK_SWAP_BYTES, info.layout.endian != memory::getEndian()); #endif // TLRENDER_API_GL_4_1 - const GLenum format = gl::getReadPixelsFormat(info.pixelType); - const GLenum type = gl::getReadPixelsType(info.pixelType); - if (GL_NONE == format || GL_NONE == type) - { - throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); + const GLenum format = gl::getReadPixelsFormat(info.pixelType); + const GLenum type = gl::getReadPixelsType(info.pixelType); + if (GL_NONE == format || GL_NONE == type) + { + throw std::runtime_error(string::Format("{0}: Cannot open").arg(output)); + } + glReadPixels( + 0, + 0, + info.size.w, + info.size.h, + format, + type, + image->getData()); + writer->writeVideo(time, image); } - glReadPixels( - 0, - 0, - info.size.w, - info.size.h, - format, - type, - image->getData()); - writer->writeVideo(time, image); } - } - otioTimeline->to_json_file(string::Format("{0}.otio").arg(size)); + otioTimeline->to_json_file(string::Format("{0}.otio").arg(size)); + } } + return _exit; } } } diff --git a/examples/test-patterns/App.h b/examples/test-patterns/App.h index 99674919b..3757578a6 100644 --- a/examples/test-patterns/App.h +++ b/examples/test-patterns/App.h @@ -23,8 +23,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); App(); @@ -33,12 +32,11 @@ namespace tl //! Create a new application. static std::shared_ptr create( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); //! Run the application. - void run(); + int run(); private: std::shared_ptr _window; diff --git a/examples/test-patterns/main.cpp b/examples/test-patterns/main.cpp index dd323eecd..da5ea6828 100644 --- a/examples/test-patterns/main.cpp +++ b/examples/test-patterns/main.cpp @@ -8,24 +8,19 @@ #include -int main(int argc, char* argv[]) +TLRENDER_MAIN() { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); tl::timeline::init(context); - auto app = tl::examples::test_patterns::App::create(argc, argv, context); - if (0 == app->getExit()) - { - app->run(); - r = app->getExit(); - } + auto app = tl::examples::test_patterns::App::create(tl::app::convert(argc, argv), context); + r = app->run(); } catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/widgets-gl/App.cpp b/examples/widgets-gl/App.cpp index 65b06f053..9656fe65e 100644 --- a/examples/widgets-gl/App.cpp +++ b/examples/widgets-gl/App.cpp @@ -15,12 +15,10 @@ namespace tl namespace widgets_gl { void App::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { IApp::_init( - argc, argv, context, "widgets-gl", @@ -41,12 +39,11 @@ namespace tl {} std::shared_ptr App::create( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { auto out = std::shared_ptr(new App); - out->_init(argc, argv, context); + out->_init(argv, context); return out; } } diff --git a/examples/widgets-gl/App.h b/examples/widgets-gl/App.h index 98fe5a537..01439253d 100644 --- a/examples/widgets-gl/App.h +++ b/examples/widgets-gl/App.h @@ -25,8 +25,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); App(); @@ -36,8 +35,7 @@ namespace tl //! Create a new application. static std::shared_ptr create( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); private: diff --git a/examples/widgets-gl/main.cpp b/examples/widgets-gl/main.cpp index cc0a7ca31..e0affec19 100644 --- a/examples/widgets-gl/main.cpp +++ b/examples/widgets-gl/main.cpp @@ -8,24 +8,19 @@ #include -int main(int argc, char* argv[]) +TLRENDER_MAIN() { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); tl::ui::init(context); - auto app = tl::examples::widgets_gl::App::create(argc, argv, context); - if (0 == app->getExit()) - { - app->run(); - r = app->getExit(); - } + auto app = tl::examples::widgets_gl::App::create(tl::app::convert(argc, argv), context); + r = app->run(); } catch(const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/examples/widgets-qtwidget/main.cpp b/examples/widgets-qtwidget/main.cpp index 62f15e733..06d65758e 100644 --- a/examples/widgets-qtwidget/main.cpp +++ b/examples/widgets-qtwidget/main.cpp @@ -12,7 +12,7 @@ int main(int argc, char* argv[]) { - int r = 0; + int r = 1; try { auto context = tl::system::Context::create(); @@ -28,7 +28,6 @@ int main(int argc, char* argv[]) catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; - r = 1; } return r; } diff --git a/lib/tlApp/IApp.cpp b/lib/tlApp/IApp.cpp index 5c20a9f98..4e487bebf 100644 --- a/lib/tlApp/IApp.cpp +++ b/lib/tlApp/IApp.cpp @@ -13,43 +13,67 @@ namespace tl { namespace app { + std::vector convert(int argc, char* argv[]) + { + std::vector out; + for (int i = 0; i < argc; ++i) + { + out.push_back(argv[i]); + } + return out; + } + + std::vector convert(int argc, wchar_t* argv[]) + { + std::vector out; + for (int i = 0; i < argc; ++i) + { + out.push_back(string::fromWide(argv[i])); + } + return out; + } + struct IApp::Private { - std::vector cmdLine; - std::string cmdLineName; - std::string cmdLineSummary; - std::vector > cmdLineArgs; - std::vector > cmdLineOptions; + struct CmdLineData + { + std::vector argv; + std::string name; + std::string summary; + std::vector > args; + std::vector > options; + }; + CmdLineData cmdLine; + std::shared_ptr > logObserver; }; void IApp::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context, const std::string& cmdLineName, const std::string& cmdLineSummary, - const std::vector >& args, - const std::vector >& options) + const std::vector >& cmdLineArgs, + const std::vector >& cmdLineOptions) { TLRENDER_P(); _context = context; // Parse the command line. - for (int i = 1; i < argc; ++i) + for (size_t i = 1; i < argv.size(); ++i) { - p.cmdLine.push_back(argv[i]); + p.cmdLine.argv.push_back(argv[i]); } - p.cmdLineName = cmdLineName; - p.cmdLineSummary = cmdLineSummary; - p.cmdLineArgs = args; - p.cmdLineOptions = options; - p.cmdLineOptions.push_back(CmdLineFlagOption::create( + p.cmdLine.name = cmdLineName; + p.cmdLine.summary = cmdLineSummary; + p.cmdLine.args = cmdLineArgs; + p.cmdLine.options = cmdLineOptions; + p.cmdLine.options.push_back(CmdLineFlagOption::create( _options.log, { "-log" }, "Print the log to the console.")); - p.cmdLineOptions.push_back(CmdLineFlagOption::create( + p.cmdLine.options.push_back(CmdLineFlagOption::create( _options.help, { "-help", "-h", "--help", "--h" }, "Show this message.")); @@ -93,7 +117,7 @@ namespace tl void IApp::_log(const std::string& value, log::Type type) { - _context->log(_p->cmdLineName, value, type); + _context->log(_p->cmdLine.name, value, type); } void IApp::_print(const std::string& value) @@ -114,11 +138,11 @@ namespace tl int IApp::_parseCmdLine() { TLRENDER_P(); - for (const auto& i : p.cmdLineOptions) + for (const auto& i : p.cmdLine.options) { try { - i->parse(p.cmdLine); + i->parse(p.cmdLine.argv); } catch (const std::exception& e) { @@ -129,7 +153,7 @@ namespace tl } size_t requiredArgs = 0; size_t optionalArgs = 0; - for (const auto& i : p.cmdLineArgs) + for (const auto& i : p.cmdLine.args) { if (!i->isOptional()) { @@ -140,20 +164,20 @@ namespace tl ++optionalArgs; } } - if (p.cmdLine.size() < requiredArgs || - p.cmdLine.size() > requiredArgs + optionalArgs || + if (p.cmdLine.argv.size() < requiredArgs || + p.cmdLine.argv.size() > requiredArgs + optionalArgs || _options.help) { - _printCmdLineHelp(); + _printCmdLineHelp(); return 1; } - for (const auto& i : p.cmdLineArgs) + for (const auto& i : p.cmdLine.args) { try { - if (!(p.cmdLine.empty() && i->isOptional())) + if (!(p.cmdLine.argv.empty() && i->isOptional())) { - i->parse(p.cmdLine); + i->parse(p.cmdLine.argv); } } catch (const std::exception& e) @@ -169,16 +193,16 @@ namespace tl void IApp::_printCmdLineHelp() { TLRENDER_P(); - _print("\n" + p.cmdLineName + "\n"); - _print(" " + p.cmdLineSummary + "\n"); + _print("\n" + p.cmdLine.name + "\n"); + _print(" " + p.cmdLine.summary + "\n"); _print("Usage:\n"); { std::stringstream ss; - ss << " " + p.cmdLineName; - if (p.cmdLineArgs.size()) + ss << " " + p.cmdLine.name; + if (p.cmdLine.args.size()) { std::vector args; - for (const auto& i : p.cmdLineArgs) + for (const auto& i : p.cmdLine.args) { const bool optional = i->isOptional(); args.push_back( @@ -188,7 +212,7 @@ namespace tl } ss << " " << string::join(args, " "); } - if (p.cmdLineOptions.size()) + if (p.cmdLine.options.size()) { ss << " [option],..."; } @@ -196,14 +220,14 @@ namespace tl _printNewline(); } _print("Arguments:\n"); - for (const auto& i : p.cmdLineArgs) + for (const auto& i : p.cmdLine.args) { _print(" " + i->getName()); _print(" " + i->getHelp()); _printNewline(); } _print("Options:\n"); - for (const auto& i : p.cmdLineOptions) + for (const auto& i : p.cmdLine.options) { bool first = true; for (const auto& j : i->getHelpText()) diff --git a/lib/tlApp/IApp.h b/lib/tlApp/IApp.h index b98273798..3919ffc5b 100644 --- a/lib/tlApp/IApp.h +++ b/lib/tlApp/IApp.h @@ -8,6 +8,14 @@ #include +#if defined(_WINDOWS) +#define TLRENDER_MAIN() \ + int wmain(int argc, wchar_t* argv[]) +#else // _WINDOWS +#define TLRENDER_MAIN() \ + int main(int argc, char* argv[]) +#endif // _WINDOWS + namespace tl { //! General application functionality. @@ -23,6 +31,12 @@ namespace tl bool help = false; }; + //! Convert command line arguments. + std::vector convert(int argc, char* argv[]); + + //! Convert command line arguments. + std::vector convert(int argc, wchar_t* argv[]); + //! Base class for applications. class IApp : public std::enable_shared_from_this { @@ -30,8 +44,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&, const std::string& cmdLineName, const std::string& cmdLineSummary, diff --git a/lib/tlBakeApp/App.cpp b/lib/tlBakeApp/App.cpp index b61eb0667..f188d5d4e 100644 --- a/lib/tlBakeApp/App.cpp +++ b/lib/tlBakeApp/App.cpp @@ -23,12 +23,10 @@ namespace tl namespace bake { void App::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { IApp::_init( - argc, argv, context, "tlbake", @@ -233,107 +231,106 @@ namespace tl {} std::shared_ptr App::create( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { auto out = std::shared_ptr(new App); - out->_init(argc, argv, context); + out->_init(argv, context); return out; } - void App::run() + int App::run() { - if (_exit != 0) + if (0 == _exit) { - return; - } + _startTime = std::chrono::steady_clock::now(); - _startTime = std::chrono::steady_clock::now(); + // Create the window. + _window = gl::GLFWWindow::create( + "test-patterns", + math::Size2i(1, 1), + _context, + static_cast(gl::GLFWWindowOptions::MakeCurrent)); - // Create the window. - _window = gl::GLFWWindow::create( - "test-patterns", - math::Size2i(1, 1), - _context, - static_cast(gl::GLFWWindowOptions::MakeCurrent)); + // Read the timeline. + _timeline = timeline::Timeline::create(file::Path(_input), _context); + _timeRange = _timeline->getTimeRange(); + _print(string::Format("Timeline range: {0}-{1}"). + arg(_timeRange.start_time().value()). + arg(_timeRange.end_time_inclusive().value())); + _print(string::Format("Timeline speed: {0}").arg(_timeRange.duration().rate())); - // Read the timeline. - _timeline = timeline::Timeline::create(file::Path(_input), _context); - _timeRange = _timeline->getTimeRange(); - _print(string::Format("Timeline range: {0}-{1}"). - arg(_timeRange.start_time().value()). - arg(_timeRange.end_time_inclusive().value())); - _print(string::Format("Timeline speed: {0}").arg(_timeRange.duration().rate())); + // Time range. + if (time::isValid(_options.inOutRange)) + { + _timeRange = _options.inOutRange; + } + _print(string::Format("In/out range: {0}-{1}"). + arg(_timeRange.start_time().value()). + arg(_timeRange.end_time_inclusive().value())); + _inputTime = _timeRange.start_time(); + _outputTime = otime::RationalTime(0.0, _timeRange.duration().rate()); - // Time range. - if (time::isValid(_options.inOutRange)) - { - _timeRange = _options.inOutRange; - } - _print(string::Format("In/out range: {0}-{1}"). - arg(_timeRange.start_time().value()). - arg(_timeRange.end_time_inclusive().value())); - _inputTime = _timeRange.start_time(); - _outputTime = otime::RationalTime(0.0, _timeRange.duration().rate()); + // Render information. + const auto& info = _timeline->getIOInfo(); + if (info.video.empty()) + { + throw std::runtime_error("No video information"); + } + _renderSize = _options.renderSize.isValid() ? + _options.renderSize : + math::Size2i(info.video[0].size.w, info.video[0].size.h); + _print(string::Format("Render size: {0}").arg(_renderSize)); - // Render information. - const auto& info = _timeline->getIOInfo(); - if (info.video.empty()) - { - throw std::runtime_error("No video information"); - } - _renderSize = _options.renderSize.isValid() ? - _options.renderSize : - math::Size2i(info.video[0].size.w, info.video[0].size.h); - _print(string::Format("Render size: {0}").arg(_renderSize)); + // Create the renderer. + _render = timeline::GLRender::create(_context); + gl::OffscreenBufferOptions offscreenBufferOptions; + offscreenBufferOptions.colorType = gl::OffscreenColorDefault; + _buffer = gl::OffscreenBuffer::create(_renderSize, offscreenBufferOptions); - // Create the renderer. - _render = timeline::GLRender::create(_context); - gl::OffscreenBufferOptions offscreenBufferOptions; - offscreenBufferOptions.colorType = gl::OffscreenColorDefault; - _buffer = gl::OffscreenBuffer::create(_renderSize, offscreenBufferOptions); + // Create the writer. + _writerPlugin = _context->getSystem()->getPlugin(file::Path(_output)); + if (!_writerPlugin) + { + throw std::runtime_error(string::Format("{0}: Cannot open").arg(_output)); + } + io::Info ioInfo; + _outputInfo.size.w = _renderSize.w; + _outputInfo.size.h = _renderSize.h; + _outputInfo.pixelType = _options.outputPixelType != image::PixelType::None ? + _options.outputPixelType : + info.video[0].pixelType; + _outputInfo = _writerPlugin->getWriteInfo(_outputInfo); + if (image::PixelType::None == _outputInfo.pixelType) + { + _outputInfo.pixelType = image::PixelType::RGB_U8; + } + _print(string::Format("Output info: {0} {1}"). + arg(_outputInfo.size). + arg(_outputInfo.pixelType)); + _outputImage = image::Image::create(_outputInfo); + ioInfo.video.push_back(_outputInfo); + ioInfo.videoTime = _timeRange; + _writer = _writerPlugin->write(file::Path(_output), ioInfo); + if (!_writer) + { + throw std::runtime_error(string::Format("{0}: Cannot open").arg(_output)); + } - // Create the writer. - _writerPlugin = _context->getSystem()->getPlugin(file::Path(_output)); - if (!_writerPlugin) - { - throw std::runtime_error(string::Format("{0}: Cannot open").arg(_output)); - } - io::Info ioInfo; - _outputInfo.size.w = _renderSize.w; - _outputInfo.size.h = _renderSize.h; - _outputInfo.pixelType = _options.outputPixelType != image::PixelType::None ? - _options.outputPixelType : - info.video[0].pixelType; - _outputInfo = _writerPlugin->getWriteInfo(_outputInfo); - if (image::PixelType::None == _outputInfo.pixelType) - { - _outputInfo.pixelType = image::PixelType::RGB_U8; - } - _print(string::Format("Output info: {0} {1}"). - arg(_outputInfo.size). - arg(_outputInfo.pixelType)); - _outputImage = image::Image::create(_outputInfo); - ioInfo.video.push_back(_outputInfo); - ioInfo.videoTime = _timeRange; - _writer = _writerPlugin->write(file::Path(_output), ioInfo); - if (!_writer) - { - throw std::runtime_error(string::Format("{0}: Cannot open").arg(_output)); - } + // Start the main loop. + gl::OffscreenBufferBinding binding(_buffer); + while (_running) + { + _tick(); + } - // Start the main loop. - gl::OffscreenBufferBinding binding(_buffer); - while (_running) - { - _tick(); + const auto now = std::chrono::steady_clock::now(); + const std::chrono::duration diff = now - _startTime; + _print(string::Format("Seconds elapsed: {0}").arg(diff.count())); + _print(string::Format("Average FPS: {0}").arg(_timeRange.duration().value() / diff.count())); } - const auto now = std::chrono::steady_clock::now(); - const std::chrono::duration diff = now - _startTime; - _print(string::Format("Seconds elapsed: {0}").arg(diff.count())); - _print(string::Format("Average FPS: {0}").arg(_timeRange.duration().value() / diff.count())); + return _exit; } void App::_tick() diff --git a/lib/tlBakeApp/App.h b/lib/tlBakeApp/App.h index adbd8db4c..b7f3ec304 100644 --- a/lib/tlBakeApp/App.h +++ b/lib/tlBakeApp/App.h @@ -65,8 +65,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); App(); @@ -75,12 +74,11 @@ namespace tl //! Create a new application. static std::shared_ptr create( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); //! Run the application. - void run(); + int run(); private: void _tick(); diff --git a/lib/tlGLApp/IApp.cpp b/lib/tlGLApp/IApp.cpp index 3c26620a9..1ab424404 100644 --- a/lib/tlGLApp/IApp.cpp +++ b/lib/tlGLApp/IApp.cpp @@ -122,13 +122,12 @@ namespace tl }; void IApp::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context, const std::string& cmdLineName, const std::string& cmdLineSummary, - const std::vector >& args, - const std::vector >& options) + const std::vector >& cmdLineArgs, + const std::vector >& cmdLineOptions) { TLRENDER_P(); if (const GLFWvidmode* monitorMode = glfwGetVideoMode( @@ -137,26 +136,25 @@ namespace tl _options.windowSize.w = monitorMode->width * .7F; _options.windowSize.h = monitorMode->height * .7F; } - std::vector > options2 = options; - options2.push_back( + std::vector > cmdLineOptions2 = cmdLineOptions; + cmdLineOptions2.push_back( app::CmdLineValueOption::create( _options.windowSize, { "-windowSize", "-ws" }, "Window size.", string::Format("{0}x{1}").arg(_options.windowSize.w).arg(_options.windowSize.h))); - options2.push_back( + cmdLineOptions2.push_back( app::CmdLineFlagOption::create( _options.fullscreen, { "-fullscreen", "-fs" }, "Enable full screen mode.")); app::IApp::_init( - argc, argv, context, cmdLineName, cmdLineSummary, - args, - options2); + cmdLineArgs, + cmdLineOptions2); if (_exit != 0) { return; @@ -260,155 +258,154 @@ namespace tl IApp::~IApp() {} - void IApp::run() + int IApp::run() { TLRENDER_P(); - if (_exit != 0) - { - return; - } - - // Start the main loop. - while (p.running && !p.window->shouldClose()) + if (0 == _exit) { - glfwPollEvents(); + // Start the main loop. + while (p.running && !p.window->shouldClose()) + { + glfwPollEvents(); - _context->tick(); + _context->tick(); - _tick(); + _tick(); - p.eventLoop->setDisplaySize(p.frameBufferSize); - p.eventLoop->setDisplayScale(p.contentScale.x); - p.eventLoop->tick(); + p.eventLoop->setDisplaySize(p.frameBufferSize); + p.eventLoop->setDisplayScale(p.contentScale.x); + p.eventLoop->tick(); - gl::OffscreenBufferOptions offscreenBufferOptions; - offscreenBufferOptions.colorType = image::PixelType::RGBA_U8; - if (gl::doCreate(p.offscreenBuffer, p.frameBufferSize, offscreenBufferOptions)) - { - p.offscreenBuffer = gl::OffscreenBuffer::create( - p.frameBufferSize, - offscreenBufferOptions); - } - if ((p.eventLoop->hasDrawUpdate() || p.refresh) && - p.offscreenBuffer) - { - p.refresh = false; + gl::OffscreenBufferOptions offscreenBufferOptions; + offscreenBufferOptions.colorType = image::PixelType::RGBA_U8; + if (gl::doCreate(p.offscreenBuffer, p.frameBufferSize, offscreenBufferOptions)) { - gl::OffscreenBufferBinding binding(p.offscreenBuffer); - p.render->begin( + p.offscreenBuffer = gl::OffscreenBuffer::create( p.frameBufferSize, - p.colorConfigOptions, - p.lutOptions); - p.eventLoop->draw(p.render); - p.render->end(); + offscreenBufferOptions); } - glViewport( - 0, - 0, - GLsizei(p.frameBufferSize.w), - GLsizei(p.frameBufferSize.h)); - glClearColor(1.F, 0.F, 0.F, 0.F); - glClear(GL_COLOR_BUFFER_BIT); -#if defined(TLRENDER_API_GL_4_1) - glBindFramebuffer( - GL_READ_FRAMEBUFFER, - p.offscreenBuffer->getID()); - glBlitFramebuffer( - 0, - 0, - p.frameBufferSize.w, - p.frameBufferSize.h, - 0, - 0, - p.frameBufferSize.w, - p.frameBufferSize.h, - GL_COLOR_BUFFER_BIT, - GL_LINEAR); -#elif defined(TLRENDER_API_GLES_2) - if (!p.shader) + if ((p.eventLoop->hasDrawUpdate() || p.refresh) && + p.offscreenBuffer) { - try + p.refresh = false; { - const std::string vertexSource = - "precision mediump int;\n" - "precision mediump float;\n" - "\n" - "attribute vec3 vPos;\n" - "attribute vec2 vTexture;\n" - "\n" - "varying vec2 fTexture;\n" - "\n" - "uniform struct Transform\n" - "{\n" - " mat4 mvp;\n" - "} transform;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = transform.mvp * vec4(vPos, 1.0);\n" - " fTexture = vTexture;\n" - "}\n"; - const std::string fragmentSource = - "precision mediump int;\n" - "precision mediump float;\n" - "\n" - "varying vec2 fTexture;\n" - "\n" - "uniform sampler2D textureSampler;\n" - "\n" - "void main()\n" - "{\n" - //" gl_FragColor = texture2D(textureSampler, fTexture);\n" - " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" - "}\n"; - p.shader = gl::Shader::create(vertexSource, fragmentSource); + gl::OffscreenBufferBinding binding(p.offscreenBuffer); + p.render->begin( + p.frameBufferSize, + p.colorConfigOptions, + p.lutOptions); + p.eventLoop->draw(p.render); + p.render->end(); } - catch (const std::exception& e) - { - _log(string::Format("Cannot compile shader: {0}").arg(e.what()), - log::Type::Error); - } - } - if (p.shader) - { - glDisable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); - - p.shader->bind(); - p.shader->setUniform("transform.mvp", - math::ortho( - 0.F, - static_cast(p.frameBufferSize.w), - static_cast(p.frameBufferSize.h), - 0.F, - -1.F, - 1.F)); - p.shader->setUniform("textureSampler", 0); - - glActiveTexture(static_cast(GL_TEXTURE0)); - glBindTexture(GL_TEXTURE_2D, p.offscreenBuffer->getColorID()); - - auto mesh = geom::box(math::Box2i( + glViewport( + 0, + 0, + GLsizei(p.frameBufferSize.w), + GLsizei(p.frameBufferSize.h)); + glClearColor(1.F, 0.F, 0.F, 0.F); + glClear(GL_COLOR_BUFFER_BIT); +#if defined(TLRENDER_API_GL_4_1) + glBindFramebuffer( + GL_READ_FRAMEBUFFER, + p.offscreenBuffer->getID()); + glBlitFramebuffer( 0, 0, p.frameBufferSize.w, - p.frameBufferSize.h)); - auto vboData = gl::convert( - mesh, - gl::VBOType::Pos3_F32_UV_U16, - math::SizeTRange(0, mesh.triangles.size() - 1)); - auto vbo = gl::VBO::create(mesh.triangles.size() * 3, gl::VBOType::Pos3_F32_UV_U16); - vbo->copy(vboData); - auto vao = gl::VAO::create(gl::VBOType::Pos3_F32_UV_U16, vbo->getID()); - vao->bind(); - vao->draw(GL_TRIANGLES, 0, mesh.triangles.size() * 3); - } + p.frameBufferSize.h, + 0, + 0, + p.frameBufferSize.w, + p.frameBufferSize.h, + GL_COLOR_BUFFER_BIT, + GL_LINEAR); +#elif defined(TLRENDER_API_GLES_2) + if (!p.shader) + { + try + { + const std::string vertexSource = + "precision mediump int;\n" + "precision mediump float;\n" + "\n" + "attribute vec3 vPos;\n" + "attribute vec2 vTexture;\n" + "\n" + "varying vec2 fTexture;\n" + "\n" + "uniform struct Transform\n" + "{\n" + " mat4 mvp;\n" + "} transform;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = transform.mvp * vec4(vPos, 1.0);\n" + " fTexture = vTexture;\n" + "}\n"; + const std::string fragmentSource = + "precision mediump int;\n" + "precision mediump float;\n" + "\n" + "varying vec2 fTexture;\n" + "\n" + "uniform sampler2D textureSampler;\n" + "\n" + "void main()\n" + "{\n" + //" gl_FragColor = texture2D(textureSampler, fTexture);\n" + " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" + "}\n"; + p.shader = gl::Shader::create(vertexSource, fragmentSource); + } + catch (const std::exception& e) + { + _log(string::Format("Cannot compile shader: {0}").arg(e.what()), + log::Type::Error); + } + } + if (p.shader) + { + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + + p.shader->bind(); + p.shader->setUniform("transform.mvp", + math::ortho( + 0.F, + static_cast(p.frameBufferSize.w), + static_cast(p.frameBufferSize.h), + 0.F, + -1.F, + 1.F)); + p.shader->setUniform("textureSampler", 0); + + glActiveTexture(static_cast(GL_TEXTURE0)); + glBindTexture(GL_TEXTURE_2D, p.offscreenBuffer->getColorID()); + + auto mesh = geom::box(math::Box2i( + 0, + 0, + p.frameBufferSize.w, + p.frameBufferSize.h)); + auto vboData = gl::convert( + mesh, + gl::VBOType::Pos3_F32_UV_U16, + math::SizeTRange(0, mesh.triangles.size() - 1)); + auto vbo = gl::VBO::create(mesh.triangles.size() * 3, gl::VBOType::Pos3_F32_UV_U16); + vbo->copy(vboData); + auto vao = gl::VAO::create(gl::VBOType::Pos3_F32_UV_U16, vbo->getID()); + vao->bind(); + vao->draw(GL_TRIANGLES, 0, mesh.triangles.size() * 3); + } #endif // TLRENDER_API_GL_4_1 - p.window->swap(); - } + p.window->swap(); + } - time::sleep(std::chrono::milliseconds(5)); + time::sleep(std::chrono::milliseconds(5)); + } } + return _exit; } void IApp::exit(int r) diff --git a/lib/tlGLApp/IApp.h b/lib/tlGLApp/IApp.h index 469c0a67c..25f48e602 100644 --- a/lib/tlGLApp/IApp.h +++ b/lib/tlGLApp/IApp.h @@ -31,8 +31,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&, const std::string& cmdLineName, const std::string& cmdLineSummary, @@ -45,7 +44,7 @@ namespace tl ~IApp(); //! Run the application. - virtual void run(); + virtual int run(); //! Exit the application. void exit(int = 0); diff --git a/lib/tlPlayGLApp/App.cpp b/lib/tlPlayGLApp/App.cpp index 05889e66a..1887f87b5 100644 --- a/lib/tlPlayGLApp/App.cpp +++ b/lib/tlPlayGLApp/App.cpp @@ -101,8 +101,7 @@ namespace tl }; void App::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { TLRENDER_P(); @@ -112,7 +111,6 @@ namespace tl const std::string settingsFileName = play::settingsName(appName, appDirPath); IApp::_init( - argc, argv, context, appName, @@ -582,12 +580,11 @@ namespace tl } std::shared_ptr App::create( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { auto out = std::shared_ptr(new App); - out->_init(argc, argv, context); + out->_init(argv, context); return out; } diff --git a/lib/tlPlayGLApp/App.h b/lib/tlPlayGLApp/App.h index c8158c0bd..9d6c82966 100644 --- a/lib/tlPlayGLApp/App.h +++ b/lib/tlPlayGLApp/App.h @@ -39,8 +39,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); App(); @@ -50,8 +49,7 @@ namespace tl //! Create a new application. static std::shared_ptr create( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); //! Open a file. diff --git a/lib/tlPlayQtApp/App.cpp b/lib/tlPlayQtApp/App.cpp index 8ed8dbafd..5da7c5a97 100644 --- a/lib/tlPlayQtApp/App.cpp +++ b/lib/tlPlayQtApp/App.cpp @@ -119,8 +119,7 @@ namespace tl const std::string settingsFileName = play::settingsName(appName, appDirPath); IApp::_init( - argc, - argv, + app::convert(argc, argv), context, appName, "Play timelines, movies, and image sequences.", diff --git a/lib/tlResourceApp/App.cpp b/lib/tlResourceApp/App.cpp index afdbf7554..0faabdd6e 100644 --- a/lib/tlResourceApp/App.cpp +++ b/lib/tlResourceApp/App.cpp @@ -13,12 +13,10 @@ namespace tl namespace resource { void App::_init( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { IApp::_init( - argc, argv, context, "tlresource", @@ -47,48 +45,46 @@ namespace tl {} std::shared_ptr App::create( - int argc, - char* argv[], + const std::vector& argv, const std::shared_ptr& context) { auto out = std::shared_ptr(new App); - out->_init(argc, argv, context); + out->_init(argv, context); return out; } - void App::run() + int App::run() { - if (_exit != 0) + if (0 == _exit) { - return; - } + _startTime = std::chrono::steady_clock::now(); - _startTime = std::chrono::steady_clock::now(); - - auto inputIO = file::FileIO::create(_input, file::Mode::Read); - const size_t size = inputIO->getSize(); - std::vector data; - data.reserve(size); - inputIO->readU8(data.data(), size); + auto inputIO = file::FileIO::create(_input, file::Mode::Read); + const size_t size = inputIO->getSize(); + std::vector data; + data.reserve(size); + inputIO->readU8(data.data(), size); - auto outputIO = file::FileIO::create(_output, file::Mode::Write); - outputIO->write(string::Format("const std::vector {0} = {\n").arg(_varName)); - const size_t columns = 15; - for (size_t i = 0; i < size; i += columns) - { - outputIO->write(" "); - for (size_t j = i; j < i + columns && j < size; ++j) + auto outputIO = file::FileIO::create(_output, file::Mode::Write); + outputIO->write(string::Format("const std::vector {0} = {\n").arg(_varName)); + const size_t columns = 15; + for (size_t i = 0; i < size; i += columns) { - outputIO->write(string::Format("{0}, "). - arg(static_cast(data[j]))); + outputIO->write(" "); + for (size_t j = i; j < i + columns && j < size; ++j) + { + outputIO->write(string::Format("{0}, "). + arg(static_cast(data[j]))); + } + outputIO->write("\n"); } - outputIO->write("\n"); - } - outputIO->write("};\n"); + outputIO->write("};\n"); - const auto now = std::chrono::steady_clock::now(); - const std::chrono::duration diff = now - _startTime; - _print(string::Format("Seconds elapsed: {0}").arg(diff.count())); + const auto now = std::chrono::steady_clock::now(); + const std::chrono::duration diff = now - _startTime; + _print(string::Format("Seconds elapsed: {0}").arg(diff.count())); + } + return _exit; } } } diff --git a/lib/tlResourceApp/App.h b/lib/tlResourceApp/App.h index 9cb1a994e..c7071c122 100644 --- a/lib/tlResourceApp/App.h +++ b/lib/tlResourceApp/App.h @@ -30,8 +30,7 @@ namespace tl protected: void _init( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); App(); @@ -41,12 +40,11 @@ namespace tl //! Create a new application. static std::shared_ptr create( - int argc, - char* argv[], + const std::vector&, const std::shared_ptr&); //! Run the application. - void run(); + int run(); private: std::string _input; diff --git a/lib/tlTimelineUI/AudioClipItem.cpp b/lib/tlTimelineUI/AudioClipItem.cpp index 588133bc1..bae892eb0 100644 --- a/lib/tlTimelineUI/AudioClipItem.cpp +++ b/lib/tlTimelineUI/AudioClipItem.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace tl { namespace timelineui @@ -29,39 +31,44 @@ namespace tl SizeData size; ui::InfoRequest infoRequest; - std::unique_ptr ioInfo; + std::shared_ptr ioInfo; std::map waveformRequests; - struct Waveform - { - std::shared_ptr mesh; - std::chrono::steady_clock::time_point time; - }; - std::map waveforms; }; void AudioClipItem::_init( const otio::SerializableObject::Retainer& clip, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { const auto path = timeline::getPath( clip->media_reference(), - itemData.directory, - itemData.options.pathOptions); + itemData->directory, + itemData->options.pathOptions); IBasicItem::_init( !clip->name().empty() ? clip->name() : path.get(-1, false), ui::ColorRole::AudioClip, "tl::timelineui::AudioClipItem", clip.value, + scale, + options, itemData, context, parent); TLRENDER_P(); + p.clip = clip; p.path = path; p.memoryRead = timeline::getMemoryRead(clip->media_reference()); p.thumbnailSystem = context->getSystem(); + + const auto i = itemData->info.find(path.get()); + if (i != itemData->info.end()) + { + p.ioInfo = i->second; + } } AudioClipItem::AudioClipItem() : @@ -75,12 +82,14 @@ namespace tl std::shared_ptr AudioClipItem::create( const otio::SerializableObject::Retainer& clip, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { auto out = std::shared_ptr(new AudioClipItem); - out->_init(clip, itemData, context, parent); + out->_init(clip, scale, options, itemData, context, parent); return out; } @@ -96,7 +105,6 @@ namespace tl TLRENDER_P(); if (changed) { - p.waveforms.clear(); _cancelRequests(); _updates |= ui::Update::Draw; } @@ -113,7 +121,6 @@ namespace tl TLRENDER_P(); if (thumbnailsChanged) { - p.waveforms.clear(); _cancelRequests(); _updates |= ui::Update::Draw; } @@ -128,10 +135,12 @@ namespace tl TLRENDER_P(); // Check if the I/O information is finished. + const std::string fileName = p.path.get(); if (p.infoRequest.future.valid() && p.infoRequest.future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - p.ioInfo = std::make_unique(p.infoRequest.future.get()); + p.ioInfo = std::make_shared(p.infoRequest.future.get()); + _data->info[fileName] = p.ioInfo; _updates |= ui::Update::Size; _updates |= ui::Update::Draw; } @@ -145,7 +154,7 @@ namespace tl i->second.future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { const auto mesh = i->second.future.get(); - p.waveforms[i->first] = { mesh, now }; + _data->waveforms[_getWaveformKey(i->second.timeRange)] = mesh; i = p.waveformRequests.erase(i); _updates |= ui::Update::Draw; } @@ -154,16 +163,6 @@ namespace tl ++i; } } - - // Check if any waveforms need to be redrawn. - for (const auto& waveform : p.waveforms) - { - const std::chrono::duration diff = now - waveform.second.time; - if (diff.count() <= _options.thumbnailFade) - { - _updates |= ui::Update::Draw; - } - } } void AudioClipItem::sizeHintEvent(const ui::SizeHintEvent& event) @@ -189,7 +188,6 @@ namespace tl p.size.clipRect = clipRect; if (clipped) { - p.waveforms.clear(); _cancelRequests(); _updates |= ui::Update::Draw; } @@ -206,6 +204,12 @@ namespace tl } } + std::string AudioClipItem::_getWaveformKey(const otime::TimeRange& timeRange) const + { + TLRENDER_P(); + return string::Format("{0}_{1}").arg(p.path.get()).arg(timeRange); + } + void AudioClipItem::_drawWaveforms( const math::Box2i& drawRect, const ui::DrawEvent& event) @@ -229,12 +233,6 @@ namespace tl event.render->setClipRectEnabled(true); event.render->setClipRect(box.intersect(clipRectState.getClipRect())); - std::set waveformsDelete; - for (const auto& waveform : p.waveforms) - { - waveformsDelete.insert(waveform.first); - } - const math::Box2i clipRect = _getClipRect( drawRect, _options.clipRectScale); @@ -249,9 +247,8 @@ namespace tl } } - if (_options.waveformWidth > 0 && thumbnailSystem) + if (_options.waveformWidth > 0 && p.ioInfo && thumbnailSystem) { - const auto now = std::chrono::steady_clock::now(); const int w = _sizeHint.w; for (int x = 0; x < w; x += _options.waveformWidth) { @@ -269,57 +266,42 @@ namespace tl (w > 0 ? (x / static_cast(w)) : 0) * _timeRange.duration().value(), _timeRange.duration().rate())); - auto i = p.waveforms.find(time); - if (i != p.waveforms.end()) + const otime::RationalTime time2 = time::round(otime::RationalTime( + _timeRange.start_time().value() + + (w > 0 ? ((x + _options.waveformWidth) / static_cast(w)) : 0) * + _timeRange.duration().value(), + _timeRange.duration().rate())); + const otime::TimeRange mediaRange = timeline::toAudioMediaTime( + otime::TimeRange::range_from_start_end_time(time, time2), + p.clip, + p.ioInfo->audio.sampleRate); + + const auto i = _data->waveforms.find(_getWaveformKey(mediaRange)); + if (i != _data->waveforms.end()) { - if (i->second.mesh) + if (i->second) { - const std::chrono::duration diff = now - i->second.time; - float a = 1.F; - if (_options.thumbnailFade > 0.F) - { - a = std::min(diff.count() / _options.thumbnailFade, 1.F); - } event.render->drawMesh( - *i->second.mesh, + *i->second, box.min, - image::Color4f(1.F, 1.F, 1.F, a)); + image::Color4f(1.F, 1.F, 1.F)); } - waveformsDelete.erase(time); } else if (p.ioInfo && p.ioInfo->audio.isValid()) { - const auto j = p.waveformRequests.find(time); + const auto j = p.waveformRequests.find(mediaRange.start_time()); if (j == p.waveformRequests.end()) { - const otime::RationalTime time2 = time::round(otime::RationalTime( - _timeRange.start_time().value() + - (w > 0 ? ((x + _options.waveformWidth) / static_cast(w)) : 0) * - _timeRange.duration().value(), - _timeRange.duration().rate())); - const otime::TimeRange mediaRange = timeline::toAudioMediaTime( - otime::TimeRange::range_from_start_end_time(time, time2), - p.clip, - p.ioInfo->audio.sampleRate); - p.waveformRequests[time] = thumbnailSystem->getWaveform( - box.getSize(), + p.waveformRequests[mediaRange.start_time()] = thumbnailSystem->getWaveform( p.path, p.memoryRead, + box.getSize(), mediaRange); } } } } } - - for (auto i : waveformsDelete) - { - const auto j = p.waveforms.find(i); - if (j != p.waveforms.end()) - { - p.waveforms.erase(j); - } - } } void AudioClipItem::_cancelRequests() diff --git a/lib/tlTimelineUI/AudioClipItem.h b/lib/tlTimelineUI/AudioClipItem.h index c1d05dede..83fc30179 100644 --- a/lib/tlTimelineUI/AudioClipItem.h +++ b/lib/tlTimelineUI/AudioClipItem.h @@ -18,7 +18,9 @@ namespace tl protected: void _init( const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent); @@ -30,7 +32,9 @@ namespace tl //! Create a new item. static std::shared_ptr create( const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent = nullptr); @@ -54,6 +58,8 @@ namespace tl const ui::DrawEvent&) override; private: + std::string _getWaveformKey(const otime::TimeRange&) const; + void _drawWaveforms( const math::Box2i&, const ui::DrawEvent&); diff --git a/lib/tlTimelineUI/GapItem.cpp b/lib/tlTimelineUI/GapItem.cpp index 7e3b247f0..80dc21426 100644 --- a/lib/tlTimelineUI/GapItem.cpp +++ b/lib/tlTimelineUI/GapItem.cpp @@ -11,7 +11,9 @@ namespace tl void GapItem::_init( ui::ColorRole colorRole, const otio::SerializableObject::Retainer& gap, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { @@ -20,6 +22,8 @@ namespace tl colorRole, "tl::timelineui::GapItem", gap.value, + scale, + options, itemData, context, parent); @@ -34,12 +38,14 @@ namespace tl std::shared_ptr GapItem::create( ui::ColorRole colorRole, const otio::SerializableObject::Retainer& gap, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { auto out = std::shared_ptr(new GapItem); - out->_init(colorRole, gap, itemData, context, parent); + out->_init(colorRole, gap, scale, options, itemData, context, parent); return out; } } diff --git a/lib/tlTimelineUI/GapItem.h b/lib/tlTimelineUI/GapItem.h index f6079f228..a13546409 100644 --- a/lib/tlTimelineUI/GapItem.h +++ b/lib/tlTimelineUI/GapItem.h @@ -19,7 +19,9 @@ namespace tl void _init( ui::ColorRole, const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent); @@ -32,7 +34,9 @@ namespace tl static std::shared_ptr create( ui::ColorRole, const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent = nullptr); }; diff --git a/lib/tlTimelineUI/IBasicItem.cpp b/lib/tlTimelineUI/IBasicItem.cpp index 682f5f2a3..514baa0a5 100644 --- a/lib/tlTimelineUI/IBasicItem.cpp +++ b/lib/tlTimelineUI/IBasicItem.cpp @@ -46,7 +46,9 @@ namespace tl ui::ColorRole colorRole, const std::string& objectName, const otio::SerializableObject::Retainer& item, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { @@ -55,6 +57,8 @@ namespace tl objectName, item.value, timeRangeOpt.has_value() ? timeRangeOpt.value() : time::invalidTimeRange, + scale, + options, itemData, context, parent); diff --git a/lib/tlTimelineUI/IBasicItem.h b/lib/tlTimelineUI/IBasicItem.h index 8a5b1c207..f5f55bbe7 100644 --- a/lib/tlTimelineUI/IBasicItem.h +++ b/lib/tlTimelineUI/IBasicItem.h @@ -21,7 +21,9 @@ namespace tl ui::ColorRole, const std::string& objectName, const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent = nullptr); diff --git a/lib/tlTimelineUI/IItem.cpp b/lib/tlTimelineUI/IItem.cpp index bf620a70c..3caa09a10 100644 --- a/lib/tlTimelineUI/IItem.cpp +++ b/lib/tlTimelineUI/IItem.cpp @@ -108,19 +108,23 @@ namespace tl const std::string& objectName, const otio::SerializableObject::Retainer& composable, const otime::TimeRange& timeRange, - const ItemData& data, + double scale, + const ItemOptions& options, + const std::shared_ptr& data, const std::shared_ptr& context, const std::shared_ptr& parent) { IWidget::_init(objectName, context, parent); TLRENDER_P(); - _composable = composable; _timeRange = timeRange; + _scale = scale; + _options = options; + _composable = composable; _data = data; p.timeUnitsObserver = observer::ValueObserver::create( - data.timeUnitsModel->observeTimeUnitsChanged(), + data->timeUnitsModel->observeTimeUnitsChanged(), [this](bool) { _timeUnitsUpdate(); @@ -191,9 +195,9 @@ namespace tl std::string IItem::_getDurationLabel(const otime::RationalTime& value) { - const otime::RationalTime rescaled = value.rescaled_to(_data.speed); + const otime::RationalTime rescaled = value.rescaled_to(_data->speed); return string::Format("{0}"). - arg(_data.timeUnitsModel->getLabel(rescaled)); + arg(_data->timeUnitsModel->getLabel(rescaled)); } otime::RationalTime IItem::_posToTime(float value) const diff --git a/lib/tlTimelineUI/IItem.h b/lib/tlTimelineUI/IItem.h index 1572fb2e2..6ef71e7dc 100644 --- a/lib/tlTimelineUI/IItem.h +++ b/lib/tlTimelineUI/IItem.h @@ -24,6 +24,9 @@ namespace tl std::string directory; timeline::Options options; std::shared_ptr timeUnitsModel; + std::map > info; + std::map > thumbnails; + std::map > waveforms; }; //! In/out points display options. @@ -106,7 +109,9 @@ namespace tl const std::string& objectName, const otio::SerializableObject::Retainer&, const otime::TimeRange&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent = nullptr); @@ -145,11 +150,11 @@ namespace tl virtual void _timeUnitsUpdate(); - otio::SerializableObject::Retainer _composable; otime::TimeRange _timeRange = time::invalidTimeRange; - ItemData _data; double _scale = 500.0; ItemOptions _options; + otio::SerializableObject::Retainer _composable; + std::shared_ptr _data; private: TLRENDER_PRIVATE(); diff --git a/lib/tlTimelineUI/TimelineItem.cpp b/lib/tlTimelineUI/TimelineItem.cpp index 7f5323157..2126e86cb 100644 --- a/lib/tlTimelineUI/TimelineItem.cpp +++ b/lib/tlTimelineUI/TimelineItem.cpp @@ -111,7 +111,9 @@ namespace tl void TimelineItem::_init( const std::shared_ptr& player, const otio::SerializableObject::Retainer& stack, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { @@ -119,6 +121,8 @@ namespace tl "tl::timelineui::TimelineItem", stack.value, player->getTimeRange(), + scale, + options, itemData, context, parent); @@ -173,6 +177,8 @@ namespace tl case TrackType::Video: track.items.push_back(VideoClipItem::create( clip, + scale, + options, itemData, context, shared_from_this())); @@ -180,6 +186,8 @@ namespace tl case TrackType::Audio: track.items.push_back(AudioClipItem::create( clip, + scale, + options, itemData, context, shared_from_this())); @@ -194,6 +202,8 @@ namespace tl ui::ColorRole::VideoGap : ui::ColorRole::AudioGap, gap, + scale, + options, itemData, context, shared_from_this())); @@ -241,12 +251,14 @@ namespace tl std::shared_ptr TimelineItem::create( const std::shared_ptr& player, const otio::SerializableObject::Retainer& stack, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { auto out = std::shared_ptr(new TimelineItem); - out->_init(player, stack, itemData, context, parent); + out->_init(player, stack, scale, options, itemData, context, parent); return out; } @@ -653,7 +665,7 @@ namespace tl const int handle = event.style->getSizeRole(ui::SizeRole::Handle, event.displayScale); const math::Box2i& g = _geometry; - const std::string labelMax = _data.timeUnitsModel->getLabel(_timeRange.duration()); + const std::string labelMax = _data->timeUnitsModel->getLabel(_timeRange.duration()); const math::Size2i labelMaxSize = event.fontSystem->getSize(labelMax, p.size.fontInfo); const int distanceMin = p.size.border + p.size.margin + labelMaxSize.w; @@ -768,7 +780,7 @@ namespace tl p.size.fontMetrics.lineHeight); if (time != currentTime && box.intersects(drawRect)) { - const std::string label = _data.timeUnitsModel->getLabel(time); + const std::string label = _data->timeUnitsModel->getLabel(time); event.render->drawText( event.fontSystem->getGlyphs(label, p.size.fontInfo), math::Vector2i( @@ -893,7 +905,7 @@ namespace tl g.h()), event.style->getColorRole(ui::ColorRole::Red)); - const std::string label = _data.timeUnitsModel->getLabel(currentTime); + const std::string label = _data->timeUnitsModel->getLabel(currentTime); event.render->drawText( event.fontSystem->getGlyphs(label, p.size.fontInfo), math::Vector2i( @@ -915,9 +927,9 @@ namespace tl TrackType::Audio == track.type ? (duration.rate() >= 1000.0) : false; - const otime::RationalTime rescaled = duration.rescaled_to(_data.speed); + const otime::RationalTime rescaled = duration.rescaled_to(_data->speed); const std::string label = string::Format("{0}, {1}{2}"). - arg(_data.timeUnitsModel->getLabel(rescaled)). + arg(_data->timeUnitsModel->getLabel(rescaled)). arg(khz ? (duration.rate() / 1000.0) : duration.rate()). arg(khz ? "kHz" : "FPS"); track.durationLabel->setText(label); diff --git a/lib/tlTimelineUI/TimelineItem.h b/lib/tlTimelineUI/TimelineItem.h index 68e205a7f..63b91a25f 100644 --- a/lib/tlTimelineUI/TimelineItem.h +++ b/lib/tlTimelineUI/TimelineItem.h @@ -27,7 +27,9 @@ namespace tl void _init( const std::shared_ptr&, const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent); @@ -40,7 +42,9 @@ namespace tl static std::shared_ptr create( const std::shared_ptr&, const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent = nullptr); diff --git a/lib/tlTimelineUI/TimelineWidget.cpp b/lib/tlTimelineUI/TimelineWidget.cpp index 6b9be00ee..f4b27c870 100644 --- a/lib/tlTimelineUI/TimelineWidget.cpp +++ b/lib/tlTimelineUI/TimelineWidget.cpp @@ -12,7 +12,7 @@ namespace tl { struct TimelineWidget::Private { - std::shared_ptr timeUnitsModel; + std::shared_ptr itemData; std::shared_ptr player; std::shared_ptr > timelineObserver; std::shared_ptr > editable; @@ -53,7 +53,8 @@ namespace tl _setMouseHover(true); _setMousePress(true, 0, static_cast(p.scrollKeyModifier)); - p.timeUnitsModel = timeUnitsModel; + p.itemData = std::make_shared(); + p.itemData->timeUnitsModel = timeUnitsModel; p.editable = observer::Value::create(false); p.frameView = observer::Value::create(true); @@ -103,12 +104,16 @@ namespace tl if (player == p.player) return; + p.itemData->info.clear(); + p.itemData->thumbnails.clear(); + p.itemData->waveforms.clear(); p.timelineObserver.reset(); p.scrollWidget->setWidget(nullptr); p.timelineItem.reset(); p.player = player; + p.scale = _getTimelineScale(); if (p.player) { p.timelineObserver = observer::ValueObserver::create( @@ -116,15 +121,11 @@ namespace tl [this](bool) { _timelineUpdate(); - _setItemScale(_p->timelineItem, _p->scale); }); } - - _timelineUpdate(); - if (p.timelineItem) + else { - p.scale = _getTimelineScale(); - _setItemScale(p.timelineItem, p.scale); + _timelineUpdate(); } } @@ -172,10 +173,7 @@ namespace tl TLRENDER_P(); p.scrollWidget->setScrollPos(math::Vector2i()); p.scale = _getTimelineScale(); - if (p.timelineItem) - { - _setItemScale(p.timelineItem, p.scale); - } + _setItemScale(); _updates |= ui::Update::Size; _updates |= ui::Update::Draw; } @@ -272,6 +270,9 @@ namespace tl TLRENDER_P(); if (p.itemOptions->setIfChanged(value)) { + p.itemData->info.clear(); + p.itemData->thumbnails.clear(); + p.itemData->waveforms.clear(); if (p.timelineItem) { _setItemOptions(p.timelineItem, value); @@ -296,6 +297,14 @@ namespace tl } } + void TimelineWidget::tickEvent( + bool parentsVisible, + bool parentsEnabled, + const ui::TickEvent& event) + { + IWidget::tickEvent(parentsVisible, parentsEnabled, event); + } + void TimelineWidget::sizeHintEvent(const ui::SizeHintEvent& event) { IWidget::sizeHintEvent(event); @@ -454,10 +463,7 @@ namespace tl if (zoomClamped != p.scale) { p.scale = zoomClamped; - if (p.timelineItem) - { - _setItemScale(p.timelineItem, p.scale); - } + _setItemScale(); const double s = zoomClamped / zoomPrev; const math::Vector2i scrollPosNew( (scrollPos.x + focus.x) * s - focus.x, @@ -485,6 +491,16 @@ namespace tl return out; } + void TimelineWidget::_setItemScale() + { + TLRENDER_P(); + p.itemData->waveforms.clear(); + if (p.timelineItem) + { + _setItemScale(p.timelineItem, p.scale); + } + } + void TimelineWidget::_setItemScale( const std::shared_ptr& widget, double value) @@ -526,21 +542,19 @@ namespace tl { if (auto context = _context.lock()) { - ItemData itemData; - itemData.speed = p.player->getDefaultSpeed(); - itemData.directory = p.player->getPath().getDirectory(); - itemData.options = p.player->getOptions(); - itemData.timeUnitsModel = p.timeUnitsModel; - + p.itemData->speed = p.player->getDefaultSpeed(); + p.itemData->directory = p.player->getPath().getDirectory(); + p.itemData->options = p.player->getOptions(); p.timelineItem = TimelineItem::create( p.player, p.player->getTimeline()->getTimeline()->tracks(), - itemData, + p.scale, + p.itemOptions->get(), + p.itemData, context); p.timelineItem->setEditable(p.editable->get()); p.timelineItem->setStopOnScrub(p.stopOnScrub->get()); p.scrollWidget->setScrollPos(scrollPos); - _setItemOptions(p.timelineItem, p.itemOptions->get()); p.scrollWidget->setWidget(p.timelineItem); } } diff --git a/lib/tlTimelineUI/TimelineWidget.h b/lib/tlTimelineUI/TimelineWidget.h index 2028c3182..4966f62fd 100644 --- a/lib/tlTimelineUI/TimelineWidget.h +++ b/lib/tlTimelineUI/TimelineWidget.h @@ -114,6 +114,10 @@ namespace tl bool isDragging() const; void setGeometry(const math::Box2i&) override; + void tickEvent( + bool, + bool, + const ui::TickEvent&) override; void sizeHintEvent(const ui::SizeHintEvent&) override; void mouseMoveEvent(ui::MouseMoveEvent&) override; void mousePressEvent(ui::MouseClickEvent&) override; @@ -134,6 +138,7 @@ namespace tl double _getTimelineScale() const; + void _setItemScale(); void _setItemScale( const std::shared_ptr&, double); diff --git a/lib/tlTimelineUI/TransitionItem.cpp b/lib/tlTimelineUI/TransitionItem.cpp index a057ac350..54c224cbf 100644 --- a/lib/tlTimelineUI/TransitionItem.cpp +++ b/lib/tlTimelineUI/TransitionItem.cpp @@ -10,7 +10,9 @@ namespace tl { void TransitionItem::_init( const otio::SerializableObject::Retainer& transition, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { @@ -19,6 +21,8 @@ namespace tl "tl::timelineui::TransitionItem", transition.value, timeRangeOpt.has_value() ? timeRangeOpt.value() : time::invalidTimeRange, + scale, + options, itemData, context, parent); @@ -32,12 +36,14 @@ namespace tl std::shared_ptr TransitionItem::create( const otio::SerializableObject::Retainer& transition, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { auto out = std::shared_ptr(new TransitionItem); - out->_init(transition, itemData, context, parent); + out->_init(transition, scale, options, itemData, context, parent); return out; } } diff --git a/lib/tlTimelineUI/TransitionItem.h b/lib/tlTimelineUI/TransitionItem.h index 6ae8958dd..041ebc47e 100644 --- a/lib/tlTimelineUI/TransitionItem.h +++ b/lib/tlTimelineUI/TransitionItem.h @@ -18,7 +18,9 @@ namespace tl protected: void _init( const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent); @@ -30,7 +32,9 @@ namespace tl //! Create a new item. static std::shared_ptr create( const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent = nullptr); }; diff --git a/lib/tlTimelineUI/VideoClipItem.cpp b/lib/tlTimelineUI/VideoClipItem.cpp index 1a7fdbc32..283ec7292 100644 --- a/lib/tlTimelineUI/VideoClipItem.cpp +++ b/lib/tlTimelineUI/VideoClipItem.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace tl { namespace timelineui @@ -29,39 +31,44 @@ namespace tl SizeData size; ui::InfoRequest infoRequest; - std::unique_ptr ioInfo; + std::shared_ptr ioInfo; std::map thumbnailRequests; - struct Thumbnail - { - std::shared_ptr image; - std::chrono::steady_clock::time_point time; - }; - std::map thumbnails; }; void VideoClipItem::_init( const otio::SerializableObject::Retainer& clip, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { const auto path = timeline::getPath( clip->media_reference(), - itemData.directory, - itemData.options.pathOptions); + itemData->directory, + itemData->options.pathOptions); IBasicItem::_init( !clip->name().empty() ? clip->name() : path.get(-1, false), ui::ColorRole::VideoClip, "tl::timelineui::VideoClipItem", clip.value, + scale, + options, itemData, context, parent); TLRENDER_P(); + p.clip = clip; p.path = path; p.memoryRead = timeline::getMemoryRead(clip->media_reference()); p.thumbnailSystem = context->getSystem(); + + const auto i = itemData->info.find(path.get()); + if (i != itemData->info.end()) + { + p.ioInfo = i->second; + } } VideoClipItem::VideoClipItem() : @@ -75,12 +82,14 @@ namespace tl std::shared_ptr VideoClipItem::create( const otio::SerializableObject::Retainer& clip, - const ItemData& itemData, + double scale, + const ItemOptions& options, + const std::shared_ptr& itemData, const std::shared_ptr& context, const std::shared_ptr& parent) { auto out = std::shared_ptr(new VideoClipItem); - out->_init(clip, itemData, context, parent); + out->_init(clip, scale, options, itemData, context, parent); return out; } @@ -96,7 +105,6 @@ namespace tl TLRENDER_P(); if (changed) { - p.thumbnails.clear(); _cancelRequests(); _updates |= ui::Update::Draw; } @@ -111,7 +119,6 @@ namespace tl TLRENDER_P(); if (thumbnailsChanged) { - p.thumbnails.clear(); _cancelRequests(); _updates |= ui::Update::Draw; } @@ -126,16 +133,17 @@ namespace tl TLRENDER_P(); // Check if the I/O information is finished. + const std::string fileName = p.path.get(); if (p.infoRequest.future.valid() && p.infoRequest.future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - p.ioInfo = std::make_unique(p.infoRequest.future.get()); + p.ioInfo = std::make_shared(p.infoRequest.future.get()); + _data->info[fileName] = p.ioInfo; _updates |= ui::Update::Size; _updates |= ui::Update::Draw; } // Check if any thumbnails are finished. - const auto now = std::chrono::steady_clock::now(); auto i = p.thumbnailRequests.begin(); while (i != p.thumbnailRequests.end()) { @@ -143,7 +151,7 @@ namespace tl i->second.future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { const auto image = i->second.future.get(); - p.thumbnails[i->first] = { image, now }; + _data->thumbnails[_getThumbnailKey(i->first)] = image; i = p.thumbnailRequests.erase(i); _updates |= ui::Update::Draw; } @@ -152,16 +160,6 @@ namespace tl ++i; } } - - // Check if any thumbnails need to be redrawn. - for (const auto& thumbnail : p.thumbnails) - { - const std::chrono::duration diff = now - thumbnail.second.time; - if (diff.count() <= _options.thumbnailFade) - { - _updates |= ui::Update::Draw; - } - } } void VideoClipItem::sizeHintEvent(const ui::SizeHintEvent& event) @@ -187,7 +185,6 @@ namespace tl p.size.clipRect = clipRect; if (clipped) { - p.thumbnails.clear(); _cancelRequests(); _updates |= ui::Update::Draw; } @@ -204,6 +201,12 @@ namespace tl } } + std::string VideoClipItem::_getThumbnailKey(const otime::RationalTime& time) const + { + TLRENDER_P(); + return string::Format("{0}_{1}").arg(p.path.get()).arg(time); + } + void VideoClipItem::_drawThumbnails( const math::Box2i& drawRect, const ui::DrawEvent& event) @@ -227,12 +230,6 @@ namespace tl event.render->setClipRectEnabled(true); event.render->setClipRect(box.intersect(clipRectState.getClipRect())); - std::set thumbnailsDelete; - for (const auto& thumbnail : p.thumbnails) - { - thumbnailsDelete.insert(thumbnail.first); - } - const math::Box2i clipRect = _getClipRect( drawRect, _options.clipRectScale); @@ -251,7 +248,6 @@ namespace tl 0; if (thumbnailWidth > 0 && thumbnailSystem) { - const auto now = std::chrono::steady_clock::now(); const int w = _sizeHint.w; for (int x = 0; x < w; x += thumbnailWidth) { @@ -269,53 +265,34 @@ namespace tl (w > 0 ? (x / static_cast(w - 1)) : 0) * _timeRange.duration().value(), _timeRange.duration().rate())); + const otime::RationalTime mediaTime = timeline::toVideoMediaTime( + time, + p.clip, + p.ioInfo->videoTime.duration().rate()); - const auto i = p.thumbnails.find(time); - if (i != p.thumbnails.end()) + const auto i = _data->thumbnails.find(_getThumbnailKey(mediaTime)); + if (i != _data->thumbnails.end()) { - if (i->second.image) + if (i->second) { - const std::chrono::duration diff = now - i->second.time; - float a = 1.F; - if (_options.thumbnailFade > 0.F) - { - a = std::min(diff.count() / _options.thumbnailFade, 1.F); - } - event.render->drawImage( - i->second.image, - box, - image::Color4f(1.F, 1.F, 1.F, a)); + event.render->drawImage(i->second, box); } - thumbnailsDelete.erase(time); } else if (p.ioInfo && !p.ioInfo->video.empty()) { - const auto k = p.thumbnailRequests.find(time); + const auto k = p.thumbnailRequests.find(mediaTime); if (k == p.thumbnailRequests.end()) { - const auto mediaTime = timeline::toVideoMediaTime( - time, - p.clip, - p.ioInfo->videoTime.duration().rate()); - p.thumbnailRequests[time] = thumbnailSystem->getThumbnail( - _options.thumbnailHeight, + p.thumbnailRequests[mediaTime] = thumbnailSystem->getThumbnail( p.path, p.memoryRead, + _options.thumbnailHeight, mediaTime); } } } } } - - for (auto i : thumbnailsDelete) - { - const auto j = p.thumbnails.find(i); - if (j != p.thumbnails.end()) - { - p.thumbnails.erase(j); - } - } } void VideoClipItem::_cancelRequests() diff --git a/lib/tlTimelineUI/VideoClipItem.h b/lib/tlTimelineUI/VideoClipItem.h index 2fedf1df4..eeae89bef 100644 --- a/lib/tlTimelineUI/VideoClipItem.h +++ b/lib/tlTimelineUI/VideoClipItem.h @@ -18,7 +18,9 @@ namespace tl protected: void _init( const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent); @@ -30,7 +32,9 @@ namespace tl //! Create a new item. static std::shared_ptr create( const otio::SerializableObject::Retainer&, - const ItemData&, + double scale, + const ItemOptions&, + const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& parent = nullptr); @@ -54,6 +58,8 @@ namespace tl const ui::DrawEvent&) override; private: + std::string _getThumbnailKey(const otime::RationalTime&) const; + void _drawInfo( const math::Box2i&, const ui::DrawEvent&); diff --git a/lib/tlUI/FileBrowserButton.cpp b/lib/tlUI/FileBrowserButton.cpp index 085101251..a7958ba4d 100644 --- a/lib/tlUI/FileBrowserButton.cpp +++ b/lib/tlUI/FileBrowserButton.cpp @@ -271,8 +271,8 @@ namespace tl { p.thumbnail.init = false; p.thumbnail.request = thumbnailSystem->getThumbnail( - p.options.thumbnailHeight, - p.fileInfo.getPath()); + p.fileInfo.getPath(), + p.options.thumbnailHeight); } } } diff --git a/lib/tlUI/ThumbnailSystem.cpp b/lib/tlUI/ThumbnailSystem.cpp index 754be8c5a..1b52f8176 100644 --- a/lib/tlUI/ThumbnailSystem.cpp +++ b/lib/tlUI/ThumbnailSystem.cpp @@ -45,9 +45,9 @@ namespace tl struct ThumbnailRequest { uint64_t id = 0; - int height = 0; file::Path path; std::vector memoryRead; + int height = 0; otime::RationalTime time = time::invalidTime; uint16_t layer = 0; std::promise > promise; @@ -56,9 +56,9 @@ namespace tl struct WaveformRequest { uint64_t id = 0; - math::Size2i size; file::Path path; std::vector memoryRead; + math::Size2i size; otime::TimeRange timeRange = time::invalidTimeRange; std::promise > promise; }; @@ -191,18 +191,18 @@ namespace tl } ThumbnailRequest ThumbnailSystem::getThumbnail( - int height, const file::Path& path, + int height, const otime::RationalTime& time, uint16_t layer) { - return getThumbnail(height, path, {}, time, layer); + return getThumbnail(path, {}, height, time, layer); } ThumbnailRequest ThumbnailSystem::getThumbnail( - int height, const file::Path& path, const std::vector& memoryRead, + int height, const otime::RationalTime& time, uint16_t layer) { @@ -210,13 +210,15 @@ namespace tl (p.requestId)++; auto request = std::make_shared(); request->id = p.requestId; - request->height = height; request->path = path; request->memoryRead = memoryRead; + request->height = height; request->time = time; request->layer = layer; ThumbnailRequest out; out.id = p.requestId; + out.height = height; + out.time = time; out.future = request->promise.get_future(); bool valid = false; { @@ -239,29 +241,31 @@ namespace tl } WaveformRequest ThumbnailSystem::getWaveform( - const math::Size2i& size, const file::Path& path, + const math::Size2i& size, const otime::TimeRange& range) { - return getWaveform(size, path, {}, range); + return getWaveform(path, {}, size, range); } WaveformRequest ThumbnailSystem::getWaveform( - const math::Size2i& size, const file::Path& path, const std::vector& memoryRead, + const math::Size2i& size, const otime::TimeRange& timeRange) { TLRENDER_P(); (p.requestId)++; auto request = std::make_shared(); request->id = p.requestId; - request->size = size; request->path = path; request->memoryRead = memoryRead; + request->size = size; request->timeRange = timeRange; WaveformRequest out; out.id = p.requestId; + out.size = size; + out.timeRange = timeRange; out.future = request->promise.get_future(); bool valid = false; { diff --git a/lib/tlUI/ThumbnailSystem.h b/lib/tlUI/ThumbnailSystem.h index 3a77043dd..c3095cc60 100644 --- a/lib/tlUI/ThumbnailSystem.h +++ b/lib/tlUI/ThumbnailSystem.h @@ -29,6 +29,8 @@ namespace tl struct ThumbnailRequest { uint64_t id; + int height = 0; + otime::RationalTime time = time::invalidTime; std::future > future; }; @@ -36,6 +38,8 @@ namespace tl struct WaveformRequest { uint64_t id; + math::Size2i size; + otime::TimeRange timeRange = time::invalidTimeRange; std::future > future; }; @@ -64,30 +68,30 @@ namespace tl //! Get a video thumbnail. ThumbnailRequest getThumbnail( - int height, const file::Path&, + int height, const otime::RationalTime& = time::invalidTime, uint16_t layer = 0); //! Get a video thumbnail. ThumbnailRequest getThumbnail( - int height, const file::Path&, const std::vector&, + int height, const otime::RationalTime& = time::invalidTime, uint16_t layer = 0); //! Get an audio waveform. WaveformRequest getWaveform( - const math::Size2i&, const file::Path&, + const math::Size2i&, const otime::TimeRange& = time::invalidTimeRange); //! Get an audio waveform. WaveformRequest getWaveform( - const math::Size2i&, const file::Path&, const std::vector&, + const math::Size2i&, const otime::TimeRange& = time::invalidTimeRange); //! Cancel pending requests. diff --git a/tests/tlAppTest/AppTest.cpp b/tests/tlAppTest/AppTest.cpp index 41b693071..3cecdb6b3 100644 --- a/tests/tlAppTest/AppTest.cpp +++ b/tests/tlAppTest/AppTest.cpp @@ -25,40 +25,10 @@ namespace tl namespace { - class Args - { - public: - Args(const std::vector& args) - { - argc = args.size(); - argv = new char* [argc]; - for (int i = 0; i < argc; ++i) - { - const size_t size = args[i].size(); - argv[i] = new char[size + 1]; - memcpy(argv[i], args[i].c_str(), size); - argv[i][size] = 0; - }; - } - - ~Args() - { - for (int i = 0; i < argc; ++i) - { - delete [] argv[i]; - } - delete [] argv; - } - - int argc = 0; - char** argv = nullptr; - }; - class App : public IApp { void _init( - int argc, - char* argv[], + const std::vector& args, const std::shared_ptr& context) { auto inputArg = CmdLineValueArg::create( @@ -75,8 +45,7 @@ namespace tl { "-option" }, "This is the help for the option."); IApp::_init( - argc, - argv, + args, context, "test", "Test application.", @@ -90,12 +59,11 @@ namespace tl public: static std::shared_ptr create( - int argc, - char* argv[], + const std::vector& args, const std::shared_ptr& context) { auto out = std::shared_ptr(new App); - out->_init(argc, argv, context); + out->_init(args, context); return out; } @@ -109,19 +77,16 @@ namespace tl void AppTest::run() { { - const auto args = Args({ "app" }); - auto app = App::create(args.argc, args.argv, _context); + auto app = App::create({ "app" }, _context); TLRENDER_ASSERT(app->getContext()); TLRENDER_ASSERT(1 == app->getExit()); } { - const auto args = Args({ "app", "-h" }); - auto app = App::create(args.argc, args.argv, _context); + auto app = App::create({ "app", "-h" }, _context); TLRENDER_ASSERT(1 == app->getExit()); } { - const auto args = Args({ "app", "input", "-log" }); - auto app = App::create(args.argc, args.argv, _context); + auto app = App::create({ "app", "input", "-log" }, _context); for (size_t i = 0; i < 10; ++i) { _context->tick(); @@ -130,8 +95,7 @@ namespace tl } try { - const auto args = Args({ "app", "input", "output", "-option" }); - auto app = App::create(args.argc, args.argv, _context); + auto app = App::create({ "app", "input", "output", "-option" }, _context); TLRENDER_ASSERT(false); } catch (const std::exception&) diff --git a/tests/tltest/main.cpp b/tests/tltest/main.cpp index 40bfba8ce..a5d9212b6 100644 --- a/tests/tltest/main.cpp +++ b/tests/tltest/main.cpp @@ -113,7 +113,8 @@ int main(int argc, char* argv[]) std::vector > tests; if (0) { - tests.push_back(core_tests::PathTest::create(context)); + tests.push_back(app_tests::AppTest::create(context)); + tests.push_back(app_tests::CmdLineTest::create(context)); } else {