Skip to content

Repository files navigation

README!!😴 OR-> Read The Fucking 👨‍💻‍Source Code //☀️ 🌧 ⚡️ 🌪 🌫 ❄️... (clone -b develop & init_modules)

KMedia-Logo

一个为Android打造的应用级媒体框架, 它可以助你快速搭建媒体应用. 内部重新定义Android MediaPlayer API并对其封装, 简化和扩展一些原生API不支持的功能. 其中涵盖了, AB播放/循环位置单元/间隔/循环变速播放媒体队列管理媒体服务/绑定音频后台/通知栏控制媒体按键自定义处理音频焦点管理媒体引擎切换/扩展... 等功能的快速实现. 以及, 对视频播放实现方面的封装. 其中将视频视图主要分为: 绘制层 控制组 控制层, 三个部分. 从而能够快速并灵活的实现Video相关应用的大部分功能, 包括 视频浮窗/拖动/调整位置大小横竖屏自动切换全屏锁定手势调整亮度/音量/进度字幕/切换/拖动视频段落/间隔复读视频续集/列表/循环播放动态切换视频控制层控制层分离... 等功能的快速实现.

使用

KMedia框架可以直接从JCenter添加依赖, 或者以子模块的形式添加到工程后再依赖.

从JCenter添加依赖 快捷方便

KMedia-CoreKMedia-UieKMedia-Exo

compile 'com.jcodeing:kmedia-core:r1.0.10'//KMedia核心模块
compile 'com.jcodeing:kmedia-uie:r1.0.10'//KMedia界面扩展模块 (可选)
compile 'com.jcodeing:kmedia-exo:r1.0.10'//KMedia媒体引擎扩展模块 (可选)

添加Submodule到工程后再依赖 自定义强

KMedia-Core-ForkKMedia-Uie-ForkKMedia-Exo-Fork

Step 1: Fork(↑)模块仓库到你的Github.

当然, 你也可以Fork我的仓库到你的私人remote仓库.
这步主要是为了生成一个remote仓库地址.

Step 2: 在你工程的根目录, 用 git submodule 命令添加子模块.

git submodule add "Your KMedia-Core Remote Repositorie Path" kmedia-core
git submodule add "Your KMedia-Uie Remote Repositorie Path" kmedia-uie
git submodule add "Your KMedia-Mpe Remote Repositorie Path" kmedia-mpe

Step 3: 根据上面所添加的子模块配置你工程的 settings.gradle 文件.

include ':kmedia-core'
include ':kmedia-uie'
include ':kmedia-exo'
project(':kmedia-exo').projectDir =newFile(settingsDir, 'kmedia-mpe/exo')

Step 4: 经过上面的添加配置步骤后, 你就可以在本地依赖并随时开发自定义KMedia的各个模块.

compile project(':kmedia-core')
compile project(':kmedia-uie')
compile project(':kmedia-exo')

演示

Demo-Screen-Record-1Demo-UiDemo-Screen-Record-2
===============================点击下载 KMedia Demo 4.9 MB===============================

k, 2017-9-24, PrivateKeyEntry,
证书指纹 (SHA1): 43:79:8C:03:1A:8A:2A:E8:CC:CA:D4:E3:63:9A:0F:70:29:C4:69:9B

Example 1: 简单的视频播放

首先在Layout中添加PlayerView.

<com.jcodeing.kmedia.video.PlayerView
android:id="@id/k_player_view"android:layout_width="match_parent"android:layout_height="200dp"/>

然后在Activity中将Player设置给PlayerView即可去播放.

Playerplayer = newPlayer(context).init(newAndroidMediaPlayer());
((PlayerView) findViewById(R.id.k_player_view)).setPlayer(player);
player.play(Uri.parse("video"));

最后当你确定不再播放时, 请调用以下方法去释放资源对象.

playerView.finish();
player.shutdown();

Example 2: 简单的视频浮窗

把Player交给VideoFloatingWindowController去显示即可浮屏播放.

Playerplayer = newPlayer(context).init(newExoMediaPlayer(context));
newVideoFloatingWindowController(getApplicationContext()).show(player);
layer.play(Uri.parse("video"));

最后当你确定不再播放时, 请调用以下方法去释放资源对象.

player.shutdown();
vFloatingWinControler.hide();

