Flutter视频滤镜处理插件flutter_gpu_video_filters的使用

Flutter视频滤镜处理插件flutter_gpu_video_filters的使用

介绍

flutter_gpu_video_filters 是一个用于在 Flutter 应用中应用 OpenGL 滤镜到视频的插件。它支持多种滤镜效果,并且可以轻松地将这些效果应用于视频文件或实时视频流。

使用方法

如何生成和使用多个滤镜

详细说明请参阅 如何生成和使用多个滤镜。

如何添加自定义滤镜

详细说明请参阅 如何添加自定义滤镜。

导出和保存视频

以下是一个示例代码,展示了如何导出并保存带有滤镜效果的视频:

import 'package:flutter_gpu_video_filters/flutter_gpu_video_filters.dart';

Future<void> exportVideo() async {
  final inputSource = AssetInputSource('demo.mp4');
  final output = File('result.mp4');
  final configuration = GPUGrayScaleConfiguration();
  final processStream = configuration.exportVideoFile(VideoExportConfig(inputSource, output));

  await for (final progress in processStream) {
    debugPrint('Exporting file ${(progress * 100).toInt()}%');
  }
}

GPUVideoNativePreview 示例

以下是一个示例代码,展示了如何在 Flutter 应用中预览带有滤镜效果的视频:

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

class PreviewPage extends StatefulWidget {
  const PreviewPage({Key? key}) : super(key: key);

  @override
  State<PreviewPage> createState() => _PreviewPageState();
}

class _PreviewPageState extends State<PreviewPage> {
  late GPUVideoPreviewController configuration;
  late final GPUGrayScaleConfiguration controller;
  late final GPUVideoPreviewParams previewParams;
  bool paramsReady = false;
  static const _assetPath = 'videos/demo.mp4';

  @override
  void initState() {
    super.initState();
    _prepare().whenComplete(() {
      setState(() {});
    });
  }

  Future<void> _prepare() async {
    configuration = GPUGrayScaleConfiguration();
    previewParams = await GPUVideoPreviewParams.create(configuration);
    paramsReady = true;
  }

  @override
  void dispose() {
    controller.dispose();
    configuration.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return paramsReady
        ? GPUVideoNativePreview(
            params: previewParams,
            configuration: configuration,
            onViewCreated: (controller, outputSizeStream) async {
              this.controller = controller;
              await this.controller.setVideoAsset(_assetPath);
              await for (final size in outputSizeStream) {
                setState(() {});
              }
            },
          )
        : const Offstage();
  }
}

额外信息

GPUImage for Android 的支持状态

以下表格列出了 GPUImage for Android 中各种滤镜的支持状态:

