我正在尝试使用 Flutter 从动画 gif 中提取帧。通过研究 Flutter API,我认为以下代码应该可以工作,但它只给了我第一帧,尽管它给了我正确的帧计数。
static Future<List<ui.Image>> loadAnimatedGif(String assetPath) async {
List<ui.Image> gifImage = [];
ByteData data = await rootBundle.load(assetPath);
var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
int frmCnt = codec.frameCount;
print("frmcnt is $frmCnt"); //it gives correct frames count
for (int i = 0; i < frmCnt; i++) {
var frame = await codec.getNextFrame();
gifImage.add(frame.Image);
}
return gifImage;
}
我用这个制作了一个简单的应用程序,它使用相同的代码工作,但在其中添加了一个转换器。
我一开始注意到什么,因为什么都没有
ui.Image
加载帧,但会延迟大约 10 秒,然后再使用断点进行调试来显示所有帧。ui.Image
转换为 Uint8List
所需的时间与帧一样多,但提取完成后格式会显示所有帧。这是dart pad,可以快速运行和测试它。
如果您想扫描的话,这是代码,我正在从网络加载 gif。
class GifFramesDisplay extends StatefulWidget {
const GifFramesDisplay({super.key});
@override
State<GifFramesDisplay> createState() => _GifFramesDisplayState();
}
class _GifFramesDisplayState extends State<GifFramesDisplay> {
List<Uint8List> _frames = [];
@override
void initState() {
super.initState();
loadGif();
}
Future<void> loadGif() async {
// Load the gif image from the network url and store the bytes in a ByteData object
final url = Uri.parse(
'https://raw.githubusercontent.com/Aman-Malhotra/animate_icons/main/demo/animate_icons.gif');
final ByteData data = await NetworkAssetBundle(url).load(url.path);
// Using the _extractGifFrames function to extract the frames
_extractGifFrames(data).then((List<Uint8List> frames) {
// Set the state to update the UI
setState(() {
_frames = frames;
});
});
}
// Function to extract gif image frames
Future<List<Uint8List>> _extractGifFrames(ByteData data) async {
// Create a list to store the frames
final List<Uint8List> frames = <Uint8List>[];
// Create a codec to decode the gif
final ui.Codec codec =
await ui.instantiateImageCodec(data.buffer.asUint8List());
// Count the number of frames in the gif
final int frameCount = codec.frameCount;
print('Total frameCount: $frameCount');
// Loop through the frames and add them to the list
for (int i = 0; i < frameCount; i++) {
// Get the next frame
final ui.FrameInfo fi = await codec.getNextFrame();
// Add the frame to the list
final frame = await loadImage(fi.image);
// Add the frame to the list if it is not null
if (frame != null) {
frames.add(frame);
}
}
return frames;
}
Future<Uint8List?> loadImage(ui.Image image) async {
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return byteData?.buffer.asUint8List();
}
@override
Widget build(BuildContext context) {
return Center(
child: _frames.isEmpty
? const CircularProgressIndicator()
: ListView.builder(
itemCount: _frames.length,
itemBuilder: (BuildContext context, int index) {
return Image.memory(_frames[index]);
},
),
);
}
}
单击观看上述代码加载的 gif 帧的预览
这里我使用ffmpeg_kit_flutter从GIF中提取所有帧,将所有帧显示在列表中,并单独显示第10帧作为预览。这是我的代码实现:
GIF 提取:FFmpegKit 从资源中存储的 GIF 文件中提取帧并将其保存在临时目录中。 UI:第 10 帧显示在顶部,所有帧都列在下面。
String gifPath = 'assets/bird-dog1.gif'; // GIF asset path
List<String> framePaths = [];
String? tenthFramePath;
Future<void> _extractAllFrames() async {
try {
final directory = await getTemporaryDirectory();
final tempGifPath = '${directory.path}/${gifPath.split('/').last}';
final byteData = await rootBundle.load(gifPath);
final gifFile = File(tempGifPath);
await gifFile.writeAsBytes(byteData.buffer.asUint8List());
final framesDir = '${directory.path}/frames';
final dir = Directory(framesDir);
if (!dir.existsSync()) {
dir.createSync(recursive: true);
}
String ffmpegCommand = '-y -i $tempGifPath $framesDir/frame_%03d.png';
await FFmpegKit.execute(ffmpegCommand).then((session) async {
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
final extractedFrames = dir.listSync().map((file) => file.path).toList();
setState(() {
framePaths = extractedFrames;
if (extractedFrames.length >= 10) {
tenthFramePath = extractedFrames[9];
}
});
}
});
} catch (e) {
print('Error extracting frames: $e');
}
}
Pubspec.yaml
name: ffmpeflutterproject
description: "A new Flutter project."
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ^3.5.0
dependencies:
flutter:
sdk: flutter
ffmpeg_kit_flutter: ^6.0.3
path_provider:
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^4.0.0
flutter:
uses-material-design: true
assets:
- assets/bird-dog1.gif
- assets/bird-dog2.gif
- assets/bird-dog3.gif
- assets/bird-dog4.gif