Android Video简述

简介:
Android Video 简述
         Video的播放实现,至于录制请参见《Android Camera简述》。
 
一、播放方式
1 )自带播放器播放
         入口Action为ACTION_VIEW,Data为Uri,Type为MIME类型。

 
 
  1. // 自带播放器播放 
  2. private void sysPlayer(String filename) { 
  3.     Uri uri = Uri.parse(filename); 
  4.     Intent intent = new Intent(Intent.ACTION_VIEW); 
  5.     intent.setDataAndType(uri, "video/3gp"); 
  6.     startActivity(intent); 
 
2 )VideoView 播放

         VideoView组件进行播放。在《Android Camera简述》的Camera摄像样例中的预览操作即是这种方式。(例子选择VideoView播放,仍是用的同一个类==)

 
3 )MediaPlayer 播放
         MediaPlayer进行播放控制。不再赘述了==,具体看下节例子。
 
二、视频播放器
1 )视频播放活动
         文件浏览方式,找到3gp文件进行播放。
         1)直接点击,以MediaPlayer方式播放(简单的自定义了下界面^^)
         2)长按时,可选"自带播放器播放"、"VideoView播放"、"MediaPlayer播放"三种方式
 
2 )文件浏览器接口
         定义了文件夹点击、文件点击、文件长按、错误四方法。
 
3 )文件列表适配器
         自定义文件列表适配器。主要就是getView()内对一个Item布局的的赋值,有图片、名称、类型、大小四项。
 
4 )文件浏览器
         自定义的文件浏览器。
         1)属性资源的使用。即在attrs.xml定义的FileBrowser的属性。