状态 名称 显示名称
✅ GPU3x3ConvolutionConfiguration 3x3 Convolution
✅ GPUAddBlendConfiguration Add Blend
✅ GPUAlphaBlendConfiguration Alpha Blend
❌ GPUBilateralConfiguration Bilateral
❌ GPUBoxBlurConfiguration Box Blur
✅ GPUBrightnessConfiguration Brightness
✅ GPUBulgeDistortionConfiguration Bulge Distortion Configuration
✅ GPUCGAColorspaceConfiguration CGA Colorspace
✅ GPUChromaKeyBlendConfiguration Chroma Key Blend
✅ GPUColorBalanceConfiguration Color Balance
✅ GPUColorBlendConfiguration Color Blend
✅ GPUColorBurnBlendConfiguration Color Burn Blend
✅ GPUColorDodgeBlendConfiguration Color Dodge Blend
✅ GPUColorInvertConfiguration Color Invert
✅ GPUColorMatrixConfiguration Color Matrix
✅ GPUContrastConfiguration Contrast
✅ GPUCrosshatchConfiguration Crosshatch
✅ GPUDarkenBlendConfiguration Darken Blend
✅ GPUDifferenceBlendConfiguration Difference Blend
✅ GPUDirectionalSobelEdgeDetectionConfiguration Directional Sobel Edge Detection
✅ GPUDissolveBlendConfiguration Dissolve Blend
✅ GPUDivideBlendConfiguration Divide Blend
✅ GPUEmbossConfiguration Emboss
✅ GPUExposureConfiguration Exposure
✅ GPUFalseColorConfiguration False Color
✅ GPUGammaConfiguration Gamma
❌ GPUGaussianBlurConfiguration Gaussian Blur
✅ GPUGlassSphereConfiguration Glass Sphere
✅ GPUGrayScaleConfiguration Gray Scale
✅ GPUHALDLookupTableConfiguration HALD Lookup Table
✅ GPUHalftoneConfiguration Halftone
✅ GPUHardLightBlendConfiguration Hard Light Blend
✅ GPUHazeConfiguration Haze
✅ GPUHighlightShadowConfiguration Highlight Shadow
✅ GPUHueBlendConfiguration Hue Blend
✅ GPUHueConfiguration Hue
✅ GPUInvertConfiguration Invert
✅ GPUKuwaharaConfiguration Kuwahara
✅ GPULaplacianConfiguration Laplacian
✅ GPULevelsConfiguration Levels
✅ GPULightenBlendConfiguration Lighten Blend
✅ GPULinearBurnBlendConfiguration Linear Burn Blend
✅ GPULuminanceThresholdConfiguration Luminance Threshold
✅ GPULuminanceConfiguration Luminance
✅ GPULuminosityBlendConfiguration Luminosity Blend
✅ GPUMonochromeConfiguration Monochrome
✅ GPUMultiplyBlendConfiguration Multiply Blend
✅ GPUNonMaximumSuppressionConfiguration Non Maximum Suppression
✅ GPUNormalBlendConfiguration Normal Blend
‽ GPUOpacityConfiguration Opacity
✅ GPUOverlayBlendConfiguration Overlay Blend
✅ GPUOverlayConfiguration Overlay
✅ GPUPixelationConfiguration Pixelation
✅ GPUPosterizeConfiguration Posterize
✅ GPURGBConfiguration RGB
✅ GPUSaturationBlendConfiguration Saturation Blend
✅ GPUSaturationConfiguration Saturation
✅ GPUScreenBlendConfiguration Screen Blend
✅ GPUSepiaToneConfiguration Sepia Tone
✅ GPUSepiaConfiguration Sepia
✅ GPUSharpenConfiguration Sharpen
✅ GPUSobelThresholdConfiguration Sobel Threshold
✅ GPUSoftLightBlendConfiguration Soft Light Blend
✅ GPUSolarizeConfiguration Solarize
✅ GPUSourceOverBlendConfiguration Source Over Blend
✅ GPUSphereRefractionConfiguration Sphere Refraction
✅ GPUSquareLookupTableConfiguration Square Lookup Table
✅ GPUSubtractBlendConfiguration Subtract Blend
✅ GPUSwirlConfiguration Swirl
✅ GPUToneCurveConfiguration Tone Curve
✅ GPUToonConfiguration Toon
✅ GPUVibranceConfiguration Vibrance
✅ GPUVignetteConfiguration Vignette
✅ GPUWatermarkConfiguration Watermark
✅ GPUWeakPixelInclusionConfiguration Weak Pixel Inclusion
✅ GPUWhiteBalanceConfiguration WhiteBalance
✅ GPUZoomBlurConfiguration Zoom Blur

示例

维护者

完整示例代码

以下是 example/lib/main.dart 文件的完整示例代码:

import 'dart:io' show File;

import 'package:gallery_saver/gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart' hide Rect;
import 'package:flutter_gpu_video_filters/flutter_gpu_video_filters.dart';
import 'package:flutter_gpu_filters_interface/flutter_gpu_filters_interface.dart';

import 'approved_filters.dart';
import 'filters.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Filters Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      home: const ListPage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Filters'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: CustomScrollView(
          slivers: [
            SliverFixedExtentList(
              delegate: SliverChildBuilderDelegate(
                (context, index) {
                  final item = kFailedFilters[index];
                  return Card(
                    child: ListTile(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) {
                              return FilterPage2(
                                configuration: item.configuration,
                              );
                            },
                          ),
                        );
                      },
                      title: Text(item.name),
                      trailing: Icon(
                        Icons.arrow_forward,
                        color: Theme.of(context).colorScheme.error,
                      ),
                    ),
                  );
                },
                childCount: kFailedFilters.length,
              ),
              itemExtent: 64,
            ),
            SliverFixedExtentList(
              delegate: SliverChildBuilderDelegate(
                (context, index) {
                  final item = kFilters[index];
                  return Card(
                    child: ListTile(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) {
                              return FilterPage2(
                                configuration: item.configuration,
                              );
                            },
                          ),
                        );
                      },
                      title: Text(item.name),
                      trailing: Icon(
                        Icons.arrow_forward,
                        color: Theme.of(context).primaryColor,
                      ),
                    ),
                  );
                },
                childCount: kFilters.length,
              ),
              itemExtent: 64,
            ),
          ],
        ),
      ),
    );
  }
}

class FilterPage extends StatefulWidget {
  final GPUFilterConfiguration configuration;

