Amazon S3 stores video files that need to be streamed inside applications. In a Flutter app, these videos can be accessed either through pre-signed URLs that provide temporary access to private S3 objects or through CloudFront, which serves as a CDN layer on top of S3. The goal is to keep videos secure in storage while still allowing playback inside the app.

Preparing Videos in S3

Video files, whether MP4 or adaptive streaming formats like HLS and DASH, are first uploaded to an S3 bucket. Buckets should be private so that files are not directly exposed. Access is instead managed using pre-signed URLs or by integrating the bucket with CloudFront.

When uploading, the correct content type must be assigned to each file so that clients interpret them properly, for example, video/mp4 for MP4 files or application/vnd.apple.mpegurl for HLS playlists.

Generating Pre-Signed URLs

Since the bucket is private, direct access is not possible. A backend service generates pre-signed URLs using the AWS SDK. These URLs grant temporary permission to read a specific file and expire after a set duration. The Flutter app then uses this URL to stream the video. The generation step must happen on the server side because AWS credentials should not be exposed in the mobile app.

code
import boto3
import datetime

s3 = boto3.client("s3")
url = s3.generate_presigned_url(
"get_object",
Params={"Bucket": "my-videos", "Key": "sample.mp4"},
ExpiresIn=3600
)
print(url)
Video CDN

Flutter Playback with video_player

In Flutter, playback is initialized with a video controller that accepts a network source. The pre-signed URL returned from the backend is passed into the controller, which loads the video for playback.

MP4 files can be played directly using the video_player package. For HLS streams, packages like Better Player are better suited because they handle adaptive bitrate switching and streaming playlists.

code
import 'package:video_player/video_player.dart';

final controller = VideoPlayerController.network(
'https://your-s3-presigned-url',
);

await controller.initialize();
controller.play();

Using CloudFront for Optimized Delivery

CloudFront can be added as a distribution layer on top of S3. Instead of pointing pre-signed URLs directly at S3, they can point to CloudFront, which caches and serves content from edge locations closer to users. This reduces latency and improves playback stability, especially for global audiences. CloudFront also supports signed URLs or cookies to enforce access control in the same way S3 pre-signed URLs do.

Handling Large Video Files

When storing or delivering large videos, a multipart upload is used for efficient transfers. If the app needs to support uploads, the server can generate a pre-signed PUT URL, which allows the client to upload directly to S3 without routing the file through the backend. On the Flutter side, the file bytes are uploaded to S3 using this signed URL. This prevents backend servers from handling large payloads directly.

code
import 'package:http/http.dart' as http;

final request = http.Request("PUT", Uri.parse(preSignedUrl))
..headers['Content-Type'] = 'video/mp4'
..bodyBytes = videoFileBytes;
await request.send();