温馨提示,请根据你的具体使用需求合理申请WINDOW权限. 点击查看源码片段

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || OS.i().isMIUI()) {
//<!--Using WindowManager.LayoutParams.TYPE_PHONE For Floating Window View-->//<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;//2002
} else {
layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;//2005
}

Example 3: 简单的视频全屏

首先在AndroidManifest文件中, 添加configChanges 到你的Activity.

<activityandroid:configChanges="orientation|keyboardHidden|screenLayout|screenSize"
</activity>

然后在Activity中覆写onConfigurationChanged, 并在方法内处理横竖屏切换时需要隐藏和显示的View.

@OverridepublicvoidonConfigurationChanged(ConfigurationnewConfig) {
super.onConfigurationChanged(newConfig);
//Dispose view in control layer switchif (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
other.setVisibility(View.GONE);
} elseif (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
other.setVisibility(View.VISIBLE);
}
}

最后调用playerView启用方向助手即可实现简单的横竖屏自动切换.

playerView.setOrientationHelper(this, 1);//enable sensor

Example 4: 简单的使用控制层

ControlLayerView在Layout中的简单使用.

<com.jcodeing.kmedia.video.PlayerView
android:id="@id/k_player_view"android:layout_width="match_parent"android:layout_height="200dp">
<!--app:use_gesture_detector="true"--><!--可以开启简单的手势,上下滑动调整音量,左右滑动调整进度,双击播放/暂停等-->
<com.jcodeing.kmedia.video.ControlLayerView
android:id="@id/k_ctrl_layer_port"android:layout_width="match_parent"android:layout_height="match_parent"app:use_part_top="true">
<!--使用KID @id/k_ctrl_layer_port 可以标识为竖屏下的默认控制层--><!--使用KID @id/k_ctrl_layer_land 可以标识为横屏下的默认控制层--><!--app:interaction_area_always_visible="true"--><!--可以使各个Part部分一直为显示状态,不会在超时等情况下自动隐藏 --><!--app:part_top_min_height="17dp"--><!--该属性可以直接设置顶部Part的最小高度,另外相对应的还有_bottom --><!--View frame explain: |************************| | top | | | | | | left middle right | | | | | | bottom | |************************|--><!--其中bottom,middle都为默认使用--><!--想用其他Part请设置app:use_part_xxx="true"--><!--使用 KID 标识你要添加的各个Part--><!--=========@Top@=========--><!--android:id="@id/k_ctrl_layer_part_top"--><!--=========@Bottom@=========--><!--android:id="@id/k_ctrl_layer_part_bottom"--><!--=========@Xxx@=========--><!--android:id="@id/k_ctrl_layer_part_Xxx"-->
<XxxxLayoutandroid:id="@id/k_ctrl_layer_part_Xxx"android:layout_width="match_parent"android:layout_height="match_parent">
<!--XxxxLayout你可以随意替换为FrameLayout,RelativeLayout等--><!--内部你可以随心所欲添加你要的View--><!--其中可以使用一些公共的Id来快速完成不同的需求--><!--使用KID @id/k_play 和 @id/k_pause--><!--内部会自动帮你完成视频播放过程中,播放/暂停控制按钮的各个响应--><!--使用KID @id/k_position_tv 和 @id/k_duration_tv--><!--内部会自动按照你的标识,去显示播放进度和总时长--><!--使用KID @id/k_progress_any 和 @id/k_progress_bar--><!--内部会帮你处理,播放进展 两个id可以一起使用--><!--其中k_progress_any可以为你自定的任何View只要实现ProgressAny这个接口--><!--Tips: 这些 KID 不局限于Part内部使用,只要在控制层内都可以-->
</XxxxLayout>
<!--注意: 上面提到的Top,Bottom,Xxx这些方位Part的layout_xx属性 都是相对于内部方位容器的, 它们在控制层内的位置是固定的--><!--注意: 下面这两个Part的layout_xx属性是相对于这个控制层的--><!--=========@Buffer@=========--><!--android:id="@id/k_ctrl_layer_part_buffer"--><!--用于快速标识该View为播放中视频缓冲时显示--><!--=========@Tips@=========--><!--android:id="@id/k_ctrl_layer_part_tips_tv"--><!--这个TextView用于内部小提示,比如手势改变音量等-->
</com.jcodeing.kmedia.video.ControlLayerView>
</com.jcodeing.kmedia.video.PlayerView>

ControlLayerView在Activity中的简单使用. 点击查看源码片段

