ffmpeg实战教程(十)ffmpeg/camera实现最近很火的视频壁纸,相机壁纸

2023-05-16

本篇实现一个有意思的玩意儿,视频壁纸,相机壁纸
这玩意好像现在还都是国外版本,哈哈

先上图:
视频壁纸
这里写图片描述

相机壁纸
这里写图片描述

1.动态壁纸制作的知识:

每一个动态壁纸都继承自WallpaperService,其中必须实现的抽象方法onCreateEngine,返回一个Engine对象,实际上所有的绘图与刷新都是由engine完成。如下

public class VideoLiveWallpaper extends WallpaperService {
    // 实现WallpaperService必须实现的抽象方法  
    public Engine onCreateEngine() {
        return new VideoEngine();
    }



    class VideoEngine extends Engine {


        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);

            // 设置处理触摸事件  
            setTouchEventsEnabled(true);

        }
          }

        }

必须在清单文件中进行一些配置,比如:

<!-- 配置动态壁纸Service -->
<service android:label="@string/app_name"
    android:name=".LiveWallpaper"
    android:permission="android.permission.BIND_WALLPAPER">
    <!-- 为动态壁纸配置intent-filter -->
    <intent-filter>
        <action android:name="android.service.wallpaper.WallpaperService" />
    </intent-filter>
    <!-- 为动态壁纸配置meta-data -->
    <meta-data android:name="android.service.wallpaper"
        android:resource="@xml/livewallpaper" />
</service>
 比较重要的部分首先是权限android:permission=”android.permission.BIND_WALLPAPER”;
   其次service需要响应action:android:name=”android.service.wallpaper.WallpaperService;
 接下来接收配置文件。首先在res文件夹下建立一个xml目录,和写appwidget一样。在目录下我们创建一个xml文件:
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"  
    android:settingsActivity="LiveWallPreference"  
    android:thumbnail="@drawable/ic_launcher"  
    android:description="@string/wallpaper_description"  
    />

然后启动选择壁纸的代码是这样的:

 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
        Intent chooser = Intent.createChooser(pickWallpaper, getString(R.string.choose_wallpaper));
        startActivity(chooser);

2.相机壁纸:

下面是相机壁纸实现的源码

最精华的一句: camera.setPreviewDisplay(getSurfaceHolder());
直接把相机预览数据传给WallpaperService。

package com.ws.ffmpegandroidwallpaper;

import android.hardware.Camera;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

import java.io.IOException;

public class CameraLiveWallpaper extends WallpaperService {

    public Engine onCreateEngine() {
        return new CameraEngine();
    }


    class CameraEngine extends Engine  {
        private Camera camera;

        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);

            startPreview();
            // 设置处理触摸事件  
            setTouchEventsEnabled(true);

        }

        @Override
        public void onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event); 
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            stopPreview();
        }

        @Override
        public void onVisibilityChanged(boolean visible) {
            if (visible) {
                startPreview();
            } else {
                stopPreview();
            }
        }

        /**
         * 开始预览
         */
        public void startPreview() {
            camera = Camera.open();
            camera.setDisplayOrientation(90);

            try {
                camera.setPreviewDisplay(getSurfaceHolder());
            } catch (IOException e) {
                e.printStackTrace();
            }
            camera.startPreview();

        }

        /**
         * 停止预览
         */
        public void stopPreview() {
            if (camera != null) {
                try {
                    camera.stopPreview();
                    camera.setPreviewCallback(null);
                    // camera.lock();
                    camera.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                camera = null;
            }
        }
    }
}  

视频壁纸

实现视频壁纸的时候本来打算用mediaplayer实现,后来发现mediaplayer实现在某些机型上报JNI层错误。
于是改用ffmpeg自己实现JNI层,当然这样做的好处是可以更多的定制化,比如示例上的快速播放视频。

主要就一个函数
即把WallpaperService 的Surface传给native的play方法。

package com.ws.ffmpegandroidwallpaper;

import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class VideoLiveWallpaper extends WallpaperService {
    // 实现WallpaperService必须实现的抽象方法  
    public Engine onCreateEngine() {
        return new VideoEngine();
    }
    class VideoEngine extends Engine {


        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);

                    play(getSurfaceHolder().getSurface());
            // 设置处理触摸事件  
            setTouchEventsEnabled(true);

        }

    static {
        System.loadLibrary("native-lib");
    }
    public native int play(Object surface);
}

