【问题标题】:How to play mp3 from & to specific time in milliseconds on iOS?如何在 iOS 上以毫秒为单位从 & 到特定时间播放 mp3?
【发布时间】:2016-02-17 06:50:51
【问题描述】:

我需要在特定时间 [以毫秒为单位] 播放 mp3。 经过一番研究,我赌上了 AVPlayer,这似乎是每个有类似要求的人的选择。 不幸的是,对我来说它不起作用[代码包含在最后]。 我想知道是否还有其他东西可以用来播放任何 iOS 支持的文件格式,并使用毫秒时间刻度指定开始和停止?

这是我使用 AVPlayer 的代码。我创建了一个 mp3,它在 4.3 秒时有大约 600 毫秒的静音,然后是大约一秒钟的噪音。 我有 3 个选项来创建 CMTime 以与 seekToTime 一起使用:

  • 4300 值与 1000 时间刻度
  • 4.3 * 60000 与 60000 时间刻度
  • 4.3 * mp3 文件的时间刻度与 mp3 文件的时间刻度。

不幸的是,没有一个选项对我有用,它总是被设置在噪音中的某个地方。

我尝试使用较低的值,只是为了它。 我达到了 3.7 秒,当设置时,它开始于寂静之前一点点。 有趣的是:如果我使用 3.75,它会在噪音中间开始播放。

代码的关键部分:

CMTime 声明:

mediaAudioStart = CMTimeMake((int64_t) (floatValue * 60000), 60000);

或

int32_t timeScale = _mediaAudioPlayer.currentItem.asset.duration.timescale;
mediaAudioStart = CMTimeMake((int64_t) (floatValue * timeScale), timeScale);

或

mediaAudioStart = CMTimeMake(4300, 1000);

完整代码

seekToTime 的用法:

[_mediaAudioPlayer seekToTime:mediaAudioStart toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
        if (finished) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaAudioPlaybackFinished) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
            NSLog(@"CURRENT TIME: %lld", _mediaAudioPlayer.currentTime.value);
            [_mediaAudioPlayer play];

        } else {
            NSLog(@"Could not seek media overlay file.");
        }
    }];

ViewController.m

- (void)prepareMediaAudio {
    NSLog(@"prepareMediaAudio");
    NSString *mediaAudioPath = [[NSBundle mainBundle] pathForResource:@"boostmiddle" ofType:@"mp3"];
    NSURL *mediaAudioURL = [[NSURL alloc] initFileURLWithPath:mediaAudioPath];
    if (!_mediaAudioPlayer) {
        AVPlayer *player = [AVPlayer playerWithURL:mediaAudioURL];
        [self setMediaAudioPlayer:player];
        [_mediaAudioPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
    } else {
        [self mediaAudioSeek];
    }
}

- (void)stopMediaAudio {
    NSLog(@"stopMediaAudio");
    if (_mediaAudioPlayer) {
        [_mediaAudioPlayer removeObserver:self forKeyPath:@"status"];
        [_mediaAudioPlayer pause];
        _mediaAudioPlayer = nil;
    }
}

- (void)mediaAudioSeek {
    NSLog(@"mediaAudioSeek %lld %d", mediaAudioStart.value, mediaAudioStart.timescale);

    [_mediaAudioPlayer seekToTime:mediaAudioStart toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
        if (finished) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaAudioPlaybackFinished) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
            NSLog(@"CURRENT TIME: %lld", _mediaAudioPlayer.currentTime.value);
            [_mediaAudioPlayer play];

        } else {
            NSLog(@"Could not seek media overlay file.");
        }
    }];
}

- (void)mediaAudioPlaybackFinished {
    NSLog(@"mediaAudioPlaybackFinished");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    [self stopMediaAudio];
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context {
    NSLog(@"observeValueForKeyPath");
    if (object == _mediaAudioPlayer && [keyPath isEqualToString:@"status"]) {
        if (_mediaAudioPlayer.status == AVPlayerStatusReadyToPlay) {
            NSLog(@"READY TO PLAY");
        } else if (_mediaAudioPlayer.status == AVPlayerStatusFailed) {
            NSLog(@"Could not load Media Overlay audio");
        }
    }
}

- (IBAction)button1Action:(id)sender {
    NSString *value = valueText.text;
    float floatValue = [value floatValue];
    NSLog(@"button1Action %f", floatValue);
    mediaAudioStart = CMTimeMake((int64_t) (floatValue * 60000), 60000);
    [self mediaAudioSeek];
}

- (IBAction)button2Action:(id)sender {
    NSString *value = valueText.text;
    float floatValue = [value floatValue];
    NSLog(@"button2Action %f", floatValue);
    int32_t timeScale = _mediaAudioPlayer.currentItem.asset.duration.timescale;
    mediaAudioStart = CMTimeMake((int64_t) (floatValue * timeScale), timeScale);
    [self prepareMediaAudio];
}

- (IBAction)button3Action:(id)sender {
    NSLog(@"button3Action [4300, 1000]");
    mediaAudioStart = CMTimeMake(4300, 1000);
    [self prepareMediaAudio];
}

- (IBAction)stopAction:(id)sender {
    [self stopMediaAudio];
}

- (IBAction)prepareAction:(id)sender {
    [self prepareMediaAudio];
}

示例项目

您可以从这里获取我的项目,包括我用于测试的 mp3 文件。只需在文本字段中输入 4.3。

https://www.dropbox.com/s/7q2xmuhdu1xih75/iosAudioTest.zip?dl=0

编辑:

在更新 mp3 文件(使其更短)后,用于到达同一位置的值发生了变化。现在 4.3 指向其他地方:/ 现在我对 seekToTime 方法的操作完全一无所知。

【问题讨论】:

    标签: ios audio avplayer


    【解决方案1】:

    https://warrenmoore.net/understanding-cmtime

    上面的帖子对我遇到的 CMTime 进行了最清晰的描述。恐怕你解释错了。如果你想跳 4.3 秒,你需要一个 (4.3, 60000) 的 CMTime,其中 4.3 是秒数,60000 是你每秒所需的精度。

    [av_player seekToTime:CMTimeMakeWithSeconds(4.3, 60000) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
    

    会给你一个设置为 4.3 秒的 CMTime,其精度为 60000 - 这意味着你可以在每一秒的时间之间跳转到 60000 个不同位置中的任何一个。

    【讨论】:

    • 谢谢,我阅读了那个页面,它似乎在每个关于seekToTime 的问题中都有链接,但是CMTimeMake 需要int64_t(即long long),而不是float ,所以它会被转换为 4,这是没有意义的。如果 floatValue 设置为 4.3,它将作为 4 传递,它将除以 timescale(在您的示例中为 60000),该值将用作开始,这使其几乎是文件的开头。我已经在我的测试项目中尝试过,声音从头开始播放。
    • 对不起 Krystian - 应该重新阅读文档。 CMTimeMakeWithSeconds 是方法。我已经改变了答案。
    • amergin:谢谢,但恐怕这两种方法之间几乎没有区别。我得到或多或少相同的结果。应该从 4.3 开始的沉默从 3.6 开始。我不知道这个偏移量来自哪里。同样,如果我更改文件的长度,“击中”静音所需的值也会发生变化。
    • 您尝试过不同的曲目吗?我时不时会遇到元数据不正确的音频文件,例如以 44.1k 编码但元数据显示为 48k
    • 是的,除了我用Audacity多次编码外,用其他工具检查,AVPlayerItem报告的时间刻度确实是44.100。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多