很久没有更新博客了,最近短视频的应用那是相当火,我自己手机也装了好几个应用,内涵段子,抖音,快手,等等,前些时候我打开今日头条,特么的新闻也嵌入了娱乐短视频,最近看了比较成熟的视频框架源码——Vitamio。

没做过视频类的app,今天写了一个demo,大家一起学习。

直入主题,首先需要从vitamio的官网下载源码,下载下来之后应该是名为VitamioBundleStudio-master的文件夹,详细目录

我们只需要vitamio这个module就好了,vitamio-sample是官方的demo,需要了解更多的读者可以去看看,本篇只做简单的加载视频还有一些需要注意的小细节,望见谅!
创建了自己的项目之后,导入下载的vitamio,如下:

然后选择下载好的VitamioBundleStudio-master下的vitamio文件夹,导入之后我们在自己的项目中可以看到多了vitamio

接着:

之后在自己项目app的build.gradle里面的dependencies就能看到

  compile project(':vitamio')

一切准备就绪,将官方demo中用到的代码抽了出来,下面一步一步贴出来,有注释~
先完善activity_main的布局文件,代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><io.vov.vitamio.widget.CenterLayout
        android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><io.vov.vitamio.widget.VideoView
            android:id="@+id/buffer"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:layout_centerVertical="true" /></io.vov.vitamio.widget.CenterLayout><LinearLayout
        android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:orientation="horizontal"><ProgressBar
            android:id="@+id/probar"style="?android:attr/progressBarStyleLarge"android:layout_width="50dp"android:layout_height="50dp" /><TextView
            android:id="@+id/download_rate"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#FFFFFF"android:text="" /><TextView
            android:id="@+id/load_rate"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#FFFFFF"android:text="" /></LinearLayout></RelativeLayout>

VideoView包裹在CenterLayout里面,目的就是让视频居中显示,下面是ProgressBar 用来加载时用得进度对话框,两个textview表示下载和缓存速度。
官方demo中的视频播放样式很简单,控制播放暂停按钮,进度时长,还有一个seekbar。这时就要自己写样式了,重写他的MediaController,大致:

代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/video_player_bg_color"android:orientation="vertical"><RelativeLayout
        android:layout_width="match_parent"android:layout_height="match_parent"><RelativeLayout
            android:layout_width="match_parent"android:layout_height="40dp"android:background="#77000000"><ImageButton
                android:id="@+id/top_back"android:layout_width="50dp"android:layout_height="match_parent"android:layout_alignParentLeft="true"android:background="@null"android:src="@drawable/ic_player_close_white" /><TextView
                android:id="@+id/filename"style="@style/MediaController_Text"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_centerVertical="true"android:layout_marginLeft="5dp"android:gravity="center"android:layout_toRightOf="@+id/top_back"android:ellipsize="marquee"android:singleLine="true"android:text="名称" /><ImageButton
                android:id="@+id/mediacontroller_share"android:layout_width="50dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:background="@null"android:src="@drawable/ic_action_share_without_padding" /><ImageButton
                android:id="@+id/mediacontroller_favorite"android:layout_width="50dp"android:layout_height="match_parent"android:layout_alignParentTop="true"android:layout_toLeftOf="@+id/mediacontroller_share"android:layout_toStartOf="@+id/mediacontroller_share"android:background="@null"android:src="@drawable/ic_action_favorites" /></RelativeLayout><ImageButton
            android:id="@+id/mediacontroller_play_pause"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:background="@null"android:src="@drawable/paly_selector" /><RelativeLayout
            android:id="@+id/operation_volume_brightness"android:layout_width="150dp"android:layout_height="75dp"android:layout_centerInParent="true"android:background="@drawable/videobg"android:orientation="horizontal"android:padding="0dip"android:visibility="gone"><ImageView
                android:id="@+id/operation_bg"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:src="@drawable/video_volumn_bg" /><TextView
                android:id="@+id/operation_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignBottom="@+id/operation_bg"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:text="32:22/45:00"android:textColor="#ffffff"android:textSize="10sp"android:visibility="gone" /></RelativeLayout><RelativeLayout
            android:layout_width="match_parent"android:layout_height="50dp"android:layout_alignParentBottom="true"android:background="#77000000"><TextView
                android:id="@+id/mediacontroller_time_current"style="@style/MediaController_Text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="15dp"android:text="33:33:33" /><TextView
                android:id="@+id/mediacontroller_time_total"style="@style/MediaController_Text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="15dp"android:text="33:33:33" /><ImageView
                android:id="@+id/mediacontroller_scale"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:src="@drawable/ic_action_scale" /><SeekBar
                android:id="@+id/mediacontroller_seekbar"style="@style/MediaController_SeekBar"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_toLeftOf="@id/mediacontroller_time_total"android:layout_toRightOf="@id/mediacontroller_time_current"android:focusable="true"android:max="1000" /></RelativeLayout></RelativeLayout>
</LinearLayout>

之前了解vitamio的时候,网上很多说返回按钮必须id为 mediacontroller_top_back 和视频的名称必须是 mediacontroller_filename,其实并不是。

下面接着看业务逻辑:
自定义的mediaController代码如下:

package com.vitamiodemo;import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.view.Display;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;import io.vov.vitamio.widget.MediaController;
import io.vov.vitamio.widget.VideoView;/*** @author: Allen.* @date: 2018/1/17* @description: 自定义Controller*/public class CustomMediaController extends MediaController {private static final int HIDEFRAM = 0;//控制提示窗口的显示private GestureDetector mGestureDetector;private ImageButton img_back;//返回按钮private TextView mFileName;//文件名private VideoView videoView;private Activity activity;private Context context;private String videoname;//视频名称private int controllerWidth = 0;//设置mediaController高度为了使横屏时top显示在屏幕顶端private View mVolumeBrightnessLayout;//提示窗口private ImageView mOperationBg;//提示图片private TextView mOperationTv;//提示文字private AudioManager mAudioManager;//最大声音private int mMaxVolume;// 当前声音private int mVolume = -1;//当前亮度private float mBrightness = -1f;//返回监听private View.OnClickListener backListener = new View.OnClickListener() {public void onClick(View v) {if (activity != null) {activity.finish();}}};//全屏按钮的监听private View.OnClickListener scaleListener = new View.OnClickListener() {@Overridepublic void onClick(View v) {if (activity != null) {switch (activity.getResources().getConfiguration().orientation) {case Configuration.ORIENTATION_LANDSCAPE://横屏activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);break;case Configuration.ORIENTATION_PORTRAIT://竖屏activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);break;}}}};private Handler myHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case HIDEFRAM://隐藏提示窗口mVolumeBrightnessLayout.setVisibility(View.GONE);mOperationTv.setVisibility(View.GONE);break;}}};private ImageView mIvScale;//videoview 用于对视频进行控制的等,activity为了退出public CustomMediaController(Context context, VideoView videoView, Activity activity) {super(context);this.context = context;this.videoView = videoView;this.activity = activity;WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);controllerWidth = wm.getDefaultDisplay().getWidth();mGestureDetector = new GestureDetector(context, new MyGestureListener());}@Overrideprotected View makeControllerView() {//加载自定义的layoutView v = LayoutInflater.from(context).inflate(R.layout.mymediacontroller, null);v.setMinimumHeight(controllerWidth);//获取控件img_back = (ImageButton) v.findViewById(R.id.top_back);mFileName = (TextView) v.findViewById(R.id.filename);//缩放控件mIvScale = (ImageView) v.findViewById(R.id.mediacontroller_scale);//视频名称if (mFileName != null) {mFileName.setText(videoname);}//声音控制mVolumeBrightnessLayout = (RelativeLayout) v.findViewById(R.id.operation_volume_brightness);mOperationBg = (ImageView) v.findViewById(R.id.operation_bg);mOperationTv = (TextView) v.findViewById(R.id.operation_tv);mOperationTv.setVisibility(View.GONE);mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//注册事件监听img_back.setOnClickListener(backListener);mIvScale.setOnClickListener(scaleListener);return v;}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {return true;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mGestureDetector.onTouchEvent(event)) return true;// 处理手势结束switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_UP:endGesture();break;}return super.onTouchEvent(event);}/*** 手势结束*/private void endGesture() {mVolume = -1;mBrightness = -1f;// 隐藏myHandler.removeMessages(HIDEFRAM);myHandler.sendEmptyMessageDelayed(HIDEFRAM, 1);}private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {@Overridepublic boolean onSingleTapUp(MotionEvent e) {return false;}/*** 因为使用的是自定义的mediaController 当显示后,mediaController会铺满屏幕,* 所以VideoView的点击事件会被拦截,所以重写控制器的手势事件,* 将全部的操作全部写在控制器中,* 因为点击事件被控制器拦截,无法传递到下层的VideoView,* 所以 原来的单机隐藏会失效,作为代替,* 在手势监听中onSingleTapConfirmed()添加自定义的隐藏/显示,** @param e* @return*/@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {//当手势结束,并且是单击结束时,控制器隐藏/显示toggleMediaControlsVisiblity();return super.onSingleTapConfirmed(e);}@Overridepublic boolean onDown(MotionEvent e) {return true;}//滑动事件监听@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {float mOldX = e1.getX(), mOldY = e1.getY();int y = (int) e2.getRawY();int x = (int) e2.getRawX();Display disp = activity.getWindowManager().getDefaultDisplay();int windowWidth = disp.getWidth();int windowHeight = disp.getHeight();if (mOldX > windowWidth * 3.0 / 4.0) {// 右边滑动 屏幕 3/4onVolumeSlide((mOldY - y) / windowHeight);} else if (mOldX < windowWidth * 1.0 / 4.0) {// 左边滑动 屏幕 1/4onBrightnessSlide((mOldY - y) / windowHeight);}return super.onScroll(e1, e2, distanceX, distanceY);}@Overridepublic boolean onDoubleTap(MotionEvent e) {playOrPause();return true;}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return super.onFling(e1, e2, velocityX, velocityY);}}/*** 滑动改变声音大小** @param percent*/private void onVolumeSlide(float percent) {if (mVolume == -1) {mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);if (mVolume < 0)mVolume = 0;// 显示mVolumeBrightnessLayout.setVisibility(View.VISIBLE);mOperationTv.setVisibility(VISIBLE);}int index = (int) (percent * mMaxVolume) + mVolume;if (index > mMaxVolume)index = mMaxVolume;else if (index < 0)index = 0;if (index >= 10) {mOperationBg.setImageResource(R.drawable.volmn_100);} else if (index >= 5 && index < 10) {mOperationBg.setImageResource(R.drawable.volmn_60);} else if (index > 0 && index < 5) {mOperationBg.setImageResource(R.drawable.volmn_30);} else {mOperationBg.setImageResource(R.drawable.volmn_no);}//DecimalFormat    df   = new DecimalFormat("######0.00");mOperationTv.setText((int) (((double) index / mMaxVolume) * 100) + "%");// 变更声音mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);}/*** 滑动改变亮度** @param percent*/private void onBrightnessSlide(float percent) {if (mBrightness < 0) {mBrightness = activity.getWindow().getAttributes().screenBrightness;if (mBrightness <= 0.00f)mBrightness = 0.50f;if (mBrightness < 0.01f)mBrightness = 0.01f;// 显示mVolumeBrightnessLayout.setVisibility(View.VISIBLE);mOperationTv.setVisibility(VISIBLE);}//获取window参数WindowManager.LayoutParams lpa = activity.getWindow().getAttributes();lpa.screenBrightness = mBrightness + percent;if (lpa.screenBrightness > 1.0f)lpa.screenBrightness = 1.0f;else if (lpa.screenBrightness < 0.01f)lpa.screenBrightness = 0.01f;activity.getWindow().setAttributes(lpa);//亮度调节(每个节点10)mOperationTv.setText((int) (lpa.screenBrightness * 100) + "%");if (lpa.screenBrightness * 100 >= 90) {mOperationBg.setImageResource(R.drawable.light_100);} else if (lpa.screenBrightness * 100 >= 80 && lpa.screenBrightness * 100 < 90) {mOperationBg.setImageResource(R.drawable.light_90);} else if (lpa.screenBrightness * 100 >= 70 && lpa.screenBrightness * 100 < 80) {mOperationBg.setImageResource(R.drawable.light_80);} else if (lpa.screenBrightness * 100 >= 60 && lpa.screenBrightness * 100 < 70) {mOperationBg.setImageResource(R.drawable.light_70);} else if (lpa.screenBrightness * 100 >= 50 && lpa.screenBrightness * 100 < 60) {mOperationBg.setImageResource(R.drawable.light_60);} else if (lpa.screenBrightness * 100 >= 40 && lpa.screenBrightness * 100 < 50) {mOperationBg.setImageResource(R.drawable.light_50);} else if (lpa.screenBrightness * 100 >= 30 && lpa.screenBrightness * 100 < 40) {mOperationBg.setImageResource(R.drawable.light_40);} else if (lpa.screenBrightness * 100 >= 20 && lpa.screenBrightness * 100 < 20) {mOperationBg.setImageResource(R.drawable.light_30);} else if (lpa.screenBrightness * 100 >= 10 && lpa.screenBrightness * 100 < 20) {mOperationBg.setImageResource(R.drawable.light_20);}}/*** 设置视频文件名** @param name*/public void setVideoName(String name) {videoname = name;if (mFileName != null) {mFileName.setText(name);}}/*** 隐藏或显示*/private void toggleMediaControlsVisiblity() {if (isShowing()) {hide();} else {show();}}/*** 播放/暂停*/private void playOrPause() {if (videoView != null)if (videoView.isPlaying()) {videoView.pause();} else {videoView.start();}}
}

代码都有注释,就不再说了,然后贴出MainActivity的代码:

package com.vitamiodemo;import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;import io.vov.vitamio.MediaPlayer;
import io.vov.vitamio.Vitamio;
import io.vov.vitamio.widget.VideoView;public class MainActivity extends AppCompatActivity implements MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener {//视频地址private String path = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";private Uri uri;private ProgressBar pb;private TextView downloadRateView, loadRateView;private CustomMediaController mCustomMediaController;private VideoView mVideoView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//定义全屏参数// int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;//获得当前窗体对象// Window window = MainActivity.this.getWindow();//设置当前窗体为全屏显示//window.setFlags(flag, flag);//必须写这个,初始化加载库文件Vitamio.isInitialized(this);//设置视频解码监听
//        if (!LibsChecker.checkVitamioLibs(this)) {//            return;
//        }setContentView(R.layout.activity_main);initView();initData();}//初始化控件private void initView() {mVideoView = (VideoView) findViewById(R.id.buffer);mCustomMediaController = new CustomMediaController(this, mVideoView, this);mCustomMediaController.setVideoName("小故事");pb = (ProgressBar) findViewById(R.id.probar);downloadRateView = (TextView) findViewById(R.id.download_rate);loadRateView = (TextView) findViewById(R.id.load_rate);}//初始化数据private void initData() {uri = Uri.parse(path);mVideoView.setVideoURI(uri);//设置视频播放地址mCustomMediaController.show(5000);mVideoView.setMediaController(mCustomMediaController);mVideoView.setVideoQuality(MediaPlayer.VIDEOQUALITY_HIGH);//高画质mVideoView.requestFocus();mVideoView.setOnInfoListener(this);mVideoView.setOnBufferingUpdateListener(this);mVideoView.setOnCompletionListener(this);//播放完成之后//函数回调,视频预完成之后调用mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mediaPlayer) {mediaPlayer.setPlaybackSpeed(1.0f);}});}@Overridepublic boolean onInfo(MediaPlayer mp, int what, int extra) {switch (what) {case MediaPlayer.MEDIA_INFO_BUFFERING_START://等待缓冲数据if (mVideoView.isPlaying()) {//是否在播放mVideoView.pause();pb.setVisibility(View.VISIBLE);downloadRateView.setText("");loadRateView.setText("");downloadRateView.setVisibility(View.VISIBLE);loadRateView.setVisibility(View.VISIBLE);}break;case MediaPlayer.MEDIA_INFO_BUFFERING_END://缓冲数据完毕mVideoView.start();//开始播放pb.setVisibility(View.GONE);downloadRateView.setVisibility(View.GONE);loadRateView.setVisibility(View.GONE);break;case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:downloadRateView.setText(extra + "kb/s" + "  ");break;}return true;}@Overridepublic void onBufferingUpdate(MediaPlayer mp, int percent) {loadRateView.setText(percent + "%");}@Overridepublic void onConfigurationChanged(Configuration newConfig) {//屏幕切换时,设置全屏if (mVideoView != null) {mVideoView.setVideoLayout(VideoView.VIDEO_LAYOUT_SCALE, 0);}super.onConfigurationChanged(newConfig);}//播放完成之后调用的方法@Overridepublic void onCompletion(MediaPlayer mp) {Toast.makeText(this, "播放结束", Toast.LENGTH_SHORT).show();}
}