// ============================@ControlLaye@============================// 根据自己添加的控制层id,find到控制层View. 下面简单用控制层KID @id/k_ctrl_layer_port 来演示ControlLayerViewportCtrlLayer = (ControlLayerView) findViewById(R.id.k_ctrl_layer_port);
// 在initPart时,注意确保这个Part在Layout中 已经 app:use_part_xxx="true" (默认Part就不用再设置了)// Use top titleportTitle = (TextView) portCtrlLayer.initPart(R.id.part_top_tv);
// 自己处理Title, 如果使用了MediaQueue则可以在onCurrentQueueIndexUpdated(.)回调用更新你的Title//Custom top left ivportCtrlLayer.initPartIb(R.id.part_top_left_ib,
R.drawable.ic_go_back, "goBack").setOnClickListener(this);
//Custom bottom right ivportCtrlLayer.initPartIb(R.id.part_bottom_right_ib,
R.drawable.ic_go_full_screen, "goFullScreen").setOnClickListener(this);
// 自己处理不同Part按钮的点击事件// 其中goFullScreen Api调用: playerView.getOrientationHelper().goLandscape();//Custom remove positionportCtrlLayer.removePart(R.id.k_position_tv);
//Custom change bottom backgroundportCtrlLayer.findPart(R.id.k_ctrl_layer_part_bottom)
.setBackgroundResource(android.R.color.transparent);
//Custom change middle backgroundportCtrlLayer.findPart(R.id.k_ctrl_layer_part_middle)
.setBackgroundResource(android.R.color.transparent);
//Custom change middle play/pause/buffer/... size
((ImageButton) portCtrlLayer.findPart(R.id.k_play,
Metrics.dp2px(this, 31f), Metrics.dp2px(this, 31f))).setScaleType(ScaleType.FIT_CENTER);
//最后如果上面的改动中,涉及到SmartView(k_play/pause..) 需要去更新下portCtrlLayer.updateSmartView();

Example 5: 简单的使用PlayerService/Binding

首先在AndroidManifest文件中, 添加service.

<serviceandroid:name="com.jcodeing.kmedia.service.PlayerService"/>

然后在Activity中进行PlayerBinding. 点击查看源码片段

player = newPlayerBinding(this, PlayerService.class, newBindPlayer() {
@OverridepublicIPlayeronBindPlayer() {
returnnewPlayer(getApplicationContext())
.init(newExoMediaPlayer(getApplicationContext()));
}//Player service bind player, call back.(if is bound, not call back)
}, newBindingListener() {
@OverridepublicvoidonFirstBinding(PlayerServiceservice) {
//do something player service init operation.//可以在这里给服务设置一个Notifier,来实现在通知栏中控制音频播放service.setNotifier(newAudioQueueNotifier());
}//First binding call back.(if first binding finish, not call back)@OverridepublicvoidonBindingFinish() {
player.xxx();
}//Binding finish. Can play.
});
}

Example X: 更多例子参考

文档

Video

voidsetVideo(SurfaceViewsurfaceView);
voidsetVideo(TextureViewtextureView);
voidclearVideo();

AB

PsetAB(longstartPos, longendPos);
PsetABLoop(intloopMode, intloopInterval);
PsetAB(longstartPos, longendPos, intloopMode, intloopInterval);
PsetClearAB(booleanautoClear);
///////////////////////////////////////////////////////////player.setAB(abStart, abEnd, abLoop, abLoopInterval).play();

PlaybackSpeed

booleansetPlaybackSpeed(floatspeed);
floatgetPlaybackSpeed();

SeekTo

booleanseekTo(longms);
booleanseekTo(longms, intprocessingLevel);
voidseekToPending(longms);
longseekToProgress(intprogress, intprogressMax);
booleanfastForwardRewind(longms);

PositionUnit

PsetPositionUnitList(IPositionUnitListposUnitList);
intgetCurrentPositionUnitIndex();
voidsetCurrentPositionUnitIndex(intposUnitIndex);
longseekToPositionUnitIndex(intposUnitIndex);
intcalibrateCurrentPositionUnitIndex(longposition);
PsetEnabledPositionUnitLoop(booleanenabled, intloopMode, intloopInterval);
PsetPositionUnitLoopIndexList(ArrayList<Integer> posUnitLoopIndexList);

如果你想扩展媒体引擎, 可以去实现IMediaPlayer接口.

Source

voidsetDataSource(Stringpath);
voidsetDataSource(Contextcontext, Uriuri);
voidsetDataSource(Contextcontext, Uriuri, Map<String, String> headers);
voidsetDataSource(FileDescriptorfd);
UrigetDataSource();
voidprepareAsync();

Control

booleanstart();
booleanpause();
booleanseekTo(longms);
voidstop();

Control

voidsetAudioStreamType(intstreamtype);
voidsetVolume(floatleftVolume, floatrightVolume);
voidsetDisplay(SurfaceHoldersh);
voidsetSurface(Surfacesurface);
voidsetScreenOnWhilePlaying(booleanscreenOn);
voidsetLooping(booleanlooping);
booleanisLooping();
intgetVideoWidth();
intgetVideoHeight();
intgetAudioSessionId();
<!--========================================================--><!--=========@Use prefix "k_" to avoid duplication@=========--><!--========================================================--><!--=========@Player@=========-->
<itemname="k_player_view"type="id"/>
<itemname="k_content_frame"type="id"/>
<itemname="k_shutter"type="id"/>
<!--=========@Control@=========-->
<itemname="k_ctrl_group"type="id"/>
<itemname="k_ctrl_layer_port"type="id"/>
<itemname="k_ctrl_layer_land"type="id"/>
<!--=========@Layer Part-->
<itemname="k_ctrl_layer_part_top"type="id"/>
<itemname="k_ctrl_layer_part_bottom"type="id"/>
<itemname="k_ctrl_layer_part_left"type="id"/>
<itemname="k_ctrl_layer_part_right"type="id"/>
<itemname="k_ctrl_layer_part_middle"type="id"/>
<itemname="k_ctrl_layer_part_buffer"type="id"/>
<itemname="k_ctrl_layer_part_tips_tv"type="id"/>
<!--=========@Smart View-->
<itemname="k_play"type="id"/>
<itemname="k_pause"type="id"/>
<itemname="k_prev"type="id"/>
<itemname="k_next"type="id"/>
<itemname="k_rew"type="id"/>
<itemname="k_ffwd"type="id"/>
<itemname="k_position_tv"type="id"/>
<itemname="k_duration_tv"type="id"/>
<itemname="k_progress_bar"type="id"/>
<itemname="k_progress_any"type="id"/>
<!--=========@Extend-->
<itemname="k_switch_control_layer"type="id"/>
<!--=========@Floating@=========-->
<itemname="k_floating_view_close"type="id"/>
<itemname="k_floating_view_drag_location"type="id"/>
<itemname="k_floating_view_drag_size"type="id"/>
<!--=========@....................................@=========-->
<!--=========@AControlGroupView@=========-->
<attrformat="boolean"name="use_gesture_detector"/>
<declare-styleablename="AControlGroupView">
<attrformat="integer"name="show_timeout"/>
<attrformat="integer"name="rewind_increment"/>
<attrformat="integer"name="fast_forward_increment"/>
<attrformat="reference"name="default_control_layer_id"/>
<attrname="use_gesture_detector"/>
</declare-styleable>
<!--=========@AControlLayerView@=========-->
<declare-styleablename="AControlLayerView">
<attrformat="reference"name="control_layer_layout_id"/>
<!--=========@Part--><!--=====@Interaction Area-->
<attrformat="boolean"name="interaction_area_always_visible"/>
<attrformat="boolean"name="use_part_top"/>
<attrformat="boolean"name="use_part_bottom"/>
<attrformat="boolean"name="use_part_left"/>
<attrformat="boolean"name="use_part_right"/>
<attrformat="boolean"name="use_part_middle"/>
<attrformat="boolean"name="use_part_view_animation"/>
<attrformat="dimension"name="part_top_min_height"/>
<attrformat="dimension"name="part_bottom_min_height"/>
<!--=====@Other-->
<attrformat="boolean"name="use_part_buffer"/>
<attrformat="boolean"name="use_part_tips"/>
</declare-styleable>
<!--=========@AspectRatioView@=========--><!--Must be kept in sync with AspectRatioView-->
<attrformat="enum"name="resize_mode">
<enumname="fit"value="0"/>
<enumname="fixed_width"value="1"/>
<enumname="fixed_height"value="2"/>
<enumname="fill"value="3"/>
</attr>
<declare-styleablename="AspectRatioView">
<attrname="resize_mode"/>
</declare-styleable>
<!--=========@APlayerView@=========--><!--Must be kept in sync with APlayerView-->
<attrformat="enum"name="surface_type">
<enumname="none"value="0"/>
<enumname="surface_view"value="1"/>
<enumname="texture_view"value="2"/>
</attr>
<declare-styleablename="APlayerView">
<attrname="surface_type"/>
<attrformat="reference"name="player_layout_id"/>
<attrformat="boolean"name="use_control_group"/>
<!--Other-->
<attrname="resize_mode"/>
<attrname="android:layout_height"/>
<attrname="use_gesture_detector"/>
</declare-styleable>
<!--=========@.................@=========-->

