IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> flutter 使用texture实现Android渲染视频 -> 正文阅读

[移动开发]flutter 使用texture实现Android渲染视频


前言

flutter渲染视频的方法有多种,比如texture、platformview、ffi,其中texture是通过flutter自己提供的一个texture对象与dart界面关联后进行渲染,很容易搜索到android和ios的相关资料,但是大部分资料不够详细,尤其是jni渲染部分基本都略过了,对于使用flutter但不熟悉安卓的情况下,是比较难搞清楚通过texure拿到surface之后该怎么渲染。所以本文将说明整体的渲染流程。


一、如何实现?

1、定义Texture控件

在界面中定义一个Texture

Container(
  width: 640,
  height: 360,
  child: Texture(
  textureId: textureId,
))

2、创建Texture对象

java

TextureRegistry.SurfaceTextureEntry entry =flutterEngine.getRenderer().createSurfaceTexture();

3、关联TextureId

dart

  int textureId = -1;
  if (textureId < 0) {
  //调用本地方法获取textureId 
  methodChannel.invokeMethod('startPreview',<String,dynamic>{'path':'test.mov'}).then((value) {
  textureId = value;
  setState(() {
  print('textureId ==== $textureId');
  });
  });
  }

java

 //methodchannel的startPreview方法实现,此处略
TextureRegistry.SurfaceTextureEntry entry =flutterEngine.getRenderer().createSurfaceTexture();
result.success(entry.id());

4、写入rgba

java

TextureRegistry.SurfaceTextureEntry entry = flutterEngine.getRenderer().createSurfaceTexture();
SurfaceTexture surfaceTexture = entry.surfaceTexture();
Surface surface = new Surface(surfaceTexture);
String path = call.argument("path");
//调用jni并传入surface
native_start_play(path,surface);

jni c++

ANativeWindow *a_native_window = ANativeWindow_fromSurface(env,surface);
ANativeWindow_setBuffersGeometry(a_native_window,width,height,WINDOW_FORMAT_RGBA_8888);
ANativeWindow_Buffer a_native_window_buffer;   
ANativeWindow_lock(a_native_window,&a_native_window_buffer,0);
uint8_t *first_window = static_cast<uint8_t *>(a_native_window_buffer.bits);
//rgba数据
uint8_t *src_data = data[0];
int dst_stride = a_native_window_buffer.stride * 4;
//ffmpeg的linesize
int src_line_size = linesize[0];
for(int i = 0; i < a_native_window_buffer.height;i++){
    memcpy(first_window+i*dst_stride,src_data+i*src_line_size,dst_stride);
}
ANativeWindow_unlockAndPost(a_native_window);

其中jni方法定义:

extern "C" JNIEXPORT void JNICALL Java_com_example_ffplay_1plugin_FfplayPlugin_native_1start_1play( JNIEnv *env, jobject /* this*/,  jstring path,jobject surface) ;

二、示例

1.使用ffmpeg解码播放

main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
MethodChannel methodChannel = MethodChannel('ffplay_plugin');
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  int textureId = -1;
  Future<void> _createTexture() async {
  print('textureId = $textureId');
  //调用本地方法播放视频
  if (textureId < 0) {
  methodChannel.invokeMethod('startPreview',<String,dynamic>{'path':'https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv'}).then((value) {
  textureId = value;
  setState(() {
  print('textureId ==== $textureId');
  });
  });
  }
  }
  @override
  Widget build(BuildContext context) {
  return Scaffold(
  appBar: AppBar(
  title: Text(widget.title),
  ),
  //控件布局
  body: Center(
  child: Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
  if (textureId > -1)
  ClipRect (
  child: Container(
  width: 640,
  height: 360,
  child: Texture(
  textureId: textureId,
  )),
  ),
  ],
  ),
  ),
  floatingActionButton: FloatingActionButton(
  onPressed: _createTexture,
  tooltip: 'createTexture',
  child: Icon(Icons.add),
  ),
  );
  }
}

定义一个插件我这里是fflay_plugin。
fflayplugin.java

if (call.method.equals("startPreview")) {
      //创建texture
      TextureRegistry.SurfaceTextureEntry entry =flutterEngine.getRenderer().createSurfaceTexture();
      SurfaceTexture surfaceTexture = entry.surfaceTexture();
      Surface surface = new Surface(surfaceTexture);
      //获取参数
      String path = call.argument("path");
      //调用jni开始渲染
      native_start_play(path,surface);
      //返回textureId
      result.success(entry.id());
    }
public native void native_start_play(String path, Surface surface);

native-lib.cpp

extern "C" JNIEXPORT void
JNICALL
Java_com_example_ffplay_1plugin_FfplayPlugin_native_1start_1play(
        JNIEnv *env,
        jobject /* this */,  jstring path,jobject surface) {
    //获取用于绘制的NativeWindow
    ANativeWindow *a_native_window = ANativeWindow_fromSurface(env,surface);
    //转换视频路径字符串为C中可用的
    const char *video_path = env->GetStringUTFChars(path,0);
    //初始化播放器,Play中封装了ffmpeg
    Play *play=new Play;
    //播放回调
    play->Display=[=](unsigned char* data[8], int linesize[8], int width, int height, AVPixelFormat format)
        {
        //设置NativeWindow绘制的缓冲区
        ANativeWindow_setBuffersGeometry(a_native_window,width,height,WINDOW_FORMAT_RGBA_8888);
        //绘制时,用于接收的缓冲区
        ANativeWindow_Buffer a_native_window_buffer;
        //加锁然后进行渲染
        ANativeWindow_lock(a_native_window,&a_native_window_buffer,0);
        uint8_t *first_window = static_cast<uint8_t *>(a_native_window_buffer.bits);
        uint8_t *src_data = data[0];
        //拿到每行有多少个RGBA字节
        int dst_stride = a_native_window_buffer.stride * 4;
        int src_line_size = linesize[0];
        //循环遍历所得到的缓冲区数据
        for(int i = 0; i < a_native_window_buffer.height;i++){
            //内存拷贝进行渲染
            memcpy(first_window+i*dst_stride,src_data+i*src_line_size,dst_stride);
        }
        //绘制完解锁
        ANativeWindow_unlockAndPost(a_native_window);
};
    //开始播放
    play->Start(video_path,AV_PIX_FMT_RGBA);
    env->ReleaseStringUTFChars(path,video_path);
}

效果预览
android TV横屏
在这里插入图片描述


总结

以上就是今天要讲的内容,flutter在安卓上渲染视频还是相对容易实现的。因为资料比较多,但是只搜索fluter相关的资料只能了解调texture的用法,无法搞清楚texture到ffmpeg的串连,我们需要单独去了解安卓调用ffmpeg渲染才能找到的它们之间的关联。总的来说,实现相对容易效果也能接受。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 23:03:58  更:2022-07-04 23:04:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 2:28:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码