不过,extName&fileImage以后缀数字不断增加的好像不好定义,仍是自定义空间方式。
         2)接口事件,参见OnFileBrowserListener.java。
 

 
 
  1. public class FileBrowser extends ListView implements 
  2.         AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { 
  3.  
  4.     public static final int ERROR_ACCESS_DIR = 1// 访问目录错误 
  5.  
  6.     /* 
  7.      * 这是其他属性的命名空间。 
  8.      *  
  9.      * 而attrs.xml定义的FileBrowser的属性,命名空间需如下定义,否则取不到属性值。 
  10.      * xmlns:空间名="http://schemas.android.com/apk/res/自定义组件所在包名" 
  11.      */ 
  12.     private final String namespace = "http://vaero.blog.51cto.com/"
  13.  
  14.     /* 自定义属性 */ 
  15.     private String baseDir; // 初始根目录 
  16.     private boolean backFinish; // 是否返回退出 
  17.     private int folderImageResId; // 文件夹的图片资源id 
  18.     private Map<String, Integer> fileImageResIdMap = new HashMap<String, Integer>(); // 各文件类型的图片资源id 
  19.     private int otherFileImageResId; // 其他文件类型的图片资源id 
  20.  
  21.     private Stack<String> dirStack = new Stack<String>(); // 栈方式存放当前目录 
  22.     private List<File> fileList = new ArrayList<File>();; // 当前目录内File 
  23.     private FileListAdapter fileListAdapter; // 自定义文件列表适配器 
  24.     private OnFileBrowserListener listener; // 文件浏览器接口 
  25.  
  26.     public FileBrowser(Context context, AttributeSet attrs) { 
  27.         super(context, attrs); 
  28.         // 获得TypedArray对象 
  29.         TypedArray typedArray = context.obtainStyledAttributes(attrs, 
  30.                 R.styleable.FileBrowser); 
  31.         // 获取baseDir 
  32.         baseDir = typedArray.getString(R.styleable.FileBrowser_baseDir); 
  33.         if (null == baseDir) { // 未定义时为SDCard目录 
  34.             baseDir = Environment.getExternalStorageDirectory().toString(); 
  35.         } 
  36.         setCurrentPath(baseDir); // 由dir设置当前栈 
  37.         // 获取backFinish,默认true 
  38.         backFinish = typedArray.getBoolean(R.styleable.FileBrowser_backFinish, 
  39.                 false); 
  40.         // 获取folderImage,默认0 
  41.         folderImageResId = typedArray.getResourceId( 
  42.                 R.styleable.FileBrowser_folderImage, 0); 
  43.         // 获取otherFileImage,默认0 
  44.         otherFileImageResId = typedArray.getResourceId( 
  45.                 R.styleable.FileBrowser_otherFileImage, 0); 
  46.         // 获取extName&fileImage(以后缀数字不断增加的方式动态获取) 
  47.         String extName; 
  48.         int fileImageResId; 
  49.         int index = 1
  50.         while (true) { 
  51.             extName = attrs.getAttributeValue(namespace, "extName" + index); 
  52.             fileImageResId = attrs.getAttributeResourceValue(namespace, 
  53.                     "fileImage" + index, 0); 
  54.             // 不存在extName&fileImage属性时跳出循环 
  55.             if ("".equals(extName) || extName == null || fileImageResId == 0) { 
  56.                 break
  57.             } 
  58.             fileImageResIdMap.put(extName, fileImageResId); 
  59.             index++; 
  60.         } 
  61.  
  62.         // 设置Item点击监听接口 
  63.         setOnItemClickListener(this); 
  64.         setOnItemLongClickListener(this); 
  65.         // 设置当前目录的File 
  66.         setCurrentDirFiles(); 
  67.         // 设置适配器 
  68.         fileListAdapter = new FileListAdapter(context, fileList, 
  69.                 folderImageResId, fileImageResIdMap, otherFileImageResId); 
  70.         setAdapter(fileListAdapter); 
  71.     } 
  72.  
  73.     // 由dir设置当前栈 
  74.     private void setCurrentPath(String dir) { 
  75.         dirStack.clear(); 
  76.         String spDir = dir.charAt(0) == '/' ? dir.substring(1) : dir; 
  77.         for (String s : spDir.split("/")) { 
  78.             dirStack.push(s); 
  79.         } 
  80.     } 
  81.  
  82.     // 由栈获取当前目录 
  83.     private String getCurrentDir() { 
  84.         StringBuffer result = new StringBuffer(); 
  85.         result.append("/"); 
  86.         for (int i = 0; i < dirStack.size(); i++) { 
  87.             result.append(dirStack.get(i)); 
  88.             result.append("/"); 
  89.         } 
  90.         return result.toString(); 
  91.     } 
  92.  
  93.     // 设置当前目录的File 
  94.     private boolean setCurrentDirFiles() { 
  95.         // 获得当前目录中所有的File对象 
  96.         File[] files = new File(getCurrentDir()).listFiles(); 
  97.         if (null == files) { // 一些系统目录可能获不到,为null 
  98.             dirStack.pop(); 
  99.             return false
  100.         } 
  101.  
  102.         fileList.clear(); 
  103.  
  104.         // 多于一级时,增加null以表示返回上一级 
  105.         if (!dirStack.isEmpty()) 
  106.             fileList.add(null); 
  107.  
  108.         for (File file : files) { 
  109.             fileList.add(file); 
  110.         } 
  111.  
  112.         // 类型顺序:null、文件夹、文件;同类型顺序:字典顺序 
  113.         Collections.sort(fileList, new Comparator<File>() { 
  114.             @Override 
  115.             public int compare(File f1, File f2) { 
  116.                 if (null == f1) { 
  117.                     return -1
  118.                 } else if (null == f2) { 
  119.                     return 1
  120.                 } else if (f1.isDirectory() && !f2.isDirectory()) { 
  121.                     return -1
  122.                 } else if (!f1.isDirectory() && f2.isDirectory()) { 
  123.                     return 1
  124.                 } else { 
  125.                     return f1.toString().compareTo(f2.toString()); // 字典顺序比较 
  126.                 } 
  127.             } 
  128.         }); 
  129.  
  130.         return true
  131.     } 
  132.  
  133.     @Override 
  134.     public void onItemClick(AdapterView<?> parent, View view, int position, 
  135.             long id) { 
  136.         File file = fileList.get(position); 
  137.         if (file == null) { // null时,返回上一级 
  138.             dirStack.pop(); // 出栈 
  139.             setCurrentDirFiles(); // 设置当前目录的File 
  140.             fileListAdapter.notifyDataSetChanged(); // 通知数据改变,刷新列表 
  141.             if (listener != null) { 
  142.                 listener.onDirItemClick(getCurrentDir()); 
  143.             } 
  144.         } else if (file.isDirectory()) { // 目录时 
  145.             dirStack.push(file.getName()); // 压栈 
  146.             if (setCurrentDirFiles()) { // 设置当前目录的File 
  147.                 fileListAdapter.notifyDataSetChanged(); // 通知数据改变,刷新列表 
  148.                 if (listener != null) { 
  149.                     listener.onDirItemClick(getCurrentDir()); 
  150.                 } 
  151.             } else { 
  152.                 if (listener != null) { 
  153.                     listener.onError(ERROR_ACCESS_DIR); 
  154.                 } 
  155.             } 
  156.         } else { // 文件时 
  157.             if (listener != null) { 
  158.                 listener.onFileItemClick(file.getAbsolutePath()); 
  159.             } 
  160.         } 
  161.  
  162.     } 
  163.  
  164.     @Override 
  165.     public boolean onItemLongClick(AdapterView<?> parent, View view, 
  166.             int position, long id) { 
  167.         File file = fileList.get(position); 
  168.         if (file.isFile()) { // 文件时 
  169.             if (listener != null) { 
  170.                 listener.onFileItemLongClick(file.getAbsolutePath()); 
  171.                 return true
  172.             } 
  173.         } 
  174.         return false
  175.     } 
  176.  
  177.     @Override 
  178.     public boolean onKeyDown(int keyCode, KeyEvent event) { 
  179.         if (keyCode == KeyEvent.KEYCODE_BACK && !backFinish 
  180.                 && !dirStack.isEmpty()) { 
  181.             dirStack.pop(); // 出栈 
  182.             setCurrentDirFiles(); // 设置当前目录的File 
  183.             fileListAdapter.notifyDataSetChanged(); // 通知数据改变,刷新列表 
  184.             if (listener != null) { 
  185.                 listener.onDirItemClick(getCurrentDir()); 
  186.             } 
  187.             return true
  188.         } 
  189.         return super.onKeyDown(keyCode, event); 
  190.     } 
  191.  
  192.     // 设置文件浏览器接口 
  193.     public void setOnFileBrowserListener(OnFileBrowserListener listener) { 
  194.         this.listener = listener; 
  195.     } 
  196.  
 
5 )MediaPlayer 播放视频
         MediaPlayer播放视频。控制界面很简单,描述如下:
         1)暂停、播放、停止三按钮
         2)进度条实时进度显示,及拖动跳转
         3)进度条左侧当前时间、右侧总时间显示
          ps:播放画面自适应等比例满屏
 

  
  
  1. public class VideoPlayer extends Activity implements 
  2.         MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, 
  3.         SurfaceHolder.Callback, SeekBar.OnSeekBarChangeListener { 
  4.  
  5.     public static final int STATUS_STOPPED = 1
  6.     public static final int STATUS_PAUSING = 2
  7.     public static final int STATUS_PLAYING = 3
  8.  
  9.     private MediaPlayer mPlayer; // MediaPlayer对象 
  10.     private SurfaceHolder mSurfaceHolder; // SurfaceHolder对象 
  11.  
  12.     private SurfaceView surfaceView; // SurfaceView组件 
  13.     private SeekBar seekBar; // SeekBar组件 
  14.     private TextView nowTime, totleTime; // TextView组件 
  15.     private LinearLayout linearLayout; // 播放控制布局 
  16.  
  17.     private String mTimerFormat = "%02d:%02d"// 时间格式 
  18.     /* 时间更新Handler */ 
  19.     private final Handler mHandler = new Handler(); 
  20.     private Runnable mUpdateTimer = new Runnable() { 
  21.         public void run() { 
  22.             updateTimerView(); 
  23.         } 
  24.     }; 
  25.  
  26.     private String filename; // 文件名称 
  27.  
  28.     private int mStatus = STATUS_STOPPED; // 当前状态 
  29.  
  30.     @Override 
  31.     protected void onCreate(Bundle savedInstanceState) { 
  32.         super.onCreate(savedInstanceState); 
  33.         setContentView(R.layout.video_player); 
  34.  
  35.         surfaceView = (SurfaceView) findViewById(R.id.surfaceView); 
  36.         seekBar = (SeekBar) findViewById(R.id.seekBar); 
  37.         seekBar.setOnSeekBarChangeListener(this); 
  38.         nowTime = (TextView) findViewById(R.id.nowTime); 
  39.         totleTime = (TextView) findViewById(R.id.totleTime); 
  40.         linearLayout = (LinearLayout) findViewById(R.id.linearLayout); 
  41.  
  42.         // 获取文件名称 
  43.         filename = getIntent().getStringExtra(VideoPlayerActivity.KEY_FILENAME); 
  44.  
  45.         /* 初始化mSurfaceHolder */ 
  46.         mSurfaceHolder = surfaceView.getHolder(); 
  47.         mSurfaceHolder.addCallback(this); // 设置回调接口 
  48.         mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置为Buffer类型(播放视频&Camera预览) 
  49.  
  50.         /* 初始化MediaPlayer */ 
  51.         mPlayer = new MediaPlayer(); 
  52.         mPlayer.setOnPreparedListener(this); 
  53.         mPlayer.setOnCompletionListener(this); 
  54.         try { 
  55.             mPlayer.setDataSource(filename); 
  56.         } catch (Exception e) { 
  57.             e.printStackTrace(); 
  58.         } 
  59.     } 
  60.  
  61.     // 暂停按钮 
  62.     public void pauseBtn(View v) { 
  63.         if (mStatus == STATUS_PLAYING) { 
  64.             mPlayer.pause(); 
  65.             mStatus = STATUS_PAUSING; 
  66.         } 
  67.     } 
  68.  
  69.     // 播放按钮 
  70.     public void playBtn(View v) { 
  71.         if (mStatus == STATUS_PAUSING) { 
  72.             mPlayer.start(); // 继续播放 
  73.             mStatus = STATUS_PLAYING; 
  74.             updateTimerView(); // 更新时间 
  75.         } else { 
  76.             /* 重新开始播放 */ 
  77.             mPlayer.reset(); 
  78.             try { 
  79.                 mPlayer.setDataSource(filename); 
  80.             } catch (Exception e) { 
  81.                 e.printStackTrace(); 
  82.             } 
  83.             mPlayer.prepareAsync(); // 异步准备 
  84.         } 
  85.     } 
  86.  
  87.     // 停止按钮 
  88.     public void stopBtn(View v) { 
  89.         if (mStatus != STATUS_STOPPED) { 
  90.             mPlayer.stop(); // 停止播放 
  91.             mPlayer.reset(); // 重置 
  92.             mStatus = STATUS_STOPPED; 
  93.             updateTimerView(); // 更新时间 
  94.         } 
  95.     } 
  96.  
  97.     // 当Surface被创建的时被触发 
  98.     @Override 
  99.     public void surfaceCreated(SurfaceHolder holder) { 
  100.         mPlayer.setDisplay(holder); // 指定SurfaceHolder 
  101.         mPlayer.prepareAsync(); // 异步准备(将回调OnPreparedListener接口) 
  102.     } 
  103.  
  104.     // 当Surface尺寸等参数改变时触发 
  105.     @Override 
  106.     public void surfaceChanged(SurfaceHolder holder, int format, int width, 
  107.             int height) { 
  108.     } 
  109.  
  110.     // 当surface销毁时触发 
  111.     @Override 
  112.     public void surfaceDestroyed(SurfaceHolder holder) { 
  113.     } 
  114.  
  115.     @Override 
  116.     public void onPrepared(MediaPlayer mp) { 
  117.         /* 获得窗口宽长 */ 
  118.         Display display = getWindowManager().getDefaultDisplay(); 
  119.         int wWidth = display.getWidth(); 
  120.         int wHeight = display.getHeight(); 
  121.         /* 获得视频宽长 */ 
  122.         int vWidth = mPlayer.getVideoWidth(); 
  123.         int vHeight = mPlayer.getVideoHeight(); 
  124.         /* 最适屏幕 */ 
  125.         float wRatio = (float) vWidth / (float) wWidth; // 宽度比 
  126.         float hRatio = (float) vHeight / (float) wHeight; // 高度比 
  127.         float ratio = Math.max(wRatio, hRatio); // 较大的比 
  128.         vWidth = (int) Math.ceil((float) vWidth / ratio); // 新视频宽度 
  129.         vHeight = (int) Math.ceil((float) vHeight / ratio); // 新视频高度  
  130.         // 改变SurfaceHolder大小 
  131.         mSurfaceHolder.setFixedSize(vWidth, vHeight); 
  132.         // 设置新布局参数(这在samsung i9088上出现stretch的错误==) 
  133.         // surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight)); 
  134.         // 设置总时间显示 
  135.         setTimeView(totleTime, mPlayer.getDuration()); 
  136.         // 启动播放 
  137.         mPlayer.start(); 
  138.         // 设定状态 
  139.         mStatus = STATUS_PLAYING; 
  140.         // 更新时间 
  141.         updateTimerView(); 
  142.     } 
  143.  
  144.     @Override 
  145.     public void onCompletion(MediaPlayer mp) { 
  146.         mStatus = STATUS_STOPPED; 
  147.         finish(); // 播放完成后退出 
  148.     } 
  149.  
  150.     @Override 
  151.     public boolean onTouchEvent(MotionEvent event) { 
  152.         if (event.getAction() == MotionEvent.ACTION_DOWN) { 
  153.             // 播放控制布局的显示&隐藏 
  154.             linearLayout 
  155.                     .setVisibility(linearLayout.getVisibility() == View.VISIBLE ? View.GONE 
  156.                             : View.VISIBLE); 
  157.         } 
  158.         return super.onTouchEvent(event); 
  159.     } 
  160.  
  161.     @Override 
  162.     public void onBackPressed() { 
  163.         super.onBackPressed(); 
  164.         stopBtn(null); // 停止 
  165.     } 
  166.  
  167.     @Override 
  168.     protected void onDestroy() { 
  169.         super.onDestroy(); 
  170.         mPlayer.release(); // 释放 
  171.     } 
  172.  
  173.     // 更新时间 
  174.     private void updateTimerView() { 
  175.         if (mStatus == STATUS_PLAYING) { 
  176.             int position = mPlayer.getCurrentPosition(); 
  177.             int duration = mPlayer.getDuration(); 
  178.             setTimeView(nowTime, position); // 设置时间显示 
  179.             long pos = seekBar.getMax() * position / duration; 
  180.             seekBar.setProgress((int) pos); // 设置进度条 
  181.             mHandler.postDelayed(mUpdateTimer, 1000); 
  182.         } else if (mStatus == STATUS_STOPPED) { 
  183.             nowTime.setText("00:00"); 
  184.             seekBar.setProgress(0); 
  185.         } 
  186.     } 
  187.  
  188.     // 设置时间显示 
  189.     private void setTimeView(TextView textView, int msec) { 
  190.         if (msec >= 1000 * 60 * 60) { // >=1h 
  191.             textView.setText(String.format(mTimerFormat, msec / 1000 / 60 / 60
  192.                     msec / 1000 / 60 % 60)); 
  193.         } else { 
  194.             textView.setText(String.format(mTimerFormat, msec / 1000 / 60
  195.                     msec / 1000 % 60)); 
  196.         } 
  197.     } 
  198.  
  199.     // SeekBar进度改变时 
  200.     @Override 
  201.     public void onProgressChanged(SeekBar seekBar, int progress, 
  202.             boolean fromUser) { 
  203.     } 
  204.  
  205.     // SeekBar开始拖动时 
  206.     @Override 
  207.     public void onStartTrackingTouch(SeekBar seekBar) { 
  208.  
  209.     } 
  210.  
  211.     // SeekBar结束拖动时 
  212.     @Override 
  213.     public void onStopTrackingTouch(SeekBar seekBar) { 
  214.         if (mStatus == STATUS_PLAYING) { 
  215.             mPlayer.seekTo(seekBar.getProgress() * mPlayer.getDuration() 
  216.                     / seekBar.getMax()); // 跳转 
  217.         } 
  218.     } 
  219.  

三、后记
1 )扩展内容
1.1)应用资源系列之属性[Attribute]资源
 
