MicrocosmWorksNag-iinobasyon at Nagdidisenyo ng Digital Cosmos
Tungkol Sa AminMakipag-ugnayan
MicrocosmWorksNagpapabago at Nagdidisenyo ng Digital Cosmos

Nagbibigay ng mga solusyong IT na mahalaga. Kami ay masigasig sa teknolohiya, seguridad, at pagtulong sa mga negosyo na lumago sa pamamagitan ng maaasahan, makabagong IT infrastructure.

[email protected]
+91 7011868196
New Delhi, India

Sentro ng Paglago ng AI

AI HubInobasyon ng StartupPampabilis ng Negosyo

Mga Solusyon

Lahat ng SolusyonMga Wellness at Fitness AppsAI Video PlatformPag-unlad ng AI Agent

Mga Mapagkukunan

Mga PananawMga Gabay sa IndustriyaMga Plano ng PaggamitMga Pattern ng ArkitekturaMga Pag-aaral ng Kaso

Kumpanya

Tungkol sa AminMakipag-ugnayanAng Aming Gawain

Mga Serbisyo

Digital na PagkonsultaImprastraktura ng CloudPag-unlad ng SaaSPag-unlad ng AITeknolohiya ng Video
Pag-unlad ng ERPPagpapasadya ng ZohoPag-unlad ng OdooPagsasama ng SalesforcePag-unlad ng Custom na CRM
Pagsasama ng QuickBooksMga Solusyon sa IoTPag-unlad ng Blockchain
Pagkonsulta sa CybersecuritySuporta sa IT - L3

© 2026 MicrocosmWorks. Lahat ng karapatan ay nakalaan.

Patakaran sa PagkapribadoMga Tuntunin ng Serbisyo
Bumalik sa mga Pananaw
IoT Development

Pag-reframe ng 360 Video sa Flutter: Spherical Projection, Quaternion Keyframes, at GPU Export

Pag-reframe ng equirectangular na 360 footage sa Flutter gamit ang spherical projection, quaternion keyframes, at GPU-accelerated export.

Saurav Kumar Gupta's image Saurav Kumar Gupta
•
July 10, 2026
•
Na-update July 24, 2026
•
5 min read
Developer using Flutter to edit and reframe a 360° video with spherical projection.webp
5 min read

Pag-reframe ng 360° Video sa Flutter: Spherical Projection, Quaternion Keyframes, at GPU Export

Paano namin ginawa ang raw na equirectangular na 360° footage sa makinis, naka-frame, at patag na video — nang buo sa loob ng isang Flutter app.

Ang problemang walang nagbabala sa iyo

Ang isang normal na video ay may isang malinaw na bagay na ipapakita sa iyo: ang frame na tinutukan ng camera. Ang isang 360° video ay walang ganoong bagay. Ni-record ng camera ang lahat — isang buong globo ng mga pixel na nakaimbak bilang isang patag na equirectangular na rektanggulo (isang 2:1 na imahe kung saan ang X axis ay longitude at ang Y axis ay latitude). Bago mo mailagay ang footage na iyon sa Instagram, YouTube, o screen ng telepono, may kailangang sumagot sa tanong na sadyang hindi sinagot ng camera:

Saan dapat tumingin ang manonood, at kailan?

Ang "Reframing" ay ang pagkilos ng pagpapalipad ng virtual na camera sa loob ng globo sa paglipas ng panahon — pag-pan, pag-tilt, pag-zoom — at pag-render ng isang normal na patag na 16:9 (o 9:16, o 2.35:1) na video. Ito ang feature kung saan nakasentro ang GoPro Player, Insta360 Studio, at ang GoPro VR Reframe plugin ng Adobe Premiere.

Binuo namin ito nang buo sa Flutter. Ang post na ito ay isang tour sa mga bahagi na talagang mahirap: ang projection math, gimbal-lock-free interpolation, ang ±180° seam, at isang GPU export pipeline na kailangang gumana sa totoong mga Android decoder.

