Flutter ChromeCast控制插件dart_chromecast的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter ChromeCast控制插件dart_chromecast的使用

dart_chromecast简介

dart_chromecast是一个用于通过Dart语言向Chromecast设备播放视频的包。您可以在pub.dev上找到它。需要注意的是,此包目前处于开发阶段,API可能会在未来的版本中发生重大变化,请谨慎使用。

该包是基于thibauts/node-castv2-client简化移植而来。0.2.0版本添加了MDNS查找功能,现在您可以省略--host参数,并选择要使用的Chromecast设备。

查找Mac OS上的Chromecast IP地址

如果您需要在Mac上找到Chromecast设备的IP地址,可以按照以下命令操作(请注意,这些命令不一定适用于所有人):

$ dns-sd -B _googlecast local
复制实例名称
$ dns-sd -L <IntanceName> _googlecast._tcp. local.
复制名称(不包括端口)
$ dns-sd -Gv4v6 <Paste>

更多示例实现可以参考:flutter_chromecast_example,其中包含了flutter_mdns_plugin和本仓库的用法。


使用方法

选项

  • media:一个或多个媒体源URL,以空格分隔。
  • host(可选):与您同一网络中的Chromecast设备的IP地址。
  • port(可选):Chromecast设备的端口,默认为8009

标志

  • –append (-a):是否将传入的媒体附加到当前播放列表而不是替换它(如果重新连接成功)。默认值为false
  • –debug (-d):是否显示所有信息日志,默认为false

命令行用法

dart example/main.dart <media> [--host <host> [--port <port> [--append [ --debug]]]]

播放控制

在这个演示中,您可以使用以下按键来控制视频的播放:

  • space:切换暂停状态
  • s:停止播放
  • esc:断开设备连接
  • 左箭头键:回退10秒
  • 右箭头键:前进10秒
  • 上箭头键:音量增加5%
  • 下箭头键:音量降低5%

示例

dart example/main.dart http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4

重新连接到活动会话

当您未断开设备连接就退出命令行时,视频将继续播放。要重新连接而不干扰当前播放列表,只需运行命令而无需提供任何媒体URL。例如:

dart example/main.dart --host=192.168.1.1

示例代码

以下是example/main.dart文件的内容,展示了如何使用dart_chromecast插件与Chromecast设备进行交互。

import 'dart:convert';
import 'dart:math';

import 'package:args/args.dart';
import 'package:dart_chromecast/casting/cast.dart';
import 'package:dart_chromecast/utils/mdns_find_chromecast.dart' as find_chromecast;
import 'package:logging/logging.dart';
import 'package:universal_io/io.dart';

final Logger log = new Logger('Chromecast CLI');

void main(List<String> arguments) async {
  // 创建参数解析器以读取CLI的参数和选项
  final parser = new ArgParser()
    ..addOption('host', abbr: 'h', defaultsTo: '')
    ..addOption('port', abbr: 'p', defaultsTo: '8009')
    ..addOption('title', abbr: 't', defaultsTo: null)
    ..addOption('subtitle', abbr: 's', defaultsTo: null)
    ..addOption('image', abbr: 'i', defaultsTo: '')
    ..addFlag('append', abbr: 'a', defaultsTo: false)
    ..addFlag('debug', abbr: 'd', defaultsTo: false);

  final ArgResults argResults = parser.parse(arguments);

  if (true == argResults['debug']) {
    Logger.root.level = Level.ALL;
    Logger.root.onRecord.listen((LogRecord rec) {
      print('${rec.level.name}: ${rec.message}');
    });
  } else {
    Logger.root.level = Level.OFF;
  }

  String imageUrl = argResults['image'];
  final List<String> images = imageUrl != '' ? [imageUrl] : [];

  // 将每个剩余参数字符串转换为CastMedia实例
  final List<CastMedia> media = argResults.rest
      .map((String i) => CastMedia(
          contentId: i,
          images: images,
          title: argResults['title'],
          subtitle: argResults['subtitle']))
      .toList();

  String host = argResults['host'];
  int? port = int.parse(argResults['port']);
  if ('' == host.trim()) {
    // 搜索!
    print('Looking for ChromeCast devices...');

    List<find_chromecast.CastDevice> devices =
        await find_chromecast.find_chromecasts();
    if (devices.length == 0) {
      print('No devices found!');
      return;
    }

    print("Found ${devices.length} devices:");
    for (int i = 0; i < devices.length; i++) {
      int index = i + 1;
      find_chromecast.CastDevice device = devices[i];
      print("$index: ${device.name}");
    }

    print("Pick a device (1-${devices.length}):");

    int? choice;

    while (choice == null || choice < 0 || choice > devices.length) {
      choice = int.parse(stdin.readLineSync()!);
      print(
          "Please pick a number (1-${devices.length}) or press return to search again");
    }

    find_chromecast.CastDevice pickedDevice = devices[choice - 1];

    host = pickedDevice.ip!;
    port = pickedDevice.port;

    print("Connecting to device: $host:$port");

    log.fine("Picked: $pickedDevice");
  }

  startCasting(media, host, port, argResults['append']);
}