1.2)RMVB软解,有兴趣的可以找找(我稍搜了下,无发现==)
 
2 )模块概览
2.1)Video播放

Video播放

 
3 )运行效果
3.1)Video播放

Video播放

 
3.2)文件浏览器

Video播放

 
3.3)长按选择方式

Video播放

 
3.4)自带播放器播放

Video播放

 
3.5)VideoView播放

Video播放

 
3.6)MediaPlayer播放

Video播放 

 

 




     本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/834887,如需转载请自行联系原作者





相关文章
|
Android开发
Android TextureView &amp; MediaPlayer implements video media player
Android TextureView & MediaPlayer implements video media player code: import android.
1173 0
|
Android开发
【我的Android进阶之旅】推荐一款视频转换GIF图片格式的转换工具(Video to GIF)
一、背景 最近想把一些Android Demo的运行效果图获取下来,但是一直使用真机进行调试,在电脑上不好截取一段gif动画。而之前使用模拟器的时候可以使用 GifCam 工具进行屏幕动画截取。
2117 0
|
编解码 Android开发
Android Codec 集成和 video Overlay
Codec 集成和video overlay是现在FSL对android 多媒体修改的所有东西,codec library以.so的形式放在prebuilt目录下,没有源文件 。而video overlay的实现主要是使用了FSL的ipu底层库,将视频数据 直接发送到硬件,由硬件进行merge。
730 0
|
11天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
16天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
2天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
18天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。