然后JNI的play方法具体实现。

关键地方都有注释,可以结合我之前分享的ffmpeg源码看
ffmpeg源码简析(一)结构总览 :http://blog.csdn.net/king1425/article/details/70597642


JNIEXPORT jint JNICALL
Java_com_ws_ffmpegandroidwallpaper_VideoLiveWallpaper_play
        (JNIEnv *env, jclass clazz, jobject surface) {
    LOGD("play");

    // sd卡中的视频文件地址,可自行修改或者通过jni传入
    //char *file_name = "/storage/emulated/0/ws.mp4";
    char *file_name = "/storage/emulated/0/video.avi";

    av_register_all();

    AVFormatContext *pFormatCtx = avformat_alloc_context();

    // Open video file
    if (avformat_open_input(&pFormatCtx, file_name, NULL, NULL) != 0) {

        LOGD("Couldn't open file:%s\n", file_name);
        return -1; // Couldn't open file
    }

    // Retrieve stream information
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGD("Couldn't find stream information.");
        return -1;
    }

    // Find the first video stream
    int videoStream = -1, i;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO
            && videoStream < 0) {
            videoStream = i;
        }
    }
    if (videoStream == -1) {
        LOGD("Didn't find a video stream.");
        return -1; // Didn't find a video stream
    }

    // Get a pointer to the codec context for the video stream
    AVCodecContext *pCodecCtx = pFormatCtx->streams[videoStream]->codec;

    // Find the decoder for the video stream
    AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        LOGD("Codec not found.");
        return -1; // Codec not found
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        LOGD("Could not open codec.");
        return -1; // Could not open codec
    }

    // 获取native window
    ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);

    // 获取视频宽高
    int videoWidth = pCodecCtx->width;
    int videoHeight = pCodecCtx->height;

    // 设置native window的buffer大小,可自动拉伸
    ANativeWindow_setBuffersGeometry(nativeWindow, videoWidth, videoHeight,
                                     WINDOW_FORMAT_RGBA_8888);
    ANativeWindow_Buffer windowBuffer;

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        LOGD("Could not open codec.");
        return -1; // Could not open codec
    }

    // Allocate video frame
    AVFrame *pFrame = av_frame_alloc();

    // 用于渲染
    AVFrame *pFrameRGBA = av_frame_alloc();
    if (pFrameRGBA == NULL || pFrame == NULL) {
        LOGD("Could not allocate video frame.");
        return -1;
    }

    // Determine required buffer size and allocate buffer
    // buffer中数据就是用于渲染的,且格式为RGBA
    int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, pCodecCtx->width, pCodecCtx->height,
                                            1);
    uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    av_image_fill_arrays(pFrameRGBA->data, pFrameRGBA->linesize, buffer, AV_PIX_FMT_RGBA,
                         pCodecCtx->width, pCodecCtx->height, 1);

    // 由于解码出来的帧格式不是RGBA的,在渲染之前需要进行格式转换
    struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width,
                                                pCodecCtx->height,
                                                pCodecCtx->pix_fmt,
                                                pCodecCtx->width,
                                                pCodecCtx->height,
                                                AV_PIX_FMT_RGBA,
                                                SWS_BILINEAR,
                                                NULL,
                                                NULL,
                                                NULL);

    int frameFinished;
    AVPacket packet;
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        // Is this a packet from the video stream?
        if (packet.stream_index == videoStream) {

            // Decode video frame
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

            // 并不是decode一次就可解码出一帧
            if (frameFinished) {

                // lock native window buffer
                ANativeWindow_lock(nativeWindow, &windowBuffer, 0);

                // 格式转换
                sws_scale(sws_ctx, (uint8_t const *const *) pFrame->data,
                          pFrame->linesize, 0, pCodecCtx->height,
                          pFrameRGBA->data, pFrameRGBA->linesize);

                // 获取stride
                uint8_t *dst = (uint8_t *) windowBuffer.bits;
                int dstStride = windowBuffer.stride * 4;
                uint8_t *src = (pFrameRGBA->data[0]);
                int srcStride = pFrameRGBA->linesize[0];

                // 由于window的stride和帧的stride不同,因此需要逐行复制
                int h;
                for (h = 0; h < videoHeight; h++) {
                    memcpy(dst + h * dstStride, src + h * srcStride, srcStride);
                }

                ANativeWindow_unlockAndPost(nativeWindow);
            }

        }
        av_packet_unref(&packet);
    }

    av_free(buffer);
    av_free(pFrameRGBA);

    // Free the YUV frame
    av_free(pFrame);

    // Close the codecs
    avcodec_close(pCodecCtx);

    // Close the video file
    avformat_close_input(&pFormatCtx);
    return 0;
}
}

