Home vlcios
Post
Cancel

vlcios

声音控制

class VerticalSlider:UIControl

1
public let slider = UISlider()

class SliderInfoView

1
2
3
var iconNames: [String] = []
let levelSlider: VerticalSlider
var levelImageView: UIImageView

class VolumeControlView:SliderInfoView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
init() {
    super.init(frame: .zero)
    self.levelSlider.value = AVAudioSession.sharedInstance().outputVolume // 初始值
    if  !UIAccessibility.isVoiceOverRunning {
        levelSlider.setThumbImage(image: UIImage(), for: .normal)
    }
    levelSlider.addTarget(self, action: #selector(self.onVolumeChange), for:
.valueChanged)
    self.iconNames = ["noSound", "lowSound", "mediumSound", "highSound"]
    levelSlider.accessibilityLabel = NSLocalizedString("VOLUME_SLIDER", comment: "")
    levelSlider.accessibilityHint = NSLocalizedString("VOLUME_HINT", comment: "")
    levelSlider.accessibilityTraits = .adjustable
}

@objc func onVolumeChange() {
    MPVolumeView.setVolume(levelSlider.value)
    updateIcon(level: levelSlider.value)
}
1
2
3
4
5
6
7
8
9
extension MPVolumeView {
    static func setVolume(_ volume: Float) {
        let volumeView = MPVolumeView()
        let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
            slider?.value = volume
        }
    }
}

亮度控制

class BrightnessControlView: SliderInfoView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
init() {
    super.init(frame: .zero)
    if  !UIAccessibility.isVoiceOverRunning {
        levelSlider.setThumbImage(image: UIImage(), for: .normal)
    }
    self.iconNames = ["brightnessLow", "brightnessLow", "brightnessMedium",
"brightnessHigh"]
    levelSlider.addTarget(self, action: #selector(self.onLuminosityChange), for:
.valueChanged)
    levelSlider.accessibilityLabel = NSLocalizedString("BRIGHTNESS_SLIDER", comment: "")
    levelSlider.accessibilityHint = NSLocalizedString("BRIGHTNESS_HINT", comment: "")
    levelSlider.accessibilityTraits = .adjustable
    updateIcon(level: Float(UIScreen.main.brightness)) // 初始值
}

@objc func onLuminosityChange() {
    UIScreen.main.brightness = CGFloat(levelSlider.value)
    updateIcon(level: levelSlider.value)
}

播放进度控制

protocol MediaScrubProgressBarDelegate

1
func mediaScrubProgressBarShouldResetIdleTimer()

class OBSlider:UISlider

@property (assign, nonatomic, readonly) float scrubbingSpeed;
@property (strong, nonatomic) NSArray *scrubbingSpeeds;
@property (strong, nonatomic) NSArray *scrubbingSpeedChangePositions;

@property (assign, nonatomic, readwrite) float scrubbingSpeed;
@property (assign, nonatomic, readwrite) float realPositionValue;
@property (assign, nonatomic) CGPoint beganTrackingLocation;

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    BOOL beginTracking = [super beginTrackingWithTouch:touch withEvent:event];
    if (beginTracking)
    {
		// Set the beginning tracking location to the centre of the current
		// position of the thumb. This ensures that the thumb is correctly re-positioned
		// when the touch position moves back to the track after tracking in one
		// of the slower tracking zones.
		CGRect thumbRect = [self thumbRectForBounds:self.bounds 
										  trackRect:[self trackRectForBounds:self.bounds]
											  value:self.value];
        self.beganTrackingLocation = CGPointMake(thumbRect.origin.x + thumbRect.size.width / 2.0f, 
												 thumbRect.origin.y + thumbRect.size.height / 2.0f); 
        self.realPositionValue = self.value;
    }
    return beginTracking;
}

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    if (self.tracking)
    {
        CGPoint previousLocation = [touch previousLocationInView:self];
        CGPoint currentLocation  = [touch locationInView:self];
        CGFloat trackingOffset = currentLocation.x - previousLocation.x;
        
        // Find the scrubbing speed that curresponds to the touch's vertical offset
        CGFloat verticalOffset = fabsf(currentLocation.y - self.beganTrackingLocation.y);
        NSUInteger scrubbingSpeedChangePosIndex = [self indexOfLowerScrubbingSpeed:self.scrubbingSpeedChangePositions forOffset:verticalOffset];        
        if (scrubbingSpeedChangePosIndex == NSNotFound) {
            scrubbingSpeedChangePosIndex = [self.scrubbingSpeeds count];
        }
        self.scrubbingSpeed = [[self.scrubbingSpeeds objectAtIndex:scrubbingSpeedChangePosIndex - 1] floatValue];
         
        CGRect trackRect = [self trackRectForBounds:self.bounds];
        self.realPositionValue = self.realPositionValue + (self.maximumValue - self.minimumValue) * (trackingOffset / trackRect.size.width);
		
		CGFloat valueAdjustment = self.scrubbingSpeed * (self.maximumValue - self.minimumValue) * (trackingOffset / trackRect.size.width);
		CGFloat thumbAdjustment = 0.0f;
        if ( ((self.beganTrackingLocation.y < currentLocation.y) && (currentLocation.y < previousLocation.y)) ||
             ((self.beganTrackingLocation.y > currentLocation.y) && (currentLocation.y > previousLocation.y)) )
            {
            // We are getting closer to the slider, go closer to the real location
			thumbAdjustment = (self.realPositionValue - self.value) / (1 + fabsf(currentLocation.y - self.beganTrackingLocation.y));
        }
		self.value += valueAdjustment + thumbAdjustment;

        if (self.continuous) {
            [self sendActionsForControlEvents:UIControlEventValueChanged];
        }
    }
    return self.tracking;
}

- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    if (self.tracking) 
    {
        self.scrubbingSpeed = [[self.scrubbingSpeeds objectAtIndex:0] floatValue];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

- (NSArray *) defaultScrubbingSpeeds
{
    return [NSArray arrayWithObjects:
            [NSNumber numberWithFloat:1.0f],
            [NSNumber numberWithFloat:0.5f],
            [NSNumber numberWithFloat:0.25f],
            [NSNumber numberWithFloat:0.1f],
            nil];
}

- (NSArray *) defaultScrubbingSpeedChangePositions
{
    return [NSArray arrayWithObjects:
            [NSNumber numberWithFloat:0.0f],
            [NSNumber numberWithFloat:50.0f],
            [NSNumber numberWithFloat:100.0f],
            [NSNumber numberWithFloat:150.0f],
            nil];
}

class VLCOBSlider:OBSlider

class MediaScrubProgressBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@objc weak var delegate: MediaScrubProgressBarDelegate?
private var playbackService = PlaybackService.sharedInstance()
private var positionSet: Bool = true
private var isScrubbing: Bool = false

@objc lazy private(set) var progressSlider: VLCOBSlider

private lazy var elapsedTimeLabel: UILabel = {
    var label = UILabel()
    label.font = UIFont.boldSystemFont(ofSize: 13)
    label.textColor = PresentationTheme.current.colors.orangeUI
    label.text = "--:--"
    label.numberOfLines = 1
    label.setContentHuggingPriority(.defaultLow, for: .horizontal)
    return label
}()

private lazy var remainingTimeButton: UIButton = {
    let remainingTimeButton = UIButton(type: .custom)
    remainingTimeButton.addTarget(self,
                                  action: #selector(handleTimeDisplay),
                                  for: .touchUpInside)
    remainingTimeButton.setTitle("--:--", for: .normal)
    remainingTimeButton.setTitleColor(.white, for: .normal)
    // Use a monospace variant for the digits so the width does not jitter as the
numbers changes.
    remainingTimeButton.titleLabel?.font = UIFont.monospacedDigitSystemFont(ofSize: 13,
                                                                            weight:
.semibold)
    remainingTimeButton.setContentHuggingPriority(.defaultLow, for: .horizontal)
    return remainingTimeButton
}()

其他控制

class VideoPlayerControls

播放网络串流

class VLCOpenNetworkStreamViewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- (void)_openURLStringAndDismiss:(NSString *)url
{
    NSURL *playbackURL = [NSURL URLWithString:url];
    NSURL *subtitlesURL = nil;

    if (([playbackURL.scheme isEqualToString:@"http"] || [playbackURL.scheme isEqualToString:@"https"]) && self.ScanSubToggleSwitch.on) {
        subtitlesURL = [self _checkURLofSubtitle:playbackURL];
    }

    VLCMedia *media = [VLCMedia mediaWithURL:[NSURL URLWithString:url]];
    VLCMediaList *medialist = [[VLCMediaList alloc] init];
    [medialist addMedia:media];
    [[VLCPlaybackService sharedInstance] playMediaList:medialist firstIndex:0 subtitlesFilePath:subtitlesURL.absoluteString];
}

// 检查下载可能存在的字幕文件
- (NSURL *)_checkURLofSubtitle:(NSURL *)url
{
    NSCharacterSet *characterFilter = [NSCharacterSet characterSetWithCharactersInString:@"\\.():$"];
    NSString *subtitleFileExtensions = [[kSupportedSubtitleFileExtensions componentsSeparatedByCharactersInSet:characterFilter] componentsJoinedByString:@""];
    NSArray *arraySubtitleFileExtensions = [subtitleFileExtensions componentsSeparatedByString:@"|"];
    NSURL *urlWithoutExtension = [url URLByDeletingPathExtension];
    NSUInteger count = arraySubtitleFileExtensions.count;

    for (int i = 0; i < count; i++) {
        NSURL *checkURL = [urlWithoutExtension URLByAppendingPathExtension:arraySubtitleFileExtensions[i]];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:checkURL];
        request.HTTPMethod = @"HEAD";

        NSURLResponse *response = nil;
        NSError *error = nil;
        [self sendSynchronousRequest:request returningResponse:&response error:&error];
        NSInteger httpStatus = [(NSHTTPURLResponse *)response statusCode];

        if (httpStatus == 200) {
            APLog(@"%s:found matching spu file: %@", __PRETTY_FUNCTION__, checkURL);
            return checkURL;
        }
    }
    return nil;
}

支持的字幕文件格式

#define kSupportedSubtitleFileExtensions @"\\.(cdg|idx|srt|sub|utf|ass|ssa|aqt|jss|psb|rt|smi|txt|smil|stl|usf|dks|pjs|mpl2|mks|vtt|ttml|dfxp)$"
This post is licensed under CC BY 4.0 by the author.