MicrocosmWorksDijital Kozmosu Yenilikçi ve Mimari Olarak Tasarlamak
Hakkındaİletişim
MicrocosmWorksDijital Kozmosu Yenilikçi ve Mimari Olarak İnşa Etmek

Önemli BT çözümleri sunuyoruz. Teknoloji, güvenlik ve işletmelerin güvenilir, yenilikçi BT altyapısı ile büyümesine yardımcı olmaktan tutkuluyuz.

[email protected]
+91 7011868196
New Delhi, India

AI Büyüme Merkezi

AI MerkeziStartup İnovasyonuKurumsal Hızlandırıcı

Çözümler

Tüm ÇözümlerSağlık ve Fitness UygulamalarıAI Video PlatformuAI Ajan Geliştirme

Kaynaklar

ÖngörülerSektör RehberleriKullanım Durumu ŞablonlarıMimari KalıplarVaka Çalışmaları

Şirket

HakkımızdaİletişimÇalışmalarımız

Hizmetler

Dijital DanışmanlıkBulut AltyapısıSaaS GeliştirmeYapay Zeka GeliştirmeVideo Teknolojisi
ERP GeliştirmeZoho ÖzelleştirmeOdoo GeliştirmeSalesforce EntegrasyonuÖzel CRM Geliştirme
QuickBooks EntegrasyonuIoT ÇözümleriBlokzincir Geliştirme
Siber Güvenlik DanışmanlığıIT Desteği - L3

© 2026 MicrocosmWorks. Tüm hakları saklıdır.

Gizlilik PolitikasıHizmet Şartları
Öngörülere Geri Dön
AI Development

Flutter'da WiFi Üzerinden bir GoPro'yu Kontrol Etme: Open GoPro HTTP API, Keep-Alive'lar ve Akışlı Dosya Aktarımı

Flutter'dan WiFi üzerinden bir GoPro'ya, Open GoPro HTTP API'si, keep-alive'lar ve akışlı dosya aktarımı kullanarak bağlanma.

Saurav Kumar Gupta's image Saurav Kumar Gupta
•
July 17, 2026
•
Güncellendi August 19, 2026
•
2 min read
GoPro camera connected to a Flutter app over Wi-Fi for remote control and media file transfer..webp
2 min read

SDK yok, BLE eşleştirme karmaşası yok — sadece kameranın sabit IP'si, bir Dio istemcisi ve bilmediğiniz takdirde gününüzü sessizce mahvedecek birkaç ince detay.

Neden Bluetooth Değil de WiFi?

Çoğu "GoPro'ya bağlanma" eğitimi Bluetooth Low Energy ile başlar: tarama, eşleştirme, GATT özelliklerini değiş tokuş etme, ardından WiFi'yi açmak için BLE kullanma, sonra yine de WiFi üzerinden aktarım. Bu yöntem çalışır, ancak çok fazla seremoni gerektirir — ve BLE, kullanıcıların gerçekten istediği şey olan kamerayı kontrol etme ve görüntüleri hızlı bir şekilde aktarma için yavaş ve karmaşıktır.

Bu yüzden bu uygulamada BLE'yi tamamen atlıyoruz. Kullanıcı, telefonunun ayarlarından GoPro'nun WiFi erişim noktasına katılır ve o noktadan itibaren her şey kameranın sabit bir adresteki Open GoPro API'sine karşı düz HTTP'dir:

http://10.5.5.9:8080

Kamera kontrolü, ayarlar, medya listeleme ve dosya indirme işlemlerinin tümü bu IP adresine yapılan HTTP GET istekleridir. Tüm entegrasyon, dio paketiyle %100 Dart'tır — sıfır yerel Kotlin/Swift. Manşet bu, ve sonunda göreceğimiz gibi, tasarımın kırılgan olduğu tek yer de burasıdır.

Akış:

