MicrocosmWorksInovasi dan Seni Bina Kosmos Digital
TentangHubungi
MicrocosmWorksMemperbaharui dan Merangka Kosmos Digital

Menyampaikan penyelesaian IT yang penting. Kami bersemangat tentang teknologi, keselamatan, dan membantu perniagaan berkembang melalui infrastruktur IT yang boleh dipercayai dan inovatif.

[email protected]
+91 7011868196
New Delhi, India

Pusat Pertumbuhan AI

AI HubInovasi PermulaanPemecut Perusahaan

Penyelesaian

Semua PenyelesaianAplikasi Kesihatan & KecergasanPlatform Video AIPembangunan Ejen AI

Sumber

WawasanPanduan IndustriPelan Tindakan Kes PenggunaanCorak Seni BinaKajian Kes

Syarikat

Tentang KamiHubungiKerja Kami

Perkhidmatan

Perundingan DigitalInfrastruktur AwanPembangunan SaaSPembangunan AITeknologi Video
Pembangunan ERPPenyesuaian ZohoPembangunan OdooIntegrasi SalesforcePembangunan CRM Tersuai
Integrasi QuickBooksPenyelesaian IoTPembangunan Blockchain
Perundingan Keselamatan SiberSokongan IT - L3

© 2026 MicrocosmWorks. Hak cipta terpelihara.

Dasar PrivasiTerma Perkhidmatan
Kembali ke Wawasan
IoT Development

Pembingkaian Semula Video 360 dalam Flutter: Unjuran Sfera, Keyframe Quaternion, dan Eksport GPU

Membingkai semula rakaman 360 equirectangular dalam Flutter menggunakan unjuran sfera, keyframe quaternion, dan eksport pecutan GPU.

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

Pembingkaian Semula Video 360° dalam Flutter: Unjuran Sfera, Keyframe Quaternion, dan Eksport GPU

Bagaimana kami mengubah rakaman 360° equirectangular mentah menjadi video rata, berbingkai, dan licin — sepenuhnya di dalam aplikasi Flutter.

Masalah yang tidak diberitahu kepada anda

Video biasa mempunyai satu perkara yang jelas untuk ditunjukkan kepada anda: bingkai yang diarahkan oleh kamera. Video 360° tidak mempunyai perkara sedemikian. Kamera merekodkan segala-galanya — sfera penuh piksel yang disimpan sebagai segi empat tepat equirectangular rata (imej 2:1 di mana paksi X adalah longitud dan paksi Y adalah latitud). Sebelum anda boleh memuat naik rakaman itu ke Instagram, YouTube, atau skrin telefon, seseorang perlu menjawab soalan yang sengaja enggan dijawab oleh kamera:

Ke mana seharusnya penonton melihat, dan bila?

"Reframing" adalah tindakan menerbangkan kamera maya melalui sfera itu dari masa ke masa — menyorot (panning), mencondongkan (tilting), mengezum — dan mengeluarkan video rata 16:9 (atau 9:16, atau 2.35:1) biasa. Ini adalah ciri yang menjadi asas kepada GoPro Player, Insta360 Studio, dan plugin GoPro VR Reframe Adobe Premiere.

Kami membangunkannya sepenuhnya dalam Flutter. Catatan ini adalah tinjauan bahagian-bahagian yang benar-benar sukar: matematik unjuran, interpolasi bebas gimbal lock, jahitan ±180°, dan saluran paip eksport GPU yang perlu berfungsi dengan dekoder Android sebenar.

Aliran hujung-ke-hujung:

tangkap/import video 360
  → transcode proksi pratonton ringan
  → pengguna mengetuk / melukis / mengesan subjek secara automatik
  → penjejakan objek menghasilkan laluan
  → hasilkan + lancarkan keyframe
  → SLERP-interpolasi orientasi kamera setiap bingkai (pratonton langsung)
  → eksport GPU asli ke video rata
  → FFmpeg post-pass (moov atom + A/V sync fix)

