According to the live options, add the live entrance

This commit is contained in:
hongdaLL 2024-02-06 10:50:31 +08:00
parent 418a9c3e12
commit 915c8d0829
28 changed files with 312 additions and 162 deletions

View File

@ -1,6 +1,6 @@
<!-- Describe the main content of this issue's open source and the estimated time and content of the next open source --> <!-- Describe the main content of this issue's open source and the estimated time and content of the next open source -->
# Contents of this open source # Contents of first open source
- Open source code for playing intermediate layer (including: instance management, interface calls, etc.) - Open source code for playing intermediate layer (including: instance management, interface calls, etc.)
- RedPlayer is open source in the form of SDK this time - RedPlayer is open source in the form of SDK this time

View File

@ -3,9 +3,9 @@
![示例图片](./redplayer.jpg) ![示例图片](./redplayer.jpg)
![GitHub release (latest by date including pre-releases)](https://img.shields.io/badge/release-v1.0.0-blue) ![GitHub release (latest by date including pre-releases)](https://img.shields.io/badge/release-v1.0.0-blue)
![GitHub issues](https://img.shields.io/github/issues/badges/RedPlayer/open)
![GitHub pull requests](https://img.shields.io/bitbucket/pr/badge/RedPlayer?label=pull%20requests)
![GitHub license](https://img.shields.io/badge/license-LGPL2.1-blue) ![GitHub license](https://img.shields.io/badge/license-LGPL2.1-blue)
<!-- [GitHub issues](https://img.shields.io/github/issues/badges/RedPlayer/open) -->
<!-- [GitHub pull requests](https://img.shields.io/bitbucket/pr/badge/RedPlayer?label=pull%20requests) -->
Platform | Build Status Platform | Build Status
-------- | ------------ -------- | ------------
@ -64,6 +64,9 @@
### Open Content ### Open Content
```bash
# Describe the main contents of current open source and the estimated time and contents of the next open source
```
- [CONTENTS.md](CONTENTS.md) - [CONTENTS.md](CONTENTS.md)
### Usage ### Usage

View File

@ -104,6 +104,8 @@ public interface IMediaPlayer {
*/ */
void setVideoCacheDir(String dir); void setVideoCacheDir(String dir);
void setLiveMode(boolean enable);
void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
void setDataSource(Context context, Uri uri, Map<String, String> headers) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; void setDataSource(Context context, Uri uri, Map<String, String> headers) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;

View File

@ -138,6 +138,10 @@ public class AndroidMediaPlayer extends AbstractMediaPlayer {
public void setVideoCacheDir(String dir) { public void setVideoCacheDir(String dir) {
} }
@Override
public void setLiveMode(boolean enable) {
}
@Override @Override
public String getAudioCodecInfo() { public String getAudioCodecInfo() {
return "unknown"; return "unknown";

View File

@ -323,6 +323,13 @@ public class RedMediaPlayer extends AbstractMediaPlayer {
private native void _setVideoCacheDir(String dir); private native void _setVideoCacheDir(String dir);
@Override
public void setLiveMode(boolean enable) {
_setLiveMode(enable);
}
private native void _setLiveMode(boolean enable);
@Override @Override
public String getAudioCodecInfo() { public String getAudioCodecInfo() {

View File

@ -27,7 +27,7 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled true minifyEnabled false
signingConfig signingConfigs.release signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }

View File

@ -47,6 +47,11 @@
android:name=".input.XhsInputActivity" android:name=".input.XhsInputActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<!-- 直播输入页 -->
<activity
android:name=".live.XhsLiveInputActivity"
android:screenOrientation="portrait" />
<!-- 双列Feed页 --> <!-- 双列Feed页 -->
<activity <activity
android:name=".feed.XhsFeedActivity" android:name=".feed.XhsFeedActivity"

View File

@ -155,6 +155,10 @@ val VIDEO_ONE =
val VIDEO_TWO = val VIDEO_TWO =
Utils.decodeBase64("aHR0cDovL3Nucy12aWRlby1hbC54aHNjZG4uY29tL3NwZWN0cnVtLzAxZTI5NjRlNDE2YmUwNzcwMTgzNzAwMzgxMWI0ZTM5YzlfNTEzLm1wNA==") Utils.decodeBase64("aHR0cDovL3Nucy12aWRlby1hbC54aHNjZG4uY29tL3NwZWN0cnVtLzAxZTI5NjRlNDE2YmUwNzcwMTgzNzAwMzgxMWI0ZTM5YzlfNTEzLm1wNA==")
val LIVE_VIDEO =
Utils.decodeBase64("aHR0cDovL2xpdmUtcGxheS54aHNjZG4uY29tL2xpdmUvNTY5MDcyNTczNzk5OTkxNzI5LmZsdj91aWQ9NjQxMjkwNWUwMDAwMDAwMDEyMDEwNjIx")
/** the json data source */ /** the json data source */
val JSON_DATA_SOURCE = Utils.decodeBase64( val JSON_DATA_SOURCE = Utils.decodeBase64(
"eyJzdHJlYW0iOnsiaDI2NCI6W3sid2lkdGgiOjcyMCwiaGVpZ2h0IjoxMjgwLCJhdmdfYml0cmF0ZSI6MTAzNTE1NCwibWFzdGVyX3VybCI6Imh0dHBzOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC80MDUvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ5NjJmYl80MDUubXA0IiwiYmFja3VwX3VybHMiOlsiaHR0cHM6Ly9zbnMtdmlkZW8tYWwueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzQwNS8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4YzhhZDk2MmZiXzQwNS5tcDQiXX1dLCJoMjY1IjpbeyJ3aWR0aCI6NzIwLCJoZWlnaHQiOjEyODAsImF2Z19iaXRyYXRlIjo2ODcwMjYsIm1hc3Rlcl91cmwiOiJodHRwczovL3Nucy12aWRlby1hbC54aHNjZG4uY29tL3N0cmVhbS8xMTAvNDA1LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkOTYyZmJfNDA1Lm1wNCIsImJhY2t1cF91cmxzIjpbImh0dHBzOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC80MDUvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ5NjJmYl80MDUubXA0Il19LHsid2lkdGgiOjEwODAsImhlaWdodCI6MTkyMCwiYXZnX2JpdHJhdGUiOjkzOTU1NCwibWFzdGVyX3VybCI6Imh0dHBzOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC80MDUvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ5NjJmYl80MDUubXA0IiwiYmFja3VwX3VybHMiOlsiaHR0cHM6Ly9zbnMtdmlkZW8tYWwueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzQwNS8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4YzhhZDk2MmZiXzQwNS5tcDQiXX1dfX0=" "eyJzdHJlYW0iOnsiaDI2NCI6W3sid2lkdGgiOjcyMCwiaGVpZ2h0IjoxMjgwLCJhdmdfYml0cmF0ZSI6MTAzNTE1NCwibWFzdGVyX3VybCI6Imh0dHBzOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC80MDUvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ5NjJmYl80MDUubXA0IiwiYmFja3VwX3VybHMiOlsiaHR0cHM6Ly9zbnMtdmlkZW8tYWwueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzQwNS8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4YzhhZDk2MmZiXzQwNS5tcDQiXX1dLCJoMjY1IjpbeyJ3aWR0aCI6NzIwLCJoZWlnaHQiOjEyODAsImF2Z19iaXRyYXRlIjo2ODcwMjYsIm1hc3Rlcl91cmwiOiJodHRwczovL3Nucy12aWRlby1hbC54aHNjZG4uY29tL3N0cmVhbS8xMTAvNDA1LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkOTYyZmJfNDA1Lm1wNCIsImJhY2t1cF91cmxzIjpbImh0dHBzOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC80MDUvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ5NjJmYl80MDUubXA0Il19LHsid2lkdGgiOjEwODAsImhlaWdodCI6MTkyMCwiYXZnX2JpdHJhdGUiOjkzOTU1NCwibWFzdGVyX3VybCI6Imh0dHBzOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC80MDUvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ5NjJmYl80MDUubXA0IiwiYmFja3VwX3VybHMiOlsiaHR0cHM6Ly9zbnMtdmlkZW8tYWwueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzQwNS8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4YzhhZDk2MmZiXzQwNS5tcDQiXX1dfX0="

View File

@ -17,6 +17,7 @@ import com.xingin.openredplayer.feed.model.covertToVideoUrls
import com.xingin.openredplayer.floating.XhsFloatingService import com.xingin.openredplayer.floating.XhsFloatingService
import com.xingin.openredplayer.floating.XhsFloatingWindowHelper import com.xingin.openredplayer.floating.XhsFloatingWindowHelper
import com.xingin.openredplayer.input.XhsInputActivity import com.xingin.openredplayer.input.XhsInputActivity
import com.xingin.openredplayer.live.XhsLiveInputActivity
import com.xingin.openredplayer.player.XhsPlayerActivity import com.xingin.openredplayer.player.XhsPlayerActivity
import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.disposables.Disposable
import java.io.Serializable import java.io.Serializable
@ -24,6 +25,7 @@ import java.io.Serializable
class XhsHomeFragment : Fragment() { class XhsHomeFragment : Fragment() {
private lateinit var rootView: View private lateinit var rootView: View
private lateinit var itemUrlView: View private lateinit var itemUrlView: View
private lateinit var itemLiveUrlView: View
private lateinit var itemAlbumView: View private lateinit var itemAlbumView: View
private lateinit var itemFeedView: View private lateinit var itemFeedView: View
private lateinit var itemWindowView: View private lateinit var itemWindowView: View
@ -53,6 +55,8 @@ class XhsHomeFragment : Fragment() {
private fun initView() { private fun initView() {
itemUrlView = rootView.findViewById(R.id.layout_play_url) itemUrlView = rootView.findViewById(R.id.layout_play_url)
itemUrlView.setOnClickListener { openInputPage() } itemUrlView.setOnClickListener { openInputPage() }
itemLiveUrlView = rootView.findViewById(R.id.layout_play_live_url)
itemLiveUrlView.setOnClickListener { openLiveInputPage() }
itemAlbumView = rootView.findViewById(R.id.layout_local_album) itemAlbumView = rootView.findViewById(R.id.layout_local_album)
itemAlbumView.setOnClickListener { openAlbumPage() } itemAlbumView.setOnClickListener { openAlbumPage() }
itemFeedView = rootView.findViewById(R.id.layout_double_column) itemFeedView = rootView.findViewById(R.id.layout_double_column)
@ -68,6 +72,11 @@ class XhsHomeFragment : Fragment() {
startActivity(intent) startActivity(intent)
} }
private fun openLiveInputPage() {
val intent = Intent(this.activity, XhsLiveInputActivity::class.java)
startActivity(intent)
}
private fun openAlbumPage() { private fun openAlbumPage() {
permissionDisposable = rxPermissions.request( permissionDisposable = rxPermissions.request(
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,

View File

@ -47,6 +47,7 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
const val INTENT_KEY_URLS = "video_urls" const val INTENT_KEY_URLS = "video_urls"
const val INTENT_KEY_INDEX = "video_index" const val INTENT_KEY_INDEX = "video_index"
const val INTENT_KEY_SHOW_LOADING = "show_loading" const val INTENT_KEY_SHOW_LOADING = "show_loading"
const val INTENT_KEY_IS_LIVE = "is_live"
} }
/** DrawerLayout UI */ /** DrawerLayout UI */
@ -88,6 +89,7 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
/** data **/ /** data **/
private var isShowLoading = false private var isShowLoading = false
private var isLive = false
private var videoUrls = arrayListOf<String>() private var videoUrls = arrayListOf<String>()
private var playIndex = 0 private var playIndex = 0
@ -124,6 +126,7 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
private fun initData() { private fun initData() {
playIndex = intent.getIntExtra(INTENT_KEY_INDEX, 0) playIndex = intent.getIntExtra(INTENT_KEY_INDEX, 0)
isShowLoading = intent.getBooleanExtra(INTENT_KEY_SHOW_LOADING, false) isShowLoading = intent.getBooleanExtra(INTENT_KEY_SHOW_LOADING, false)
isLive = intent.getBooleanExtra(INTENT_KEY_IS_LIVE, false)
val list = intent.getSerializableExtra(INTENT_KEY_URLS) as List<String> val list = intent.getSerializableExtra(INTENT_KEY_URLS) as List<String>
videoUrls.addAll(list) videoUrls.addAll(list)
// init config // init config
@ -236,26 +239,34 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
/** update the video play progress */ /** update the video play progress */
private fun initVideoProgressView() { private fun initVideoProgressView() {
progressSeekBar = findViewById(R.id.video_view_seekbar) progressSeekBar = findViewById(R.id.video_view_seekbar)
progressSeekBar.setOnProgressChangedListener(object : if (!isLive) {
XhsProgressBar.OnProgressChangedListener { progressSeekBar.setOnProgressChangedListener(object :
override fun onProgressChanged( XhsProgressBar.OnProgressChangedListener {
signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float, fromUser: Boolean override fun onProgressChanged(
) { signSeekBar: XhsProgressBar,
} progress: Int,
progressFloat: Float,
fromUser: Boolean
) {
}
override fun getProgressOnActionUp( override fun getProgressOnActionUp(
signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float
) { ) {
val percentage = progress / 1000.0 val percentage = progress / 1000.0
videoPlayerView.seekTo((percentage * videoPlayerView.duration).toInt()) videoPlayerView.seekTo((percentage * videoPlayerView.duration).toInt())
progressSeekBar.setProgress(progressFloat) progressSeekBar.setProgress(progressFloat)
} }
override fun getProgressOnFinally( override fun getProgressOnFinally(
signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float, fromUser: Boolean signSeekBar: XhsProgressBar,
) { progress: Int,
} progressFloat: Float,
}) fromUser: Boolean
) {
}
})
}
// update the seekbar // update the seekbar
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
var position: Int var position: Int
@ -342,7 +353,10 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
} }
videoPlayerView.setOnErrorListener { _, what, extra -> handleVideoErrorEvent(what, extra) } videoPlayerView.setOnErrorListener { _, what, extra -> handleVideoErrorEvent(what, extra) }
// 1.1 set the video url and wait play // 1.1 set the video url and wait play
videoPlayerView.setVideoPath(getCurrentPlayUrl()) videoPlayerView.setVideoPath(getCurrentPlayUrl(), isLive)
if (isLive) {
playPauseButton.visibility = View.GONE
}
} }
/** init the video debug info view */ /** init the video debug info view */
@ -539,7 +553,7 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
/** handle video speed play: support 0.751.01.251.52.0 */ /** handle video speed play: support 0.751.01.251.52.0 */
private fun handleVideoSpeedPlay(speed: Float, isShow: Boolean) { private fun handleVideoSpeedPlay(speed: Float, isShow: Boolean) {
if (!videoPlayerView.isPlaying) { if (!videoPlayerView.isPlaying || isLive) {
return return
} }
videoPlayerView.setSpeed(speed) videoPlayerView.setSpeed(speed)

View File

@ -29,6 +29,7 @@ import java.io.IOException;
public class XhsVideoPlayerView extends FrameLayout implements MediaController.MediaPlayerControl { public class XhsVideoPlayerView extends FrameLayout implements MediaController.MediaPlayerControl {
private String TAG = "VideoPlayerView"; private String TAG = "VideoPlayerView";
private String mUrl; private String mUrl;
private Boolean mIsLive;
private static final int STATE_ERROR = -1; private static final int STATE_ERROR = -1;
private static final int STATE_IDLE = 0; private static final int STATE_IDLE = 0;
private static final int STATE_PREPARING = 1; private static final int STATE_PREPARING = 1;
@ -136,6 +137,11 @@ public class XhsVideoPlayerView extends FrameLayout implements MediaController.M
} }
} }
public void setVideoPath(String path, boolean isLive) {
mIsLive = isLive;
setVideoPath(path);
}
public void setVideoPath(String path) { public void setVideoPath(String path) {
mUrl = path; mUrl = path;
mSeekWhenPrepared = 0; mSeekWhenPrepared = 0;
@ -217,10 +223,11 @@ public class XhsVideoPlayerView extends FrameLayout implements MediaController.M
private IMediaPlayer createMediaPlayer() { private IMediaPlayer createMediaPlayer() {
IMediaPlayer mediaPlayer = mSettings.getEnableAndroidMediaPlayer() ? new AndroidMediaPlayer() : new RedMediaPlayer(); IMediaPlayer mediaPlayer = mSettings.getEnableAndroidMediaPlayer() ? new AndroidMediaPlayer() : new RedMediaPlayer();
// 设置播放器使用硬解: 具体是否有硬解可用,内核来决定
mediaPlayer.setEnableMediaCodec(mSettings.getUsingMediaCodec()); mediaPlayer.setEnableMediaCodec(mSettings.getUsingMediaCodec());
// 设置视频缓存路径: 边播边下的视频缓存路径设置后无网可播播过的视频 // set video cache url: downloading when playing, if network error, can also play
mediaPlayer.setVideoCacheDir(Utils.INSTANCE.getVideCacheDir()); mediaPlayer.setVideoCacheDir(Utils.INSTANCE.getVideCacheDir());
// set whether url is live
mediaPlayer.setLiveMode(mIsLive);
return mediaPlayer; return mediaPlayer;
} }

View File

@ -29,7 +29,7 @@ class XhsPlayerSettings(context: Context) {
val usingMediaCodec: Boolean val usingMediaCodec: Boolean
get() { get() {
val key = mAppContext.getString(R.string.pref_key_using_media_codec) val key = mAppContext.getString(R.string.pref_key_using_media_codec)
return mSharedPreferences.getBoolean(key, false) return mSharedPreferences.getBoolean(key, true)
} }
val enableAndroidMediaPlayer: Boolean val enableAndroidMediaPlayer: Boolean

View File

@ -30,6 +30,32 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/layout_play_live_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:src="@drawable/icon_live" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="13dp"
android:text="@string/home_item_live_url"
android:textColor="@android:color/white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/layout_local_album" android:id="@+id/layout_local_album"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -4,8 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/player_bottom_action_layout" android:id="@+id/player_bottom_action_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
tools:showIn="@layout/activity_xhs_player_layout">
<!-- 蒙层 --> <!-- 蒙层 -->
<View <View

View File

@ -17,7 +17,7 @@
<string name="pref_title_player_core">Media Player Setting</string> <string name="pref_title_player_core">Media Player Setting</string>
<string name="pref_key_using_media_codec">pref.using_media_codec</string> <string name="pref_key_using_media_codec">pref.using_media_codec</string>
<string name="pref_title_using_media_codec">Use MediaCodec Decode</string> <string name="pref_title_using_media_codec">Use MediaCodec Decode</string>
<string name="pref_summary_using_media_codec">Default, software decoding is used. opening, hardware decoder used"</string> <string name="pref_summary_using_media_codec">Default, MediaCodec decoding is used. opening, soft decoder used</string>
<string name="pref_key_using_android_media_player">pref.using_android_media_player</string> <string name="pref_key_using_android_media_player">pref.using_android_media_player</string>
<string name="pref_title_using_android_media_player">Use System Media Player</string> <string name="pref_title_using_android_media_player">Use System Media Player</string>
@ -55,7 +55,8 @@
<string name="home_tab_home_title">Home</string> <string name="home_tab_home_title">Home</string>
<string name="home_tab_setting_title">Settings</string> <string name="home_tab_setting_title">Settings</string>
<string name="home_title">Demo Of Video Feed Page</string> <string name="home_title">Demo Of Video Feed Page</string>
<string name="home_item_url">Play URL/JSON</string> <string name="home_item_url">URL/JSON</string>
<string name="home_item_live_url">LIVE URL</string>
<string name="home_item_album">Play Local Resource</string> <string name="home_item_album">Play Local Resource</string>
<string name="home_item_feed">Online Samples</string> <string name="home_item_feed">Online Samples</string>
<string name="home_item_window">Pip Samples</string> <string name="home_item_window">Pip Samples</string>
@ -70,6 +71,10 @@
<string name="input_clear">Clear</string> <string name="input_clear">Clear</string>
<string name="input_jump">Play</string> <string name="input_jump">Play</string>
<string name="input_live_url_title">Demo of Live URL Page</string>
<string name="input_live_url_default">Live URL</string>
<string name="input_live_url_hint">Input live url to play</string>
<string name="play_section">All</string> <string name="play_section">All</string>
<string name="play_speed">Playing Speed</string> <string name="play_speed">Playing Speed</string>
<string name="play_show_or_hide">Show/Hide</string> <string name="play_show_or_hide">Show/Hide</string>

View File

@ -19,7 +19,7 @@
<PreferenceCategory android:title="@string/pref_title_player_core"> <PreferenceCategory android:title="@string/pref_title_player_core">
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="true"
android:key="@string/pref_key_using_media_codec" android:key="@string/pref_key_using_media_codec"
android:persistent="true" android:persistent="true"
android:summary="@string/pref_summary_using_media_codec" android:summary="@string/pref_summary_using_media_codec"

View File

@ -88,7 +88,13 @@ self.player.notPauseGlviewBackground = NO;
- Set whether to use hardware decoding (VideoToolbox). **The default is software decoding (NO**): - Set whether to use hardware decoding (VideoToolbox). **The default is software decoding (NO**):
```objective-c ```objective-c
[self.player setEnableVTB:![RedPlayerSettings sharedInstance].softDecoder]; [self.player setEnableVTB:YES];
```
- **Be sure** to set it to YES if it is a **live broadcast** scene. **The default is NO**:
```objective-c
[self.player setLiveMode:YES];
``` ```
- Set the cache path. **The default is no caching**: - Set the cache path. **The default is no caching**:

View File

@ -184,6 +184,9 @@ typedef NS_ENUM(NSUInteger, RedRenderType) {
/// Sets whether Video Toolbox is enabled. /// Sets whether Video Toolbox is enabled.
- (void)setEnableVTB:(BOOL)enable; - (void)setEnableVTB:(BOOL)enable;
/// Sets whether video is live source.
- (void)setLiveMode:(BOOL)enable;
/// Returns whether Video Toolbox is open. /// Returns whether Video Toolbox is open.
- (BOOL)isVideoToolboxOpen; - (BOOL)isVideoToolboxOpen;

View File

@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 51; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@ -428,7 +428,7 @@
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WA7K45T45A; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = B92R44VSX2;
EXCLUDED_ARCHS = "armv7 armv7s"; EXCLUDED_ARCHS = "armv7 armv7s";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -484,7 +484,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WA7K45T45A; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = B92R44VSX2;
EXCLUDED_ARCHS = "armv7 armv7s"; EXCLUDED_ARCHS = "armv7 armv7s";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (

View File

@ -82,14 +82,14 @@ class MainViewController: UIViewController,
} }
func playURLResource() { func playURLResource(isLive: Bool) {
inputAlertViewBgView = UIView(frame: self.view.bounds) inputAlertViewBgView = UIView(frame: self.view.bounds)
if let bgView = inputAlertViewBgView { if let bgView = inputAlertViewBgView {
bgView.backgroundColor = UIColor.gray.withAlphaComponent(0.2) bgView.backgroundColor = UIColor.gray.withAlphaComponent(0.2)
self.view.addSubview(bgView) self.view.addSubview(bgView)
} }
inputAlertView = RedAlertView(frame: CGRect(x: self.view.bounds.size.width / 2, y: self.view.bounds.size.height / 2, width: 0, height: 0)) inputAlertView = RedAlertView(frame: CGRect(x: self.view.bounds.size.width / 2, y: self.view.bounds.size.height / 2, width: 0, height: 0), isLive: isLive)
inputAlertView?.translatesAutoresizingMaskIntoConstraints = false inputAlertView?.translatesAutoresizingMaskIntoConstraints = false
if let alertView = inputAlertView { if let alertView = inputAlertView {
alertView.playButtonTapped = { [weak self] input, isJSON in alertView.playButtonTapped = { [weak self] input, isJSON in
@ -109,7 +109,7 @@ class MainViewController: UIViewController,
self?.present(alertController, animated: true, completion: nil) self?.present(alertController, animated: true, completion: nil)
return return
} }
RedDemoPlayerViewController.present(from: self, withTitle: "", url: input, isJson: isJSON, playList: nil, playListIndex: 0, completion: nil, close: nil) RedDemoPlayerViewController.present(from: self, withTitle: "", url: input, isJson: isJSON, isLive: isLive, playList: nil, playListIndex: 0, completion: nil, close: nil)
} }
alertView.closeButtonTapped = { [weak self] in alertView.closeButtonTapped = { [weak self] in
self?.closeAlertView() self?.closeAlertView()
@ -249,7 +249,7 @@ class MainViewController: UIViewController,
DispatchQueue.main.async { DispatchQueue.main.async {
self.dismiss(animated: true) { self.dismiss(animated: true) {
RedDemoPlayerViewController.present(from: self, withTitle: "", url: firstUrl, isJson: false, playList: videoDictArray, playListIndex: 0, completion: nil, close: nil) RedDemoPlayerViewController.present(from: self, withTitle: "", url: firstUrl, isJson: false, isLive: false, playList: videoDictArray, playListIndex: 0, completion: nil, close: nil)
} }
} }
} }
@ -316,7 +316,7 @@ class MainViewController: UIViewController,
extension MainViewController: UITableViewDataSource { extension MainViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3 return 4
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@ -324,12 +324,15 @@ extension MainViewController: UITableViewDataSource {
switch indexPath.row { switch indexPath.row {
case 0: case 0:
cell.iconImageView.image = UIImage(named: "link")?.withRenderingMode(.alwaysTemplate) cell.iconImageView.image = UIImage(named: "vod_link")?.withRenderingMode(.alwaysTemplate)
cell.titleLabel.text = "Play URL / Json" cell.titleLabel.text = "VOD URL / Json"
case 1: case 1:
cell.iconImageView.image = UIImage(named: "live_link")?.withRenderingMode(.alwaysTemplate)
cell.titleLabel.text = "LIVE URL"
case 2:
cell.iconImageView.image = UIImage(named: "album")?.withRenderingMode(.alwaysTemplate) cell.iconImageView.image = UIImage(named: "album")?.withRenderingMode(.alwaysTemplate)
cell.titleLabel.text = "Play Local Resources" cell.titleLabel.text = "Play Local Resources"
case 2: case 3:
cell.iconImageView.image = UIImage(named: "flow_icon")?.withRenderingMode(.alwaysTemplate) cell.iconImageView.image = UIImage(named: "flow_icon")?.withRenderingMode(.alwaysTemplate)
cell.titleLabel.text = "Video Samples" cell.titleLabel.text = "Video Samples"
default: default:
@ -352,10 +355,12 @@ extension MainViewController: UITableViewDelegate {
switch indexPath.row { switch indexPath.row {
case 0: case 0:
playURLResource() playURLResource(isLive: false)
case 1: case 1:
playLocalVideo() playURLResource(isLive: true)
case 2: case 2:
playLocalVideo()
case 3:
showRedFlowViewController() showRedFlowViewController()
default: default:
break break

View File

@ -17,6 +17,7 @@ typedef void(^DemoCloseBlock)(void);
withTitle:(NSString *)title withTitle:(NSString *)title
URL:(NSString *)url URL:(NSString *)url
isJson:(BOOL)isJson isJson:(BOOL)isJson
isLive:(BOOL)isLive
playList:(NSArray *)playList playList:(NSArray *)playList
playListIndex:(NSInteger)playListIndex playListIndex:(NSInteger)playListIndex
completion:(void(^)(void))completion completion:(void(^)(void))completion

View File

@ -34,6 +34,7 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
@interface RedDemoPlayerViewController () <RedMediaControlDelegate> @interface RedDemoPlayerViewController () <RedMediaControlDelegate>
@property (nonatomic, strong) NSString *url; @property (nonatomic, strong) NSString *url;
@property (nonatomic, assign) BOOL isJson; @property (nonatomic, assign) BOOL isJson;
@property (nonatomic, assign) BOOL isLive;
@property (nonatomic, strong) NSArray *playList; @property (nonatomic, strong) NSArray *playList;
@property (nonatomic, assign) NSInteger playListIndex; @property (nonatomic, assign) NSInteger playListIndex;
@property (nonatomic, assign) RedScalingMode scaleMode; @property (nonatomic, assign) RedScalingMode scaleMode;
@ -88,13 +89,14 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
withTitle:(NSString *)title withTitle:(NSString *)title
URL:(NSString *)url URL:(NSString *)url
isJson:(BOOL)isJson isJson:(BOOL)isJson
isLive:(BOOL)isLive
playList:(NSArray *)playList playList:(NSArray *)playList
playListIndex:(NSInteger)playListIndex playListIndex:(NSInteger)playListIndex
completion:(void (^)(void))completion completion:(void (^)(void))completion
closeBlock:(void (^)(void))closeBlock { closeBlock:(void (^)(void))closeBlock {
[RedDemoPlayerViewController setupLogCallback]; [RedDemoPlayerViewController setupLogCallback];
RedDemoPlayerViewController *demoPlayerVC = [[RedDemoPlayerViewController alloc] initWithURL:url isJson:isJson]; RedDemoPlayerViewController *demoPlayerVC = [[RedDemoPlayerViewController alloc] initWithURL:url isJson:isJson isLive:isLive];
demoPlayerVC.playList = [playList copy]; demoPlayerVC.playList = [playList copy];
demoPlayerVC.playListIndex = playListIndex; demoPlayerVC.playListIndex = playListIndex;
demoPlayerVC.modalPresentationStyle = UIModalPresentationFullScreen; demoPlayerVC.modalPresentationStyle = UIModalPresentationFullScreen;
@ -103,11 +105,12 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
demoPlayerVC.closeBlock = closeBlock; demoPlayerVC.closeBlock = closeBlock;
} }
- (instancetype)initWithURL:(NSString *)url isJson:(BOOL)isJson { - (instancetype)initWithURL:(NSString *)url isJson:(BOOL)isJson isLive:(BOOL)isLive {
self = [super init]; self = [super init];
if (self) { if (self) {
self.url = url; self.url = url;
self.isJson = isJson; self.isJson = isJson;
self.isLive = isLive;
} }
return self; return self;
} }
@ -216,7 +219,9 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
[self.player setMute:NO]; [self.player setMute:NO];
[self.player setEnableVTB:![RedPlayerSettings sharedInstance].softDecoder]; [self.player setEnableVTB:![RedPlayerSettings sharedInstance].softDecoder];
[self.player setVideoCacheDir:[RedMediaUtil cachePath]]; [self.player setVideoCacheDir:[RedMediaUtil cachePath]];
if (self.isLive) {
[self.player setLiveMode:YES]; // config live mode
}
[self.view addSubview:self.player.view]; [self.view addSubview:self.player.view];
[self.view sendSubviewToBack:self.player.view]; [self.view sendSubviewToBack:self.player.view];
[self installMovieNotificationObservers]; [self installMovieNotificationObservers];
@ -417,6 +422,9 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
- (void)closeClick:(id)sender { - (void)closeClick:(id)sender {
[self shutdown]; [self shutdown];
[self stopLoading]; [self stopLoading];
[self.mediaControl removeFromSuperview];
[self.mediaControl stopRefresh];
self.mediaControl = nil;
[self dismissViewControllerAnimated:NO completion:^{ [self dismissViewControllerAnimated:NO completion:^{
if (self.closeBlock) { if (self.closeBlock) {
self.closeBlock(); self.closeBlock();
@ -498,7 +506,7 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
self.mediaControl.delegatePlayer = nil; self.mediaControl.delegatePlayer = nil;
self.mediaControl = nil; self.mediaControl = nil;
} }
self.mediaControl = [[RedMediaControl alloc] initWithFrame:CGRectZero viewController:self playList:[self.playList copy] isLoop:self.isLoop]; self.mediaControl = [[RedMediaControl alloc] initWithFrame:CGRectZero viewController:self playList:[self.playList copy] isLoop:self.isLoop isLive:self.isLive];
self.mediaControl.playListIndex = _playListIndex; self.mediaControl.playListIndex = _playListIndex;
self.mediaControl.layer.cornerRadius = 10; self.mediaControl.layer.cornerRadius = 10;
self.mediaControl.layer.masksToBounds = YES; self.mediaControl.layer.masksToBounds = YES;
@ -526,7 +534,6 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
} }
- (void)hideMediaControl { - (void)hideMediaControl {
[self.mediaControl stopRefresh];
self.mediaControl.hidden = YES; self.mediaControl.hidden = YES;
self.closeButton.hidden = YES; self.closeButton.hidden = YES;
self.scaleModeButton.hidden = YES; self.scaleModeButton.hidden = YES;
@ -597,21 +604,25 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
// MARK: Gestures // MARK: Gestures
- (void)addGestureControls { - (void)addGestureControls {
// Long Press - 2x Playback Speed UITapGestureRecognizer *doubleTap = nil;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; if (!self.isLive) {
[self.view addGestureRecognizer:longPress]; // Long Press - 2x Playback Speed
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
// Double Tap [self.view addGestureRecognizer:longPress];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
doubleTap.numberOfTapsRequired = 2; // Double Tap
[self.view addGestureRecognizer:doubleTap]; doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
doubleTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:doubleTap];
}
// Single Tap - Show/Hide tips // Single Tap - Show/Hide tips
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1; singleTap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:singleTap]; [self.view addGestureRecognizer:singleTap];
if (doubleTap) {
[singleTap requireGestureRecognizerToFail:doubleTap]; [singleTap requireGestureRecognizerToFail:doubleTap];
}
// Pan - Volume/Brightness // Pan - Volume/Brightness
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
@ -962,5 +973,9 @@ typedef NS_ENUM(NSUInteger, RedTipsPosition) {
[self performSelector:@selector(hideMediaControl) withObject:nil afterDelay:5]; [self performSelector:@selector(hideMediaControl) withObject:nil afterDelay:5];
} }
- (void)mediaControlLiveRefresh {
[self shutdown];
[self setupPlayer];
}
@end @end

View File

@ -14,6 +14,7 @@
- (void)switchPrevious:(id)sender; - (void)switchPrevious:(id)sender;
- (void)switchNext:(id)sender; - (void)switchNext:(id)sender;
- (void)onClickMute:(id)sender; - (void)onClickMute:(id)sender;
- (void)mediaControlLiveRefresh;
- (void)didSliderTouchUpInside; - (void)didSliderTouchUpInside;
- (void)slideCancelMediaControlHide; - (void)slideCancelMediaControlHide;
- (void)slideContinueMediaControlHide; - (void)slideContinueMediaControlHide;
@ -35,7 +36,8 @@
- (id)initWithFrame:(CGRect)frame - (id)initWithFrame:(CGRect)frame
viewController:(UIViewController *)viewController viewController:(UIViewController *)viewController
playList:(NSArray *)playList playList:(NSArray *)playList
isLoop:(BOOL)isLoop; isLoop:(BOOL)isLoop
isLive:(BOOL)isLive;
- (float)progressValue; - (float)progressValue;
- (void)updatePlayPauseBtn:(BOOL)isPlaying; - (void)updatePlayPauseBtn:(BOOL)isPlaying;
- (void)updateMuteBtn:(BOOL)isMute; - (void)updateMuteBtn:(BOOL)isMute;

View File

@ -26,10 +26,13 @@
@property (nonatomic, strong) UIButton *muteButton; @property (nonatomic, strong) UIButton *muteButton;
@property (nonatomic, strong) UIButton *loopButton; @property (nonatomic, strong) UIButton *loopButton;
@property (nonatomic, assign) BOOL isLoop;
@property (nonatomic, strong) UIButton *listButton; @property (nonatomic, strong) UIButton *listButton;
@property (nonatomic, strong) UIButton *liveRefreshButton;
@property (nonatomic, strong) NSArray *playList; @property (nonatomic, strong) NSArray *playList;
@property (nonatomic, assign) BOOL isLoop;
@property (nonatomic, assign) BOOL isLive;
@end @end
@implementation RedMediaControl { @implementation RedMediaControl {
@ -45,7 +48,8 @@
- (id)initWithFrame:(CGRect)frame - (id)initWithFrame:(CGRect)frame
viewController:(UIViewController *)viewController viewController:(UIViewController *)viewController
playList:(NSArray *)playList playList:(NSArray *)playList
isLoop:(BOOL)isLoop { isLoop:(BOOL)isLoop
isLive:(BOOL)isLive {
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
if (self) { if (self) {
_viewController = viewController; _viewController = viewController;
@ -54,6 +58,7 @@
self.playListIndex = 0; self.playListIndex = 0;
self.playList = playList; self.playList = playList;
self.isLoop = isLoop; self.isLoop = isLoop;
self.isLive = isLive;
[self setupSubviews]; [self setupSubviews];
[self startRefresh]; [self startRefresh];
@ -71,56 +76,71 @@
#define kTimeLabelWidth 50 #define kTimeLabelWidth 50
#define kTimeLabelHeight 30 #define kTimeLabelHeight 30
- (void)setupSubviews { - (void)setupSubviews {
if (!self.playButton) { if (!_isLive) {
self.playButton = [[UIButton alloc] initWithFrame:CGRectZero]; if (!self.playButton) {
[self.playButton setImage:[UIImage imageNamed:@"btn_player_pause"] forState:UIControlStateNormal]; self.playButton = [[UIButton alloc] initWithFrame:CGRectZero];
[self.playButton addTarget:self action:@selector(playAction:) forControlEvents:UIControlEventTouchUpInside]; [self.playButton setImage:[UIImage imageNamed:@"btn_player_pause"] forState:UIControlStateNormal];
[self addSubview:self.playButton]; [self.playButton addTarget:self action:@selector(playAction:) forControlEvents:UIControlEventTouchUpInside];
} [self addSubview:self.playButton];
[self.playButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.centerY.mas_equalTo(self);
make.width.height.mas_equalTo(kPlayButtonWidth);
}];
if (self.playList.count > 0) {
if (!self.preButton) {
self.preButton = [[UIButton alloc] initWithFrame:CGRectZero];
[self.preButton setImage:[UIImage imageNamed:@"icon_pre"] forState:UIControlStateNormal];
[self.preButton addTarget:self action:@selector(preAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.preButton];
} }
self.preButton.enabled = (self.playListIndex > 0); [self.playButton mas_makeConstraints:^(MASConstraintMaker *make) {
[self.preButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.centerY.mas_equalTo(self);
make.centerY.mas_equalTo(self);
make.right.mas_equalTo(self.playButton.mas_left).offset(-20);
make.width.height.mas_equalTo(kPlayButtonWidth); make.width.height.mas_equalTo(kPlayButtonWidth);
}]; }];
if (!self.nextButton) {
self.nextButton = [[UIButton alloc] initWithFrame:CGRectZero]; if (self.playList.count > 0) {
[self.nextButton setImage:[UIImage imageNamed:@"icon_next"] forState:UIControlStateNormal]; if (!self.preButton) {
[self.nextButton addTarget:self action:@selector(nextAction:) forControlEvents:UIControlEventTouchUpInside]; self.preButton = [[UIButton alloc] initWithFrame:CGRectZero];
[self addSubview:self.nextButton]; [self.preButton setImage:[UIImage imageNamed:@"icon_pre"] forState:UIControlStateNormal];
[self.preButton addTarget:self action:@selector(preAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.preButton];
}
self.preButton.enabled = (self.playListIndex > 0);
[self.preButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self);
make.right.mas_equalTo(self.playButton.mas_left).offset(-20);
make.width.height.mas_equalTo(kPlayButtonWidth);
}];
if (!self.nextButton) {
self.nextButton = [[UIButton alloc] initWithFrame:CGRectZero];
[self.nextButton setImage:[UIImage imageNamed:@"icon_next"] forState:UIControlStateNormal];
[self.nextButton addTarget:self action:@selector(nextAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.nextButton];
}
self.nextButton.enabled = (self.playListIndex < self.playList.count - 1);
[self.nextButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self);
make.left.mas_equalTo(self.playButton.mas_right).offset(20);
make.width.height.mas_equalTo(kPlayButtonWidth);
}];
} }
self.nextButton.enabled = (self.playListIndex < self.playList.count - 1); } else { // Live refresh
[self.nextButton mas_makeConstraints:^(MASConstraintMaker *make) { if (!self.liveRefreshButton) {
make.centerY.mas_equalTo(self); self.liveRefreshButton = [[UIButton alloc] initWithFrame:CGRectZero];
make.left.mas_equalTo(self.playButton.mas_right).offset(20); [self.liveRefreshButton setImage:[UIImage imageNamed:@"icon_refresh"] forState:UIControlStateNormal];
[self.liveRefreshButton addTarget:self action:@selector(refreshButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.liveRefreshButton];
}
[self.liveRefreshButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.centerY.mas_equalTo(self);
make.width.height.mas_equalTo(kPlayButtonWidth); make.width.height.mas_equalTo(kPlayButtonWidth);
}]; }];
} }
if (!self.mediaProgressSlider) { if (!self.mediaProgressSlider) {
self.mediaProgressSlider = [[UISlider alloc] initWithFrame:CGRectZero]; self.mediaProgressSlider = [[UISlider alloc] initWithFrame:CGRectZero];
self.mediaProgressSlider.maximumTrackTintColor = UIColor.grayColor; self.mediaProgressSlider.maximumTrackTintColor = UIColor.grayColor;
self.mediaProgressSlider.tintColor = UIColor.redColor; self.mediaProgressSlider.tintColor = UIColor.redColor;
[self.mediaProgressSlider setThumbImage:[UIImage imageNamed:@"slider"] forState:UIControlStateNormal]; [self.mediaProgressSlider setThumbImage:[UIImage imageNamed:@"slider"] forState:UIControlStateNormal];
[self addSubview:self.mediaProgressSlider]; [self addSubview:self.mediaProgressSlider];
if (!_isLive) {
[self.mediaProgressSlider addTarget:self action:@selector(slideTouchDown) forControlEvents:UIControlEventTouchDown]; [self.mediaProgressSlider addTarget:self action:@selector(slideTouchDown) forControlEvents:UIControlEventTouchDown];
[self.mediaProgressSlider addTarget:self action:@selector(slideTouchUpInside) forControlEvents:UIControlEventTouchUpInside]; [self.mediaProgressSlider addTarget:self action:@selector(slideTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
[self.mediaProgressSlider addTarget:self action:@selector(slideValueChanged) forControlEvents:UIControlEventValueChanged]; [self.mediaProgressSlider addTarget:self action:@selector(slideValueChanged) forControlEvents:UIControlEventValueChanged];
[self.mediaProgressSlider addTarget:self action:@selector(slideTouchUpOutside) forControlEvents:UIControlEventTouchUpOutside]; [self.mediaProgressSlider addTarget:self action:@selector(slideTouchUpOutside) forControlEvents:UIControlEventTouchUpOutside];
[self.mediaProgressSlider addTarget:self action:@selector(slideTouchCancel) forControlEvents:UIControlEventTouchCancel]; [self.mediaProgressSlider addTarget:self action:@selector(slideTouchCancel) forControlEvents:UIControlEventTouchCancel];
} else {
self.mediaProgressSlider.userInteractionEnabled = NO;
}
} }
[self.mediaProgressSlider mas_makeConstraints:^(MASConstraintMaker *make) { [self.mediaProgressSlider mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self); make.centerX.mas_equalTo(self);
@ -184,56 +204,61 @@
make.height.mas_equalTo(30); make.height.mas_equalTo(30);
}]; }];
if (!_isLive) {
if (!self.listButton) { if (!self.listButton) {
self.listButton = [[UIButton alloc] initWithFrame:CGRectZero]; self.listButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.listButton.titleLabel.textAlignment = NSTextAlignmentCenter; self.listButton.titleLabel.textAlignment = NSTextAlignmentCenter;
[self.listButton setImage:[UIImage imageNamed:@"list"] forState:UIControlStateNormal]; [self.listButton setImage:[UIImage imageNamed:@"list"] forState:UIControlStateNormal];
[self.listButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; if (self.playList.count == 0) {
if (self.playList.count == 0) { self.listButton.enabled = NO;
self.listButton.enabled = NO; }
[self.listButton addTarget:self action:@selector(listButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.listButton];
} }
[self.listButton addTarget:self action:@selector(listButtonClick:) forControlEvents:UIControlEventTouchUpInside]; [self.listButton mas_makeConstraints:^(MASConstraintMaker *make) {
[self addSubview:self.listButton]; make.right.mas_equalTo(self).offset(-kSliderMargin);
} make.centerY.mas_equalTo(self.hudButton);
[self.listButton mas_makeConstraints:^(MASConstraintMaker *make) { make.width.mas_equalTo(20);
make.right.mas_equalTo(self).offset(-kSliderMargin); make.height.mas_equalTo(20);
make.centerY.mas_equalTo(self.hudButton); }];
make.width.mas_equalTo(20);
make.height.mas_equalTo(20);
}];
if (!self.loopButton) { if (!self.loopButton) {
self.loopButton = [[UIButton alloc] initWithFrame:CGRectZero]; self.loopButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.loopButton.titleLabel.textAlignment = NSTextAlignmentCenter; self.loopButton.titleLabel.textAlignment = NSTextAlignmentCenter;
[self.loopButton setImage:[UIImage imageNamed:self.isLoop ? @"loop" : @"sequence"] forState:UIControlStateNormal]; [self.loopButton setImage:[UIImage imageNamed:self.isLoop ? @"loop" : @"sequence"] forState:UIControlStateNormal];
[self.loopButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; [self.loopButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
[self.loopButton addTarget:self action:@selector(loopButtonClick:) forControlEvents:UIControlEventTouchUpInside]; [self.loopButton addTarget:self action:@selector(loopButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.loopButton]; [self addSubview:self.loopButton];
}
[self.loopButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.listButton.mas_left).offset(-20);
make.centerY.mas_equalTo(self.hudButton);
make.width.mas_equalTo(20);
make.height.mas_equalTo(20);
}];
if (!self.speedButton) {
self.speedButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.speedButton.titleLabel.textAlignment = NSTextAlignmentCenter;
[self.speedButton setTitle:@"1.0x" forState:UIControlStateNormal];
[self.speedButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.speedButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
[self.speedButton addTarget:self action:@selector(speedButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.speedButton];
}
[self.speedButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.loopButton.mas_left).offset(-10);
make.centerY.mas_equalTo(self.hudButton);
make.width.mas_equalTo(50);
make.height.mas_equalTo(30);
}];
} }
[self.loopButton mas_makeConstraints:^(MASConstraintMaker *make) { }
make.right.mas_equalTo(self.listButton.mas_left).offset(-20);
make.centerY.mas_equalTo(self.hudButton);
make.width.mas_equalTo(20);
make.height.mas_equalTo(20);
}];
if (!self.speedButton) {
self.speedButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.speedButton.titleLabel.textAlignment = NSTextAlignmentCenter;
[self.speedButton setTitle:@"1.0x" forState:UIControlStateNormal];
[self.speedButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.speedButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
[self.speedButton addTarget:self action:@selector(speedButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.speedButton];
}
[self.speedButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.mas_equalTo(self.loopButton.mas_left).offset(-10);
make.centerY.mas_equalTo(self.hudButton);
make.width.mas_equalTo(50);
make.height.mas_equalTo(30);
}];
- (void)refreshButtonClick:(UIButton *)sender {
if ([self.delegatePlayer respondsToSelector:@selector(mediaControlLiveRefresh)]) {
[self.delegatePlayer mediaControlLiveRefresh];
}
} }
- (void)loopButtonClick:(UIButton *)sender { - (void)loopButtonClick:(UIButton *)sender {

View File

@ -8,19 +8,18 @@
import UIKit import UIKit
class RedAlertView: UIView { class RedAlertView: UIView {
let urlDefault = "aHR0cDovL3Nucy12aWRlby1hbC54aHNjZG4uY29tL3NwZWN0cnVtLzAxZTI5NjRlNDE2YmUwNzcwMTgzNzAwMzgxMWI0ZTM5YzlfNTEzLm1wNA==" let urlVodDefault = "aHR0cDovL3Nucy12aWRlby1hbC54aHNjZG4uY29tL3NwZWN0cnVtLzAxZTI5NjRlNDE2YmUwNzcwMTgzNzAwMzgxMWI0ZTM5YzlfNTEzLm1wNA=="
let urlLiveDefault = "aHR0cDovL2xpdmUtcGxheS54aHNjZG4uY29tL2xpdmUvNTY5MDcxNDQ3Mjc2ODQwMjY4LmZsdg=="
let jsonDefault = "ewogICAgInN0cmVhbSI6IHsKICAgICAgICAiYXYxIjogW10sCiAgICAgICAgImgyNjUiOiBbCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJtYXN0ZXJfdXJsIjogImh0dHA6Ly9zbnMtdmlkZW8tYmQueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzgvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ2NTM3OV84Lm1wNCIsCiAgICAgICAgICAgICAgICAid2VpZ2h0IjogNjIsCiAgICAgICAgICAgICAgICAiYmFja3VwX3VybHMiOiBbCiAgICAgICAgICAgICAgICAgICAgImh0dHA6Ly9zbnMtdmlkZW8tcWMueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzgvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ2NTM3OV84Lm1wND9zaWduPTgxMzQyZGQwZGM1YjViYmViY2I0N2NmYTVmNzQ1YmY1JnQ9NjViNTI3NTQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWh3Lnhoc2Nkbi5jb20vc3RyZWFtLzExMC84LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkNjUzNzlfOC5tcDQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC84LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkNjUzNzlfOC5tcDQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWh3Lnhoc2Nkbi5uZXQvc3RyZWFtLzExMC84LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkNjUzNzlfOC5tcDQiCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgImhlaWdodCI6IDk2MCwKICAgICAgICAgICAgICAgICJ3aWR0aCI6IDcyMCwKICAgICAgICAgICAgICAgICJhdmdfYml0cmF0ZSI6IDM4MDYzNAogICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAiaDI2NCI6IFsKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgIm1hc3Rlcl91cmwiOiAiaHR0cDovL3Nucy12aWRlby1iZC54aHNjZG4uY29tL3N0cmVhbS8xMTAvMjU4LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOTVmODg0MmJfMjU4Lm1wNCIsCiAgICAgICAgICAgICAgICAid2VpZ2h0IjogNjIsCiAgICAgICAgICAgICAgICAiYmFja3VwX3VybHMiOiBbCiAgICAgICAgICAgICAgICAgICAgImh0dHA6Ly9zbnMtdmlkZW8tcWMueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzI1OC8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4Yzk1Zjg4NDJiXzI1OC5tcDQ/c2lnbj0yOGUxMzk3OWE2MGJhMjRiODk4ZGE0NzEyM2M5NmRkMiZ0PTY1YjUyNzU0IiwKICAgICAgICAgICAgICAgICAgICAiaHR0cDovL3Nucy12aWRlby1ody54aHNjZG4uY29tL3N0cmVhbS8xMTAvMjU4LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOTVmODg0MmJfMjU4Lm1wNCIsCiAgICAgICAgICAgICAgICAgICAgImh0dHA6Ly9zbnMtdmlkZW8tYWwueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzI1OC8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4Yzk1Zjg4NDJiXzI1OC5tcDQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWh3Lnhoc2Nkbi5uZXQvc3RyZWFtLzExMC8yNTgvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM5NWY4ODQyYl8yNTgubXA0IgogICAgICAgICAgICAgICAgXSwKICAgICAgICAgICAgICAgICJoZWlnaHQiOiA5NjAsCiAgICAgICAgICAgICAgICAid2lkdGgiOiA3MjAsCiAgICAgICAgICAgICAgICAiYXZnX2JpdHJhdGUiOiA1ODA4MTQKICAgICAgICAgICAgfQogICAgICAgIF0KICAgIH0KfQ==" let jsonDefault = "ewogICAgInN0cmVhbSI6IHsKICAgICAgICAiYXYxIjogW10sCiAgICAgICAgImgyNjUiOiBbCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJtYXN0ZXJfdXJsIjogImh0dHA6Ly9zbnMtdmlkZW8tYmQueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzgvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ2NTM3OV84Lm1wNCIsCiAgICAgICAgICAgICAgICAid2VpZ2h0IjogNjIsCiAgICAgICAgICAgICAgICAiYmFja3VwX3VybHMiOiBbCiAgICAgICAgICAgICAgICAgICAgImh0dHA6Ly9zbnMtdmlkZW8tcWMueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzgvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM4YWQ2NTM3OV84Lm1wND9zaWduPTgxMzQyZGQwZGM1YjViYmViY2I0N2NmYTVmNzQ1YmY1JnQ9NjViNTI3NTQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWh3Lnhoc2Nkbi5jb20vc3RyZWFtLzExMC84LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkNjUzNzlfOC5tcDQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWFsLnhoc2Nkbi5jb20vc3RyZWFtLzExMC84LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkNjUzNzlfOC5tcDQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWh3Lnhoc2Nkbi5uZXQvc3RyZWFtLzExMC84LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOGFkNjUzNzlfOC5tcDQiCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgImhlaWdodCI6IDk2MCwKICAgICAgICAgICAgICAgICJ3aWR0aCI6IDcyMCwKICAgICAgICAgICAgICAgICJhdmdfYml0cmF0ZSI6IDM4MDYzNAogICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICAgICAiaDI2NCI6IFsKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgIm1hc3Rlcl91cmwiOiAiaHR0cDovL3Nucy12aWRlby1iZC54aHNjZG4uY29tL3N0cmVhbS8xMTAvMjU4LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOTVmODg0MmJfMjU4Lm1wNCIsCiAgICAgICAgICAgICAgICAid2VpZ2h0IjogNjIsCiAgICAgICAgICAgICAgICAiYmFja3VwX3VybHMiOiBbCiAgICAgICAgICAgICAgICAgICAgImh0dHA6Ly9zbnMtdmlkZW8tcWMueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzI1OC8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4Yzk1Zjg4NDJiXzI1OC5tcDQ/c2lnbj0yOGUxMzk3OWE2MGJhMjRiODk4ZGE0NzEyM2M5NmRkMiZ0PTY1YjUyNzU0IiwKICAgICAgICAgICAgICAgICAgICAiaHR0cDovL3Nucy12aWRlby1ody54aHNjZG4uY29tL3N0cmVhbS8xMTAvMjU4LzAxZTU4M2NiNmUwZmVkNWEwMTAzNzAwMzhjOTVmODg0MmJfMjU4Lm1wNCIsCiAgICAgICAgICAgICAgICAgICAgImh0dHA6Ly9zbnMtdmlkZW8tYWwueGhzY2RuLmNvbS9zdHJlYW0vMTEwLzI1OC8wMWU1ODNjYjZlMGZlZDVhMDEwMzcwMDM4Yzk1Zjg4NDJiXzI1OC5tcDQiLAogICAgICAgICAgICAgICAgICAgICJodHRwOi8vc25zLXZpZGVvLWh3Lnhoc2Nkbi5uZXQvc3RyZWFtLzExMC8yNTgvMDFlNTgzY2I2ZTBmZWQ1YTAxMDM3MDAzOGM5NWY4ODQyYl8yNTgubXA0IgogICAgICAgICAgICAgICAgXSwKICAgICAgICAgICAgICAgICJoZWlnaHQiOiA5NjAsCiAgICAgICAgICAgICAgICAid2lkdGgiOiA3MjAsCiAgICAgICAgICAgICAgICAiYXZnX2JpdHJhdGUiOiA1ODA4MTQKICAgICAgICAgICAgfQogICAgICAgIF0KICAgIH0KfQ=="
var playButtonTapped: ((String, Bool) -> Void)? var playButtonTapped: ((String, Bool) -> Void)?
var closeButtonTapped: (() -> Void)? var closeButtonTapped: (() -> Void)?
var isLive = false
let titleLabel: UILabel = { let titleLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .boldSystemFont(ofSize: 18) label.font = .boldSystemFont(ofSize: 18)
label.text = "Enter URL or JSON"
label.textAlignment = .center label.textAlignment = .center
label.heightAnchor.constraint(equalToConstant: 20).isActive = true
return label return label
}() }()
let urlTextView: UITextView = { let urlTextView: UITextView = {
let textView = UITextView() let textView = UITextView()
@ -29,7 +28,6 @@ class RedAlertView: UIView {
textView.layer.borderWidth = 1 textView.layer.borderWidth = 1
textView.layer.borderColor = UIColor.gray.cgColor textView.layer.borderColor = UIColor.gray.cgColor
textView.layer.cornerRadius = 5 textView.layer.cornerRadius = 5
textView.heightAnchor.constraint(equalToConstant: 60).isActive = true
return textView return textView
}() }()
@ -41,7 +39,6 @@ class RedAlertView: UIView {
textView.layer.borderWidth = 1 textView.layer.borderWidth = 1
textView.layer.borderColor = UIColor.gray.cgColor textView.layer.borderColor = UIColor.gray.cgColor
textView.layer.cornerRadius = 5 textView.layer.cornerRadius = 5
textView.heightAnchor.constraint(equalToConstant: 140).isActive = true
return textView return textView
}() }()
@ -94,41 +91,52 @@ class RedAlertView: UIView {
return button return button
} }
override init(frame: CGRect) { public init(frame: CGRect, isLive: Bool) {
super.init(frame: frame) super.init(frame: frame)
self.isLive = isLive
self.backgroundColor = .systemBackground self.backgroundColor = .systemBackground
self.layer.cornerRadius = 10 self.layer.cornerRadius = 10
self.layer.shadowColor = UIColor.lightGray.cgColor self.layer.shadowColor = UIColor.lightGray.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 1) self.layer.shadowOffset = CGSize(width: 0, height: 1)
self.layer.shadowOpacity = 0.3 self.layer.shadowOpacity = 0.3
self.layer.shadowRadius = 4.0 self.layer.shadowRadius = 4.0
let stackViewH1 = UIStackView(arrangedSubviews: [urlButton, jsonButton, clearButton]) let stackViewH1 = UIStackView(arrangedSubviews: isLive ? [urlButton, clearButton] : [urlButton, jsonButton, clearButton])
let stackViewH2 = UIStackView(arrangedSubviews: [closeButton, playButton])
let stackView = UIStackView(arrangedSubviews: isLive ? [titleLabel, urlTextView, stackViewH1, stackViewH2] : [titleLabel, urlTextView, jsonTextView, stackViewH1, stackViewH2])
stackViewH1.axis = .horizontal stackViewH1.axis = .horizontal
stackViewH1.distribution = .fillEqually stackViewH1.distribution = .fillEqually
stackViewH1.spacing = 10 stackViewH1.spacing = 10
stackViewH1.translatesAutoresizingMaskIntoConstraints = false stackViewH1.translatesAutoresizingMaskIntoConstraints = false
let stackViewH2 = UIStackView(arrangedSubviews: [closeButton, playButton])
stackViewH2.axis = .horizontal stackViewH2.axis = .horizontal
stackViewH2.distribution = .fillEqually stackViewH2.distribution = .fillEqually
stackViewH2.spacing = 10 stackViewH2.spacing = 10
stackViewH2.translatesAutoresizingMaskIntoConstraints = false stackViewH2.translatesAutoresizingMaskIntoConstraints = false
let stackView = UIStackView(arrangedSubviews: [titleLabel, urlTextView, jsonTextView, stackViewH1, stackViewH2])
stackView.axis = .vertical stackView.axis = .vertical
stackView.distribution = .fillProportionally stackView.distribution = .fillProportionally
stackView.spacing = 10 stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false stackView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(stackView) self.addSubview(stackView)
if isLive {
titleLabel.text = "Enter Live URL"
urlTextView.heightAnchor.constraint(equalToConstant: 200).isActive = true
} else {
titleLabel.text = "Enter VOD URL or JSON"
urlTextView.heightAnchor.constraint(equalToConstant: 60).isActive = true
jsonTextView.heightAnchor.constraint(equalToConstant: 140).isActive = true
}
titleLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10), stackView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10), stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10),
stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10), stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10),
stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10) stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10)
]) ])
urlTextView.delegate = self urlTextView.delegate = self
jsonTextView.delegate = self jsonTextView.delegate = self
urlButton.addTarget(self, action: #selector(urlButtonAction), for: .touchUpInside) urlButton.addTarget(self, action: #selector(urlButtonAction), for: .touchUpInside)
@ -147,7 +155,7 @@ class RedAlertView: UIView {
@objc func urlButtonAction() { @objc func urlButtonAction() {
disableInput(textView: jsonTextView, content: "Please clear the JSON field first") disableInput(textView: jsonTextView, content: "Please clear the JSON field first")
enableInput(textView: urlTextView, content: RedMediaUtil.decodeBase64(base64EncodedString: urlDefault) ?? "") enableInput(textView: urlTextView, content: RedMediaUtil.decodeBase64(base64EncodedString: isLive ? urlLiveDefault : urlVodDefault) ?? "")
} }
@objc func jsonButtonAction() { @objc func jsonButtonAction() {

View File

@ -245,7 +245,7 @@ open class RedFlowViewController: UIViewController, RedFlowLayoutDelegate, UICol
videoDictArray.append(videoDict) videoDictArray.append(videoDict)
} }
RedDemoPlayerViewController.present(from: self, withTitle: "", url: playUrl, isJson: item.isJson, playList: videoDictArray, playListIndex: indexPath.item, completion: nil) { RedDemoPlayerViewController.present(from: self, withTitle: "", url: playUrl, isJson: item.isJson, isLive: false, playList: videoDictArray, playListIndex: indexPath.item, completion: nil) {
self.playingCell = nil self.playingCell = nil
self.scrollViewEndDecelerating() self.scrollViewEndDecelerating()
} }