Ang end-to-end flow:

kumuha/mag-import ng 360 video
  → mag-transcode ng lightweight preview proxy
  → nag-tap / nag-drawing / auto-detect ng subject ang user
  → ang object tracking ay lumilikha ng path
  → bumuo + pakinisin ang keyframes
  → SLERP-interpolate per-frame camera orientation (live preview)
  → native GPU export sa isang flat video
  → FFmpeg post-pass (moov atom + pag-ayos ng A/V sync)

Arkitektura sa isang sulyap

Pinanatili namin ang sistema sa tatlong malinis na layer upang ang math ay masubukan nang hiwalay sa Flutter:

LayerNasaanResponsibilidad
Modelslib/data/models/reframe/SphericalCoordinates/Quaternion, CameraOrientation/MotionConstraints, ReframeKeyframe, ReframeProject
Serviceslib/services/reframe/SphericalProjection (ray casting), CoordinateConverter, KeyframeInterpolator/Generator/Smoother, ReframeExportService
Presentationlib/presentation/.../reframe/Riverpod ReframeNotifier, the native OpenGL Spherical360Player, bbox-drawing overlay, keyframe timeline

Ang ginintuang tuntunin: lahat ng trigonometry ay purong Dart na walang Flutter imports. Nangangahulugan iyan na ang bawat projection at interpolation function ay unit-testable nang walang widget tree, at ang parehong math ang nagpapatakbo ng live preview at ng export.

1. Ang puso nito: equirectangular ⟷ spherical

Nagsisimula ang lahat sa isang mapping. Ang isang equirectangular frame ay isa lamang rektanggulo kung saan:

  • X (0 → lapad) sumasakop sa longitude (yaw) mula −180° hanggang +180°
  • Y (0 → taas) sumasakop sa latitude (pitch) mula +90° (itaas/zenith) hanggang −90° (ibaba/nadir)

Ang SphericalCoordinates ay sadyang maliit — yaw at pitch lang. Ang Roll, FOV, at zoom ay sa camera, hindi sa direksyon. Narito ang conversion na pinagbabatayan ng buong feature:

factory SphericalCoordinates.fromEquirectangular({
  required double x, required double y,
  required double videoWidth, required double videoHeight,
}) {
  final normalizedX = x / videoWidth;
final normalizedY = y / videoHeight;
// X maps to yaw: 0->-180°, 0.5->0°, 1.0->180°
final yaw = (normalizedX - 0.5) * 360.0;
// Y maps to pitch: 0->90° (top), 0.5->0° (horizon), 1.0->-90° (bottom)
final pitch = (0.5 - normalizedY) * 180.0;
return SphericalCoordinates(
    yaw: yaw.clamp(-180.0, 180.0),
    pitch: pitch.clamp(-90.0, 90.0),
  );
}

Ang kabaligtaran ay ang mirror image (normalizedX = yaw/360 + 0.5), at ginagamit namin ito palagi upang iguhit ang isang tracked bounding box pabalik sa player.

"I-tap ang nakikita mo" — perspective ray casting

Ang user ay hindi nakikipag-ugnayan sa equirectangular na rektanggulo. Nakikipag-ugnayan sila sa isang rendered perspective view. Kaya kapag nag-tap sila ng isang punto sa player, kailangan nating mag-cast ng ray sa isang virtual na pinhole camera at hanapin kung aling direksyon sa sphere ang kanilang tinamaan. Iyan ang SphericalProjection.screenToSpherical:

final ndcX = (2.0 * screenPoint.dx / playerSize.width) - 1.0;
final ndcY = 1.0 - (2.0 * screenPoint.dy / playerSize.height);
final aspectRatio = playerSize.width / playerSize.height;
final tanHalfFov = math.tan(cameraFov * math.pi / 360.0); // half-angle
double rayX = ndcX * aspectRatio * tanHalfFov;
double rayY = ndcY * tanHalfFov;
double rayZ = 1.0;
// ...normalize, rotate the ray by pitch (around X) then yaw (around Y)...
final sphericalYaw   = math.atan2(finalRayX, finalRayZ) * 180.0 / math.pi;
final sphericalPitch = math.asin(finalRayY.clamp(-1.0, 1.0)) * 180.0 / math.pi;