Sekilas Pandang Seni Bina

Kami mengekalkan sistem dalam tiga lapisan yang jelas supaya matematik boleh diuji secara berasingan daripada Flutter:

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

Peraturan emas: semua trigonometri adalah Dart tulen tanpa import Flutter. Ini bermakna setiap fungsi unjuran dan interpolasi boleh diuji unit tanpa pokok widget, dan matematik yang sama menggerakkan kedua-dua pratonton langsung dan eksport.

1. Inti Pati: equirectangular ⟷ sfera

Semuanya bermula dengan satu pemetaan. Bingkai equirectangular hanyalah segi empat tepat di mana:

  • X (0 → lebar) menyapu longitud (yaw) dari −180° hingga +180°
  • Y (0 → tinggi) menyapu latitud (pitch) dari +90° (atas/zenith) hingga −90° (bawah/nadir)

SphericalCoordinates sengaja kecil — hanya yaw dan pitch. Roll, FOV, dan zoom adalah milik kamera, bukan arah. Berikut adalah penukaran yang menjadi asas keseluruhan ciri:

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),
  );
}

Songsangan adalah imej cermin (normalizedX = yaw/360 + 0.5), dan kami menggunakannya secara berterusan untuk melukis semula kotak pembatas yang dijejaki ke atas pemain.

"Ketuk apa yang anda lihat" — perspective ray casting

Pengguna tidak berinteraksi dengan segi empat tepat equirectangular. Mereka berinteraksi dengan paparan perspektif yang dirender. Jadi apabila mereka mengetuk satu titik pada pemain, kita perlu melancarkan pancaran melalui kamera lubang jarum maya dan mencari arah mana pada sfera yang mereka kenakan. Itulah 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;

Penggunaan .clamp(-1.0, 1.0) sebelum asin kelihatan paranoid, tetapi sisihan floating-point akan memberikan anda 1.0000001 dan asin akan mengembalikan NaN — dan satu NaN akan merosakkan setiap bingkai hiliran. Penyekat defensif di sekitar trig songsang adalah tema berulang dalam pangkalan kod ini.

2. Pergerakan Kamera yang Licin: Quaternion, bukan Sudut Euler

Pendekatan naif adalah menyimpan yaw/pitch pada setiap keyframe dan menginterpolasi nombor secara linear. Ia kelihatan mengerikan. Interpolasi sudut Euler menyebabkan gimbal lock, halaju sudut tidak seragam, dan 'snap' yang tidak elok berhampiran kutub.

Kami menginterpolasi orientasi sebagai quaternion menggunakan SLERP (interpolasi linear sfera). Setiap arah menukar kepada quaternion melalui konvensyen ZYX Euler:

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);
}

Dan SLERP itu sendiri mempunyai dua injap keselamatan gred pengeluaran yang diperlukan oleh setiap implementasi:

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
                      y: a.y + t*(b.y-a.y), z: a.z + t
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          (b.x-a.x),
                      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();
}

Dua butiran yang tidak boleh dirunding:

  • Hemisfera terpendek — q dan −q mewakili orientasi yang sama. Jika dot product adalah negatif, anda menidakkan satu input atau kamera anda akan mengambil jalan yang lebih jauh mengelilingi sfera.
  • Pengembalian LERP hampir selari — apabila dua keyframe hampir sama, sinTheta0 → 0 dan anda membahagi dengan hampir sifar. Menggunakan LERP biasa apabila dot > 0.9995 mengelakkan 'blow-up'.

Keputusan reka bentuk yang halus tetapi penting: kami SLERP arah, tetapi LERP FOV/zoom dan angle-LERP roll. Interpolasi sfera adalah alat yang tepat untuk arah; zoom hanyalah skalar dan sepatutnya bergerak secara linear:

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);

💡 Apabila menukar kembali dari quaternion ke yaw/pitch, kami menggunakan atan2 dan bukannya asin "untuk kestabilan angka (mengelakkan isu domain asin)." Selepas SLERP, quaternion mungkin sedikit ternormal, dan atan2 merosot dengan baik di mana asin membuang NaN.

