android播放网络视频截图

本文记录了作者在Android项目中实现电视客户端播放网络视频并通过遥控器截图的过程。从使用VideoView及MediaMetadataRetriever遇到的问题,到最后采用TextureView成功实现无延迟、高质量截图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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 .寻找第三方框架截图

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

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();
        }  
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111

问题终结


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



转载至:

http://blog.csdn.net/yuehenhn/article/details/51560229

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值