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

先上图:
视频壁纸

相机壁纸

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

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

public class VideoLiveWallpaper extends WallpaperService {// 实现WallpaperService必须实现的抽象方法  public Engine onCreateEngine() {return new VideoEngine();}class VideoEngine extends Engine {@Overridepublic 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;@Overridepublic void onCreate(SurfaceHolder surfaceHolder) {super.onCreate(surfaceHolder);startPreview();// 设置处理触摸事件  setTouchEventsEnabled(true);}@Overridepublic void onTouchEvent(MotionEvent event) {super.onTouchEvent(event); }@Overridepublic void onDestroy() {super.onDestroy();stopPreview();}@Overridepublic 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 {@Overridepublic 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 fileif (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 informationif (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGD("Couldn't find stream information.");return -1;}// Find the first video streamint 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 streamAVCodecContext *pCodecCtx = pFormatCtx->streams[videoStream]->codec;// Find the decoder for the video streamAVCodec *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 windowANativeWindow *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 frameAVFrame *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中数据就是用于渲染的,且格式为RGBAint 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 frameavcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);// 并不是decode一次就可解码出一帧if (frameFinished) {// lock native window bufferANativeWindow_lock(nativeWindow, &windowBuffer, 0);// 格式转换sws_scale(sws_ctx, (uint8_t const *const *) pFrame->data,pFrame->linesize, 0, pCodecCtx->height,pFrameRGBA->data, pFrameRGBA->linesize);// 获取strideuint8_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 frameav_free(pFrame);// Close the codecsavcodec_close(pCodecCtx);// Close the video fileavformat_close_input(&pFormatCtx);return 0;
}
}

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

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

  1. ffmpeg实战教程(十一)手把手教你实现直播功能,不依赖第三方SDK

    直播,2016最火的技术之一了,更多的关于直播的知识:http://blog.csdn.net/king1425/article/details/72489272 -这篇我们就不依赖任何集成好的SDK ...

  2. ffmpeg实战教程(八)Android平台下AVfilter 实现水印,滤镜等特效功能

    ffmpeg实战教程(八)Android平台下AVfilter 实现水印,滤镜等特效功能 ffmpeg实战教程(七)Android CMake avi解码后SurfaceView显示 本篇我们在此基础 ...

  3. ffmpeg实战教程(七)Android CMake avi解码后SurfaceView显示

    ffmpeg实战教程(七)Android CMake avi解码后SurfaceView显示 本篇我们实现Android平台解码avi并用SurfaceView播放. 先上图看效果: 思路:  1.把 ...

  4. ffmpeg实战教程(六)Android CMake实现解码(MP4转YUV)

    ffmpeg实战教程(六)Android CMake实现解码(MP4转YUV) 我们将使用最新版: 最新版ffmpeg ffmpeg3.3  新版Android studio Android stud ...

  5. ffmpeg实战教程(三)音频PCM采样为AAC,视频YUV编码为H264/HEVC

    ffmpeg实战教程(三)音频PCM采样为AAC,视频YUV编码为H264/HEVC https://blog.csdn.net/King1425/article/details/71180330 音 ...

  6. ffmpeg实战教程(二)用SDL播放YUV,并结合ffmpeg实现简易播放器

    ffmpeg实战教程(二)用SDL播放YUV,并结合ffmpeg实现简易播放器 https://blog.csdn.net/King1425/article/details/71171142 我们先实 ...

  7. ffmpeg实战教程(一)Mp4,mkv等格式解码为h264和yuv数据

    FFmpeg有非常强大的功能包括视频采集功能.视频格式转换.视频抓图.给视频加水印等.而网上对这些功能的使用大多是基于命令行的.这不利于我们深入学习定制化ffmpeg,今后我将写一系列的用代码实现这些 ...

  8. JavaWeb实战教程(Servlet+JSP+JavaBean)-雷伟-专题视频课程

    JavaWeb实战教程(Servlet+JSP+JavaBean)-1401人已学习 课程介绍         本课程主要介绍Jsp,Servlet基础知识,讲解Jsp+Servlet+JavaBea ...

  9. ffmpeg实战教程(十三)iJKPlayer源码简析

    要使用封装优化ijk就必须先了解ffmpeg,然后看ijk对ffmpeg的C层封装! 这是我看ijk源码时候的笔记,比较散乱.不喜勿喷~ ijk源码简析: 1.ijkplayer_jni.c 封装的播 ...

最新文章

  1. 小甲鱼Python笔记(下)
  2. js math.hypot_带有Python示例的math.hypot()方法
  3. TCP/IP系列——长连接与短连接的区别
  4. runtimeerror怎么解决python_如何解决这个python错误? RuntimeError:字典在迭代期间改变了大小...
  5. 扩展欧几里得算法(求逆元)总结
  6. 笔记本辐射与日常电器辐射对比
  7. 吉他的起源与发展史_吉他的由来与发展简史
  8. linux上mysql定时备份数据库数据_linux下如何实现mysql数据库每天自动备份定时备份...
  9. 骨传导耳机工作原理,骨传导耳机优缺点
  10. 仁兄:腾讯区块链学习后的一些粗浅观点
  11. Mathorcup数学建模竞赛第六届-【妈妈杯】B题:车位分布的优化设计与评价(附一等奖获奖论文和matlab代码)
  12. 机器学习之随机森林填补缺失值和众数填补缺失值
  13. (FAQ)VM log是做什么的,4 Way VM又是什么
  14. SimCSE论文及源码解读
  15. 谷歌机器学习规则要点简析:43条黄金法则
  16. WordPress 衍生产品 BuddyPress 年底发布
  17. vid2vid 代码调试+训练+测试(debug+train+test)(二)训练篇
  18. ubuntu 下安装AMD显卡驱动
  19. Cluster: FarthestFirst
  20. poi4.0及其以上判断单元格是否为空

热门文章

  1. Mac电脑隔空投递如何添加到菜单栏?
  2. oracle查看数据被谁删掉了,oracle数据文件被误删恢复
  3. OpenV2X Beihai版本线上分享会
  4. html大作业网页代码 html期末作业代码网页设计——食品网5页面模板 HTML+CSS+JavaScript 学生DW网页设计作业成品
  5. 解决mysql 修改密码报错
  6. Ubuntu 18.04安装显卡驱动
  7. mysql 车辆管理_PHP+Mysql车辆管理系统(车管所)
  8. 【Adams-Car】专栏目录汇总
  9. SSL P1597 石子合并问题 题目
  10. 郑州钢丝网骨架塑料复合管应用领域