demo :https://github.com/WangShuo1143368701/FFmpegAndroid/tree/master/ffmpegandroidwallpaper

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ffmpeg实战教程(十)ffmpeg/camera实现最近很火的视频壁纸,相机壁纸 的相关文章

  • 使用树莓派学习PostgreSQL

    知乎上刷到一个口水仗 xff0c 大意是mysql和postgresql哪个好 这种问题 xff0c 正如Java和C 哪个好之类的 xff0c 我一向都是当八卦浏览的 xff0c 只看不参与 xff0c 又不是豆花该吃甜还是该吃咸这种原则
  • 使用树莓派学习PostgreSQL(二):通过MSSQL Linked Server导入数据

    作为曾经呆过证券公司营业部的从业未遂人员 xff0c 兼十年老韭菜 xff0c 对我大A还是有蜜汁感情的 看着树莓派里空空荡荡的PGSQL xff0c 突发奇想 xff0c 尝试把今天的A股数据导进来 xff1f 之前写过一个基于MSSQL
  • 试图在WinPE下用cmd.exe调包sethc.exe/utilman.exe/osk.exe来重置管理员密码,但是无法调出命令行

    忘记windows密码 xff0c 最简单的办法就是使用自带账号工具的第三方winpe xff0c 直接重置密码完事儿 xff08 但如果有基于该账号加密的东西那就回不来了 xff09 xff1b 如果手头没有这样的pe盘也没关系 xff0
  • 树莓派:在VSCode中使用C#开发.Net软件(console)

    树莓派官方桌面系统支持VSCode xff0c 我们也可以在树莓派上使用C 开发 Net软件 本文作为一个笔记简单记录 1 安装VSCode xff1a 首先打开首选项中的Recommended Software xff0c 找到Progr
  • Debian DNS完整配置

    DNS服务 执行下面的命令安装 apt install y bind9 dnsutils 这些文件分别对应的作用如下 xff1a db 127 反向区域数据库 xff0c 用于将ip解析为对应的域名 db local 正向区域数据库 xff
  • 树莓派4B:跑通Paddle-Lite-Demo

    上周跟以前单位的大神见面 xff0c 期间大神聊到了百度飞桨框架 xff0c 并建议我看看能不能做点什么有意思的东西跑在树莓派上 大神就是大神 xff0c AI是我等学渣也配玩的吗 不过既然大神都这么推荐了那就尝试一下 xff0c 大不了从
  • 在NUC8上折腾安装Windows Server 2019

    整理我的吃灰物资时意外发现还有个八代的NUC迷你主机 想着现在内存和SSD都不贵 xff0c 干脆升级了然后装个Windows Server 2019玩玩 xff0c 跑个Hyper V做些实验 于是买了2根16G 2400的内存和1条1T
  • Ubuntu上跑通PaddleOCR

    书接上文 刚才说到我已经在NUC8里灌上了Windows Server 2019 接下来也顺利的启用了Hyper V角色并装好了一台Ubuntu 22 04 LTS 的虚机 由于自从上回在树莓派上跑通了Paddle Lite Demo之后想
  • 树莓派4:跑通Tensorflow的Sequential模型用于图片分类

    重要提示 xff1a 由于树莓派相对孱弱的性能 xff0c 直接在其上训练模型可能花 xff08 lang4 xff09 费非常长的时间 本文仅作为示例性的可行性参考 xff0c 请酌情考虑实验平台 著名的Tensorflow框架也可以运行
  • C# + .Net6 实现TensorFlow图片分类

    微软官网上发现一篇很有意思的文档 xff1a 教程 xff1a 用于对图像进行分类的 ML NET 分类模型 ML NET Microsoft Learn 这篇教程写的很学院派 xff0c 但有点碎 xff0c 属于上课不能打一秒钟瞌睡的那
  • 如何用一个废旧的笔记本打造一个家庭网络服务器?

    本来好好的笔记本的 xff0c 可偏偏屏幕说烂就烂 xff0c 那就干脆不要了 xff0c 改造改造吧 xff01 他本来长这样的 xff0c 如图 xff1a 什么 xff0c 你看见了水印 老哥 xff0c 别在意这些细节 简单说一下配
  • 基于Swing与MySQL之货物仓库管理系统(可为Java大作业、甚至毕业设计)

    前言 xff1a 该系统总代码行数约2100行 xff0c 采用技术栈为Swing框架与MySQL xff0c 编码环境为Eclipse 实现的功能有 xff1a 用户注册 登陆 xff1b 货物入仓 货物出仓 xff0c 货物信息更新与货
  • 使用MySQL WorkBench 如何导出数据库(包括数据)

    1 xff1a 打开MySQL WorkBench后 xff0c 选中Server xff0c Data Export 2 xff1a 在红色圈1内选中我们要导出的数据库 红色圈2是导出是否包含数据 xff1b 红色圈3是触发器等导出选项
  • Swift 如何生成随机数

    1 生成Int类型随机数 span class token keyword var span numberOne span class token punctuation span span class token builtin Int
  • Failed to start remote service “com.apple.debugserver“ on device.

    今天用真机调试时报出 Failed to start remote service com apple debugserver on device 百思不得解 遂关机重启 xff0c 果然运行成功 猜测可能是真机长时间没关机 xff0c 一
  • debian静态路由

    实验环境 xff1a 三台Debian最小化系统 xff0c 物理机是两张不同的网卡 第一台的第一张vm1网卡 192 168 10 1 24 第二台的第一张vm1网卡 192 168 10 254 24 第二台的第二张vm2网卡 192
  • 行列式与矩阵

    说明 xff1a 本公式只针对在二维或三通道的计算机视觉中所遇到的问题 xff0c 不代表传统意义上数学知识点范围 行列式 行列式概念 矩阵的行列式 xff0c 称之为det xff0c 是基于矩阵所包含的行列数据计算得到的标量 本质上是一
  • 基于SpringBoot与iOS(Swift)的电商平台设计

    技术栈 xff1a Server SpringBootSpring Data JPARedis简易分词 Client MVC架构SnpakitAlamofireSwiftyJSONkingfser xff08 过小的第三方库不阐述 xff0
  • 推荐系统 Mahout入门之简单使用

    Mahout xff1a Apache Mahout 是 Apache Software Foundation xff08 ASF xff09 旗下的一个开源项目 xff0c 提供一些可扩展的机器学习领域经典算法的实现 xff0c 旨在帮助
  • Command CompileSwiftSources failed with a nonzero exit code

    报错 xff1a Command CompileSwiftSources failed with a nonzero exit code 原因 xff1a 可能是项目目录下存在 同名不同文件夹的文件 解决方法 xff1a 检查 同名不同文件