  const FilterPage({super.key, required this.configuration});

  @override
  State<FilterPage> createState() => _FilterPageState();
}

class _FilterPageState extends State<FilterPage> {
  late final VideoPreviewController controller;
  late final GPUVideoPreviewParams previewParams;
  bool previewParamsReady = false;
  static const _videoAsset = 'videos/demo.mp4';

  @override
  void initState() {
    super.initState();
    _prepare().whenComplete(() => setState(() {}));
  }

  @override
  void dispose() {
    controller.dispose();
    widget.configuration.dispose();
    super.dispose();
  }

  Future<void> _prepare() async {
    await widget.configuration.prepare();
    previewParams = await GPUVideoPreviewParams.create(widget.configuration);
    previewParamsReady = true;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Preview'),
      ),
      floatingActionButton: FloatingActionButton(
        heroTag: null,
        onPressed: () {
          _exportVideo().catchError((e) => ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(e.toString()))));
        },
        tooltip: 'Export video',
        child: const Icon(Icons.save),
      ),
      body: Stack(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Container(
              child: previewParamsReady
                  ? GPUVideoSurfacePreview(
                      configuration: widget.configuration,
                      onViewCreated: (controller, outputSizeStream) async {
                        this.controller = controller;
                        await controller
                            .setVideoSource(AssetInputSource(_videoAsset));
                        await widget.configuration.update();
                        await for (final _ in outputSizeStream) {
                          setState(() {});
                        }
                      },
                    )
                  : const Center(
                      child: CircularProgressIndicator(),
                    ),
            ),
          ),
          Row(
            children: [
              TextButton(
                  onPressed: () {
                    controller.pause();
                  },
                  child: const Text('Pause')),
              TextButton(
                  onPressed: () {
                    controller.play();
                  },
                  child: const Text('Play')),
            ],
          )
        ],
      ),
    );
  }

  File? latestFile;

  Future<void> _exportVideo() async {
    const asset = _videoAsset;
    final root = await getTemporaryDirectory();
    final output = File(
      '${root.path}/${DateTime.now().millisecondsSinceEpoch}.${asset.split('.').last}',
    );
    final watch = Stopwatch();
    watch.start();
    final processStream = widget.configuration.exportVideoFile(
      VideoExportConfig(
        latestFile == null
            ? AssetInputSource(asset)
            : FileInputSource(latestFile!),
        output,
      ),
    );
    await for (final progress in processStream) {
      debugPrint('_exportVideo: Exporting file ${(progress * 100).toInt()}%');
    }
    debugPrint(
        '_exportVideo: Exporting file took ${watch.elapsedMilliseconds} milliseconds');
    await GallerySaver.saveVideo(output.absolute.path);
    latestFile = output;
    debugPrint('_exportVideo: Exported: ${output.absolute}');
  }
}

class FilterPage2 extends StatefulWidget {
  final GPUFilterConfiguration configuration;

  const FilterPage2({super.key, required this.configuration});

  @override
  State<FilterPage2> createState() => _FilterPageState2();
}

class _FilterPageState2 extends State<FilterPage2> {
  late final VideoPreviewController controller;
  bool previewParamsReady = false;
  static const _videoAsset = 'videos/demo.mp4';

  @override
  void initState() {
    super.initState();
    _prepare().whenComplete(() => setState(() {}));
  }

  @override
  void dispose() {
    controller.dispose();
    widget.configuration.dispose();
    super.dispose();
  }

  Future<void> _prepare() async {
    await widget.configuration.prepare();
    controller = await GPUVideoPreviewController.initialize();
    await controller.connect(widget.configuration);
    await controller.setVideoSource(AssetInputSource(_videoAsset));
    previewParamsReady = true;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Preview'),
      ),
      floatingActionButton: FloatingActionButton(
        heroTag: null,
        onPressed: () {
          _exportVideo().catchError((e) => ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(e.toString()))));
        },
        tooltip: 'Export video',
        child: const Icon(Icons.save),
      ),
      body: Stack(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Container(
              child: previewParamsReady
                  ? VideoPreview(
                      controller: controller,
                    )
                  : const Center(
                      child: CircularProgressIndicator(),
                    ),
            ),
          ),
          Row(
            children: [
              TextButton(
                  onPressed: () {
                    controller.pause();
                  },
                  child: const Text('Pause')),
              TextButton(
                  onPressed: () {
                    controller.play();
                  },
                  child: const Text('Play')),
            ],
          )
        ],
      ),
    );
  }

  File? latestFile;

  Future<void> _exportVideo() async {
    const asset = _videoAsset;
    final root = await getTemporaryDirectory();
    final output = File(
      '${root.path}/${DateTime.now().millisecondsSinceEpoch}.${asset.split('.').last}',
    );
    final watch = Stopwatch();
    watch.start();
    final processStream = widget.configuration.exportVideoFile(
      VideoExportConfig(
        latestFile == null
            ? AssetInputSource(asset)
            : FileInputSource(latestFile!),
        output,
      ),
    );
    await for (final progress in processStream) {
      debugPrint('_exportVideo: Exporting file ${(progress * 100).toInt()}%');
    }
    debugPrint(
        '_exportVideo: Exporting file took ${watch.elapsedMilliseconds} milliseconds');
    await GallerySaver.saveVideo(output.absolute.path);
    latestFile = output;
    debugPrint('_exportVideo: Exported: ${output.absolute}');
  }
}