Easing: buatan tangan, pada transisi masuk

Setiap keyframe memiliki lengkung easing untuk transisi ke dalamnya. Lengkung-lengkung ini ditulis secara manual supaya ia adalah sama pada setiap 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. Jahitan ±180°: pepijat yang menghantui setiap ciri 360

Inilah perangkapnya. Yaw berada dalam bulatan: +179° dan −179° adalah 2° berasingan, bukan 358°. Seketika subjek anda melintasi bahagian belakang sfera, matematik naif akan "snap" kamera separuh jalan mengelilingi dunia. Isu tunggal ini muncul pada empat lapisan sistem yang berbeza, dan setiap satunya memerlukan penyelesaiannya sendiri.

Lapisan 1 — primitif. Dua pembantu kecil yang digunakan oleh keseluruhan pangkalan kod:

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

// "CRITICAL for seam-safe tracking ... avoids 180° snaps"
static double shortestYawDelta(double from, double to) {
  double delta = to - from;
  if (delta > 180) delta -= 360;
  if (delta < -180) delta += 360;
  return delta;
}

Lapisan 2 — penjanaan keyframe mengumpul yaw berterusan (tanpa batas) supaya SLERP sentiasa menginterpolasi jalan terpendek. Daripada menyekat setiap sampel kepada ±180°, kami menjumlahkan delta terpendek dan membiarkan nilai berjalan melepasi 180° dengan sengaja:

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);

Lapisan 3 — pembinaan semula kotak pembatas (bounding-box). Apabila pengguna melukis kotak yang merentangi jahitan, empat penjuru melaporkan yaw seperti +170°, +175°, −175°, −170°. SphericalProjection mengesan ini (mana-mana penjuru > 90° dan mana-mana penjuru < −90°) dan membina semula "bbox selamat jahitan" yaw berterusan dengan menganjakkan nilai negatif sebanyak +360° sebelum mengukur rentangnya.

Lapisan 4 — pemain langsung melicinkan ke arah sasarannya menggunakan shortestYawDelta supaya menyeret melintasi jahitan tidak pernah tersentak.

Jika anda ingat satu perkara tentang membina alatan 360: jahitan bukanlah kes pinggir, ia adalah isu menyeluruh. Sediakan peruntukan untuknya di mana sahaja yaw muncul.

4. Menjadikannya terasa seperti Insta360/GoPro

Interpolasi licin memberikan anda kamera yang betul dari segi teknikal. Ia tidak memberikan anda kamera yang baik. Tiga elemen "kejuruteraan rasa" menutup jurang itu.

Kekangan pergerakan — hadkan halaju dan pecutan. Data penjejakan mentah adalah tidak stabil (jittery). Kami melalukan keyframe melalui MotionConstraints, yang menghadkan kelajuan dan kekasaran kamera maya boleh bergerak (cth. maxYawSpeed = 120°/s, maxPitchSpeed = 90°/s, maxAcceleration = 180°/s²), dengan pratetap cinematic(), responsive(), dan production(). Ini adalah perbezaan antara "kamera keselamatan menjejaki lalat" dan "pengendali dengan tangan yang stabil."

FOV adaptif — zum untuk memuatkan subjek. Kami mengukur saiz sudut subjek dalam ruang equirectangular dan memilih FOV supaya ia memenuhi pecahan sasaran bingkai:

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. Kami menolak pembingkaian kira-kira 15% daripada lebar bbox ke hadapan dalam arah perjalanan, supaya subjek tidak tersepit di tepi yang mereka tuju. Dan apabila pengesanan muka terhenti, kami tidak serta-merta beralih ke pusat badan — kami melakukan lerp dari pusat muka yang terakhir diketahui ke pusat badan dalam ~10 bingkai.

5. Helah dua video: proksi pratonton berbanding asal resolusi penuh

