Android Studio上手,基于VideoView的本地文件及流媒体播放器

既然是第一个Android程序,少不了要Hello World。


1. 新建安卓工程

技术分享


2. 输入工程名称

技术分享


3. 选择平台版本

技术分享


4. 选择一个空的Activity

技术分享


5. 定制自己的Activity

技术分享

点击Finish后,便生成了可以直接运行的Hello World程序。下面开始讨论怎样使这个只能打印Hello World的程序能够播放本地和网络视频。

此处附上功能目录结构:

技术分享


6. 布局文件

首先需要重新布局。设计器的设计结果是保存在“activity_video_view_demo.xml”这个XML文件中的,所以,稍微花一点时间看下这个XML文件,就很容易看懂。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".VideoViewDemo">


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="= Stream Player ="
        android:id="@+id/textView"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:textIsSelectable="false"
        android:layout_alignParentBottom="false"
        android:gravity="center_horizontal" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/url"
        android:layout_below="@+id/textView"
        android:layout_alignParentTop="false"
        android:layout_alignParentLeft="true"
        android:text="rtsp://ipaddr:port/domain"
        android:layout_alignRight="@+id/textView"
        android:layout_alignEnd="@+id/textView" />

    <RadioGroup
        android:id="@+id/radioGroup1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_below="@+id/url"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="false">

        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Stream"
            android:id="@+id/radioButtonStream"
            android:layout_below="@+id/url"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:checked="false"
            android:layout_alignBottom="@+id/start_play" />

        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="51dp"
            android:text="File"
            android:id="@+id/radioButtonFile"
            android:checked="false"
            android:layout_alignBottom="@+id/radioButtonStream"
            android:layout_toRightOf="@+id/radioButtonStream"
            android:layout_below="@+id/url" />
    </RadioGroup>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="PLAY"
        android:id="@+id/start_play"
        android:layout_below="@+id/url"
        android:layout_alignRight="@+id/url"
        android:layout_alignEnd="@+id/url"
        android:layout_toRightOf="@+id/radioGroup1"
        android:layout_toEndOf="@+id/radioGroup1" />

    <VideoView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rtsp_player"
        android:layout_below="@+id/start_play"
        android:layout_alignRight="@+id/url"
        android:layout_alignEnd="@+id/url" />

</RelativeLayout>
布局设计器效果:
技术分享

对照XML文件,最外层是一个关系布局。里面依次是TextView(用于显示标题);EditText(用于输入文件名或流媒体的URL);一个RadioGroup,其中包含两个RadioButton(用于区分是区网络流还是读本地文件);一个Button(单击开始播放);一个VideoView(视频播放区)。

其中的layout_width可以是wrap_content(根据内容自适应),fill_parent(填充整个外部空间),match_parent(根据外部空间自适应),这个当视频播放区载入视频后会看出差别。android:id这个属性非常重要,是Java程序中调用布局控件的关键。


7. 源码

package com.example.chenth.videoviewdemo;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.VideoView;

public class VideoViewDemo extends Activity {
    /** Called when the activity is first created. */

    Button playButton ;
    VideoView videoView ;
    EditText rtspUrl ;
    RadioButton radioStream;
    RadioButton radioFile;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_view_demo);

        rtspUrl = (EditText)this.findViewById(R.id.url);
        playButton = (Button)this.findViewById(R.id.start_play);
        radioStream = (RadioButton)this.findViewById(R.id.radioButtonStream);
        radioFile = (RadioButton)this.findViewById(R.id.radioButtonFile);

        playButton.setOnClickListener(new Button.OnClickListener(){
            public void onClick(View v) {
                if (radioStream.isChecked()) {
                    PlayRtspStream(rtspUrl.getEditableText().toString());
                }
                else if (radioFile.isChecked()){
                    PlayLocalFile(rtspUrl.getEditableText().toString());
                }
            }
        });

        videoView = (VideoView)this.findViewById(R.id.rtsp_player);

    }

    //play rtsp stream
    private void PlayRtspStream(String rtspUrl){
        videoView.setVideoURI(Uri.parse(rtspUrl));
        videoView.requestFocus();
        videoView.start();
    }

    //play rtsp stream
    private void PlayLocalFile(String filePath){
        videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/" + filePath);
        videoView.requestFocus();
        videoView.start();
    }
}
在on鞥的Create函数中根据ID从布局中获取控件,当playButton点击时根据RadioButton的选择(取网络流还是播放本地文件)调用不同的函数。(主要是用到了Android原生的VideoView,而这个控件播放网络流使用 videoView.setVideoURI()函数,播放本地文件使用setVideoPath()函数)。

8. 增加权限

在调试这个程序的时候,总是出现“Can‘t Play This Video”或者“无法播放该视频”的错误,我也为此花费了很多时间,最后发现是因为没有给这个APP赋予访问网络和本地文件的权限。权限配置在AndroidManifest.xml这个文件中。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.chenth.videoviewdemo" >

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".VideoViewDemo"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
注意"uses-permission"的两行,分别表示该应用需要访问网络,以及读写本地文件,这两个权限对应了播放网络流和本地文件。
加了这两行以后,打包安装时,就可以看到APP想Android系统申请权限的提示了。


9. 在模拟器上调试

点击Run app,会出现选择设备的窗口,没有现成设备的话就新增一个。

技术分享

技术分享

技术分享

技术分享

因为我不知道模拟器对应的根目录在PC的什么位置,所以无法在模拟器中直接测试播放本地文件。另外,在模拟器中测试播放网络流也不成功,猜测应该是模拟器权限未设置好。所以,直接进入最后一步吧。


10. 打包发布

点击菜单栏的Build -> Generate Signed APK。第一次使用时会要求创建一个Key,就创建吧。然后一路Next 就可以了。最后把生成的apk文件拷贝到手机,安装,搞定!

最后上手机截屏效果:一张是播放RTSP实时流,另一张是我家小盆友的录像。

技术分享

技术分享


11. 附:Android原生VideoView的播放缓冲大小好像是不能调整的,导致网络RTSP流的延时达到了9-10秒。各位看官如果知道什么好的办法,欢迎留言告知,谢谢!


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。