随机推荐

  • Flutter 自定义SwitchButton

    效果 xff1a xff08 AppBar中间的SwitchButton xff09 代码 xff1a span class token keyword import span span class token string literal
  • diff算法swift版

    前几天用chatgpt生成diff算法 xff0c 配上swift的简洁语法 xff0c 可以称之为锦上添花 下面是代码 xff1a span class token keyword func span span class token f
  • Flutter 调用摄像头扫描二维码插件

    原生平台如果想要实现二维码扫描很简单 xff0c 例如iOS可以根据Version库或者AVFoudation库很快定制出相应工具来 xff0c Flutter只能依赖第三方插件了 插件依赖 xff1a span class token k
  • 【Linux】搭建Yapi接口管理工具

    在年前员工访谈时发现 xff0c 前后端协作开发会出现一定的内耗现象 xff0c 在当前前后端分离的情况下前端往往会因为后端接口存在问题影响对接速度 xff0c 在接口设计不明确的情况下还需要反复确认和沟通 公司过往使用ApiManager
  • 树莓派3B安装VScode

    树莓派3B安装VScode sudo apt install code xff08 一句命令搞定 xff09 xff08 ps 本来想在树莓派3b上安装VScode 网上找半天 xff0c 结果又臭又长还不能解决问题 xff0c 直到翻了贼
  • Angular is running in the development mode. Call enableProdMode() to enable the production mode.

    控制台输出 xff1a Angular is running in the development mode Call enableProdMode to enable the production mode 解决方法 xff1a 在mai
  • Debian SSH免密登录

    安装服务后修改主配置文件 vim etc ssh sshd config 改这个配置文件 在第32行PermttRootLogin yes 允许root用户登录 systemctl restart sshd 然后再重启一下服务 修改端口号
  • HDFS编程实践(Hadoop3.1.3)

    一 利用Shell命令与HDFS进行交互 1 目录操作 在HDFS中为hadoop用户创建一个用户目录 span class token builtin class name cd span usr local hadoop bin hdf
  • 浅谈<<EOF作用与用法

    其实我对这个东西的用法理解也不深 xff0c 目前也只用过两次 xff0c 但真的可以解决大问题 先说一下 lt lt efo的作用 xff0c 官方的说法是这样的 xff1a 在shell脚本中 xff08 bash编程 xff09 xf
  • 斐讯 r1 起死回生

    此方法已无法使用 xff0c 请需要的网友留言 xff0c 并留邮箱 xff0c 我发新的方法 这里写不了外部连接 2022 7 2 昨天三哥给我这么个东西 xff0c 当初卖2499元 xff0c 因为公司没了 xff0c 现在只能当蓝牙
  • 深入学习 esp8266 wifimanager源码解析(打造专属自己的web配网)(最全的wifimanager介绍))

    原文地址 xff1a https my oschina net u 4269090 blog 3329239 1 前言 废话少说 xff0c 本篇博文的目的就是深入学习 WifiManager 这个github上非常火爆的ESP8266 w
  • 华为p40pro NFC模拟加密小区卡详细教程

    先说点题外话 xff1a 我们小区的门禁卡是加密的 xff0c 去年小区强制使用门禁卡 xff0c 配一个卡要30块 xff0c 我们家附近的所有配门禁卡的店一听说我们小区都说做不了 我对这个东西有一点了解 xff0c 小区不可能用智能卡只
  • 求助帖:如何获取局部变量

    最近在做一个获取和风天气城市 ID的小东西 xff0c 大致内容如下 xff1a 通过 xff1a QWeather getGeoCityLookup MainActivity this inputct new QWeather OnRes
  • arduino esp-01s开发环境配置(备忘)

    很久没玩arduino了 xff0c 前天一个网友提了一个问题要我帮忙 xff0c 结果电脑重新做了系统 xff0c 又要重新设置环境 xff0c 结果忘记了 xff0c 做个备忘 xff0c 省得以后又要重新研究 1 附加开发板管理器网址
  • crontab设置定时重启Apache服务

    重启Apache方法 创建一个新的crontab文件 crontab rootcron root用户设定服务 同时 xff0c 文件副本在 var spool cron目录中 列出crontab文件 每天4点重启 crontab l 0 4
  • PVE安装画面灰白只显示鼠标解决方案

    PVE安装画面灰白只显示鼠标解决方案 最近喜欢到某鱼上折腾一下工控机主板回来搞PVE xff0c 发现不少小伙伴遇到与我一样的问题 xff0c 网上搜索文章 xff0c 并没有很完整 xff0c 于是整理了一下 出现这种情况一般是PVE5
  • 解决java poi之SXSSFWorkbook导出大数据量时自适应列宽效果异常问题

    场景 xff1a 使用SXSSFWorkbook导出大数据量 百万行 excel报表 xff0c 表格样式统一使用自适应列宽时 xff0c 发现导出表头行格式异常 xff0c 异常情况如下图所示 xff1a 分析 xff1a 从导出效果看
  • Win11基于WSL2安装CUDA、cuDNN和TensorRT(2023-03-01)

    文章目录 2023 03 06 更新2023 03 05 更新前言TensorRT介绍环境配置CUDA Driver检查安装 安装CUDA安装nvcc安装cuDNN安装验证 安装TensorRT安装验证 2023 03 06 更新 如果有小
  • python OCR Tesseract 训练

    Ps xff1a Tesseract识别英文和字母效果好 中文的话 xff0c 虽然有训练数据也可以识别 xff0c 但是效果不是很好 Tesseract的安装和使用 xff1a 1 首先用 pip 下载包 pip install tess
  • ffmpeg实战教程(十)ffmpeg/camera实现最近很火的视频壁纸,相机壁纸

    本篇实现一个有意思的玩意儿 xff0c 视频壁纸 xff0c 相机壁纸 这玩意好像现在还都是国外版本 xff0c 哈哈 先上图 xff1a 视频壁纸 相机壁纸 1 动态壁纸制作的知识 xff1a 每一个动态壁纸都继承自WallpaperSe