Ini adalah keputusan seni bina yang menjadikan keseluruhan perkara boleh digunakan pada telefon sebenar.

Fail 360° GoPro/Insta360 selalunya 4096×2048 HEVC. Dekoder Android bajet sukar untuk memainkan semula itu dengan lancar, apatah lagi semasa menjalankan penjejakan objek di atasnya. Jadi kami menjalankan penjejakan dan pratonton langsung pada proksi transcoded (~1440×720, libx264 -preset veryfast -crf 28), dan menyimpan asal resolusi penuh hanya untuk eksport akhir.

// "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()

Perkara yang perlu anda kendalikan: kotak pembatas keyframe dikira dalam koordinat proksi dan mesti diskalakan ke koordinat asal sebelum eksport. Jika penskalaan ini salah, bingkai eksport anda akan menunjukkan kawasan yang sedikit berbeza daripada yang dipratontonkan oleh pengguna. (Kami juga menggunakan ffprobe, bukan MediaMetadataRetriever, untuk metadata dan pengekstrakkan bingkai pertama — retriever terbina dalam Android tidak stabil pada codec luar biasa yang dikeluarkan oleh kamera 360.)

6. Eksport GPU: OpenGL + MediaCodec melalui MethodChannel

Menayangkan semula setiap bingkai sfera 4K melalui penapis v360 FFmpeg pada CPU telefon terlalu perlahan. (Kami memang menyimpan rentetan v360 — ia mendokumentasikan semantik unjuran dengan kemas, v360=e:flat:... bermaksud "equirectangular masuk, rata keluar" — tetapi ia bukan laluan langsung.)

Eksport sebenar menyerahkan keyframe kepada renderer OpenGL ES + MediaCodec asli melalui 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
});

Kami mengawal ini di sebalik pemeriksaan keupayaan (Android API 21+, pengekod H.264 perkakasan, OpenGL ES 3.0) dan menstrimkan kemajuan kembali melalui EventChannel. Beberapa masalah sukar yang ditemui:

  • Ofset yaw −90°. Konvensyen Dart kami meletakkan yaw = 0 di pusat imej equirectangular (u = 0.5); renderer OpenGL meletakkan yaw = 0 pada u = 0.75. Jadi setiap keyframe yaw dipetakan semula sebanyak −90° (dan dibalut semula kepada ±180°) semasa melintasi saluran — diterapkan secara simetri kepada kedua-dua saluran eksport dan saluran pemain langsung, supaya pratonton dan eksport bersetuju.
  • Kiraan keyframe merosot. Sifar keyframe → sintesiskan dua yang serupa dari orientasi awal (eksport statik). Satu keyframe → duplikasikannya. Renderer sentiasa mahu sekurang-kurangnya permulaan dan akhir.
  • Pangkasan mengubah suai cap masa. Eksport yang dipangkas mengalihkan setiap keyframe sebanyak t − trimStartMs.

FFmpeg post-pass yang tidak dijangka

Anda mungkin berfikir eksport GPU adalah garisan penamat. Tidak — kerana MediaMuxer Android menulis moov atom di akhir fail dan boleh membawa ofset audio PTS sumber. Hasilnya: ExoPlayer (dan video_player Flutter) membekukan gambar sementara jam terus berjalan. Jadi selepas GPU pass, FFmpeg melakukan pembersihan yang cepat dan tanpa kehilangan:

// 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

Dan satu masalah kecil yang brutal menjaganya: kami mencari trek audio terlebih dahulu, kerana meluluskan bendera audio kepada video yang tiada trek audio membuat FFmpeg tergantung selama-lamanya pada sesetengah peranti. Keseluruhan post-pass dibalut dalam had masa 60 saat dengan pembatalan jika tamat masa.

7. Pratonton Langsung juga adalah Pemain Asli

Pratonton dalam editor bukanlah CustomPaint Flutter — ia adalah permukaan OpenGL asli yang dibenamkan melalui AndroidView (dengan viewType seperti 'your_app/spherical360player'), dengan saluran method/event per-view sendiri. Spherical360PlayerController Dart mencerminkan yaw/pitch/roll/fov dan menerapkan ofset −90° yang sama sebelum dihantar.

