Android视频缩略图(二)

Android视频缩略图(二)

上篇文章[Android视频图片缩略图的获取](http://blog.csdn.net/mr_dsw/article/details/48521333),我们使用ThumbnailUtils工具类进行图片和视频的缩略图获取,我们提到了

  • android.provider.MediaStore.Images.Thumbnails
  • android.provider.MediaStore.Video.Thumbnails
  • MediaMetadataRetriever

这三个玩意,但是没具体说,只是在ThumbnailUtils的源码中见到了他们的影子。其实,深入了解的原因来源于:项目中,我们拍摄的照片和视频上传后,那么我们再次获取视频的时候,视频是在服务器,我们不可能将视频下载下来,然后仅仅为了获取一个缩略图使用的,这样岂不是很坑。所以就需要深入的学习下如何获取视频缩略图。

上篇文章中,我们在分析ThumbnailUtils.createVideoThumbnail方法的源码时,见到该方法的内部使用MediaMetadataRetriever对象进行视频缩略图的获取,好吧!终于发现,原来是MediaMetadataRetriever我们真正获取视频缩略图的处理类。下面让我们一起来看看MediaMetadataRetriever的真面目吧!

MediaMetadataRetriever

1、包路径:android.media.MediaMetadataRetriever
2、方法集合:

通过研究ThumbnailUtils.createVideoThumbnail源码中MediaMetadataRetriever的使用方法,我们可以依葫芦画瓢来使用MediaMetadataRetriever的用法。
简要介绍下核心方法:
1、setDataSource()
设置数据源的方法,设置数据源绝对路径,可以为本地路径也可以为网络url地址。但是要注意,我们要判断api的版本,因为setDataSource()方法在不同api版本中不同,所以在使用前应使用android.os.Build.VERSION.SDK_INT获取当前SDK版本。

MediaMetadataRetriever media = new MediaMetadataRetriever();
	if(SDKVersion >= 14){//根据不同的api等级进行调用方法
		media.setDataSource(info.srcPath,new HashMap<String, String>());
	}else{
		media.setDataSource(info.srcPath);
	}
	info.bitmap = media.getFrameAtTime();
复制代码

2、getFrameAtTime()
该方法就是用于获取我们的缩略图,我们可以获取指定时间的缩略图。注意可能返回为null,所以需要我们在使用时进行判断。

3、release()方法
使用后,进行释放。

简要看下源码:
1、setDataSource()方法

/**
     * Sets the data source (file pathname) to use. Call this
     * method before the rest of the methods in this class. This method may be
     * time-consuming.
     * 
     * @param path The path of the input media file.
     * @throws IllegalArgumentException If the path is invalid.
     */
    public void setDataSource(String path) throws IllegalArgumentException {
        FileInputStream is = null;
        try {
            is = new FileInputStream(path);
            FileDescriptor fd = is.getFD();
            setDataSource(fd, 0, 0x7ffffffffffffffL);
        } catch (FileNotFoundException fileEx) {
            throw new IllegalArgumentException();
        } catch (IOException ioEx) {
            throw new IllegalArgumentException();
        }

        try {
            if (is != null) {
                is.close();
            }
        } catch (Exception e) {}
    }
复制代码

源码中首先根据路径创建一个文件流,然后调用 setDataSource(FileDescriptor fd, long offset, long length)方法。我们看下这个方法的源码:

  public native void setDataSource(FileDescriptor fd, long offset, long length)
        throws IllegalArgumentException;
复制代码

发现这个方法是个native方法,关于native的介绍,大家可以google。同样对于别的几个setDataSource方法,底部也是调用固定的native方法去执行。

(2)、getFrameAtTime
源码:

扫描二维码关注公众号,回复: 13389048 查看本文章
/**
     * Call this method after setDataSource(). This method finds a
     * representative frame at any time position if possible,
     * and returns it as a bitmap. This is useful for generating a thumbnail
     * for an input data source. Call this method if one does not
     * care about where the frame is located; otherwise, please call
     * {@link #getFrameAtTime(long)} or {@link #getFrameAtTime(long, int)}
     *
     * @return A Bitmap containing a representative video frame, which
     *         can be null, if such a frame cannot be retrieved.
     *
     * @see #getFrameAtTime(long)
     * @see #getFrameAtTime(long, int)
     */
    public Bitmap getFrameAtTime() {
        return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
    }
复制代码

最底部还是调用

private native Bitmap _getFrameAtTime(long timeUs, int option);
复制代码

基本的介绍就是这么多,我们做个案例了解下,还是基于上篇文章的那个demo。我们只修改获取视频缩略图的部分。

private void getBitmapFromFile() {
		Bitmap bitmap = null;
		if(infor.type == 0){//若果是图片,即拍照
			//直接通过路径利用BitmapFactory来形成bitmap
			bitmap = BitmapFactory.decodeFile(infor.srcPath);
		}else if(infor.type == 1){//如果是视频,即拍摄视频
			//利用ThumnailUtils
			//bitmap = ThumbnailUtils.createVideoThumbnail(infor.srcPath, Images.Thumbnails.MINI_KIND);
			MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
			try{
				if(android.os.Build.VERSION.SDK_INT >= 14){
					metadataRetriever.setDataSource(infor.srcPath, new HashMap<String, String>());;
				}else{
					metadataRetriever.setDataSource(infor.srcPath);
				}
				bitmap = metadataRetriever.getFrameAtTime();
				infor.bitmap = bitmap;
			}catch(Exception ex){
				ex.printStackTrace();
			}finally{
				metadataRetriever.release();
			}
		}
复制代码

我们测试下,效果图:

medat

MediaMetadataRetriever的基本使用就是这么多,更多的研究,去看源码吧!

* android.provider.MediaStore.Images.Thumbnails * android.provider.MediaStore.Video.Thumbnails

为什么先说上一个,就是因为上一个简单,这两个货比较复杂,首先通过包名路径即可看出,这俩二货不是android.media包下的东西,而是属于android.provider下的东西。是不是很吃惊,竟然属于系统ContentProvider下的东西。思前想后,我们想象ContentProvider有什么用途呢?数据存储。莫非通过这俩货,我们可以获取手机里所有的图片or缩略图。好吧!我们暂且这样,我们先到手机的/data/data/com.android.providers.media目录下的数据库整出来看看。导出external和internal两个数据库。 如图: ![provider](https://img-blog.csdnimg.cn/img_convert/8b65e868e9efe7b62732bd79e355bd6d.png) ![databse](https://img-blog.csdnimg.cn/img_convert/4706be1388881c9b978985bb5be19af5.png)

我们通过SQLiteSpy打开数据库看看,我们查看thumbnails数据表,关于thumbnails表,百度词条有个描述可以看看。如图:

table

在这张表中,我们看到了众多的图片信息,参照一片文档说的‘表thumbnails和images通过thumbnails.image_id与images._id关联的,通过images的_id,就可以找出来’。但是我并没有发现有images这张表,不知道为什么?哪位大侠知道,麻烦留言指教。

通过上面的数据表展示,在结合对ContentProvider的分析,基本可以确定我们能通过ContentProvider这条路径获取手机里所有的图片。

源码简介:

打开MediaStore类的源码,我们可以看到定义的这两个变量:

public static final String AUTHORITY = "media";

    private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
复制代码

显然这就是为了我们ContentProvider的使用服务的。接着往下看,我们发现了在MediaStore类的内部定义了多个静态类,其中就包含我们的:

  • android.provider.MediaStore.Images
  • android.provider.MediaStore.Video

我们查看这两个静态内部类的内部结构: Images如下图:

images

Video如下图:

vids

我们发现在二者的内部,都包含一个Thumbnails静态类,这个类就是我们查询的通口。

thumbnaiimage

thumbnails_source

我们对源码就看这么多吧!也不贴了,有兴趣的可以自己到sdk下看源码,我们只了解下这几个类的组织结构关系吧!通过一些截图,看看类的说明和成员变量。下面我们就通过一个小实例来看看怎么用的吧!

案例还是基于上篇的demo,这次新增的地方有:我们新增一个媒体信息集合

private ArrayList<MeadiaInformation> list;  
复制代码

下面直接看查询的代码:

/**
 * 获取数据库中存储的图片
 */
private void getImagesFromDb(){
	//获取系统的ContentResolver
	ContentResolver contentResolver = getContentResolver();
	//列出我们需要获取的字段信息
	String[] projection = { Thumbnails._ID, Thumbnails.IMAGE_ID,  
            Thumbnails.DATA };
	//查询需要的信息
	Cursor cursor = contentResolver.query(Thumbnails.EXTERNAL_CONTENT_URI, projection, 
			null, null, null);
	getColumnData(cursor);
	//设置到listviewAdapter
	listViewAdapter.addInformationList(list);
}

/**
 * 获取数据表中的数据
 * @param cur
 */
private void getColumnData(Cursor cur) {
    if (cur.moveToFirst()) {  
        int _id;  
        int image_id;  
        String image_path;  
        int _idColumn = cur.getColumnIndex(Thumbnails._ID);  
        int image_idColumn = cur.getColumnIndex(Thumbnails.IMAGE_ID);  
        int dataColumn = cur.getColumnIndex(Thumbnails.DATA); 
        do {
        	//获取id
            _id = cur.getInt(_idColumn);  
            //获取图片id
            image_id = cur.getInt(image_idColumn);  
            //获取图片路径
            image_path = cur.getString(dataColumn);
            Log.d("Images:", "_id:" + _id + "\n" + "image_id:" + 
            		image_id + "\n" + "image_path:"+image_path);
            //形成文件
            Bitmap bitmap = BitmapFactory.decodeFile(image_path);
            infor = new MeadiaInformation();
            infor.bitmap = bitmap;
            infor.srcPath = image_path;
            infor.type = 0;
            list.add(infor);

        } while (cur.moveToNext());  

    }  
}
复制代码

我们在onCreate中调用getImagesFromDb()方法,让我们的程序刚进来就加载。看下效果图吧!

result

猜你喜欢

转载自blog.csdn.net/chunzhenwang666/article/details/121188906