Flutter視頻渲染系列
第一章 Android使用Texture渲染視頻(本章)
第二章 Windows使用Texture渲染視頻
第三章 Linux使用Texture渲染視頻
第四章 全平臺(tái)FFI+CustomPainter渲染視頻
第五章 Windows使用Native窗口渲染視頻
第六章 桌面端使用texture_rgba_renderer渲染視頻
前言
flutter渲染視頻的方法有多種,比如texture、platformview、ffi,其中texture是通過flutter自己提供的一個(gè)texture對(duì)象與dart界面關(guān)聯(lián)后進(jìn)行渲染,很容易搜索到android和ios的相關(guān)資料,但是大部分資料不夠詳細(xì),尤其是jni渲染部分基本都略過了,對(duì)于使用flutter但不熟悉安卓的情況下,是比較難搞清楚通過texure拿到surface之后該怎么渲染。所以本文將說明整體的渲染流程。
一、如何實(shí)現(xiàn)?
1、定義Texture控件
在界面中定義一個(gè)Texture
Container(
width: 640,
height: 360,
child: Texture(
textureId: textureId,
))
2、創(chuàng)建Texture對(duì)象
java
TextureRegistry.SurfaceTextureEntry entry =flutterEngine.getRenderer().createSurfaceTexture();
3、關(guān)聯(lián)TextureId
dart
int textureId = -1;
if (textureId < 0) {
//調(diào)用本地方法獲取textureId
methodChannel.invokeMethod('startPreview',<String,dynamic>{'path':'test.mov'}).then((value) {
textureId = value;
setState(() {
print('textureId ==== $textureId');
});
});
}
java
//methodchannel的startPreview方法實(shí)現(xiàn),此處略
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");
//調(diào)用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數(shù)據(jù)
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');
//調(diào)用本地方法播放視頻
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),
),
);
}
}
定義一個(gè)插件我這里是fflay_plugin。
fflayplugin.java
if (call.method.equals("startPreview")) {
//創(chuàng)建texture
TextureRegistry.SurfaceTextureEntry entry =flutterEngine.getRenderer().createSurfaceTexture();
SurfaceTexture surfaceTexture = entry.surfaceTexture();
Surface surface = new Surface(surfaceTexture);
//獲取參數(shù)
String path = call.argument("path");
//調(diào)用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);
//轉(zhuǎn)換視頻路徑字符串為C中可用的
const char *video_path = env->GetStringUTFChars(path,0);
//初始化播放器,Play中封裝了ffmpeg
Play *play=new Play;
//播放回調(diào)
play->Display=[=](unsigned char* data[8], int linesize[8], int width, int height, AVPixelFormat format)
{
//設(shè)置NativeWindow繪制的緩沖區(qū)
ANativeWindow_setBuffersGeometry(a_native_window,width,height,WINDOW_FORMAT_RGBA_8888);
//繪制時(shí),用于接收的緩沖區(qū)
ANativeWindow_Buffer a_native_window_buffer;
//加鎖然后進(jìn)行渲染
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];
//拿到每行有多少個(gè)RGBA字節(jié)
int dst_stride = a_native_window_buffer.stride * 4;
int src_line_size = linesize[0];
//循環(huán)遍歷所得到的緩沖區(qū)數(shù)據(jù)
for(int i = 0; i < a_native_window_buffer.height;i++){
//內(nèi)存拷貝進(jìn)行渲染
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);
}
效果預(yù)覽
android TV橫屏
三、完整代碼
https://download.csdn.net/download/u013113678/87094784
包含完整代碼的flutter項(xiàng)目,版本3.0.4、3.3.8都成功運(yùn)行,目錄說明如下。文章來源:http://www.zghlxwxcb.cn/news/detail-402245.html
總結(jié)
以上就是今天要講的內(nèi)容,flutter在安卓上渲染視頻還是相對(duì)容易實(shí)現(xiàn)的。因?yàn)橘Y料比較多,但是只搜索fluter相關(guān)的資料只能了解調(diào)texture的用法,無法搞清楚texture到ffmpeg的串連,我們需要單獨(dú)去了解安卓調(diào)用ffmpeg渲染才能找到的它們之間的關(guān)聯(lián)??偟膩碚f,實(shí)現(xiàn)相對(duì)容易效果也能接受。文章來源地址http://www.zghlxwxcb.cn/news/detail-402245.html
到了這里,關(guān)于Flutter 使用Texture實(shí)現(xiàn)Android渲染視頻的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!