Android播放网络视频截图

原创 2016年06月02日 00:18:43

Android播放网络视频截图

最近博主遇到一个Android电视的开发项目,项目需要电视客户端播放服务器端视频,通过遥控器一键截图,并将截图云推送到手机客户端,于是博主就开始找度神去求助了,毕竟以前没搞过视频,当然要去先搜集下资料再开工啦,正所谓知己知彼,百战百胜嘛,于是,你将看到博主以下两天中的蛋疼经历

参考内容

videoview视频播放 http://blog.csdn.net/shenxiaolei507/article/details/41046345

MediaPlayer+SurfaceView视频播放 http://blog.csdn.net/shenxiaolei507/article/details/41349295

博主在搜集完资料之后,将两篇文章进行了对比,videoview一切都集成好了,而SurfaceView要设置很多东西,看起来复杂一些,虽然提供了截图方法,但只是用于本地截图,对博主没什么用啊,于是本着省事的原则,果断的选择了videoview。

1 .
videoView.getDrawingCache();获取截图

博主屁颠屁颠的将代码copy下来,先跑跑看,果然执行没问题。好了,那就来找找videoview怎样截图吧,博主回想到以前View截图为调用getDrawingCache(),于是博主机智的先看了看videoview的API文档,发现其中也有这个方法:videoView.getDrawingCache(); 该方法返回值为Bitmap类型,我擦,赶快去使用。结果截到的图片果然不出意外——-黑屏

2 .
videoView.setDrawingCacheEnabled(true);
bitmap = videoView.getDrawingCache();获取截图

既然bitmap = videoView.getDrawingCache();获取不到截图,就该查找原因额,博主又去找度神了,果然好多哥们和博主一样的问题,皆以失败告终。最后碰见一CSDN论坛的朋友回复,知道了其中的缘由,因为view走的是framebuffer,而videoview走的是overlay

附上各种求救链接

3 .
MediaMetaDataRetreive获取截图

既然getDrawingCache();方法行不通,看来还是搜集资料不够多,于是博主又找到了MediaMetaDataRetreive这个类

public static Bitmap getCurrentVideoBitmap(String url,VideoView videoView){
        Bitmap bitmap = null;
        MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();

        try {
            mediaMetadataRetriever.setDataSource(url,new HashMap<String, String>());
            Log.e(LOG_STRING,"截图的时间为"+videoView.getCurrentPosition());
            //取得指定时间的Bitmap,即可以实现抓图(缩略图)功能
            bitmap = mediaMetadataRetriever.getFrameAtTime(videoView.getCurrentPosition()*1000,MediaMetadataRetriever.OPTION_CLOSEST); 
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } finally {
            try {
                mediaMetadataRetriever.release();
            } catch (RuntimeException ex) {
                // Ignore failures while cleaning up.
            }
        }

        if (bitmap == null) {
            return null;
        }

        //bitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
        bitmap = Bitmap.createBitmap(bitmap);
        return bitmap;
}

getFrameAtTime说明:videoView.getCurrentPosition()得到的为毫秒,不转化为秒的话,截出来的图都是视频第一帧的图像,第二个参数可以传递的值有四个,分别为OPTION_CLOSEST, OPTION_CLOSEST_SYNC, OPTION_NEXT_SYNC, OPTION_PREVIOUS_SYNC

博主前期用的参数为OPTION_NEXT_SYNC,博主以为问题解决了,去干别的事情了,但等到晚上测试的时候却发现一个问题,这种方案截图是可以,但仅仅做个没播放视频之前的缩略图还算可以,但真的做实时截图还是不妥,这是因为:经博主测试,这种方法截图的画面,和电视截图的画面,存在着1-2秒的误差,也就是说截出的图完全不一样(测试机器三星Note4,华为荣耀6Plus),博主又失败了。

4 .OPTION_CLOSEST_SYNC参数换为OPTION_CLOSEST

博主在思考,既然网上好多文章都说这种方法可以实现,会不会是我的参数有问题,于是楼主找到了很多相同的文章介绍

例如:http://blog.sina.com.cn/s/blog_7bac47070101bys8.html
http://yashirocc.blog.sohu.com/175636801.html

同时博主又搜集了一些资料,并仔细查看了getFrameAtTime方法的详细参数

以下为各参数详解
这里写图片描述

新发现:原来视频文件存在关键帧的问题,通过度神理解,原来是可能getFrameAtTime传入的时间值,在视频的那一刻,并没有关键帧,所以OPTION_CLOSEST_SYNC返回的值为附近的时间截图。根据博主参考API的详情,果断换成了OPTION_CLOSEST。

参看资料:
这里写图片描述

失败:有些截图是正常了,但是有些时间的截图却直接返回null,尼玛这是在坑爹啊。。。

5 .寻找第三方框架截图

虽然前面都失败了,不过博主的资料搜集的又全了些,博主打算开始找第三方框架了,于是博主找到了比较出名的两个框架

  • Vitamio https://www.vitamio.org/ 国人开发的框架

    导入代码测试,这个框架好像将videoview封装了一层,叫mVideoview,据官网介绍,截图方法为getCurrentFrame();不过据博主测试,返回值为空。
    这里写图片描述

  • vlc for android http://blog.csdn.net/banketree/article/details/39575973

    导入代码进行测试,发现该框架代码里默认为rtps协议视频,并且封装了C++的so文件,使用到了JNI技术,截图什么的还要自己打补丁,看了看各位大神的文章介绍,看起来好复杂,本人水平有限,就默默放弃了。

6.TextureView播放视频并截图

