-
Android播放器(一) 通过FFmpeg解码为RGBA格式播放
2019-12-25 08:06:31本文主要介绍如何通过FFmpeg将MP4格式的视频数据解码为一帧一帧的RGBA像素格式数据来播放。 因为主要是视频的解码及播放,对于音频只是解码出了音频对应的pcm数据,并没有播放pcm。因此也不会涉及到音视频的同步。 ...代码可以参考: Github地址
本文主要介绍如何通过FFmpeg将MP4格式的视频数据解码为一帧一帧的RGBA像素格式数据来播放。
因为主要是视频的解码及播放,对于音频只是解码出了音频对应的pcm数据,并没有播放pcm。因此也不会涉及到音视频的同步。主要流程是
解封装文章目录
Java层的主要配置
首先建一个支持cpp的项目
1 app module build.grdle配置
externalNativeBuild { cmake { //cpp编译器flag cppFlags "-std=c++11" } ndk{ //指定所支持的cpu架构 abiFilters "armeabi-v7a" } } sourceSets{ main{ //指定ffmpeg路径 jniLibs.srcDirs=['libs'] } }
指定cmake路径
android { externalNativeBuild { cmake { path "CMakeLists.txt" } } }
2 cmake文件配置
#1 声明cmake版本 cmake_minimum_required(VERSION 3.4.1) #2 添加头文件路径(相对于本文件路径) include_directories(include) #3 设置ffmpeg库所在路径的变量 set(FF ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}) # 4添加ffmpeg相关库 # 4.1解码 add_library(avcodec SHARED IMPORTED) set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${FF}/libavcodec.so) # 4.2格式转换 add_library(avformat SHARED IMPORTED) set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${FF}/libavformat.so) # 4.3基础库 add_library(avutil SHARED IMPORTED) set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${FF}/libavutil.so) # 4.4格式转换 add_library(swscale SHARED IMPORTED) set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${FF}/libswscale.so) #4.5 音频重采样 add_library(swresample SHARED IMPORTED) set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${FF}/libswresample.so) #5 指定本地cpp文件 和 打包对应的so库 add_library(native-lib SHARED src/main/cpp/native-lib.cpp ) find_library(log-lib log ) # 7 链接库 target_link_libraries(native-lib avcodec avformat avutil swscale swresample android ${log-lib} )
3 创建GLSurfaceView的子类
这一步骤主要是在开启的子线程中,将GLSurfaceView的Surface传递到底层来渲染数据
//1 创建GlSurfaceView的子类 public class PlayView extends GLSurfaceView implements Runnable, SurfaceHolder.Callback, GLSurfaceView.Renderer { public PlayView(Context context, AttributeSet attrs) { super(context, attrs); } public void start(){ //2 手动创建线程 new Thread(this).start(); } @Override public void run() { String videoPath = Environment.getExternalStorageDirectory() + "/video.mp4"; //3 在子线程中 将视频url和Surface对象传递到native层 open(videoPath, getHolder().getSurface()); } @Override public void surfaceCreated(SurfaceHolder holder) { //4 android8.0必须调用此方法,否则无法显示 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ setRenderer(this); } } public native void open(String url, Object surface); @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { } @Override public void onSurfaceChanged(GL10 gl10, int i, int i1) { } @Override public void onDrawFrame(GL10 gl10) { } }
Native层的相关代码
1 初始化
首先注册解封装器,打开文件。然后就可以拿到封装数据里面的音频和视频的索引。
//1 初始化解封装 av_register_all(); AVFormatContext *ic = NULL; //2 打开文件 int re = avformat_open_input(&ic, path, 0, 0); if (re != 0) { LOGEW("avformat_open_input %s success!", path); } else { LOGEW("avformat_open_input failed!: %s", av_err2str(re)); } //3 获取流信息 re = avformat_find_stream_info(ic, 0); if (re != 0) { LOGEW("avformat_find_stream_info failed!"); } LOGEW("duration = %lld nb_streams = %d", ic->duration, ic->nb_streams); int fps = 0; int videoStream = 0; int audioStream = 1; //4 获取视频音频流位置 for (int i = 0; i < ic->nb_streams; i++) { AVStream *as = ic->streams[i]; if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { LOGEW("视频数据"); videoStream = i; fps = r2d(as->avg_frame_rate); LOGEW("fps = %d, width = %d height = %d codeid = %d pixformat = %d", fps, as->codecpar->width, as->codecpar->height, as->codecpar->codec_id, as->codecpar->format); } else if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { LOGEW("音频数据"); audioStream = i; LOGEW("sample_rate = %d channels = %d sample_format = %d", as->codecpar->sample_rate, as->codecpar->channels, as->codecpar->format); } }
对于多路流可以通过遍历的方式拿到音频和视频流的索引地址,也可以直接指定流数据类型来获取索引地址
//5 获取音频流信息 和上面遍历取出视音频的流信息是一样的,这种方式更直接 audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
2 视解码器
在这一步骤中,首先定义并初始化解码器,然后视频流索引的地址传给解码器。最后打开解码器。
//1 软解码器 AVCodec *vcodec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id); if (!vcodec) { LOGEW("avcodec_find failed"); } //2 解码器初始化 AVCodecContext *vc = avcodec_alloc_context3(vcodec); //3 解码器参数赋值 avcodec_parameters_to_context(vc, ic->streams[videoStream]->codecpar); // 定义解码的线程 vc->thread_count = 8; //4 打开解码器 re = avcodec_open2(vc, 0, 0); LOGEW("vc timebase = %d/ %d", vc->time_base.num, vc->time_base.den); if (re != 0) { LOGEW("avcodec_open2 video failed!"); }
需要注意的是,这里可以自定义解码的线程,来控制解码速度。可以自定义大小,后续代码会有介绍调整这个值以后,来测试解码速度大小。
另外,这部分只是通过软解码的方式,软解比较消耗CPU,但是兼容性好
硬解不消耗CPU,更省电,但是硬解可能会有兼容性的问题下面是获取硬解码器
AVCodec *vcodec = avcodec_find_decoder_by_name("h264_mediacodec");
在使用硬解码器,还要额外定义此方法。用于确保在获取硬解码器之前调用av_jni_set_java_vm()函数,通过调用av_jni_set_java_vm()才可以获取到硬解码器。
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm,void *res) { av_jni_set_java_vm(vm,0); return JNI_VERSION_1_4; }
3 音频解码器
和之前的视频解码器的步骤相同,只是部分参数不同
//1 软解码器 AVCodec *acodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id); if (!acodec) { LOGEW("avcodec_find failed!"); } //2 解码器初始化 AVCodecContext *ac = avcodec_alloc_context3(acodec); avcodec_parameters_to_context(ac, ic->streams[audioStream]->codecpar); ac->thread_count = 1; //3 打开解码器 re = avcodec_open2(ac, 0, 0); if (re != 0) { LOGEW("avcodec_open2 audio failed!"); }
4 开始解码
以下是解码过程的完整代码
//1 定义Packet和Frame AVPacket *pkt = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); //用于测试性能 long long start = GetNowMs(); int frameCount = 0; //2 像素格式转换的上下文 SwsContext *vctx = NULL; int outwWidth = 1280; int outHeight = 720; char *rgb = new char[1920*1080*4]; char *pcm = new char[48000*4*2]; //3 音频重采样上下文初始化 SwrContext *actx = swr_alloc(); actx = swr_alloc_set_opts(actx, av_get_default_channel_layout(2), AV_SAMPLE_FMT_S16, ac->sample_rate, av_get_default_channel_layout(ac->channels), ac->sample_fmt,ac->sample_rate,0,0); re = swr_init(actx); if(re != 0) { LOGEW("swr_init failed!"); }else { LOGEW("swr_init success!"); } //4 显示窗口初始化 ANativeWindow *nwin = ANativeWindow_fromSurface(env,surface); ANativeWindow_setBuffersGeometry(nwin,outwWidth,outHeight,WINDOW_FORMAT_RGBA_8888); ANativeWindow_Buffer wbuf; for (;;) { //这里是测试每秒解码的帧数 每三秒解码多少帧 if(GetNowMs() - start >= 3000) { LOGEW("now decode fps is %d", frameCount/3); start = GetNowMs(); frameCount = 0; } int re = av_read_frame(ic, pkt); if (re != 0) { LOGEW("读取到结尾处!"); int pos = 20 * r2d(ic->streams[videoStream]->time_base); av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME); break; } AVCodecContext *cc = vc; if (pkt->stream_index == audioStream) { cc = ac; } //1 发送到线程中解码 re = avcodec_send_packet(cc, pkt); //清理 int p = pkt->pts; av_packet_unref(pkt); if (re != 0) { LOGEW("avcodec_send_packet failed!"); continue; } //每一帧可能对应多个帧数据,所以要遍历取 for (;;) { //2 解帧数据 re = avcodec_receive_frame(cc, frame); if (re != 0) { break; } //LOGEW("avcodec_receive_frame %lld", frame->pts); //如果是视频帧 if(cc == vc){ frameCount++; //3 初始化像素格式转换的上下文 vctx = sws_getCachedContext(vctx, frame->width, frame->height, (AVPixelFormat)frame->format, outwWidth, outHeight, AV_PIX_FMT_RGBA, SWS_FAST_BILINEAR, 0,0,0); if(!vctx){ LOGEW("sws_getCachedContext failed!"); }else { uint8_t *data[AV_NUM_DATA_POINTERS] = {0}; data[0] = (uint8_t *)rgb; int lines[AV_NUM_DATA_POINTERS] = {0}; lines[0] = outwWidth * 4; int h = sws_scale(vctx, (const uint8_t **)frame->data, frame->linesize, 0, frame->height, data, lines); LOGEW("sws_scale = %d",h); if(h > 0) { ANativeWindow_lock(nwin,&wbuf,0); uint8_t *dst = (uint8_t*)wbuf.bits; memcpy(dst, rgb, outwWidth*outHeight*4); ANativeWindow_unlockAndPost(nwin); } } }else //音频帧 { uint8_t *out[2] = {0}; out[0] = (uint8_t*)pcm; //音频重采样 int len = swr_convert(actx,out,frame->nb_samples,(const uint8_t**)frame->data,frame->nb_samples); LOGEW("swr_convert = %d", len); } } } delete rgb; delete pcm;
1 这段代码中,外层循环通过av_read_frame方法来给packet赋值,因为一个packet可能对应多个frame,所以packet每次通过avcodec_send_packet()方法发送到解码线程后,需要多次调用avcodec_receive_frame()来获取frame。
2 测试性能部分通过每三秒解码的帧数,除以3来计算平均每秒解码的帧数。
下面是获取当前时间的方法long long GetNowMs() { struct timeval tv; gettimeofday(&tv,NULL); int sec = tv.tv_sec%360000; long long t = sec*1000+tv.tv_usec/1000; return t; }
3 对于获取播放时间戳pts
在ffmpeg中用的是分数的时间基AVRational来表示时间的基本单位。
AVRational有两个变量分子和分母typedef struct AVRational{ int num; ///< Numerator int den; ///< Denominator } AVRational;
例如时间基为1/1000。
通过转换可以把此分数转换为浮点数static double r2d(AVRational rational) { return rational.num == 0 || rational.den == 0 ? 0.:(double)rational.num/ (double)rational.den; }
这样就能算出每一帧对应的时间戳pts
pkt->pts = pkt->pts * (1000*r2d(ic->streams[pkt->stream_index]->time_base));
获取解码时间戳dts同理
5 硬解码和多线层解码性能测试
1 单线程解码平局速度
now decode fps is 18 now decode fps is 18 now decode fps is 19
2 六线程解码均速
now decode fps is 105
3 硬解码均速
now decode fps is 95 now decode fps is 92
-
Android显示Unity视图
2015-05-04 11:56:26这次遇到的问题纠结我好久,关键是对...用Unity制作Android上的悬浮窗口,窗口背景透明,用Unity4.2很简单,只要把摄像机的Clear Flags属性设置成Solid Color,然后BackGround属性的RGBA均设置为零, 然后再在Andro第一次写blog,呵呵,有点激动,主要是方便自己以后复习,写的不好请见谅。。。
这次遇到的问题纠结我好久,关键是对Android不熟悉,下面我来说一下我的问题:
用Unity制作Android上的悬浮窗口,窗口背景透明,用Unity4.2很简单,只要把摄像机的Clear Flags属性设置成Solid Color,然后BackGround属性的RGBA均设置为零,
然后再在Android里设置一下背景透明就可以了。
Android的设置:
private LinearLayout view; private GLSurfaceView mUnityView; public FloatWindowSmallView(Context context) { super(context); mContext = context; windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(R.layout.float_window_small, this); view = (LinearLayout) findViewById(R.id.small_window_layout); viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; LayoutParams lp = new LayoutParams (LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
但是Unity4.6这样设置就不行,背景是一片黑色://主要是这里 mUnityView = new GLSurfaceView(Common.application); mUnityView.setEGLContextClientVersion(2); mUnityView.setZOrderOnTop(true); mUnityView.setZOrderMediaOverlay(true); mUnityView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); mUnityView.setRenderer(Common.mUnityPlayer); mUnityView.getHolder().setFormat(PixelFormat.TRANSLUCENT); view.addView(mUnityView,0,lp); Common.mUnityPlayer.requestFocus(); Common.mUnityPlayer.resume(); //TextView percentView = (TextView) findViewById(R.id.percent); //percentView.setText(MyWindowManager.getUsedPercentValue(context)); mLongPressRunnable = new Runnable() { @Override public void run() { //performLongClick(); openBigWindow(); } }; }
所以换了个思路,将Unity视图输出成一个纹理,见纹理编译成bity[]传给Android,Android再解析成Imageview显示出来。
1、将摄像机视图输出成纹理:新建一个Render Texture ,将其拖给摄像机的Target Texture
2、将Render Texture 转换成byte[]
this.jc = new AndroidJavaClass("com.gesture.util.ToUnity");
放在Update每桢更新
#if UNITY_ANDROID && !UNITY_EDITOR byte[] bts = UnityView(); this.jc.CallStatic("picture", bts); #endif
public byte[] UnityView() { Texture2D myTexture2D = new Texture2D(pic.width,pic.height); RenderTexture.active = pic; myTexture2D.ReadPixels(new Rect(0, 0, pic.width, pic.height), 0, 0); myTexture2D.Apply(); RenderTexture.active = null; byte[] bytes = myTexture2D.EncodeToPNG(); Destroy (myTexture2D); return bytes; }
3、Android的设置
XML配置文档:
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/small_window_layout" android:layout_width="120dp" android:layout_height="120dp" > <ImageView android:id="@+id/image_view" android:layout_width="119dp" android:layout_height="119dp"/> </LinearLayout>
<pre name="code" class="java">private LinearLayout view; private static ImageView image;
public FloatWindowSmallView(Context context) { super(context); windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(R.layout.float_window_small, this); view = (LinearLayout) findViewById(R.id.small_window_layout); viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; image = (ImageView) findViewById(R.id.image_view); view.addView(Common.mUnityPlayer.getView()); Common.mUnityPlayer.requestFocus(); Common.mUnityPlayer.resume(); }
//把传输过来的字节流转换成Bitmap public static Bitmap getBitmaoByStream(byte[] imageByte) { if (imageByte != null) { ByteArrayInputStream byteImage = new ByteArrayInputStream (imageByte); return BitmapFactory.decodeStream(byteImage); } else { return null; } } //更新Imageview public static void ShowTexture(byte[] b) { Bitmap bitmap = getBitmaoByStream(b); image.setImageBitmap(bitmap); }
4、Unity端调用Android端方法
Unity端每桢调用此方法,来刷新ImageView来显示Unity视图,达成效果!!!//显示unity中Render Texture的内容, 供unity调用 public static void picture(final byte[] b) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { FloatWindowSmallView.ShowTexture(b); } }); }
但是目前还有一点小问题待解决,就是ImageView不能完全覆盖LinearLayout,否则Unity不运行,还有就是运行效率问题待提高,目前就这样,后续再更新。。。
-
Android Activity显示为32位颜色
2012-03-05 13:58:46getWindow().setFormat(PixelFormat.RGBA_8888);getWindow().setFormat(PixelFormat.RGBA_8888);
-
web页面在android上显示bug处理
2015-09-09 18:14:241.背景溢出(尤其是圆角时radius) background-clip: padding-box; 加入此样式即可 2.点击页面元素,随着焦点出现...-webkit-tap-highlight-color:transparent ...-webkit-tap-highlight-color:rgba(0,0,0,0); -web1.背景溢出(尤其是圆角时radius)
background-clip: padding-box;
加入此样式即可
2.点击页面元素,随着焦点出现阴影
-webkit-tap-highlight-color:transparent
或者借助伪类:
input:focus{}
-webkit-tap-highlight-color:rgba(0,0,0,0);
-webkit-user-modify:read-write-plaintext-only;或者:
outline:none;border:none
-
android 渐变效果的png失真 getWindow().setFormat(PixelFormat.RGBA_8888);
2014-05-15 18:27:23最近一个困扰很久的问题,渐变效果的... 在网上google了一下似乎这个问题很多人遇到,找到一种解释是Android设备display默认是采用16-bits color palette来表示所有颜色,因此对于带alpha值的32位png图片会出现显示... -
Android PNG渐变背景图片失真问题 getWindow().setFormat(PixelFormat.RGBA_8888);
2016-03-10 10:45:00最近一个困扰很久的问题,渐变效果的...在网上google了一下似乎这个问题很多人遇到,找到一种解释是Android设备display默认是采用16-bits color palette来表示所有颜色,因此对于带alpha值的32位png图片会出现显示失... -
$ionicActionSheet 在android 手机中显示错乱的博客
2016-09-07 18:08:47文章参考 http://blog.5ibc.net/p/14000.html .platform-android .action-sheet-backdrop.active { background-color: rgba(0, 0, 0, 0.2); } .platform-android .action-sheet { margin-left: 8px; -
Android基于OpenCV通过JNI识别并显示人脸位置
2020-04-14 17:47:12Android基于OpenCV通过JNI识别并显示人脸位置 设计思路 代码设计说明 效果如下 代码结构如下 JNI识别人脸并画区域代码如下 通过ANativeWindow显示RGBA数据到surface代码如下 将RGA数据填充到ANativeWindow_Buffer... -
微信小程序:解决Android真机调试box-shadow不显示问题
2019-05-03 23:35:58把16进制颜色代码修改成rgba格式即可。 -
Android FFmpeg YUV转RGB及使用SurfaceView+ANativeWindow显示
2019-05-31 20:19:021、FFmpeg像素格式转换 FFmpeg像素转换一般使用libswscale来进行 接口说明 1、 获取上下文SwsContext 一般我们使用下面两个函数来获取,sws_...前三个参数分变为原始宽、高、格式(如RGBA8888,YUV420等... -
Android YUV
2017-12-18 15:18:35Android 开发中,当得到一张yuv图需要显示时,之前的做法是利用ffmpeg自带的方法将其转换为RGB565或者RGBA,然后将RGB数据拷贝到aNativeWindow的图像缓冲区,达到显示的目的。这样做比较耗CPU, 最近在阅读ijkplayer... -
android在悬浮窗循环显示时间,过一段时间后卡死?
2017-08-04 12:38:44lp.format = PixelFormat.RGBA_8888; manager.addView(floatView, lp); new TimeThread().start(); floatView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch... -
关于Unity3d 调用Android jar 中的 悬浮框 没有显示的问题
2016-03-17 11:36:44mParams.format = PixelFormat.RGBA_8888; mParams.gravity = Gravity.TOP | Gravity.LEFT; mParams.x = 0;// 相对于屏幕原点的x轴距离 mParams.y = 0;// 相对于屏幕原点的y轴距离 System.out.println("--... -
opencv4android开发获取摄像头帧数据处理后返回屏幕显示问题
2015-08-31 01:50:11我在JNI中用C++的opencv对图片进行了处理,就是在人脸上画了个框,但是传回Java再显示就没有框了(用同样的方法进行单张的图片处理就没问题),请问这是为什么以及该如何解决。部分代码如下: Java代码: ``` ... -
android写了一个悬浮窗,但是输入法显示不出来了,希望能得到朋友们的帮助,谢谢了。
2013-12-13 12:54:06用android编写了悬浮窗,项目是用Unity3d做的,项目中的输入法软键盘无法显示了,能接收到按键,但是软键盘看不到。 windowParams的参数如下,主要的问题在flags windowParams.type = LayoutParams.TYPE_PHONE; ... -
Android raw to bmp
2015-06-08 15:16:00raw 保存的为裸数据,转换时都需要把它转成RGBA 的方式来显示。其中: 8位RAW: 四位RGBA 来表示一位灰度; 24位RAW: 三位RGB相同,A(alpha)用0XFF表示。 Bitmap.Config 枚举说明: 位图位数越高... -
Android下基于UVC的UsbCam的源码
2018-12-20 18:41:09Android下基于UVC的UsbCam的源码,图像采集使用的是V4L2,图像处理YUV->RGBA使用的FFmpeg,图像显示使用的GLSurfaceView -
Android色彩矩阵——ColorMatrix
2016-09-21 14:05:35在Android的开发中,我们少不了对图片进行处理,其中最常使用的数据结构就是位图Bitmap,它包含了一张图片的所有数据。...在Android中图片是以RGBA像素点的形式加载到内存中的,修改这些像素信息需 -
android背景色改为透明色
2015-07-28 11:11:56如何设置半透明、透明效果?透明色和RGB是两码事,透明色不能够用RGB表示,只能用...如显示背景为半透明android:backgroound=“#e0000000”,全透明为android:backgroound=“#00000000”,表达顺序是是“aabbggrr”。 -
Android PNG渐变背景图片失真问题
2012-05-17 14:51:12是Android设备display默认是采用16-bits color palette来表示所有颜色,因此对于带alpha值的32位png图片会出现显示失真 在设置需要显示Activity的PixelFormat, getWindow().setFormat(PixelFormat.RGBA_8888)... -
android-图像处理--ColorMatrix
2017-12-30 12:50:42在Android中,对Bitmap进行颜色方面的处理可以通过ColorMatrix类来实现。...它们共同决定了每个像素点显示的颜色,而ColorMatrix可以很好的支持RGBA的操作。 1、ColorMatrix介绍 源码上是这样解释的: 意思是 -
Android中的图像处理
2018-01-30 11:53:33图像处理之---通过改变图像的色调/色相,饱和度和亮度来改变图像的显示效果。 色光三元素中的三个概念: 色调/色相:指的是物体传递的颜色 饱和度:指的是颜色的纯度,从0(灰)到100%(饱和)来进行描述 ... -
视频学习笔记:Android OpenGL渲染YUV420P图像
2017-06-26 19:43:03背景Android 开发中,当得到一张yuv图需要显示时,之前的做法是利用ffmpeg自带的方法将其转换为RGB565或者RGBA,然后将RGB数据拷贝到aNativeWindow的图像缓冲区,达到显示的目的。这样做比较耗CPU, 最近在阅读... -
android 超出部分怎么不隐藏
2021-01-06 03:47:38在ios上 tab超出部分被显示了出来,android上就隐藏了,请问怎么办? <p>RN 0.48.2 ` { tabBarItems.map((tabBarItem, i) => { let ViewController = tabBarItem.... -
Android OpenGL添加颜色
2011-10-20 17:07:40前面的例子显示的正方形都是白色,看其来不是很吸引人,本篇介绍如何给Mesh(网格)添加颜色。OpenGL ES使用颜色是我们熟知的RGBA模式(红,绿,蓝,透明度)。 颜色的定义通常使用Hex格式0xFF00FF 或十进制格式... -
【Android】屏幕尺寸的表示
2019-08-07 16:24:23- px(pixel): 普通的像素点描述,位图中的一个颜色点(RGBA或者YUV) - dip(device independent pixels): 设备独立像素。 - dp: 与dip一样。 - pt(point): 标准长度单位。1pt = 1.0 / 72 (in)。 - sp(scaled ... -
Android OpenGL ES 简明开发教程五:添加颜色
2018-11-28 09:29:52前面的例子显示的正方形都是白色,看其来不是很吸引人,本篇介绍如何给Mesh(网格)添加颜色。OpenGL ES使用颜色是我们熟知的RGBA模式(红,绿,蓝,透明度)。 颜色的定义通常使用Hex格式0xFF00FF 或十进制格式...
-
单片机完全学习课程全五季套餐
-
计算机网络基础
-
另类创意设计网页模板
-
PFX软证书(私钥证书)下载及导出方法(请务必交给商户技术部门处理)
-
2021-01-22
-
2021最新Kubernetes(k8s)集群实战精讲
-
opencv和numpy以及pillow处理图片时数据各个维度的布局
-
ssm框架整合的第五个阶段:测试了mybats-plus的cuid功能,添加@DeleteMapping、@PostMapping等注解 完成了springmv
-
Android 性能优化一: 16.Android Performance Patterns- Understanding Overdraw.mp4
-
如何做好程序员 czhztjz
-
C++异步串口通信
-
【数据分析-随到随学】Spark理论及实战
-
交换机环路检测命令.txt
-
遗传育种学研究网页模板
-
检测特殊字符是什么类型
-
PW2300S3.pdf
-
PW2309-2.0.pdf
-
Selenium3分布式与虚拟化
-
阿里云RDS数据库突然无法操作,排查后发现死锁
-
Java无损导出及转换word文档