Android使用VideoView播放網路視訊,獲取網路視訊縮圖

yangxi_001發表於2017-09-11
 `/** 
 * 本例項演示如何在Android中播放網路上的視訊,這裡牽涉到視訊傳輸協議,視訊編解碼等知識點 
 * @author Administrator 
 *Android當前支援兩種協議來傳輸視訊流一種是Http協議,另一種是RTSP協議 
 *Http協議最常用於視訊下載等,但是目前還不支援邊傳輸邊播放的實時流媒體 
 *同時,在使用Http協議 傳輸視訊時,需要根據不同的網路方式來選擇合適的編碼方式, 
 *比如對於GPRS網路,其頻寬只有20kbps,我們需要使視訊流的傳輸速度在此範圍內。 
 *比如,對於GPRS來說,如果多媒體的編碼速度是400kbps,那麼對於一秒鐘的視訊來說,就需要20秒的時間。這顯然是無法忍受的 
 *Http下載時,在裝置上進行快取,只有當快取到一定程度時,才能開始播放。 
 * 
 *所以,在不需要實時播放的場合,我們可以使用Http協議 
 * 
 *RTSP:Real Time Streaming Protocal,實時流媒體傳輸控制協議。 
 *使用RTSP時,流媒體的格式需要是RTP。 
 *RTSP和RTP是結合使用的,RTP單獨在Android中式無法使用的。 
 * 
 *RTSP和RTP就是為實時流媒體設計的,支援邊傳輸邊播放。 
 * 
 *同樣的對於不同的網路型別(GPRS,3G等),RTSP的編碼速度也相差很大。根據實際情況來 
 * 
 *使用前面介紹的三種方式,都可以播放網路上的視訊,唯一不同的就是URI 
 * 
 *本例中使用VideoView來播放網路上的視訊 
 */  `
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
videoview = (VideoView) findViewById(R.id.videoview);
        Uri uri = Uri.parse("http://7sbsl4.com1.z0.glb.clouddn.com/gogogo.mp4");
        videoview.setVideoURI(uri);
        videoview.requestFocus();
        videoview.start();
  • 1
  • 2
  • 3
  • 4
  • 5

沒有管理MediaPalyer的各種狀態,這些狀態都讓VideoView給封裝了,並且,當VideoView建立的時候,MediaPalyer物件將會建立,當VideoView物件銷燬的時候,MediaPlayer物件將會釋放。

/**
         * 視訊或者音訊到結尾時觸發的方法
         */
        videoview.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                Log.i("通知", "完成");

            }
        });
/**
         * 註冊一個錯誤監聽,這個全部寫出來了,只是為了當遇到錯誤時做個參考吧,第一次用SurfaceView做的時候遇到了問題
         */
        videoview.setOnErrorListener(new MediaPlayer.OnErrorListener() {

            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                Log.i("通知", "播放中出現錯誤");

                switch (what) {
                case -1004:
                    Log.d("Streaming Media", "MEDIA_ERROR_IO");
                    break;
                case -1007:
                    Log.d("Streaming Media", "MEDIA_ERROR_MALFORMED");
                    break;
                case 200:
                    Log.d("Streaming Media", "MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK");
                    break;
                case 100:
                    Log.d("Streaming Media", "MEDIA_ERROR_SERVER_DIED");
                    break;
                case -110:
                    Log.d("Streaming Media", "MEDIA_ERROR_TIMED_OUT");
                    break;
                case 1:
                    Log.d("Streaming Media", "MEDIA_ERROR_UNKNOWN");
                    break;
                case -1010:
                    Log.d("Streaming Media", "MEDIA_ERROR_UNSUPPORTED");
                    break;
                }
                switch (extra) {
                case 800:
                    Log.d("Streaming Media", "MEDIA_INFO_BAD_INTERLEAVING");
                    break;
                case 702:
                    Log.d("Streaming Media", "MEDIA_INFO_BUFFERING_END");
                    break;
                case 701:
                    Log.d("Streaming Media", "MEDIA_INFO_METADATA_UPDATE");
                    break;
                case 802:
                    Log.d("Streaming Media", "MEDIA_INFO_METADATA_UPDATE");
                    break;
                case 801:
                    Log.d("Streaming Media", "MEDIA_INFO_NOT_SEEKABLE");
                    break;
                case 1:
                    Log.d("Streaming Media", "MEDIA_INFO_UNKNOWN");
                    break;
                case 3:
                    Log.d("Streaming Media", "MEDIA_INFO_VIDEO_RENDERING_START");
                    break;
                case 700:
                    Log.d("Streaming Media", "MEDIA_INFO_VIDEO_TRACK_LAGGING");
                    break;
                }

                return false;
            }
        });
  • 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