void startCasting(
    List<CastMedia> media, String host, int? port, bool? append) async {
  log.fine('Start Casting');

  // 尝试加载保存在saved_cast_state.json中的json格式的先前状态
  Map? savedState;
  try {
    File savedStateFile = File("./saved_cast_state.json");
    savedState = jsonDecode(await savedStateFile.readAsString());
  } catch (e) {
    // 文件不存在
    log.warning('error fetching saved state' + e.toString());
  }

  // 创建具有指定主机和端口的Chromecast设备
  final CastDevice device = CastDevice(
    host: host,
    port: port,
    type: '_googlecast._tcp',
  );

  // 实例化Chromecast发送者类
  final CastSender castSender = CastSender(
    device,
  );

  // 监听投射会话更新并在设备连接时保存状态
  castSender.castSessionController.stream
      .listen((CastSession? castSession) async {
    if (castSession!.isConnected) {
      File savedStateFile = File('./saved_cast_state.json');
      Map map = {
        'time': DateTime.now().millisecondsSinceEpoch,
      }..addAll(castSession.toMap());
      await savedStateFile.writeAsString(jsonEncode(map));
      log.fine('Cast session was saved to saved_cast_state.json.');
    }
  });

  CastMediaStatus? prevMediaStatus;
  // 监听媒体状态更新,如暂停、播放、寻找等
  castSender.castMediaStatusController.stream
      .listen((CastMediaStatus? mediaStatus) {
    // 显示进度等
    if (mediaStatus == null) {
      return;
    }
    if (null != prevMediaStatus &&
        mediaStatus.volume != prevMediaStatus!.volume) {
      // 音量已更新
      log.info('Volume just updated to ${mediaStatus.volume}');
    }
    if (null == prevMediaStatus ||
        mediaStatus.position != prevMediaStatus?.position) {
      // 更新当前进度
      log.info('Media Position is ${mediaStatus.position}');
    }
    prevMediaStatus = mediaStatus;
  });

  bool connected = false;
  bool didReconnect = false;

  if (null != savedState) {
    // 如果有保存的状态,
    // 尝试重新连接
    connected = await castSender.reconnect(
      sourceId: savedState['sourceId'],
      destinationId: savedState['destinationId'],
    );
    if (connected) {
      didReconnect = true;
    }
  }

  // 如果重新连接失败或从未有过保存的状态,则开始新会话
  if (!connected) {
    connected = await castSender.connect();
  }

  if (!connected) {
    log.warning('COULD NOT CONNECT!');
    return;
  }
  log.info("Connected with device");

  if (!didReconnect) {
    // 如果只是重新连接,则不要重新启动,因为这会重置播放器状态
    castSender.launch();
  }

  // 加载CastMedia播放列表并将其发送给Chromecast
  castSender.loadPlaylist(media, append: append);

  // 初始化按键处理程序
  // space = 切换暂停
  // s = 停止播放
  // left arrow = 当前播放时间回退10秒
  // right arrow = 当前播放时间前进10秒
  // up arrow = 音量增加5%
  // down arrow = 音量降低5%
  stdin.echoMode = false;
  stdin.lineMode = false;

  stdin.asBroadcastStream().listen((List<int> data) {
    _handleUserInput(castSender, data);
  });
}

