Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chromecast support #91

Open
Feichtmeier opened this issue Jul 23, 2023 · 14 comments
Open

Chromecast support #91

Feichtmeier opened this issue Jul 23, 2023 · 14 comments
Labels
help wanted Extra attention is needed

Comments

@Feichtmeier
Copy link
Member

https://pub.dev/packages/dart_chromecast/example

@peter-kal
Copy link

Okay so, I can't really contribute to any of this, my Flutter know-how is pretty basic, but I just wanted to express my excitement about this feature.
Think its a really good idea, I'm sure many people have at least one chromecast-capable device, like Smart-TVs, GoogleSpeakers and more. It would be nice to be able to click a button and listen your podcast, song, or FM station on one of those. It also provides some good kind of UX that has been missing from the linux community, you know the "google is evil", or the "we don't do big tech" vibe.
Can't wait to use it!

@allansrc
Copy link

Oh I've done something to work with Fuchsia (it`s early working) I'll make some tests if work I can PR it or push on someone's package supports to linux

@Feichtmeier
Copy link
Member Author

@allansrc wow that would be amazing!
Thanks for investing time

@Feichtmeier
Copy link
Member Author

@guyluz11 thank you I tried your branch and it works as I can list the devices and can connec tto them and can send messages to them. Maybe I misunderstand something with how chromecast works, but can I send for example a podcast or a radio station to it?

I just changed the very narrow bottom player (when musicpod is very narrow) to this, and it works as described above:

import 'package:cast/device.dart';
import 'package:cast/discovery_service.dart';
import 'package:cast/session.dart';
import 'package:cast/session_manager.dart';
import 'package:flutter/material.dart';

import '../../player.dart';
import 'bottom_player_image.dart';
import 'bottom_player_title_artist.dart';
import 'play_button.dart';

class VeryNarrowBottomPlayer extends StatelessWidget {
  const VeryNarrowBottomPlayer({
    super.key,
    required this.setFullScreen,
    required this.bottomPlayerImage,
    required this.titleAndArtist,
    required this.active,
    required this.isOnline,
    required this.track,
  });

  final void Function(bool? p1) setFullScreen;
  final BottomPlayerImage bottomPlayerImage;
  final BottomPlayerTitleArtist titleAndArtist;
  final bool active;
  final bool isOnline;
  final PlayerTrack track;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () => setFullScreen(true),
      child: SizedBox(
        height: kBottomPlayerHeight,
        child: Column(
          children: [
            Expanded(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Padding(
                    padding: const EdgeInsets.only(left: 8),
                    child: bottomPlayerImage,
                  ),
                  const SizedBox(
                    width: 20,
                  ),
                  Expanded(
                    child: titleAndArtist,
                  ),
                  Padding(
                    padding: const EdgeInsets.only(right: 20),
                    child: PlayButton(active: active),
                  ),
                  Padding(
                    padding: const EdgeInsets.only(right: 20),
                    child: IconButton(
                      onPressed: () => showDialog(
                        context: context,
                        builder: (context) => const CastTestDialog(),
                      ),
                      icon: const Icon(Icons.cast),
                    ),
                  ),
                ],
              ),
            ),
            track,
          ],
        ),
      ),
    );
  }
}

class CastTestDialogContent extends StatelessWidget {
  const CastTestDialogContent({super.key});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<CastDevice>>(
      future: CastDiscoveryService().search(),
      builder: (context, snapshot) {
        if (snapshot.hasError) {
          return Center(
            child: Text(
              'Error: ${snapshot.error.toString()}',
            ),
          );
        } else if (!snapshot.hasData) {
          return const Center(
            child: CircularProgressIndicator(),
          );
        }

        if (snapshot.data!.isEmpty) {
          return const Column(
            children: [
              Center(
                child: Text(
                  'No Chromecast founded',
                ),
              ),
            ],
          );
        }

        return Column(
          children: snapshot.data!.map((device) {
            return ListTile(
              title: Text(device.name),
              onTap: () => _connect(context, device),
            );
          }).toList(),
        );
      },
    );
  }

  Future<void> _connect(BuildContext context, CastDevice object) async {
    final session = await CastSessionManager().startSession(object);

    session.stateStream.listen((state) {
      if (state == CastSessionState.connected) {
        _sendMessage(session);
      }
    });

    session.messageStream.listen((message) {
      print('receive message: $message');
    });

    session.sendMessage(CastSession.kNamespaceReceiver, {
      'type': 'LAUNCH',
      'appId': 'YouTube', // set the appId of your app here
    });
  }

  void _sendMessage(CastSession session) {
    session.sendMessage('urn:x-cast:namespace-of-the-app', {
      'type': 'sample',
    });
  }
}