Ang .clamp(-1.0, 1.0) bago ang asin ay mukhang paranoid, ngunit ang floating-point drift ay magbibigay sa iyo ng 1.0000001 at ang asin ay magbabalik ng NaN — at ang isang NaN ay sumisira sa bawat downstream frame. Ang defensive clamping sa paligid ng inverse trig ay isang paulit-ulit na tema sa codebase na ito.

2. Makinis na paggalaw ng camera: quaternions, hindi Euler angles

Ang simpleng diskarte ay ang pag-imbak ng yaw/pitch sa bawat keyframe at linearly na i-interpolate ang mga numero. Mukha itong kakila-kilabot. Ang pag-interpolate ng Euler angles ay nagbibigay sa iyo ng gimbal lock, non-uniform angular velocity, at pangit na snaps malapit sa poles.

Nag-iinterpolate kami ng orientation bilang quaternion gamit ang SLERP (spherical linear interpolation). Bawat direksyon ay kino-convert sa isang quaternion sa pamamagitan ng ZYX Euler convention:

Quaternion toQuaternion() {
  final yawRad = yaw * math.pi / 180.0;
final pitchRad = pitch * math.pi / 180.0;
final cy = math.cos(yawRad * 0.5); final sy = math.sin(yawRad * 0.5);
final cp = math.cos(pitchRad * 0.5); final sp = math.sin(pitchRad * 0.5);
return Quaternion(w: cy * cp, x: cy * sp, y: sy * cp, z: -sy * sp);
}

At ang SLERP mismo ay mayroong dalawang production-grade safety valve na kailangan ng bawat implementasyon:

static Quaternion slerp(Quaternion a, Quaternion b, double t) {
  a = a.normalize(); b = b.normalize();
var dotProduct = a.dot(b);
if (dotProduct < 0.0) { b = -b; dotProduct = -dotProduct; }   // take the shortest hemisphere
if (dotProduct > 0.9995) {                                     // near-parallel -> fall back to LERP
    return Quaternion(w: a.w + t*(b.w-a.w), x: a.x + t*(b
                      y: a.y + t*(b.y-a.y), z: a.z + t*(b.z-a.z)).normalize();
  }
final theta0 = math.acos(dotProduct);
final theta = theta0 * t;
final sinTheta = math.sin(theta);
final sinTheta0 = math.sin(theta0);
final s0 = math.cos(theta) - dotProduct * sinTheta / sinTheta0;
final s1 = sinTheta / sinTheta0;
return Quaternion(w: s0*a.w + s1*b.w, /* ...*/).normalize();
}

Dalawang hindi-mapagtawarang detalye:

  1. Pinakamaikling hemisphere — q at −q ay kumakatawan sa parehong orientation. Kung ang dot product ay negatibo, babaguhin mo ang isang input o ang iyong camera ay dadaan sa mas mahabang ruta sa paligid ng sphere.
  2. Near-parallel LERP fallback — kapag halos magkapareho ang dalawang keyframe, sinTheta0 → 0 at hahatiin mo sa halos zero. Ang pagbabalik sa plain LERP kapag ang dot > 0.9995 ay iniiwasan ang blow-up.

Isang banayad ngunit mahalagang desisyon sa disenyo: SLERP namin ang direksyon, ngunit LERP ang FOV/zoom at angle-LERP ang roll. Ang spherical interpolation ang tamang tool para sa direksyon; ang zoom ay isang scalar lang at dapat gumalaw nang linearly:

final qInterp = Quaternion.slerp(from.direction.toQuaternion(), to.direction.toQuaternion(), t);
final interpDirection = SphericalCoordinates.fromQuaternion(qInterp).normalize();
final interpFov  = _lerp(from.fov, to.fov, t);
final interpRoll = _lerpAngle(from.roll, to.roll, t); // wraps across ±180
final interpZoom = _lerp(from.zoom, to.zoom, t);

💡 Kapag nagko-convert pabalik mula sa isang quaternion patungo sa yaw/pitch, ginagamit namin ang atan2 sa halip na asin "para sa numerical stability (iniiwasan ang mga isyu sa asin domain)." Pagkatapos ng isang SLERP, ang quaternion ay maaaring bahagyang denormalized, at ang atan2 ay nagde-degrade nang maayos kung saan ang asin ay nagbibigay ng NaN.

Easing: hand-rolled, sa papasok na transisyon

Ang bawat keyframe ay nagmamay-ari ng easing curve para sa transisyon papunta rito. Ang mga curve ay isinusulat nang mano-mano upang magkapareho ang mga ito sa bawat platform:

case KeyframeEasing.linear:    return t;
case KeyframeEasing.easeInOut: return t * t * (3 - 2 * t); // smoothstep S-curve
case KeyframeEasing.easeIn:    return t * t;               // quadratic
case KeyframeEasing.easeOut:   return 1 - (1 - t) * (1 - t);

3. Ang ±180° seam: ang bug na bumabagabag sa bawat 360 feature

Narito ang patibong. Ang Yaw ay nabubuhay sa isang bilog: ang +179° at −179° ay 2° lang ang layo, hindi 358°. Sa sandaling tumawid ang iyong subject sa likod ng sphere, ang simpleng math ay biglang ililipat ang camera sa kabilang panig ng mundo. Ang isyung ito ay lumalabas sa apat na magkakaibang layer ng sistema, at bawat isa ay nangangailangan ng sarili nitong solusyon.

Layer 1 — primitives. Dalawang maliliit na helper na sinusuportahan ng buong codebase:

static double normalizeYaw(double yaw) {
  while (yaw > 180) { yaw -= 360; }
  while (yaw < -180) { yaw += 360; }
return yaw;
}

// "more reliable than MediaMetadataRetriever for 360° videos
//  which often use unusual codecs (HEVC, high-res)"
static double shortestYawDelta(double from, double to) {
  double delta = to - from;
  if (delta > 180) delta -= 360;
  if (delta < -180) delta += 360;
return delta;
}

Layer 2 — ang pagbuo ng keyframe ay nag-iipon ng isang continuous (unbounded) yaw upang ang SLERP ay palaging nag-iinterpolate sa maikling paraan. Sa halip na i-clamp ang bawat sample sa ±180°, pinagsasama namin ang pinakamaikling deltas at hinahayaan ang tumatakbong halaga na lumampas sa 180° nang sadya:

final normalizedContinuousYaw = SphericalCoordinates.normalizeYaw(continuousYaw);
final yawDelta = SphericalCoordinates.shortestYawDelta(normalizedContinuousYaw, rawCoords.yaw);
continuousYaw = continuousYaw + yawDelta;            // may exceed ±180 on purpose
coords = SphericalCoordinates(yaw: continuousYaw, pitch: rawCoords.pitch);

Layer 3 — pagbuo muli ng bounding-box. Kapag ang user ay nag-drawing ng kahon na sumasaklaw sa seam, ang apat na sulok ay nagrereport ng yaws tulad ng +170°, +175°, −175°, −170°. Nade-detect ito ng SphericalProjection (anumang sulok > 90° at anumang sulok < −90°) at muling binubuo ang isang continuous-yaw na "seam-safe" bbox sa pamamagitan ng pag-shift ng mga negatibo ng +360° bago sukatin ang lawak nito.

Layer 4 — ang live player ay kumikinis patungo sa target nito gamit ang shortestYawDelta upang ang pag-drag sa kabila ng seam ay hindi kailanman biglang gagalaw.

Kung may isang bagay kang tatandaan tungkol sa pagbuo ng 360 tooling: ang seam ay hindi isang edge case, ito ay isang cross-cutting concern. Maglaan ng pansin dito sa lahat ng dako kung saan lumalabas ang yaw.

4. Pagpaparamdam na parang Insta360/GoPro

Ang smooth interpolation ay nagbibigay sa iyo ng isang technically correct na camera. Ngunit hindi ito nagbibigay sa iyo ng isang magandang camera. Tatlong bahagi ng "feel engineering" ang nagsasara ng agwat na iyon.

Motion constraints — i-clamp ang velocity at acceleration. Ang raw tracking data ay magalaw. Pinapadaan namin ang keyframes sa MotionConstraints, na naglilimita sa bilis at biglaan ng paggalaw ng virtual na camera (hal. maxYawSpeed = 120°/s, maxPitchSpeed = 90°/s, maxAcceleration = 180°/s²), na may cinematic(), responsive(), at production() presets. Ito ang pagkakaiba sa pagitan ng "security camera na sumusubaybay sa isang langaw" at "operator na may matatag na kamay."

Adaptive FOV — mag-zoom upang magkasya ang subject. Sinusukat namin ang angular size ng subject sa equirectangular space at pumipili ng FOV upang punan nito ang target na bahagi ng frame:

final angularWidth  = (bboxWidth  / videoWidth)  * 360.0;
final angularHeight = (bboxHeight / videoHeight) * 180.0;
final objectAngularSize = math.max(angularWidth, angularHeight);
final idealFov = objectAngularSize / targetFillRatio; // targetFillRatio ≈ 0.4
return idealFov.clamp(minFov, maxFov);                 // 45°–120°

Motion lead + face→body blend. Inilalabas namin ang framing ng ~15% ng bbox width una sa direksyon ng paggalaw, upang ang subject ay hindi nakasandal sa gilid na kanilang nilalakaran. At kapag nawala ang face detection, hindi kami biglang lumilipat sa body center — nagle-lerp kami mula sa huling-kilalang face center patungo sa body center sa loob ng ~10 frames.

5. Ang dalawang-video na trick: preview proxy vs. full-res original

Ito ang desisyong pang-arkitektura na nagpapagamit ng buong bagay sa totoong mga telepono.

Ang isang 360° GoPro/Insta360 file ay madalas na 4096×2048 HEVC. Nahihirapan ang mga budget Android decoder na i-play ito nang maayos, lalo na habang may object tracking. Kaya, pinapatakbo namin ang tracking at live preview sa isang transcoded proxy (~1440×720, libx264 -preset veryfast -crf 28), at inilalaan ang full-resolution original para sa final export lamang.

// "more reliable than MediaMetadataRetriever for 360° videos
//  which often use unusual codecs (HEVC, high-res)"
final maxSafeWidth = 2048, maxSafeHeight = 1080;
// 2:1 4K (4096×2048) is unsafe -> trigger transcodeForPlayback()

Ang dapat mong bigyang-pansin: ang mga keyframe bounding box ay kinukuwenta sa proxy coordinates at kailangang i-scale up sa original coordinates bago i-export. Maling scaling nito at ang iyong export ay magkakaroon ng bahagyang ibang rehiyon kaysa sa na-preview ng user. (Ginagamit din namin ang ffprobe, hindi MediaMetadataRetriever, para sa metadata at first-frame extraction — ang built-in retriever ng Android ay hindi maaasahan sa mga unusual na codec na nilalabas ng 360 camera.)

6. GPU export: OpenGL + MediaCodec sa pamamagitan ng MethodChannel

Masyadong mabagal ang pag-re-project ng bawat frame ng isang 4K sphere sa pamamagitan ng v360 filter ng FFmpeg sa isang phone CPU. (Oo, pinapanatili namin ang v360 string — malinaw nitong dinodokumento ang projection semantics, v360=e:flat:... na nangangahulugang "equirectangular in, flat out" — ngunit hindi ito ang live path.)

Ibinibigay ng totoong export ang mga keyframe sa isang native na OpenGL ES + MediaCodec renderer sa pamamagitan ng MethodChannel:

final result = await _methodChannel.invokeMethod('exportVideoGPU', {
  'videoPath': resolvedVideoPath,
  'outputPath': outputPath,
  'keyframes': keyframesList,
  'initialOrientation': initialOrientationMap,
  'width': settings.exportWidth,
  'height': settings.exportHeight,
  'fps': settings.outputFps.toInt(),
  'includeAudio': settings.includeAudio,
  if (isTrimmed) 'trimStartMs': trimStartMs
  if (isTrimmed) 'trimEndMs': trimEndMs
});

Ginagawa namin ito sa likod ng isang capability check (Android API 21+, isang hardware H.264 encoder, OpenGL ES 3.0) at nag-stream ng progreso pabalik sa isang EventChannel. Ilang mahirap na nakuha na gotchas:

  • Ang −90° yaw offset. Ang aming Dart convention ay naglalagay ng yaw = 0 sa gitna ng equirectangular na imahe (u = 0.5); ang OpenGL renderer ay naglalagay ng yaw = 0 sa u = 0.75. Kaya ang bawat keyframe yaw ay nire-remap ng −90° (at ni-re-wrap sa ±180°) sa pagtawid sa channel — simetrikal na inilapat sa parehong export channel at live-player channel, upang magkasundo ang preview at ang export.
  • Degenerate keyframe counts. Zero keyframes → bumubuo ng dalawang magkapareho mula sa initial orientation (static export). Isang keyframe → i-duplicate ito. Laging gusto ng renderer ang kahit isang simula at isang dulo.
  • Trimming re-bases timestamps. Ang isang trimmed export ay naglilipat ng bawat keyframe ng t − trimStartMs.

Ang FFmpeg post-pass na walang inaasahan

Maaari mong isipin na ang GPU export ang finish line. Ngunit hindi — dahil ang MediaMuxer ng Android ay sumusulat ng moov atom sa dulo ng file at maaaring magdala ng audio PTS offset ng source. Ang resulta: Ang ExoPlayer (at video_player ng Flutter) ay nag-free-freeze ang larawan habang patuloy na tumatakbo ang orasan. Kaya pagkatapos ng GPU pass, gumagawa ang FFmpeg ng mabilis, lossless na paglilinis:

// copy video (no re-encode -> zero quality loss), re-encode audio to fix PTS,
// move moov atom to the front for instant playback.
-c:v copy -af "aresample=async=1:first_pts=0" -movflags +faststart

At isang brutal na maliit na gotcha ang nagbabantay dito: ina-analyze muna namin ang audio track, dahil ang pagpasa ng audio flags sa isang video na walang audio track ay nagiging sanhi upang mag-hang ang FFmpeg nang matagal sa ilang device. Ang buong post-pass ay naka-wrap sa isang 60-segundong timeout na may cancel-on-timeout.

7. Ang live preview ay isa ring native player

Ang in-editor preview ay hindi isang Flutter CustomPaint — ito ay isang native OpenGL surface na naka-embed sa pamamagitan ng AndroidView (na may viewType na tulad ng 'your_app/spherical360player'), na may sariling per-view method/event channel. Ipinapakita ng Dart Spherical360PlayerController ang yaw/pitch/roll/fov at inilalapat ang parehong −90° offset bago ipadala.

Ang mga detalye ng "pakiramdam" na nagpapasarap dito:

  • Frame-rate-independent smoothing — factor = 1 - exp(-speed * dt) upang ang easing ay magmukhang pareho sa 60fps at 90fps, na may seam-safe shortest-path yaw na nakasama.
  • Momentum/inertia pagkatapos ng isang fling, na may friction-based decay — disabled habang nagpe-playback, dahil habang nagpe-playback ang camera ay kailangang sumunod sa mga keyframe, hindi sa huling flick ng user.
  • Isang GestureDetector na ang onScaleUpdate ay gumagawa ng pan at pinch-zoom nang sabay; ang double-tap ay nagpapalit ng play/pause. Ang FOV ay nag-cla-clamp sa 30°–120°, ang pitch sa ±90°, sa controller boundary.

Isang maliit ngunit makabuluhang pag-aayos sa UX: bago lumipat mula "tracking" sa "generating keyframes," naglalagay kami ng 120 ms delay upang talagang magpinta ang Flutter ng "100%." Kung wala ito, ang huling progress frame at ang pagbabago ng status ay mapupunta sa parehong microtask batch at hindi makikita ng user ang pagkumpleto.

State machine, sa madaling sabi

Ang lahat ng ito ay kino-coordinate ng isang Riverpod ReframeNotifier na nagpapatakbo ng isang status enum:

idle → loadingVideo → transcoding
    → (detecting | selectingObject | drawingBboxOnPlayer | autoDetectingOnPlayer)
    → tracking → generatingKeyframes → editing → exporting → completed | error

Maraming entry point ang nagpapakain sa parehong tracker: tap-to-detect (YOLO overlay), manual bbox, o ang Insta360-style draw-directly-on-the-player gesture. Lahat sila ay nagtatagpo sa KeyframeGenerator.production().generate(...) → KeyframeSmoother.smooth(...), at mula doon ay pinapatakbo ng interpolator ang bawat previewed at exported frame.

Mga natutunan

  1. Panatilihing purong ang math. Walang Flutter imports sa projection/interpolation layer. Ito lang ang dahilan kung bakit ito nasusubukan.
  2. Quaternion SLERP para sa direksyon; LERP para sa scalars. Huwag i-interpolate ang Euler angles, at huwag i-SLERP ang iyong zoom.
  3. Ang ±180° seam ay isang cross-cutting concern, hindi isang edge case. Ito ay lumalabas muli sa primitives, generation, projection, at sa player.
  4. Pagkasunduin ang iyong mga convention nang isang beses, symmetrically. Ang −90° yaw offset sa pagitan ng Dart at OpenGL ay inilalapat sa parehong channel boundaries upang ang preview == export.
  5. Proxy para sa interaction, original para sa output. Tandaan lamang na i-scale ang iyong coordinates sa pagitan ng dalawa.
  6. "Done rendering" ≠ "plays correctly." Maglaan ng badyet para sa isang FFmpeg post-pass upang ayusin ang paglalagay ng moov atom at audio PTS, at suriin bago magdagdag ng audio flags.
  7. Ang "Pakiramdam" ay isang feature. Ang motion constraints, adaptive FOV, at motion-lead ang naghihiwalay sa "technically reframed" mula sa "mukhang kinunan ng pro."
  8. Ang resulta: isang fully on-device na 360° reframe editor — i-tap ang isang subject, hayaang i-track ito, i-tweak ang keyframes, at i-export ang isang malinis na flat video — binuo sa Flutter na may manipis na native GPU layer na gumagawa lamang ng trabaho na hindi kayang gawin ng Dart nang sapat na mabilis.

Flutter360VideoOpenGLQuaternions
Saurav Kumar Gupta's image

Tungkol sa May-akda

Saurav Kumar Gupta

AI & Cloud Solutions Expert at MicrocosmWorks

Building innovative AI-powered solutions and helping businesses transform through cutting-edge technology.

Gusto mo bang matuto pa?

Makipag-ugnayan sa amin upang talakayin kung paano namin makakatulong na ipatupad ang mga solusyong ito para sa iyong negosyo.

Makipag-ugnayan

Mga Madalas Itanong

Accurate reframing requires handling spherical projection, seamless rotation across the ±180° boundary, smooth camera interpolation, and synchronized GPU rendering to produce stable, cinematic video output.

Comments (0)

Share your thoughts and join the conversation

Leave a Comment

Your email will not be published

No comments yet

Be the first to share your thoughts!