Flutter app: Chewie throws "Failed to load video: Cannot Open" if I load a thumbnail from firebase storage before the video

Viewed 471

I ran into a problem trying to make an app, so I isolated the problem in the basic app shown below. It has an Inkwell widget that initially displays a thumbnail; tapping it will switch the thumbnail to a video. (The FirebaseAPI.getNailURL and FirebaseAPI.getVideoURL methods, as well as the ChewieBox widget, are my own custom methods and widgets, which I show below as well. I doubt that the implementation for ChewieBox is important though. It's just a container for a Chewie video player.)

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:firebase_core/firebase_core.dart';

import '../api/firebase_api.dart';
import '../general/chewie_box.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'video_player test'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late Widget imageOrVideo;

  final postData = {
    'user': 'nathan',
    'videoName': 'nathan2021-12-16T13:25:35.758371'
  };

  Widget thumbnail() {
    final thumbnailURL = FirebaseAPI.getNailURL(postData['videoName']);
    return FutureBuilder<String>(
        future: thumbnailURL,
        builder: (context, AsyncSnapshot<String> snapshot) {
          if (snapshot.hasData) {
            return Image.network(snapshot.data!);
          } else {
            return const CircularProgressIndicator();
          }
        });
  }

  Widget thumbnail_not_firebase() {
    return Image.network(
        'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg');
  }

  Widget video() {
    final videoURL = FirebaseAPI.getVideoURL(postData['videoName']);
    return FutureBuilder<String>(
        future: videoURL,
        builder: (context, AsyncSnapshot<String> snapshot) {
          if (snapshot.hasData) {
            return ChewieBox(
                VideoPlayerController.network(snapshot.data!), false);
          } else {
            return const CircularProgressIndicator();
          }
        });
  }

  void swap() {
    setState(() {
      imageOrVideo = video();
    });
  }

  @override
  void initState() {
    super.initState();
    imageOrVideo = thumbnail_not_firebase();
    // imageOrVideo = thumbnail();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: InkWell(
        splashColor: Colors.blue.withAlpha(30),
        onTap: swap,
        child: imageOrVideo,
      ),
    );
  }
}

FirebaseAPI methods:

import 'package:firebase_storage/firebase_storage.dart';

class FirebaseAPI {
  static getVideoURL(videoName) {
    return FirebaseStorage.instance.ref('videos/$videoName').getDownloadURL();
  }

  static getNailURL(videoName) {
    return FirebaseStorage.instance
        .ref('thumbnails/$videoName')
        .getDownloadURL();
  }
}

ChewieBox widget:

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';

class ChewieBox extends StatefulWidget {
  final VideoPlayerController videoPlayerController;
  final bool looping;
  final dynamic aspectRatio;

  const ChewieBox(
    this.videoPlayerController,
    this.looping, {
    this.aspectRatio,
    Key? key,
  }) : super(key: key);

  @override
  _ChewieBoxState createState() => _ChewieBoxState();
}

class _ChewieBoxState extends State<ChewieBox> {
  late ChewieController _chewieController;

  @override
  void initState() {
    super.initState();
    _chewieController = ChewieController(
      videoPlayerController: widget.videoPlayerController,
      autoInitialize: true,
      looping: widget.looping,
      errorBuilder: (context, errorMessage) {
        return Center(
          child: Text(
            errorMessage,
            style: const TextStyle(color: Colors.white),
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Chewie(controller: _chewieController);
  }

  @override
  void dispose() {
    super.dispose();
    widget.videoPlayerController.dispose();
    _chewieController.dispose();
  }
}

The problem is that if the thumbnail is taken from some random image from the internet, the video works; however, if the thumbnail is an image taken from my own firebase storage, the video (I tested with videos from the same firebase storage, random videos online, and also local "asset" videos) will not play, but throw the following error:

enter image description here

There is also another error message in the console which appears after I close the app:

[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: PlatformException(VideoError, Failed to load video: Cannot Open, null, null)

I am simply baffled by how the loading of the thumbnail from my firebase storage can break the loading of the video afterwards, while using a random image from the internet has no issues. The error message isn't very helpful either. Anyone knows how to fix this? Thanks!

1 Answers

Ensure that content-type header is correct. You can see how to do this here : SettableMetadata.

Also, you can check file content-type in Firebase storage user interface.

Related