希望这些内容对你有所帮助!如果有任何问题或需要进一步的帮助,请随时提问。


更多关于Flutter视频滤镜处理插件flutter_gpu_video_filters的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter视频滤镜处理插件flutter_gpu_video_filters的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 flutter_gpu_video_filters 插件来处理视频滤镜的示例代码。这个插件允许你在 Flutter 应用中应用 GPU 加速的视频滤镜。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_gpu_video_filters 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_gpu_video_filters: ^0.10.0  # 请检查最新版本号

然后运行 flutter pub get 来获取依赖。

以下是一个简单的 Flutter 应用示例,它展示了如何使用 flutter_gpu_video_filters 插件来应用视频滤镜:

import 'package:flutter/material.dart';
import 'package:flutter_gpu_video_filters/flutter_gpu_video_filters.dart';
import 'package:video_player/video_player.dart';
import 'dart:typed_data';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: VideoFilterPage(),
    );
  }
}

class VideoFilterPage extends StatefulWidget {
  @override
  _VideoFilterPageState createState() => _VideoFilterPageState();
}

class _VideoFilterPageState extends State<VideoFilterPage> {
  VideoPlayerController _controller;
  TextureVideoFilter _filter;

  @override
  void initState() {
    super.initState();
    // 初始化视频控制器
    _controller = VideoPlayerController.network(
      'https://www.example.com/your-video-url.mp4',
    )..initialize().then((_) {
      // 初始化完成后开始播放视频
      setState(() {});
      _controller.play();

      // 初始化视频滤镜
      _filter = TextureVideoFilter(
        size: Size(_controller.value.size.width.toDouble(), _controller.value.size.height.toDouble()),
        filter: VideoFilter.sepia, // 使用棕褐色滤镜作为示例
      )..initialize().then((_) {
        // 将视频帧传递给滤镜处理
        _controller.addListener(() {
          if (_controller.value.isPlaying && _controller.value.hasInitialized && _controller.value.isBuffered) {
            _filter.setTextureId(_controller.textureId);
          }
        });
      });
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    _filter.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Video Filter Example'),
      ),
      body: Center(
        child: _controller.value.isInitialized
            ? Container(
                width: _controller.value.size.width,
                height: _controller.value.size.height,
                child: Stack(
                  alignment: Alignment.center,
                  children: <Widget>[
                    // 显示原始视频帧
                    SizedBox(
                      width: _controller.value.size.width,
                      height: _controller.value.size.height,
                      child: VideoPlayer(_controller),
                    ),
                    // 显示经过滤镜处理的视频帧
                    if (_filter.isInitialized)
                      Texture(
                        id: _filter.textureId,
                      ),
                  ],
                ),
              )
            : CircularProgressIndicator(),
      ),
    );
  }
}

说明

  1. 依赖管理:在 pubspec.yaml 文件中添加 flutter_gpu_video_filters 和 video_player 依赖。
  2. 视频控制器:使用 VideoPlayerController 来加载和控制视频。
  3. 视频滤镜:使用 TextureVideoFilter 来应用滤镜。这里以棕褐色滤镜(VideoFilter.sepia)为例。
  4. 视频显示:使用 VideoPlayer 小部件显示原始视频帧,同时使用 Texture 小部件显示经过滤镜处理的视频帧。

注意事项

  • 确保视频 URL 是有效的,并且服务器允许跨域访问。
  • 在实际开发中,你可能需要根据具体需求调整滤镜类型和参数。
  • flutter_gpu_video_filters 插件可能会随着 Flutter 生态的发展而更新,因此请参考最新的官方文档和示例代码。

希望这个示例代码能帮助你快速上手 flutter_gpu_video_filters 插件的使用!

回到顶部