下面是新增的。因為videoview剛進入介面太挫了一片漆黑。。個人技術太菜就找了一個獲取網路視訊縮圖的方法給他設定個背景:

鑑於多數人都用過ThumbnailUtils.createVideoThumbnail()方法,該方法在2.x系統下可用,API LEVEL > 14時卻只能返回null,以下為解決該問題方案:

之後再自己的後臺執行緒中呼叫該方法得到網路視訊的縮圖bitmap然後在主執行緒中呼叫imageView.setImageBitmap(bitmap)即可;

獲取縮圖程式碼:

    private Bitmap createVideoThumbnail(String url, int width, int height) {
        Bitmap bitmap = null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        int kind = MediaStore.Video.Thumbnails.MINI_KIND;
        try {
            if (Build.VERSION.SDK_INT >= 14) {
                retriever.setDataSource(url, new HashMap<String, String>());
            } else {
                retriever.setDataSource(url);
            }
            bitmap = retriever.getFrameAtTime();
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException ex) {
                // Ignore failures while cleaning up.
            }
        }
        if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) {
            bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
                    ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
        }
        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

方案二:

方案一在自己的tablet上測試不通過,於是又在github上找到了另一個擴充套件版的 FFMPEG MediaMetadataRetriever ,提供了一個可以獲取視訊詳細資訊和縮圖的統一介面,支援API 7+,(android 自帶MediaMetadataRetriever僅支援API 10+),同時支援Uri

,支援的檔案型別 file, http, https, mms and mmsh 支援的編碼格式(音訊&視訊): aac, acc+, avi, flac, mp2, mp3, mp4, ogg, 3gp and more! 擴充套件支援: ICY Metadata (SHOUTcast metadata) 使用方法異常簡單,只需將下邊的包下載到本地解壓然後把libs中的檔案拷貝到自己專案的libs目錄下即可 https://github.com/wseemann/FFmpegMediaMetadataRetriever/blob/master/fmmr-library/prebuilt-libs.tar.gz.

FFmpegMediaMetadataRetriever fmmr = new FFmpegMediaMetadataRetriever();
        try {
          fmmr.setDataSource(params[0]);
          bitmap = fmmr.getFrameAtTime();
          if (bitmap != null) {
            Bitmap b2 = fmmr
                .getFrameAtTime(
                    4000000,
                    FFmpegMediaMetadataRetriever.OPTION_CLOSEST_SYNC);
            if (b2 != null) {
              bitmap = b2;
            }
            if (bitmap.getWidth() > 640) {// 如果圖片寬度規格超過640px,則進行壓縮
              bitmap = ThumbnailUtils.extractThumbnail(bitmap,
                  640, 480,
                  ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
            }
          }
        } catch (IllegalArgumentException ex) {
          ex.printStackTrace();
        } finally {
          fmmr.release();
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

這樣就可以很方便的獲取到縮圖bitmap了。

另:可能有朋友下載了prebuilt-libs.tar.gz之後會驚訝的發現,解壓之後的libs檔案竟然多達11MB!! 這麼龐大的體積對我來說是不能接受的。。。原本2MB的apk瞬間膨脹了5倍之多啊尼瑪!

但仔細研究libs一番,發現對於我這種MMR的輕度使用者,只需縮圖的功能的話,完全可以簡化那些用不到的編碼庫和多平臺庫,同時無視mips和x86平臺的話,只需libffmpeg_mediametadataretriever_jni.so和fmmr.jar兩個檔案就可以了,瞬間<30kb了有木有!

其實官方也說了,demo中給的是全部編碼格式,可以按照自己的需求重新編譯相應編碼的lib就可以。

相關文章