Butiran rasa yang menjadikannya menyenangkan:

  • Penlancaran bebas kadar bingkai — faktor = 1 - exp(-speed * dt) supaya easing kelihatan sama pada 60fps dan 90fps, dengan yaw laluan terpendek yang selamat jahitan.
  • Momentum/inersia selepas melibas, dengan pereputan berasaskan geseran — dilumpuhkan semasa main semula, kerana semasa main semula kamera mesti mematuhi keyframe, bukan "flick" terakhir pengguna.
  • Satu GestureDetector yang onScaleUpdate melakukan pan dan pinch-zoom bersama; ketukan dua kali menogol main/jeda. FOV ditetapkan kepada 30°–120°, pitch kepada ±90°, pada sempadan pengawal.

Satu pembetulan UX yang kecil tetapi penting: sebelum bertukar daripada "tracking" kepada "generating keyframes," kami menyelitkan kelewatan 120 ms supaya Flutter benar-benar melukis "100%." Tanpa itu, bingkai kemajuan akhir dan perubahan status mendarat dalam kumpulan microtask yang sama dan pengguna tidak pernah melihat penyiapan.

Mesin keadaan, secara ringkas

Semua ini diselaraskan oleh Riverpod ReframeNotifier yang mengendalikan enum status:

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

Pelbagai titik masuk menyalurkan kepada penjejak yang sama: tap-to-detect (YOLO overlay), bbox manual, atau isyarat draw-directly-on-the-player ala Insta360. Semuanya bertemu di KeyframeGenerator.production().generate(...) → KeyframeSmoother.smooth(...), dan dari sana interpolator menggerakkan setiap bingkai yang dipratonton dan dieksport.

Pengajaran yang Dipelajari

  • Kekalkan matematik tulen. Tiada import Flutter dalam lapisan unjuran/interpolasi. Itulah satu-satunya sebab ini boleh diuji.
  • Quaternion SLERP untuk arah; LERP untuk skalar. Jangan menginterpolasi sudut Euler, dan jangan SLERP zoom anda.
  • Jahitan ±180° adalah isu menyeluruh, bukan kes pinggir. Ia muncul semula dalam primitif, penjanaan, unjuran, dan pemain.
  • Selaraskan konvensyen anda sekali, secara simetri. Ofset yaw −90° antara Dart dan OpenGL diterapkan pada kedua-dua sempadan saluran supaya pratonton == eksport.
  • Proksi untuk interaksi, asal untuk output. Ingatlah untuk menskalakan koordinat anda antara kedua-duanya.
  • "Selesai rendering" ≠ "bermain dengan betul." Sediakan peruntukan untuk FFmpeg post-pass untuk membetulkan penempatan moov atom dan audio PTS, dan uji sebelum anda menambah bendera audio.
  • "Rasa" adalah ciri. Kekangan pergerakan, FOV adaptif, dan motion-lead adalah yang membezakan antara "dibingkaikan semula secara teknikal" dan "kelihatan seperti ditembak oleh profesional."

Hasilnya: editor pembingkaian semula 360° sepenuhnya pada peranti — ketuk subjek, biarkannya menjejak, laraskan keyframe, dan eksport video rata yang bersih — dibina dalam Flutter dengan lapisan GPU asli nipis yang hanya melakukan kerja yang tidak dapat dilakukan oleh Dart dengan cukup pantas.

Flutter360VideoOpenGLQuaternions
Saurav Kumar Gupta's image

Tentang Penulis

Saurav Kumar Gupta

AI & Cloud Solutions Expert at MicrocosmWorks

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

Ingin mengetahui lebih lanjut?

Hubungi kami untuk membincangkan bagaimana kami boleh membantu melaksanakan penyelesaian ini untuk perniagaan anda.

Hubungi Kami

Soalan Lazim

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!