class CastTestDialog extends StatelessWidget {
  const CastTestDialog({super.key});

  @override
  Widget build(BuildContext context) {
    return const AlertDialog(
      content: CastTestDialogContent(),
      contentPadding: EdgeInsets.only(top: 20, bottom: 20),
    );
  }
}

@guyluz11
Copy link

guyluz11 commented Jan 3, 2024

I didn't manage to open YouTube links.
To do that I think there are 3 options

  1. Opening the link in the Chromecast browser like a browser web page (will look awkward and bad performance).
  2. Opening it in a video player, I think this is what node-red-contrib-castv2 did. This is also not the best experience because you are not inside of the YouTube app and the controls behave wired.
  3. Support a new dedicated Chromecast YouTube API, which I think will require the user to authenticate but will be like casting a video from the YouTube app. I think the following links show an implementation of it https://github.com/castjs/castjs and https://bugs.xdavidhu.me/google/2021/04/05/i-built-a-tv-that-plays-all-of-your-private-youtube-videos/

Maybe I will work on it in the future.
I will post about my progress in the following link (in the cbj repo issue)
CyBear-Jinni/cbj_integrations_controller#15

@Feichtmeier
Copy link
Member Author

I didn't manage to open YouTube links.
To do that I think there are 3 options

  1. Opening the link in the Chromecast browser like a browser web page (will look awkward and bad performance).
  2. Opening it in a video player, I think this is what node-red-contrib-castv2 did. This is also not the best experience because you are not inside of the YouTube app and the controls behave wired.
  3. Support a new dedicated Chromecast YouTube API, which I think will require the user to authenticate but will be like casting a video from the YouTube app. I think the following links show an implementation of it https://github.com/castjs/castjs and https://bugs.xdavidhu.me/google/2021/04/05/i-built-a-tv-that-plays-all-of-your-private-youtube-videos/

Maybe I will work on it in the future.
I will post about my progress in the following link (in the cbj repo issue)
CyBear-Jinni/cbj_integrations_controller#15

Hi 👋😊

I don't want to open it inside YouTube

Only stream the audio and or video (video podcasts) to a Chromecast device

@guyluz11
Copy link

guyluz11 commented Feb 26, 2024

Didn't check streaming but it should be a small change to already existing request

@Feichtmeier
Copy link
Member Author

Didn't check streaming but it should be a small change to already existing request

Alright!
I would really appreciate a pull request if you have a plan :)

@CosmicRaptor
Copy link
Member

I have a 3rd gen chromecast, it pretty much only supports H264 video and a very narrow range of audio codecs. You would need to transcode the media files for casting them.
This codec issue is why even VLC fails to cast stuff to my chromecast.

@Feichtmeier Feichtmeier added the help wanted Extra attention is needed label Nov 16, 2024
@Feichtmeier
Copy link
Member Author

@CosmicRaptor would be super awesome if we could get this rolling for radio streams and podcasts, not for local audio

@CosmicRaptor
Copy link
Member

@Feichtmeier I've never done anything related to Chromecast before unfortunately, I can't promise that I can do it

@guyluz11
Copy link

@Feichtmeier I've never done anything related to Chromecast before unfortunately, I can't promise that I can do it

If any change is needed in my fork feel free to open a pr

@CosmicRaptor
Copy link
Member

@Feichtmeier I've never done anything related to Chromecast before unfortunately, I can't promise that I can do it

If any change is needed in my fork feel free to open a pr

Sure, I'll check it out. Unfortunately my parents have the Chromecast, I can only realistically work on this when I'm visiting them some time in December.

@guyluz11
Copy link

Great

Here is the link to my fork

You can work on the example folder

  cast: 
    git: 
      url: "https://github.com/guyluz11/flutter_cast.git"
      ref: "multicast_version"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants