【问题标题】:Getting averagePowerForChannel in AVPlayer在 AVPlayer 中获取averagePowerForChannel
【发布时间】:2016-08-26 12:11:24
【问题描述】:

我怎样才能在AVPlayer 中获得averagePowerForChannel,以便在我的音乐应用程序上进行音频可视化! 我已经完成了可视化部分,但我卡在了它的引擎中(实时音量通道)。

我知道使用AVAudioPlayer 可以很容易地使用.meteringEnabled 属性完成,但由于某些已知原因AVPlayer 在我的应用程序中是必须的! 我实际上正在考虑使用AVAudioPlayerAVPlayer 来获得所需的结果,但这听起来有点混乱, 这如何影响性能和稳定性? 提前致谢

【问题讨论】:

  • 您是否找到了获取 AVPlayer 的averagePowerForChannel 的方法?
  • 嘿,我确实找到了解决方案,但它仅在您播放非在线文件时有效,如果您需要,请告诉我
  • 不幸的是,我正在使用远程文件。不过,我对您的解决方案很感兴趣。
  • 这是一个工作示例(在 swift 3 上)它应该很容易理解,github.com/Med072356/avplayer
  • 您好,您对此有什么解决方案吗?

标签: ios avplayer


【解决方案1】:

我在AVPlayer 可视化方面遇到了大约两年的问题。就我而言,它涉及 HLS 直播,在这种情况下,据我所知,您不会让它运行。

EDIT这不会让您访问averagePowerForChannel: 方法,但您可以访问原始数据并通过例如 FFT 获得所需的信息。

不过,我让它与本地播放一起工作。您基本上等待玩家播放器项目启动并运行。此时,您需要将MTAudioProcessingTap 修补到音频混音中。

处理水龙头将运行您指定的回调,您可以在其中根据需要计算原始音频数据。

这里是一个简单的例子(很抱歉在 ObjC 中使用它):

#import <AVFoundation/AVFoundation.h>
#import <MediaToolbox/MediaToolbox.h>

void init(MTAudioProcessingTapRef tap, void *clientInfo, void **tapStorageOut) {};
void finalize(MTAudioProcessingTapRef tap) {};
void prepare(MTAudioProcessingTapRef tap, CMItemCount maxFrames, const AudioStreamBasicDescription *processingFormat) {};
void unprepare(MTAudioProcessingTapRef tap) {};
void process(MTAudioProcessingTapRef tap, CMItemCount numberFrames, MTAudioProcessingTapFlags flags, AudioBufferList *bufferListInOut, CMItemCount *numberFramesOut, MTAudioProcessingTapFlags *flagsOut) {};

- (void)play {
    // player and item setup ...

    [[[self player] currentItem] addObserver:self forKeyPath:@"tracks" options:kNilOptions context:NULL];
}

//////////////////////////////////////////////////////

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)changecontext:(void *)context
    if ([keyPath isEqualToString:@"tracks"] && [[object tracks] count] > 0) {
        for (AVPlayerItemTrack *itemTrack in [object tracks]) {
            AVAssetTrack *track = [itemTrack assetTrack];

            if ([[track mediaType] isEqualToString:AVMediaTypeAudio]) {
                [self addAudioProcessingTap:track];
                break;
            }
        }
}

- (void)addAudioProcessingTap:(AVAssetTrack *)track {
    MTAudioProcessingTapRef tap;
    MTAudioProcessingTapCallbacks callbacks;
    callbacks.version = kMTAudioProcessingTapCallbacksVersion_0;
    callbacks.clientInfo = (__bridge void *)(self);
    callbacks.init = init;
    callbacks.prepare = prepare;
    callbacks.process = process;
    callbacks.unprepare = unprepare;
    callbacks.finalize = finalise;

    OSStatus err = MTAudioProcessingTapCreate(kCFAllocatorDefault, &callbacks, kMTAudioProcessingTapCreationFlag_PostEffects, &tap);

    if (err) {
        NSLog(@"error: %@", [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]);
        return;
    }

    AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];

    AVMutableAudioMixInputParameters *inputParams = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:audioTrack];
    [inputParams setAudioTapProcessor:tap];
    [audioMix setInputParameters:@[inputParams]];

    [[[self player] currentItem] setAudioMix:audioMix];
}

两年多前在my question 上进行了一些讨论,因此请务必检查一下。

【讨论】:

  • 谢谢,objective-c 也可以。我在寻找解决方案时发现了你的旧帖子!唯一的问题是我没有使用 HLS 直播。这都是关于 *.audio(mp3/wav/etc..) 文件的!我正在研究一个解决方案,我缓存文件并将其保存为 mp3 文件,然后更新可视化。生病测试你的代码,看看它是否有帮助。
  • 这很好,因为我的东西不适用于流媒体 与本地文件(您要求的)。另请查看我的编辑,我也在代码中添加了更多详细信息!
  • 事实证明 MTAudioProcessingTap 与 swift 不完全兼容。生病继续挖掘并尝试使用objective-c(我的项目在swift中)以任何方式实现它,一旦它完全工作并且没有发布进一步的答案,你就会得到奖励!如果可能的话,您仍然可以通过提供快速解决方案来提供帮助!
  • 无赖。但这很好地反映了 Swift 的当前状态和生产可用性。不幸的是,我对 Swift 无能为力,但我会推荐一个简单的包装类,它很容易与 Swift 桥接(即提供更好的回调接口,...)。您可以使用上下文在任何 thr 回调中获取您的类实例,所以它应该不会太难完成
  • @MedAbida 我也想知道...这只是一个似乎在 Swift 中可用的 C 接口。虽然,看起来有一些困难。这是我发现的一个示例实现:github.com/gchilds/MTAudioProcessingTap-in-Swift/blob/master/…
【解决方案2】:

您需要结合 AV Foundation 的音频处理器类来可视化音频样本,并将 Core Audio 音频单元效果(带通滤波器)应用于音频数据。你可以找到sample by Apple here

基本上你需要像下面这样向你的 AVPlayer 添加一个观察者:

// Notifications
let playerItem: AVPlayerItem!  = videoPlayer.currentItem
playerItem.addObserver(self, forKeyPath: "tracks", options: NSKeyValueObservingOptions.New, context:  nil);

NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: videoPlayer.currentItem, queue: NSOperationQueue.mainQueue(), usingBlock: { (notif: NSNotification) -> Void in
   self.videoPlayer.seekToTime(kCMTimeZero)
   self.videoPlayer.play()
   print("replay")
})

然后在下面的覆盖方法中处理通知:

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if (object === videoPlayer.currentItem && keyPath == "tracks"){
        if let playerItem: AVPlayerItem = videoPlayer.currentItem {
            if let tracks = playerItem.asset.tracks as? [AVAssetTrack] {
                tapProcessor = MYAudioTapProcessor(AVPlayerItem: playerItem)
                playerItem.audioMix = tapProcessor.audioMix
                tapProcessor.delegate = self
            }
        }
    }
}

这是sample project on GitHub的链接

【讨论】:

  • 请提供经过全面测试/工作的解决方案。我说过我想使用avplayer!你不知道人!
  • 最好是斯威夫特!
  • 嘿感谢您的回复!正如我在帖子中提到的,我不打算使用 AVAudioPlayer,真正的挑战是找到一种使用 AVPlayer 来实现这种事情的方法!对不起,你没有得到任何奖励!因为我已经说过我知道如何在 AVAudioPlayer 中做到这一点。 PS:你使用的是AVAudioPlayer
  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
  • @SurvivalMachine Done,添加了一些信息并提供了指向 Apple 示例的链接,以确保不会删除部分答案。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多