注意

Vector低版本兼容

KMedia各个Module中均使用支持库, 来实现Android2.1(API 7)及更高版本中支持VectorDrawable

vectorDrawables.useSupportLibrary =true

值得注意的是, 如果你的应用需要运行在低于Android 5.0(API 21)的设备上,
并使用KMedia中ANotifier的createSimpleMediaNotificationBuilder时,
你就要考虑去兼容下低版本中Vector用在Notification上的情况.
可以将KMedia中用于Notification的Vector转成png, 放到Res中. 更多参见.

RequiresPermission权限申请

KMedia各个Module中不会去主动申请任何相关权限,
对于部分需要权限的API, 会加上注解 RequiresPermission 来提醒开发者去申请权限.
值得注意的是, 在显示浮窗相关的API处, 如果需要做兼容处理时, 需要使用到WINDOW权限.
此处并未加权限注解, 但在源码中有详细的注释, 上文中也提到了这点.
因为不是所有用户都需要去做兼容浮窗. 这个就要根据你的具体使用需求合理申请WINDOW权限.
还有一些特殊的定制系统, 需要手动处理 显示悬浮窗.

开发

很高兴同大家一起来开发KMedia. 做开源贡献者的一夸克.

首先我们做下开发准备工作

Step 1: 克隆KMedia仓库的开发分支到本地

git clone -b develop https://github.com/jcodeing/KMedia.git

Step 2: 初始化KMedia各个模块

也可以使用 git submodule 命令(init & update)来完成

./init_modules.sh

Step 3: 添加属于你的远程仓库地址

远程仓库地址, 可以通过Fork我的各个模块得到.

git remote add fork "Your KMedia Remote Repositorie Path"cd core
git remote add fork "Your KMedia-Core Remote Repositorie Path"cd uie
git remote add fork "Your KMedia-Uie Remote Repositorie Path"cd mpe
git remote add fork "Your KMedia-Mpe Remote Repositorie Path"

完成上面的步骤后用IDE(AndroidStudio)打开KMedia

其次我们来了解下开发规范

Code Style

KMedia 代码统一使用 Google Style 来 Reformat Code

由于KMedia为开源项目, 开发过程中可能会涉及到多种Reformat Code风格,
为了便于大家阅读和编码, 所以在此统一使用Google Style来Reformat Code.

代码中使用以下注释条来划分具体的功能模块

你可以将下面的注释条, 加入到IDE的Live Templates中, 从而方便使用.

// ============================@Xxx Xxx@============================// ============================@Xxx// =========@Xxx Xxx@=========// =========@Xxx

最后我们具体谈下从无到有的编码贡献流程.

首先, 你要有一个需求

  • 优化KMedia.
  • 解决KMedia的Bug,修复它.
  • 一拍脑门得到一个Idea, 想加到KMedia中, 让大家一起用.
  • 等等...

然后, 你就可以狂甩代码了

甩码过程中, 请遵循KMedia的编码规范.

其次, 注意下, 在编码完成部分后先提交到自己(Fork)的远程仓库

在准备工作中, 我们各自都有一个属于自己的远程仓库.
当代码还是雏形时, 先提交到自己的仓库进行维护. 同时, 可以随时合并Origin仓库的最新代码.

最后, 当你完成这个需求或者确定要先提交到Origin仓库时

可以到你Fork的Github仓库主页, 点击 Pull request 去 Create pull request 给我.
我看到后, 会第一时间处理你的 request. 从而时KMedia更加完善和强固.

===========================感谢你使用KMedia===========================

About

An application level media framework for Android. (RTFSC)

Topics

Resources

Stars

269 stars

Watchers

9 watching

Forks

Releases

Packages

Contributors

Languages