skottie2movie.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. * Copyright 2014 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "experimental/ffmpeg/SkVideoEncoder.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkStream.h"
  10. #include "include/core/SkSurface.h"
  11. #include "include/core/SkTime.h"
  12. #include "modules/skottie/include/Skottie.h"
  13. #include "modules/skottie/utils/SkottieUtils.h"
  14. #include "src/utils/SkOSPath.h"
  15. #include "tools/flags/CommandLineFlags.h"
  16. static DEFINE_string2(input, i, "", "skottie animation to render");
  17. static DEFINE_string2(output, o, "", "mp4 file to create");
  18. static DEFINE_string2(assetPath, a, "", "path to assets needed for json file");
  19. static DEFINE_int_2(fps, f, 25, "fps");
  20. static DEFINE_bool2(verbose, v, false, "verbose mode");
  21. static DEFINE_bool2(loop, l, false, "loop mode for profiling");
  22. static DEFINE_double(motion_angle, 180, "motion blur angle");
  23. static DEFINE_double(motion_slope, 0, "motion blur slope");
  24. static DEFINE_int(motion_samples, 1, "motion blur samples");
  25. static void produce_frame(SkSurface* surf, skottie::Animation* anim, double frame_time) {
  26. anim->seekFrameTime(frame_time);
  27. surf->getCanvas()->clear(SK_ColorWHITE);
  28. anim->render(surf->getCanvas());
  29. }
  30. static void produce_frame(SkSurface* surf, SkSurface* tmp, skottie::Animation* anim,
  31. double frame_time, double frame_duration, double motion_radius,
  32. int motion_samples) {
  33. double samples_duration = frame_duration * motion_radius * 2;
  34. double dt = samples_duration / (motion_samples - 1);
  35. double t = frame_time - samples_duration / 2;
  36. SkPaint paint;
  37. paint.setAlphaf(1.0f / motion_samples);
  38. paint.setBlendMode(SkBlendMode::kPlus);
  39. surf->getCanvas()->clear(0);
  40. for (int i = 0; i < motion_samples; ++i) {
  41. if (FLAGS_verbose) {
  42. SkDebugf("time %g sample_time %g\n", frame_time, t);
  43. }
  44. produce_frame(tmp, anim, t);
  45. t += dt;
  46. tmp->draw(surf->getCanvas(), 0, 0, &paint);
  47. }
  48. }
  49. int main(int argc, char** argv) {
  50. CommandLineFlags::SetUsage("Converts skottie to a mp4");
  51. CommandLineFlags::Parse(argc, argv);
  52. if (FLAGS_input.count() == 0) {
  53. SkDebugf("-i input_file.json argument required\n");
  54. return -1;
  55. }
  56. if (FLAGS_motion_angle < 0 || FLAGS_motion_angle > 360) {
  57. SkDebugf("--motion_angle must be [0...360]\n");
  58. return -1;
  59. }
  60. if (FLAGS_motion_slope < -1 || FLAGS_motion_slope > 1) {
  61. SkDebugf("--motion_slope must be [-1...1]\n");
  62. return -1;
  63. }
  64. if (FLAGS_motion_samples < 1) {
  65. SkDebugf("--motion_samples must be >= 1\n");
  66. return -1;
  67. }
  68. // map angle=180 to radius=1/4 (of a frame duration)
  69. double motion_radius = FLAGS_motion_angle * 0.25 / 180.0;
  70. if (FLAGS_motion_samples == 1) {
  71. motion_radius = 0; // no blur if we're only 1 sample
  72. }
  73. SkString assetPath;
  74. if (FLAGS_assetPath.count() > 0) {
  75. assetPath.set(FLAGS_assetPath[0]);
  76. } else {
  77. assetPath = SkOSPath::Dirname(FLAGS_input[0]);
  78. }
  79. SkDebugf("assetPath %s\n", assetPath.c_str());
  80. auto animation = skottie::Animation::Builder()
  81. .setResourceProvider(skottie_utils::FileResourceProvider::Make(assetPath))
  82. .makeFromFile(FLAGS_input[0]);
  83. if (!animation) {
  84. SkDebugf("failed to load %s\n", FLAGS_input[0]);
  85. return -1;
  86. }
  87. SkISize dim = animation->size().toRound();
  88. double duration = animation->duration();
  89. int fps = FLAGS_fps;
  90. if (fps < 1) {
  91. fps = 1;
  92. } else if (fps > 240) {
  93. fps = 240;
  94. }
  95. const int frames = SkScalarRoundToInt(duration * fps);
  96. const double frame_duration = 1.0 / fps;
  97. if (FLAGS_verbose) {
  98. SkDebugf("size %dx%d duration %g, fps %d, frame_duration %g\n",
  99. dim.width(), dim.height(), duration, fps, frame_duration);
  100. }
  101. SkVideoEncoder encoder;
  102. sk_sp<SkSurface> surf, tmp_surf;
  103. sk_sp<SkData> data;
  104. do {
  105. double loop_start = SkTime::GetSecs();
  106. encoder.beginRecording(dim, fps);
  107. // lazily allocate the surfaces
  108. if (!surf) {
  109. surf = SkSurface::MakeRaster(encoder.preferredInfo());
  110. tmp_surf = surf->makeSurface(surf->width(), surf->height());
  111. }
  112. for (int i = 0; i <= frames; ++i) {
  113. double ts = i * 1.0 / fps;
  114. if (FLAGS_verbose) {
  115. SkDebugf("rendering frame %d ts %g\n", i, ts);
  116. }
  117. double normal_time = i * 1.0 / frames;
  118. double frame_time = normal_time * duration;
  119. if (motion_radius > 0) {
  120. produce_frame(surf.get(), tmp_surf.get(), animation.get(), frame_time, frame_duration,
  121. motion_radius, FLAGS_motion_samples);
  122. } else {
  123. produce_frame(surf.get(), animation.get(), frame_time);
  124. }
  125. SkPixmap pm;
  126. SkAssertResult(surf->peekPixels(&pm));
  127. encoder.addFrame(pm);
  128. }
  129. data = encoder.endRecording();
  130. if (FLAGS_loop) {
  131. double loop_dur = SkTime::GetSecs() - loop_start;
  132. SkDebugf("recording secs %g, frames %d, recording fps %d\n",
  133. loop_dur, frames, (int)(frames / loop_dur));
  134. }
  135. } while (FLAGS_loop);
  136. if (FLAGS_output.count() == 0) {
  137. SkDebugf("missing -o output_file.mp4 argument\n");
  138. return 0;
  139. }
  140. SkFILEWStream ostream(FLAGS_output[0]);
  141. if (!ostream.isValid()) {
  142. SkDebugf("Can't create output file %s\n", FLAGS_output[0]);
  143. return -1;
  144. }
  145. ostream.write(data->data(), data->size());
  146. return 0;
  147. }