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

发布于 1周前 作者 sinazl 来自 Flutter

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_filtersvideo_player 依赖。
  2. 视频控制器:使用 VideoPlayerController 来加载和控制视频。
  3. 视频滤镜:使用 TextureVideoFilter 来应用滤镜。这里以棕褐色滤镜(VideoFilter.sepia)为例。
  4. 视频显示:使用 VideoPlayer 小部件显示原始视频帧,同时使用 Texture 小部件显示经过滤镜处理的视频帧。

注意事项

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

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

回到顶部