GoPro WiFi AP'ye katıl (telefon ayarları)
   → kameranın erişilebilir olduğunu algıla (10.5.5.9:8080'e TCP probu)
   → bağlan + modeli tanımla (GET /gopro/camera/state)
   → keep-alive (30s) + durum sorgulama (5s) zamanlayıcılarını başlat
   → kontrol: deklanşör / mod / ayarlar
   → medyayı listele (GET /gopro/media/list)
   → turbo + ilerleme ile indir (dio.download)

1. WiFi Tarama İzinleri Olmadan Kamerayı Algılama

İlk içgüdü, WiFi SSID'lerini listelemek ve GoPro ile başlayan bir tane aramak olabilir. Yapmayın — bu, konum izinlerini ve platforma özgü WiFi tarama API'lerini gerektirir.

GoPro her zaman aynı IP'de yaşar, bu yüzden algılama sadece şudur: 10.5.5.9:8080'e bir TCP soketi açabilir miyim? HTTP yok, izin yok, SSID ayrıştırma yok.

static const String goProIp = '10.5.5.9';
static const int goProPort = 8080;
static const Duration checkInterval = Duration(seconds: 2);
static const Duration connectionTimeout = Duration(seconds: 3);

Future<void> _checkConnection() async {
  try {
    final socket = await Socket.connect(goProIp, goProPort, timeout: connectionTimeout);
    socket.destroy();
    if (_currentState != GoProWifiState.connected) {
      _currentState = GoProWifiState.connected;
      _connectionStateController.add(_currentState);
    }
  } catch (e) {
    if (_currentState != GoProWifiState.disconnected) {
      _currentState = GoProWifiState.disconnected;
      _connectionStateController.add(_currentState);
    }
  }
}

Bunu her 2 saniyede bir sorgularız ve durum değişikliklerini bir akış üzerinden yayınlarız. Bağlantı ekranı, soket açıldığı anda abone olur ve "bağlı" durumuna geçer. Gerçek bir cihaz keşfi olmadığı için, hizmet sanal bir CameraDevice (sabit IP'den anahtarlanmış) sentezler, böylece kamera soyutlamasının geri kalanı farkı bilmek zorunda kalmaz.

2. HTTP istemcisi: Dio ve Open GoPro uç noktaları

İstemci ince bir Dio sarmalayıcıdır. Kısa bağlantı zaman aşımını (bir LAN üzerindesiniz — eğer yavaşsa, bozuktur) ve kontrol çağrıları için cömert bir alma zaman aşımını unutmayın:

static const String baseUrl = 'http://10.5.5.9:8080';

GoProHttpClient() {
  _dio = Dio(BaseOptions(
    baseUrl: baseUrl,
    connectTimeout: const Duration(seconds: 5),
    receiveTimeout: const Duration(seconds: 10),
    headers: {'Accept': 'application/json'},
  ));
}

Open GoPro API hoş bir şekilde tek tiptir: komutlar ve ayar değişiklikleri bile her şey bir HTTP GET isteğidir. İşte bu uygulamanın gerçekten kullandığı uç noktalar:

AmaçYöntem + YolSorgu
Deklanşörü başlat (kayıt / fotoğraf)GET /gopro/camera/shutter/start—
Deklanşörü durdurGET /gopro/camera/shutter/stop—
Keep-aliveGET /gopro/camera/keep_alive—
Tam durum (statü + ayarlar)GET /gopro/camera/state—
Hazır ayar grubunu ayarlaGET /gopro/camera/presets/set_groupid (1000/1001/1002)
Bir ayarı değiştirGET /gopro/camera/settingsetting, option
Medya listesiGET /gopro/media/list—
Dosya indirGET /videos/DCIM/{dir}/{file}(bytes)
Küçük resim / ekran resmiGET /gopro/media/thumbnail / .../screennailpath
Turbo aktarımGET /gopro/media/turbo_transferp (1/0)
.360 grubunu sil (eski)GET /gp/gpControl/command/storage/delete/groupp

Güzel bir detay: hem modern /gopro/... ailesi hem de eski /gp/gpControl/... ailesi bir arada bulunur. Yeni API'nin düzgün bir şekilde işleyemediği chaptered.360/GS grup dosyalarının kaldırılması, eski uç noktaya başvurduğumuz tek durumdur.

403 kendi kendini onarma deseni (buradaki en temiz numara)

Kameranın mevcut modunda hangi ayarların geçerli olduğunu önceden bilemezsiniz — 16:9'da yasal olan bir çözünürlük 9:16'da yasa dışıdır, FPS seçenekleri çözünürlüğe bağlıdır vb. GoPro bunu size zor yoldan söyler: geçerli olabilecek seçeneklerin listesiyle HTTP 403, ve geçici olarak meşgul olduğunda HTTP 500 döndürür.

Bu yüzden 403 gövdesini tipik bir hataya ayrıştırır ve 500'ü yeniden deneriz:

Future<void> setSetting(int settingId, int optionValue) async {
  const maxRetries = 3;
  for (int attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      await _dio.get('/gopro/camera/setting',
          queryParameters: {'setting': settingId, 'option': optionValue});
      return;
    } catch (e) {
      if (e is DioException && e.type == DioExceptionType.badResponse) {
        final statusCode = e.response?.statusCode;
        if (statusCode == 403) {
          final body = e.response?.data;
          List<int> availableIds = [];
          if (body is Map<String, dynamic>) {
            final options = body['available_options'];
            if (options is List) {
              availableIds = options
                  .whereType<Map<String, dynamic>>()
                  .map((o) =gt; o['id'] as int? ?? -1)
                  .where((id) =gt; id >= 0).toList();
            }
          }
          throw SettingRejectedError(
            settingId: settingId, rejectedOption: optionValue,
            availableOptionIds: availableIds);
        }
        if (statusCode == 500 && attempt < maxRetries) {
          await Future.delayed(Duration(milliseconds: attempt * 500)); // linear backoff
          continue;
        }
      }
    }
}

Kameranın bildirdiği seçenekler, SettingRejectedError algıladığında servis katmanı tarafından kendi yetenek listesini yeniden yazmak için kullanılır. Her model için, iyimser, sabit kodlanmış bir yetenek tablosuyla başlarız ve kamera çalışma süresi boyunca bizi düzeltmesine izin veririz. Bu, GoPro'nun tüm ayar matrisini baştan simüle etmeye çalışmaktan daha pratik ve kendi kendini düzelten bir yaklaşımdır.

3. Keep-alive: unutmamanız gereken zamanlayıcı

Yaklaşık 60 saniye hareketsizlikten sonra, GoPro WiFi AP oturumunuzu sonlandırır. Eğer oturuma müdahale etmezseniz, kusursuz bağlantınız bir oturumun ortasında sessizce sona erer. Bu nedenle, bağlandığında iki zamanlayıcı başlatırız:

  • Her 30 saniyede bir Keep-alive → GET /gopro/camera/keep_alive
  • Her 5 saniyede bir durum sorgulama → GET /gopro/camera/state

void _startKeepAlive() {
  _keepAliveTimer?.cancel();
  _keepAliveTimer = Timer.periodic(
    GoProHttpConstants.keepAliveInterval,
    (_) =gt; _sendKeepAlive(),
  );
}

Future<void> _sendKeepAlive() async {
  if (!isConnected || _isDisposed) return;
  try {
    await _httpClient.keepAlive();
  } catch (e) {
    if (!_isDisposed) _handleDisconnection(); // a failed keep-alive == we're disconnected
  }
}

Başarısız bir keep-alive aynı zamanda bağlantı kesilme sinyalimizdir — kullanıcının WiFi menzilinden çıktığını fark etmenin en güvenilir yoludur.

4. Kamera kontrolü: deklanşör, mod ve MAX 2 lens dansı

Deklanşör kolay kısımdır — başlat, 500 ms bekle, durumu tekrar sorgula. Fotoğraf çekimi aynı deklanşör/başlatma işlemidir; video mu yoksa fotoğraf mı çektiğiniz mevcut moda bağlıdır.

Mod değiştirme ilginçleştiği yerdir, çünkü bir GoPro MAX 2'deki "mod" aslında iki ayardır: bir hazır ayar grubu (Video = 1000, Fotoğraf = 1001, Timelapse = 1002) ve bir lens (Ayar 194: 0 = tek lensli/HERO, 1 = 360). Bunu doğru yapmak, koda yerleştirilmiş birkaç açık olmayan kural gerektirdi:

  1. Mod değişikliklerini seri hale getirin. Hızlı alt mod dokunuşları, bir Completer zinciri aracılığıyla sıraya alınır, böylece tutarsız bir duruma girmeleri engellenir.
  2. Önce deklanşörü durdurun. Firmware, kayıt sırasında HTTP 400 ile hazır ayar değişikliklerini reddeder, bu nedenle geçiş yapmadan önce kodlamayı önceden durdururuz.
  3. Sıra ve gecikmeler önemlidir. MAX 2'de: önce hazır ayar grubunu ayarlayın (+400 ms), ardından Ayar 194 aracılığıyla lensi (+1500 ms). Bu 1.5 saniyelik duraklama gerçektir — fiziksel lens tertibatının geçiş yapmasıdır.
  4. Sorgulanan duruma değil, amaçlanan duruma güvenin. _lastIntendedIs360 / _lastIntendedPresetGroup'u izler ve bunlara göre farkı alırız, çünkü 5 saniyelik durum sorgulaması size geçişin ortasında eski bir anlık görüntü verebilir.

Ayrıca belirtmeye değer gerçekten sinsi bir GoPro tuhaflığı var: çözünürlük ID'leri en boy oranına bağlıdır. 4K, 16:9'da seçenek 1, 9:16'da 109 ve 4:3'te 112'dir. Bu nedenle en boy oranını değiştirdikten sonra yetkinlikleri yeniden sorgularız — aksi takdirde aynı ID farklı bir çözünürlük anlamına gelirdi.

5. Medyayı Listeleme

GET /gopro/media/list, ünlü derecede özlü anahtarlarla dizin gruplu bir yapı döndürür: d = dizin, fs = dosyalar, n = dosya adı, s = boyut, cre/mod = unix-saniye zaman damgaları, g = grup ID'si, glrv = düşük çözünürlüklü proxy boyutu.

final media = response['media'] as List<dynamic>?;
for (final dir in media) {
  final directory = dirMap['d'] as String? ?? '';
  final filesList = dirMap['fs'] as List<dynamic>?;
for (final fileJson in filesList) {
    files.add(GoProCameraFile.fromJson(directory, fileMap));
  }
}
files.sort((a, b) =gt; b.modifiedAt.compareTo(a.modifiedAt)); // newest first

Medya türü, dosya adı ön eki + uzantısından çıkarılır: GS = 360 video, GX = HERO video, GT = timelapse, .360 = küresel video, .GPR/.RAW = RAW fotoğraf. Bu ön ekler daha sonra önemlidir, çünkü .360 dosyaları diğer her şeyden farklı davranır.

6. Akışlı indirmeler: turbo, ilerleme ve yardımcı dosyalar

Gerçek bayt aktarımı, doğrudan bir dosya yoluna yapılan dio.download() işlemidir, uzun bir alma zaman aşımı ile, çünkü 1 GB'lık bir .360 klibi biraz zaman alır:

Future<void> downloadMediaToFile(
  String directory, String filename, String destPath,
  {void Function(int received, int total)? onProgress}) async {
  await _dio.download(
    '/videos/DCIM/$directory/$filename',
    destPath,
    onReceiveProgress: onProgress,
    options: Options(receiveTimeout: const Duration(minutes: 30)),
  );
}

Bu çekirdeğin etrafında, aktarım hizmeti gerçek dünya detaylarını düzenler:

Turbo aktarım. İndirmeden önce turbo modunu (turbo_transfer?p=1) açarız — GoPro, fark edilir derecede daha hızlı aktarımlar için 5 GHz WiFi'ye geçer — ve bir finally bloğunda kapatırız, böylece hata durumunda bile her zaman temizlenir.

İlerleme + hız, kısılmış. Her onReceiveProgress geri çağrısında aktarım hızını hesaplamak israf ve titrek bir durum yaratır, bu yüzden onu sadece her 500 ms'de bir yeniden hesaplarız:

onProgress: (received, total) {
  if (_isCancelled) return;
  final now = DateTime.now();
  String? speed;
  if (now.difference(lastSpeedUpdate).inMilliseconds >= 500) {
    final bytesPerSecond = (received - lastBytes) /
        (now.difference(lastSpeedUpdate).inMilliseconds / 1000);
    speed = _formatSpeed(bytesPerSecond.round());
    lastSpeedUpdate = now; lastBytes = received;
  }
  _transferController.add(GoProFileTransfer(
    status: FileTransferStatus.downloading,
    progress: total > 0 ? received / total : 0,
    speed: speed, currentBytes: received, totalBytes: total));
}

İptal, işbirliğine dayalı bir _isCancelled bayrağıdır; iptalde kısmi dosyayı siler ve iptal edilmiş bir olay yayınlarız.

Yardımcı dosyalar. Bu, belgelerde bulamayacağınız kısımdır. Tek lensli bir .MP4 için, .LRV'yi (GoPro'nun tam videonun yanında kaydettiği düşük çözünürlüklü bir proxy) ve .THM küçük resmini de çekeriz, ardından hızlı uygulama içi oynatma için LRV'yi indeksleriz. Burada iki adlandırma hatası sorun yaratır:

  • HERO8+ proxy'yi yeniden adlandırır: GX######.MP4 / GH######.MP4, GL######.LRV olur (ikinci karakter L ile değişir), daha eski kameralar için aynı adla geri dönüş mevcuttur.
  • .360 dosyaları küçük resim veya ekran resmi uç noktalarını hiç desteklemez. .THM yardımcı dosyasına geri döneriz ve son çare olarak dosyanın kendisini indiririz. Ve bir .360 çift balık gözü olduğu için, küçük resmi ön lense kırparız — compute() aracılığıyla bir arka plan izolatöründe, böylece UI asla takılmaz.

Aktarım durumu, dosya ve aktarım akışlarına abone olan, tamamlanmış bir aktarımı 2 saniye boyunca görünür tutan ve hataları 5 saniye boyunca gösteren bir Riverpod notifier aracılığıyla ifşa edilir. Dikkat çekici: kontrol ve dosya aktarımı, aynı kameraya iki bağımsız Dio istemcisi kullanır, böylece durum sorgulama ve keep-alive devam ederken bir indirme işlemi çalışabilir.

7. Hiçbir eğitimde olmayan iki ince detay

Şeffaf HTTP, modern Android'de varsayılan olarak engellenmiştir

http://10.5.5.9:8080 düz metindir ve Android 9+ varsayılan olarak şeffaf trafiği engeller. Bunu açıkça izin vermeniz gerekir:

<!-- AndroidManifest.xml -->
<application android:usesCleartextTraffic="true" ... >

artı, temel yapılandırması şeffaf metne izin veren bir network_security_config.xml dosyası (kendi bulut/arka uç alanlarınız için hala HTTPS'yi zorlayabilirsiniz). Bunu atlarsanız her GoPro çağrısı kafa karıştırıcı bir bağlantı hatasıyla başarısız olur.

Eksik ağ bağlama — tasarımın kırılgan noktası

İşte dürüst kısım. Modern Android'de, GoPro AP'ye katıldığınızda, telefon genellikle hücresel ağı varsayılan ağ olarak tutar çünkü GoPro AP'nin interneti yoktur. Bu, 10.5.5.9'a yapılan bir HTTP isteğinin hücresel ağ üzerinden yönlendirilip başarısız olabileceği anlamına gelir — kameraya "bağlı" olsanız bile.

Sağlam çözüm yereldir: aktarımlardan önce ConnectivityManager.bindProcessToNetwork(goProNetwork) çağrısını ve sonra bindProcessToNetwork(null) çağrısını yapın. Saf Dart bir GoPro yolunun eşdeğeri yoktur — ve yine de genellikle çalışır çünkü işletim sistemi sabit 10.5.5.9 adresini AP'ye yönlendirir. Ancak mimarinin en kırılgan kısmıdır ve bazı cihazlarda/Android sürümlerinde ilk bozulan şeydir. Eğer bunu yayınlarsanız, ağ bağlamayı ekleyin — %100 Dart kalmanın size güvenilirlik maliyeti getireceği tek yer burasıdır.

Dersler

  1. BLE'ye ihtiyacınız yok. Kullanıcı AP'ye katılabiliyorsa, Open GoPro HTTP API size saf Dart'ta tam kontrol ve hızlı aktarımlar sağlar.
  2. SSID taramasıyla değil, TCP probuyla algılayın — konum izni yok, platform kodu yok.
  3. Her 30 saniyede bir Keep-alive gönderin, aksi takdirde oturum ölür. Başarısız bir keep-alive'ı bağlantı kesilme sinyaliniz olarak kabul edin.
  4. Kameranın sizi düzeltmesine izin verin. 403-available_options kendi kendini onarma döngüsü, GoPro'nun tüm ayar matrisini modellemekten daha iyidir.
  5. Mod değiştirme bir durum makinesidir — onu seri hale getirin, önce kaydı durdurun, lens geçiş gecikmelerine saygı gösterin ve sorgulanan durum yerine amaçlanan duruma güvenin.
  6. Yardımcı dosyalar ve .360 tuhaflıkları sorunların saklandığı yerdir — LRV yeniden adlandırma, küçük resim uç noktası olmaması, çift balık gözü kırpma.
  7. İki altyapı ince detayı sizi sessizce batırır: şeffaf metin trafiği yapılandırması ve (özellikle) isteklerin hücresel ağ üzerinden kaçmasını önlemek için süreci kamera ağına bağlamak.

Karşılığı: WiFi üzerinden bir GoPro'ya bağlanan, her kontrolü yöneten ve canlı ilerlemeyle gigabaytlarca görüntü akışı yapan bir telefon — ve neredeyse tamamı, http://10.5.5.9:8080 ile konuşan iyi yapılandırılmış bir Dart kodudur.
 

FlutterGoProHTTP APIStreaming
Saurav Kumar Gupta's image

Yazar Hakkında

Saurav Kumar Gupta

AI & Cloud Solutions Expert at MicrocosmWorks

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

Daha fazla bilgi edinmek ister misiniz?

Bu çözümleri işletmeniz için nasıl uygulayabileceğimizi tartışmak için bizimle iletişime geçin.

İletişime Geçin

Sıkça Sorulan Sorular

Yes. Once the phone is connected to the GoPro's WiFi network, the Open GoPro HTTP API allows you to control the camera, change settings, list media, and download files using standard HTTP requests.

GoPro automatically ends inactive WiFi sessions after about 60 seconds. Sending periodic keep-alive requests maintains the connection and prevents unexpected disconnections during camera control or file transfers.

Flutter apps can use the Open GoPro HTTP API with Dio for streaming downloads, enable Turbo Transfer for faster speeds, and track download progress in real time.

A 403 response indicates the requested setting isn't valid for the camera's current mode. The API also returns supported options, allowing the application to adapt automatically.

On Android, requests may be routed over cellular instead of the GoPro's WiFi network. Binding the app to the GoPro network improves connection reliability during camera control and media transfers.

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!