void _handleUserInput(CastSender castSender, List<int> data) {
  if (data.length == 0) return;

  int keyCode = data.last;

  log.info("pressed key with key code: ${keyCode}");

  if (32 == keyCode) {
    // space = 切换暂停
    castSender.togglePause();
  } else if (115 == keyCode) {
    // s == 停止
    castSender.stop();
  } else if (27 == keyCode) {
    // escape = 断开连接
    castSender.disconnect();
  } else if (65 == keyCode) {
    // up
    double? volume = castSender.castSession?.castMediaStatus?.volume;
    if (volume != null) {
      castSender.setVolume(min(1, volume + 0.1));
    }
  } else if (66 == keyCode) {
    // down
    double? volume = castSender.castSession?.castMediaStatus?.volume;
    if (volume != null) {
      castSender.setVolume(max(0, volume - 0.1));
    }
  } else if (67 == keyCode || 68 == keyCode) {
    // left or right = 寻找10秒前后
    double seekBy = 67 == keyCode ? 10.0 : -10.0;
    if (null != castSender.castSession &&
        null != castSender.castSession!.castMediaStatus) {
      castSender.seek(
        max(0.0, castSender.castSession!.castMediaStatus!.position! + seekBy),
      );
    }
  }
}

这段代码展示了如何配置和使用dart_chromecast插件,包括设置日志记录、解析命令行参数、查找可用的Chromecast设备、连接设备、加载媒体播放列表以及处理用户输入以控制播放。


更多关于Flutter ChromeCast控制插件dart_chromecast的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter ChromeCast控制插件dart_chromecast的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter应用中使用dart_chromecast插件来控制ChromeCast设备的示例代码。dart_chromecast插件允许你发现可用的ChromeCast设备并发送媒体命令。

首先,确保你已经将dart_chromecast插件添加到你的Flutter项目中。你可以在pubspec.yaml文件中添加以下依赖项:

dependencies:
  flutter:
    sdk: flutter
  dart_chromecast: ^x.y.z  # 请使用最新版本号

然后运行flutter pub get来安装依赖项。

接下来,你可以在你的Flutter应用中使用以下代码来发现ChromeCast设备并发送一些基本的媒体命令。

1. 导入插件并初始化

import 'package:flutter/material.dart';
import 'package:dart_chromecast/dart_chromecast.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<ChromeCastDevice> devices = [];
  ChromeCastSession? session;

  @override
  void initState() {
    super.initState();
    initChromeCast();
  }

  void initChromeCast() async {
    ChromeCastManager manager = ChromeCastManager();

    manager.onDeviceDiscovered = (ChromeCastDevice device) {
      setState(() {
        devices.add(device);
      });
    };

    manager.startDiscovery();
  }

  void connectToDevice(ChromeCastDevice device) async {
    ChromeCastManager manager = ChromeCastManager();
    session = await manager.connectToDevice(device.id);

    if (session != null) {
      print("Connected to device: ${device.friendlyName}");
    } else {
      print("Failed to connect to device: ${device.friendlyName}");
    }
  }

  void loadMedia(String mediaUrl) async {
    if (session != null) {
      MediaInfo mediaInfo = MediaInfo.builder()
        ..contentType = 'video/mp4'
        ..metadata = MediaMetadata.builder()
            ..title = 'Sample Video'
            ..images = [Image(url: 'https://example.com/image.jpg')]
            .build()
        ..mediaUrl = mediaUrl
        .build();

      await session!.loadMedia(mediaInfo);
      await session!.play();
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('ChromeCast Control'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              Text('Discovered Devices:'),
              Expanded(
                child: ListView.builder(
                  itemCount: devices.length,
                  itemBuilder: (context, index) {
                    ChromeCastDevice device = devices[index];
                    return ListTile(
                      title: Text(device.friendlyName),
                      onTap: () {
                        connectToDevice(device);
                      },
                    );
                  },
                ),
              ),
              ElevatedButton(
                onPressed: () {
                  loadMedia('https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4');
                },
                child: Text('Load Media'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

2. 权限和配置

确保你已经在AndroidManifest.xmlInfo.plist中配置了必要的权限和配置,以便应用能够发现和连接到ChromeCast设备。对于Android,你可能需要添加网络权限和必要的服务声明。对于iOS,你需要确保应用具有网络访问权限。

3. 运行应用

现在,你可以运行你的Flutter应用,并应该能够看到发现的ChromeCast设备列表。选择一个设备后,你应该能够加载和播放媒体。

请注意,以上代码是一个简单的示例,用于展示如何使用dart_chromecast插件的基本功能。在实际应用中,你可能需要添加更多的错误处理和状态管理。

回到顶部