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 -->
# Contents of this open source
# Contents of first open source
- Open source code for playing intermediate layer (including: instance management, interface calls, etc.)
- RedPlayer is open source in the form of SDK this time

View File

@ -3,9 +3,9 @@
![示例图片](./redplayer.jpg)
![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 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
-------- | ------------
@ -64,6 +64,9 @@
### 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)
### Usage

View File

@ -104,6 +104,8 @@ public interface IMediaPlayer {
*/
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, 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) {
}
@Override
public void setLiveMode(boolean enable) {
}
@Override
public String getAudioCodecInfo() {
return "unknown";

View File

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

View File

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

View File

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

View File

@ -155,6 +155,10 @@ val VIDEO_ONE =
val VIDEO_TWO =
Utils.decodeBase64("aHR0cDovL3Nucy12aWRlby1hbC54aHNjZG4uY29tL3NwZWN0cnVtLzAxZTI5NjRlNDE2YmUwNzcwMTgzNzAwMzgxMWI0ZTM5YzlfNTEzLm1wNA==")
val LIVE_VIDEO =
Utils.decodeBase64("aHR0cDovL2xpdmUtcGxheS54aHNjZG4uY29tL2xpdmUvNTY5MDcyNTczNzk5OTkxNzI5LmZsdj91aWQ9NjQxMjkwNWUwMDAwMDAwMDEyMDEwNjIx")
/** the json data source */
val JSON_DATA_SOURCE = Utils.decodeBase64(
"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.XhsFloatingWindowHelper
import com.xingin.openredplayer.input.XhsInputActivity
import com.xingin.openredplayer.live.XhsLiveInputActivity
import com.xingin.openredplayer.player.XhsPlayerActivity
import io.reactivex.rxjava3.disposables.Disposable
import java.io.Serializable
@ -24,6 +25,7 @@ import java.io.Serializable
class XhsHomeFragment : Fragment() {
private lateinit var rootView: View
private lateinit var itemUrlView: View
private lateinit var itemLiveUrlView: View
private lateinit var itemAlbumView: View
private lateinit var itemFeedView: View
private lateinit var itemWindowView: View
@ -53,6 +55,8 @@ class XhsHomeFragment : Fragment() {
private fun initView() {
itemUrlView = rootView.findViewById(R.id.layout_play_url)
itemUrlView.setOnClickListener { openInputPage() }
itemLiveUrlView = rootView.findViewById(R.id.layout_play_live_url)
itemLiveUrlView.setOnClickListener { openLiveInputPage() }
itemAlbumView = rootView.findViewById(R.id.layout_local_album)
itemAlbumView.setOnClickListener { openAlbumPage() }
itemFeedView = rootView.findViewById(R.id.layout_double_column)
@ -68,6 +72,11 @@ class XhsHomeFragment : Fragment() {
startActivity(intent)
}
private fun openLiveInputPage() {
val intent = Intent(this.activity, XhsLiveInputActivity::class.java)
startActivity(intent)
}
private fun openAlbumPage() {
permissionDisposable = rxPermissions.request(
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_INDEX = "video_index"
const val INTENT_KEY_SHOW_LOADING = "show_loading"
const val INTENT_KEY_IS_LIVE = "is_live"
}
/** DrawerLayout UI */
@ -88,6 +89,7 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
/** data **/
private var isShowLoading = false
private var isLive = false
private var videoUrls = arrayListOf<String>()
private var playIndex = 0
@ -124,6 +126,7 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
private fun initData() {
playIndex = intent.getIntExtra(INTENT_KEY_INDEX, 0)
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>
videoUrls.addAll(list)
// init config
@ -236,26 +239,34 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
/** update the video play progress */
private fun initVideoProgressView() {
progressSeekBar = findViewById(R.id.video_view_seekbar)
progressSeekBar.setOnProgressChangedListener(object :
XhsProgressBar.OnProgressChangedListener {
override fun onProgressChanged(
signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float, fromUser: Boolean
) {
}
if (!isLive) {
progressSeekBar.setOnProgressChangedListener(object :
XhsProgressBar.OnProgressChangedListener {
override fun onProgressChanged(
signSeekBar: XhsProgressBar,
progress: Int,
progressFloat: Float,
fromUser: Boolean
) {
}
override fun getProgressOnActionUp(
signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float
) {
val percentage = progress / 1000.0
videoPlayerView.seekTo((percentage * videoPlayerView.duration).toInt())
progressSeekBar.setProgress(progressFloat)
}
override fun getProgressOnActionUp(
signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float
) {
val percentage = progress / 1000.0
videoPlayerView.seekTo((percentage * videoPlayerView.duration).toInt())
progressSeekBar.setProgress(progressFloat)
}
override fun getProgressOnFinally(
signSeekBar: XhsProgressBar, progress: Int, progressFloat: Float, fromUser: Boolean
) {
}
})
override fun getProgressOnFinally(
signSeekBar: XhsProgressBar,
progress: Int,
progressFloat: Float,
fromUser: Boolean
) {
}
})
}
// update the seekbar
val handler = Handler(Looper.getMainLooper())
var position: Int
@ -342,7 +353,10 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
}
videoPlayerView.setOnErrorListener { _, what, extra -> handleVideoErrorEvent(what, extra) }
// 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 */
@ -539,7 +553,7 @@ class XhsPlayerActivity : AppCompatActivity(), XhsSectionAdapter.OnSectionItemCl
/** handle video speed play: support 0.751.01.251.52.0 */
private fun handleVideoSpeedPlay(speed: Float, isShow: Boolean) {
if (!videoPlayerView.isPlaying) {
if (!videoPlayerView.isPlaying || isLive) {
return
}
videoPlayerView.setSpeed(speed)

View File

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

View File

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

View File

@ -30,6 +30,32 @@
</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
android:id="@+id/layout_local_album"
android:layout_width="match_parent"

View File

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

View File

@ -17,7 +17,7 @@
<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_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_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_setting_title">Settings</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_feed">Online Samples</string>
<string name="home_item_window">Pip Samples</string>
@ -70,6 +71,10 @@
<string name="input_clear">Clear</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_speed">Playing Speed</string>
<string name="play_show_or_hide">Show/Hide</string>

View File

@ -19,7 +19,7 @@
<PreferenceCategory android:title="@string/pref_title_player_core">
<SwitchPreference
android:defaultValue="false"
android:defaultValue="true"
android:key="@string/pref_key_using_media_codec"
android:persistent="true"
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**):
```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**:

View File

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

View File

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

View File

@ -82,14 +82,14 @@ class MainViewController: UIViewController,
}
func playURLResource() {
func playURLResource(isLive: Bool) {
inputAlertViewBgView = UIView(frame: self.view.bounds)
if let bgView = inputAlertViewBgView {
bgView.backgroundColor = UIColor.gray.withAlphaComponent(0.2)
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
if let alertView = inputAlertView {
alertView.playButtonTapped = { [weak self] input, isJSON in
@ -109,7 +109,7 @@ class MainViewController: UIViewController,
self?.present(alertController, animated: true, completion: nil)
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
self?.closeAlertView()
@ -249,7 +249,7 @@ class MainViewController: UIViewController,
DispatchQueue.main.async {
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 {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@ -324,12 +324,15 @@ extension MainViewController: UITableViewDataSource {
switch indexPath.row {
case 0:
cell.iconImageView.image = UIImage(named: "link")?.withRenderingMode(.alwaysTemplate)
cell.titleLabel.text = "Play URL / Json"
cell.iconImageView.image = UIImage(named: "vod_link")?.withRenderingMode(.alwaysTemplate)
cell.titleLabel.text = "VOD URL / Json"
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.titleLabel.text = "Play Local Resources"
case 2:
case 3:
cell.iconImageView.image = UIImage(named: "flow_icon")?.withRenderingMode(.alwaysTemplate)
cell.titleLabel.text = "Video Samples"
default:
@ -352,10 +355,12 @@ extension MainViewController: UITableViewDelegate {
switch indexPath.row {
case 0:
playURLResource()
playURLResource(isLive: false)
case 1:
playLocalVideo()
playURLResource(isLive: true)
case 2:
playLocalVideo()
case 3:
showRedFlowViewController()
default:
break

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -245,7 +245,7 @@ open class RedFlowViewController: UIViewController, RedFlowLayoutDelegate, UICol
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.scrollViewEndDecelerating()
}