博主在度神上已经实在找不到方法了,博主打算去翻墙google一下,功夫不负有心人,老外也碰到了和博主一样的问题,博主找到了新的一个解决方案,那就是TextureView播放视频并截图,后来搜集了下情报,据说这玩意是2014年google搞出来的玩意。

附上链接:http://stackoverflow.com/questions/27435985/how-to-capture-screenshot-of-videoview-when-streaming-video-in-android How to capture screenshot of VideoView when streaming video in Android [duplicate]

附上回答:
这里写图片描述

附上翻译:

**我已经找到了解决这个问题的办法。看来VideoView不允许因为底层硬件GPU的原因而使用SurfaceView。
该解决方案是使用一个textureview使用MediaPlayer播放视频里面。该活动将需要实现textureview.surfacetexturelistener。当用这个解决方案的截图,视频冻结了一段时间。同时,该textureview不显示播放进度条默认UI(播放,暂停,快进/ RW,玩的时间,等)。这是一个缺点。如果你有另一个解决方案,请让我知道:)**

看到这个页面,当然废话不多说了,建工程,copy,测试!

public class TextureViewActivity extends Activity 
    implements TextureView.SurfaceTextureListener, 
                OnBufferingUpdateListener, 
                OnCompletionListener, 
                OnPreparedListener, 
                OnVideoSizeChangedListener 
{
    private MediaPlayer mp;
    private TextureView tv;
    public static String MY_VIDEO = "https://www.blahblahblah.com/myVideo.mp4";
    public static String TAG = "TextureViewActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_texture_view);

        tv = (TextureView) findViewById(R.id.textureView1);
        tv.setSurfaceTextureListener(this);
    }

    public void getBitmap(TextureView vv)
    {
        String mPath = Environment.getExternalStorageDirectory().toString() 
                + "/Pictures/" + Utilities.getDayTimeString() + ".png";   
        Toast.makeText(getApplicationContext(), "Capturing Screenshot: " + mPath, Toast.LENGTH_SHORT).show();

        Bitmap bm = vv.getBitmap();
        if(bm == null)
            Log.e(TAG,"bitmap is null");

        OutputStream fout = null;
        File imageFile = new File(mPath);

        try {
            fout = new FileOutputStream(imageFile);
            bm.compress(Bitmap.CompressFormat.PNG, 90, fout);
            fout.flush();
            fout.close();
        } catch (FileNotFoundException e) {
            Log.e(TAG, "FileNotFoundException");
            e.printStackTrace();
        } catch (IOException e) {
            Log.e(TAG, "IOException");
            e.printStackTrace();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.media_player_video, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) 
    {
        Surface s = new Surface(surface);

        try
        {
            mp = new MediaPlayer();
            mp.setDataSource(MY_VIDEO);
            mp.setSurface(s);
            mp.prepare();

            mp.setOnBufferingUpdateListener(this);
            mp.setOnCompletionListener(this);
            mp.setOnPreparedListener(this);
            mp.setOnVideoSizeChangedListener(this);

            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mp.start();

            Button b = (Button) findViewById(R.id.textureViewButton);
            b.setOnClickListener(new OnClickListener(){

                @Override
                public void onClick(View v) 
                {
                    TextureViewActivity.this.getBitmap(tv);
                }
            });
        }
        catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  
    }

问题终结


经过博主反复测试TextureView的getBitmap();方法,完美截图,无任何时差,无返回值为null问题,无截图花屏问题,该问题终于终结了,历时一天半,过程虽然心酸,但收获了许多关于视频方面相关的知识。博主在搜集资料的同时,看到了很多像博主一样对该问题的困扰,故将详细经历总结出来,希望不要再有人步博主后尘了。完毕,收工。


本人水平有限,如有错误请及时指正

版权声明:转载请注明原始链接。

相关文章推荐

android中 代码实现截图功能(静态+动态视频)

分享下静态截图的功能代码: public class ScreenShot { // 获取指定Activity的截屏,保存到png文件 static Bitmap takeS...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Android 视频截图方法

1. 前言 本文讨论的是获取视频在某个时间点的图像, 而非屏幕截图. 如果要获取屏幕截图的话直接(电源键+音量下) 2. 获取视频截图 2.1 MediaMetadataRetriever Me...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Android含视频的截屏

最近在做一个关于一个界面的截屏功能,但是因为含有视频模块,在使用getDrawingCache时会出现视频部分黑屏的情况。主要是因为视频和Activity的展示不是一个通道,视频使用了硬件解码的原因。...

Android中截图(surfaceView)

前几天,在网上总结了一个方法,实现了在Android当前Activity的截图,本人测试确实通过了,不过有朋友说截出来的图是黑色的,不能看。我心想,这没有问题啊,相同的代码我就可以执行通过,并没有没有...
  • yilip
  • yilip
  • 2012年07月13日 23:47
  • 18533

SurfaceView播放视频的截图

//创建媒体数据寻找类 MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); //找到视频源 me...

surfaceview播放视频截图黑屏

不多说,直接上码!(原理是把视频去帧,保存到画布,然后把当前屏幕截图,然后再画到画布上)     @SuppressLint("NewApi")     private void savaScre...

Android MediaProjection截屏与录屏(surfaceview截图)(一)

MediaProjection 和 MediaProjectionManager 是 Android 5.0 开放的屏幕截图与录制视频的接口,它可以用来对 surfaceview 进行截图,解决以前 ...

视频画面帧的展示控件SurfaceView及TextureView对比

SurfaceView是什么 ?SurfaceView优点及缺点?SurfaceView中双缓冲?TextureView是什么?TextureView优点及缺点?两者的性能相比如何?播放器应该选择谁?...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android播放网络视频截图
举报原因:
原因补充:

(最多只允许输入30个字)