需要注意的问题:
1.工程都整好之后,build的时候应该会出现以下的问题:

这时候回到下载之后的详细目录中的gradle.properties文件:

打开之后:

把框框中的这一段拿过来,复制到自己根项目的gradle.properties中,重新build下就没事了。

2.在自己项目的AndroidManifest中加入相关权限:

    <uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

还需要初始化vitamio:

<!-- 必须初始化 --><activity
            android:name="io.vov.vitamio.activity.InitActivity"android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"android:launchMode="singleTop"android:theme="@android:style/Theme.NoTitleBar"android:windowSoftInputMode="stateAlwaysHidden" />

完了之后基本大功告成了,效果:

GIF图片压缩了,画质有点差:在线工具分享给大家GIF工具

读者还想学习更多可以推荐下vitamio:
1、我不只是看客的博客
2、农民伯伯的随笔录

本编demo链接:下载源码

介绍到这里,感谢!!!

打造万能视频播放器——Vitamio相关推荐

  1. Vitamio库打造万能播放器(四)

    接着上一篇文章 Vitamio库打造万能播放器(三) 终于到了介绍vitamio的使用了,这个库使用起来非常简单,很容易上手,只需要将前面我们创建好的VideoPlayerActivity的Video ...

  2. Android万能播放器Vitamio

    以下是一些参考博文,记录已下,方便以后使用 Android 如何直播RTMP流 农民伯伯-使用Vitamio打造万能播放器-1 农民伯伯-使用Vitamio打造万能播放器-2 一直更新到第9篇 农民伯 ...

  3. c#万能视频播放器 (转)

    c#万能视频播放器(附代码) c#万能视频播放器 本人之前很多的文章中均提到了使用libvlc为播放器内核制作的播放器,也许有些朋友对此感兴趣,于是我用c#写了一个调用libvlc api实现的万能视 ...

  4. android能播放4k视频格式,安卓APP,无广告支持多种格式的万能视频播放器

    原标题:安卓APP,无广告支持多种格式的万能视频播放器 万能视频播放器 万能视频播放器是一款专业的视频播放工具.它支持所有视频格式,支持 4K/超高清视频文件,并且能够高清播放.它是安卓手机和平板上欣 ...

  5. 视频教程-FFmpeg+OpenGL ES+OpenSL ES打造Android视频播放器-Android

    FFmpeg+OpenGL ES+OpenSL ES打造Android视频播放器 从事Android移动端开发多年.主导开发过直播.电商.聊天等各种类型APP和游戏SDK:熟悉Android音视频开发 ...

  6. IINA 1.1.0beta1中文版 - Mac最强万能视频播放器

    IINA 1.1.0beta1中文版是一款免费开源的 macOS 万能视频播放器,基于 mpv (一款命令行启动/高度自定义配置的高性能跨平台开源播放器) 而来,使用 Swift 语言开发,拥有强大的 ...

  7. 万能视频播放器:恒星播放器 for Mac(1.500中文免费)

    恒星播放器 mac中文版是mac上一款超级好用的万能视频播放器,可以迅速播放各种格式的视频和音频,而且不损失播放质量,为您提供流畅舒适的高清视频播放体验,欢迎需要的朋友安装体验! 原文链接:https ...

  8. 恒星播放器 1.101中文版(万能视频播放器)

    恒星播放器 mac中文版是mac上一款超级好用的万能视频播放器,可以迅速播放各种格式的视频和音频,而且不损失播放质量,为您提供流畅舒适的高清视频播放体验! 支持所有格式视频播放 播放所有格式视频文件 ...

  9. IINA for Mac(万能视频播放器)中文版

    mac视频播放器哪个好用?这里推荐IINA for Mac,这款iina播放器支持多种格式的视频文件,哪怕是播放4K这些高分辨率视频都很流畅,看着简洁,但是功能很全面,重点是这款iina播放视频器的英 ...

  10. 轻播zFuse Pro for Mac(万能视频播放器)

    轻播zFuse mac中文版是一款简单强大好用的万能视频播放器,拥有简洁.轻快.好用的特点,支持同时播放多个视频文件,也可以对视频进行旋转或者对硬件进行加速操作,支持与外部的字幕文件配合使用,是您在苹 ...

