如何在Flutter中筛选h.265编码的视频文件?
筛选H.265编码视频的解决方案
由于file_picker仅能通过扩展名筛选文件,而VideoPlayerController没有提供读取视频编码的API,要实现H.265编码视频的筛选,需要读取视频文件的元数据来判断编码格式,以下是两种可行方案:
方案一:使用第三方Dart包 video_metadata
这个包可以直接读取视频文件的元数据,包括编码信息,无需编写原生代码。
- 添加依赖到
pubspec.yaml:
dependencies: video_metadata: ^0.5.0 # 请使用最新版本
- 实现筛选逻辑:
import 'package:file_picker/file_picker.dart'; import 'package:video_metadata/video_metadata.dart'; import 'dart:io'; Future<void> pickAndValidateH265Video() async { // 先筛选MP4格式缩小范围 final fileResult = await FilePicker.platform.pickFiles( type: FileType.video, allowedExtensions: ['mp4'], ); if (fileResult == null) return; final selectedFile = fileResult.files.first; final file = File(selectedFile.path!); // 获取视频元数据 final metadata = await MetadataRetriever.fromFile(file); final videoTrack = metadata.videoTracks.firstOrNull; // H.265对应的编码标识通常为'hevc' if (videoTrack != null && videoTrack.codec == 'hevc') { // 符合条件,执行上传逻辑 print("选中的视频为H.265编码,开始上传"); } else { // 不符合,提示用户 print("请选择采用H.265编码的MP4视频"); } }
方案二:原生平台实现(Android + iOS)
如果第三方包无法满足需求,可通过MethodChannel调用原生API获取编码信息,兼容性更强。
Flutter端代码
import 'package:flutter/services.dart'; import 'package:file_picker/file_picker.dart'; final MethodChannel _videoCodecChannel = MethodChannel('video.codec.checker'); Future<void> pickH265Video() async { final fileResult = await FilePicker.platform.pickFiles( type: FileType.video, allowedExtensions: ['mp4'], ); if (fileResult == null) return; final filePath = fileResult.files.first.path!; // 调用原生方法获取编码 final codec = await _videoCodecChannel.invokeMethod<String>( 'getVideoCodec', {'filePath': filePath}, ); // H.265在不同平台的编码标识可能不同:Android返回'hevc',iOS返回'hvc1'/'hev1' if (codec != null && {'hevc', 'hvc1', 'hev1'}.contains(codec.toLowerCase())) { // 执行上传 print("视频编码符合H.265标准"); } else { print("请选择H.265编码的视频"); } }
Android端实现(Kotlin)
在MainActivity.kt中添加MethodChannel逻辑:
import android.media.MediaMetadataRetriever import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL_NAME = "video.codec.checker" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NAME).setMethodCallHandler { call, result -> when (call.method) { "getVideoCodec" -> { val filePath = call.argument<String>("filePath") if (filePath == null) { result.error("INVALID_PATH", "文件路径为空", null) return@setMethodCallHandler } val codec = getVideoCodec(filePath) result.success(codec) } else -> result.notImplemented() } } } private fun getVideoCodec(filePath: String): String? { val retriever = MediaMetadataRetriever() return try { retriever.setDataSource(filePath) retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_CODEC) } catch (e: Exception) { null } finally { retriever.release() } } }
iOS端实现(Swift)
在AppDelegate.swift中添加MethodChannel逻辑:
import UIKit import Flutter import AVFoundation @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { private let CHANNEL_NAME = "video.codec.checker" override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let channel = FlutterMethodChannel(name: CHANNEL_NAME, binaryMessenger: controller.binaryMessenger) channel.setMethodCallHandler { [weak self] call, result in guard let self = self else { return } switch call.method { case "getVideoCodec": guard let filePath = call.arguments as? String else { result(FlutterError(code: "INVALID_PATH", message: "文件路径为空", details: nil)) return } self.retrieveVideoCodec(filePath: filePath, result: result) default: result(FlutterMethodNotImplemented) } } GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func retrieveVideoCodec(filePath: String, result: @escaping FlutterResult) { let fileURL = URL(fileURLWithPath: filePath) let asset = AVAsset(url: fileURL) asset.loadValuesAsynchronously(forKeys: ["tracks"]) { var error: NSError? let status = asset.statusOfValue(forKey: "tracks", error: &error) if status == .loaded { let videoTracks = asset.tracks(withMediaType: .video) guard let videoTrack = videoTracks.first else { result(nil) return } // 获取编码子类型 let codecSubtype = videoTrack.formatDescriptions.compactMap { $0 as? CMFormatDescription }.first.flatMap { CMFormatDescriptionGetMediaSubType($0).stringValue } result(codecSubtype) } else { result(FlutterError(code: "LOAD_FAILED", message: error?.localizedDescription, details: nil)) } } } }
注意事项
- 不同平台返回的H.265编码标识可能存在差异,需多值判断(如
hevc、hvc1、hev1)。 - 读取大视频文件的元数据可能存在性能开销,建议在子线程中处理或添加加载提示。
内容的提问来源于stack exchange,提问作者BongsoonChoi
相关产品推荐
相关产品推荐