最新文章

  1. itunes刷机一直正在恢复固件要多久_iPhone “已停用”,为什么刷机后仍是“已停用”的状态?...
  2. AI在智能建筑中的应用和发展
  3. category、protocol、delegate总结
  4. Linux进程间通信五 Posix 信号量简介与示例
  5. 【风险管理】风控决策系统
  6. Serverless 究竟是什么?
  7. Vue-cli项目中路由的基础用法,以及路由嵌套
  8. (43)Xilinx RAM IP核配置(四)(第9天)
  9. N 层应用程序中的数据检索和 CUD 操作 (LINQ to SQL)
  10. win10系统远程ubuntu(linux)桌面
  11. 《社会调查数据管理——基于Stata 14管理CGSS数据》一1.3 数据管理工作主体不明...
  12. Flutter修改App名称(Android+IOS)
  13. 常见WAF_WEB应用防火墙_运维必备_应用安全
  14. Java - constants
  15. 鏈接腳本、靜態庫、共享庫
  16. C语言:字母金字塔(输入一个大写字母,输出从A到这个字母的金字塔
  17. Mencoder常用视频转换参数
  18. chrome浏览器在https网站中打开http图片,打不开的解决方案
  19. 网络流量分析 NetFlow是什么 详解 科普 ~互联网业务流量监测技术的应用和设计---perfect
  20. 做成功的CIO从反省自己开始

热门文章

  1. 【IT项目管理】第5章 保障项目进度
  2. DELL台式机安装centos系统
  3. php用户注册表单验证
  4. CC1101接口库在STM32上的移植
  5. 【Python】SVM分类 特征标准化+网格搜索最优模型参数+十折交叉验证
  6. 2017年的知识清单
  7. python爬取京东商品_Python爬取京东的商品分类与链接
  8. 谷歌浏览器网页翻译失效,无法翻译成中文,且谷歌翻译api报404问题
  9. 内点、外点、边界点(yee些概念)
  10. spark数据清洗解决方案