質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

4049閲覧

iOSでバックグラウンドから再生中の曲情報を取得する

sont

総合スコア35

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

2クリップ

投稿2016/10/17 11:12

iOSで、自分のアプリがバックグラウンドにいても、他のミュージック再生アプリで再生している曲情報を収集できるようにしたいと思っています。

http://marunouchi-tech.i-studio.co.jp/2569/
こちらのサイトの情報によると、Apple標準ミュージック再生アプリの情報であれば取得できるようなのですが、
自分のアプリがバックグラウンドにいるときでも監視してログとして、取得することはできるでしょうか?

どなたかアドバイスお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

iOSは許可された特定の状態のアプリ以外はプロセスが完全に停止します。
Appleが規定する動作(アプリがオーディオを再生しているとかGPSを取得している等)以外でバックグラウンドで動くことはできないのでそういったログ行為はできないと思います。

Objective

1 2- (BOOL)application:(UIApplication *)application 3didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 4{ 5 _player = [MPMusicPlayerController systemMusicPlayer]; 6 7 [_player beginGeneratingPlaybackNotifications]; 8 9 [[NSNotificationCenter defaultCenter] addObserver:self 10 selector:@selector(onPlayItemChanged:) 11 name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification 12 object:nil]; 13 14 [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; 15 16 return YES; 17} 18 19 20- (void)onPlayItemChanged:(NSNotification*)notify 21{ 22 MPMediaPropertyPredicate* pred; 23 NSNumber* persistentId = notify.userInfo[@"MPMusicPlayerControllerNowPlayingItemPersistentIDKey"]; 24 25 pred = [MPMediaPropertyPredicate predicateWithValue:persistentId 26 forProperty:MPMediaItemPropertyPersistentID]; 27 28 MPMediaQuery* query; 29 30 query = [[MPMediaQuery alloc] initWithFilterPredicates:[NSSet setWithObject:pred]]; 31 32 for(MPMediaItem* item in query.items){ 33 NSLog(@"%s '%@' '%@' '%@' %@", __func__, item.title, item.artist, item.albumTitle, item.lastPlayedDate); 34 } 35} 36 37- (void)application:(UIApplication *)application 38performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 39{ 40 NSLog(@"%s", __func__); 41 completionHandler(UIBackgroundFetchResultNewData); 42} 43 44

投稿2016/10/17 12:20

編集2016/10/19 15:11
toki_td

総合スコア2850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sont

2016/10/18 01:46

回答ありがとうございます! バックグラウンドでの動作に厳しい制限がiOSではあることは理解していたのですが、既存のアプリでQuietScrobというものがあり、バックグラウンドで再生コンテンツ情報の監視を行っているようです。 このようなアプリはどのような作りになっているのでしょうか? 調べてみると、「設定」の中にある「Appのバックグラウンド更新」に「QuietScrob」が含まれているようです。 この「Appのバックグラウンド更新」に入っているアプリというのは、どのような機能が存在するものなのでしょうか?
toki_td

2016/10/18 17:19

fuzzballさんが書かれているようにCoreLocation使うとか裏で無音のオーディオ再生して、とかかなと思ったのですがそれだったらリジェクトされるはずで、、、 アプリの動作調べてみたらBackground Fetchを組み合わせれば音楽なら取得する方法がありました。 1. アプリのBackground ModeのBackground Fetchを有効にしてアプリがバックグラウンドでも定期的に起動するようにしておきます。 2. アプリ起動時に[MPMusicPlayerController systemMusicPlayer]でシステムの音楽プレーヤーを取得して、beginGeneratingPlaybackNotificationsを呼び、NotificationCenterにMPMusicPlayerControllerNowPlayingItemDidChangeNotification を登録して楽曲が変わったら通知が来るようにしておきます。この通知にはドキュメントではuserInfoは情報は何もないと書いていますが実際にはMPMediaItemのPersistentIDが入っています。 こうしておくとアプリがバックグラウンドにいる間、音楽が変わったらNSNotificationCenterに通知が溜まっていきますが、Background Fetchで定期的に(20〜30分程度で)アプリが復帰するのでその際にメインスレッドが動くから通知を受け取るんだと思います。 この時に通知のPersistentIDからMPMediaItemを取得すればコンテンツが取得できる、という仕組みだと思います。 ただ、再生時間の取得方法はよくわかりませんでした。MPMediaItemのlastPlayedDateかなぁ?時間内に2回再生したらどうなるのか気になるところです。
toki_td

2016/10/18 17:23

あっ、もしかしたら通知受けずにBackground Fetchで定期的に lastPlayedDateを調べてるだけかも、、、
fuzzball

2016/10/19 08:21 編集

Background Fetchは不定期にしか呼ばれないので今回の用途には使えないと思います。(間隔も長いですし) ※すみません、これは私の勘違いでした。 >>それだったらリジェクトされるはずで 例えばですが、アプリにバックグラウンドで曲を再生する機能があり、そのついでに曲情報を取得する(という建前)であればリジェクトされないかも知れません。 また、私が知っている「とある」アプリは、曲を再生する機能が無いにも関わらず 、バックグラウンドで曲を(音量0で)再生して、バックグラウンド処理を行っているものもあります。もちろんストアに並んでいます。 バレなければ大丈夫ですw
sont

2016/10/19 03:08

toki_tdさん、fuzzballさん、ありがとうございます! 「再生曲が変わった時に直ぐにその曲情報を入手して、ログをサーバーにあげる。」みたいな挙動を期待する場合、fuzzballさんがおっしゃるように、Background Fetchでは呼ばれるタイミングや間隔を管理できないため難しそうです。 ただ、今回私が期待している挙動は、再生曲が変わって直ぐにアップロードするまではしなくても良いかなと思っているので、toki_tdさんのやり方で大丈夫そうな気がします。 「QuietScrob」はLast.fmに再生曲のログをアップロードするアプリなのですが、ローカルで再生した曲はリアルタイムではアップロードされておらず、次回アプリ起動時にまとめてLast.fmにログをアップロードしているようだったので、toki_tdさんのおっしゃるやり方でやってるのかもしれませんね。 時間内に2回再生した場合は・・・、どうしようもなさそうですね笑。lastPlayedDateも上書きされてしまいそうですし。 以前、Facebookアプリが音量0でバックグラウンド再生していたってのありましたね笑
sont

2016/10/19 08:14 編集

toki_tdさん おっしゃるとおりの方法でMPMusicPlayerControllerNowPlayingItemDidChangeNotificationを監視するような方法で実装してみましたが、これってBackgroundでは呼ばれないですかね? >音楽が変わったらNSNotificationCenterに通知が溜まっていきますが、Background Fetchで定期的に(20〜30分程度で)アプリが復帰するのでその際にメインスレッドが動くから通知を受け取るんだと思います。 このコメントを見る限り、Background中にはメインでないスレッドで通知を保持しておく、というようなことかと思いますが、Background中に通知を受けて取れるスレッドとはどのように立てることになるのでしょうか? ちなみに、通知を受けるための実装は、 http://seeku.hateblo.jp/entry/2015/06/13/071704 こちらを参考にしました。
fuzzball

2016/10/19 11:10 編集

toki_tdさんのコメントを勘違いしていたので、お詫びにコードを書いてみたのですが、MPMediaItemの取得がこれではいけないことに気が付いたので、toki_tdさんの書かれている通り、PersistentIDから取得する方法に対応中です。 ※修正しました。
toki_td

2016/10/19 15:34

fuzzballさんのと何も代わり映えしませんが手元のiOS9(iPhone6)でも同じように動いてました(Swiftあんま慣れてないのでObjective-Cで、、、) 20〜30分音楽再生しながらバックグランドで放置しておいたら呼ばれてましたよ。 Background FetchのcompletionHandlerを呼ぶまではメインスレッドのイベントループが普通に動くんでしょうね。そのためにNotifycationCenterのキューも処理されてしまうんだと思われます。completionHandlerはすぐに呼ぶよりも少しディレイ挟んでイベントループを回してやったほうがいいのかもしれません。 Appleが意図してるかどうかはわかりませんがこの動作は他にも使い道があるような気がしますね。 >> また、私が知っている「とある」アプリは、 僕も身近なアプリで知ってます。裏で無音のオーディオ回してるやつ。 適当に理由つけるかこっそりの部分以外に明らかに普通に必要な部分があったら通るんですよね、、、
sont

2016/10/20 02:01

なるほど。ありがとうございます! 私の方でももう少し詳しくチェックしてみて動作確認してみます。 もし分からなかったらソース貼り付けたいと思います。 ちなみに、toki_tdさんでの環境は実機をPC接続して、ログを監視していましたか?
toki_td

2016/10/20 15:55

そうです。iPhone6でデバッグ実行しながら音楽を再生してホーム画面でほったらかしてました。 定期的にXcode上にログが出るのを確認してます。
guest

0

ベストアンサー

出来るか出来ないかという話でしたら、出来ます。というか出来ました。
CoreLocationを使ってバックグラウンド処理を行い、1時間ほど放置した結果です。

2016-10-18 09:32:25.378 test[3519:239320] not playing 2016-10-18 09:32:35.719 test[3519:239449] Title : California (There Is No End to Love) : 2016-10-18 10:20:35.243 test[3519:249911] Title : This Is Where You Can Reach Me Now 2016-10-18 10:20:45.252 test[3519:249910] not playing : 2016-10-18 10:48:45.250 test[3519:254771] not playing

BackgroundFetch + Notification

うっかりObjective-Cで書いてしまいました。

objectivec

1- (void)initNotif 2{ 3 MPMusicPlayerController *player = [MPMusicPlayerController systemMusicPlayer]; 4 [[NSNotificationCenter defaultCenter] addObserver:self 5 selector:@selector(nowPlayingItemDidChange:) 6 name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification 7 object:player]; 8 [player beginGeneratingPlaybackNotifications]; 9} 10 11- (void)nowPlayingItemDidChange:(NSNotification *)notif 12{ 13 MPMediaEntityPersistentID persistentID = [notif.userInfo[@"MPMusicPlayerControllerNowPlayingItemPersistentIDKey"] unsignedLongLongValue]; 14 NSLog(@"persistentID=%llu", persistentID); 15 16 MPMediaQuery *query = [MPMediaQuery new]; 17 MPMediaPropertyPredicate *pred = [MPMediaPropertyPredicate predicateWithValue:@(persistentID) 18 forProperty:MPMediaItemPropertyPersistentID 19 comparisonType:MPMediaPredicateComparisonEqualTo]; 20 [query addFilterPredicate:pred]; 21 NSArray<MPMediaItem *> *items = [query items]; 22 //NSLog(@"items=%@", items); 23 24 [items enumerateObjectsUsingBlock:^(MPMediaItem *item, NSUInteger idx, BOOL *stop) { 25 MPMediaType mediaType = [[item valueForProperty:MPMediaItemPropertyMediaType] unsignedIntegerValue]; 26 if (mediaType == MPMediaTypeMusic) 27 { 28 NSLog(@"%@ [%@] [%@] [%@] %f", item.lastPlayedDate, item.title, item.albumTitle, item.artist, item.playbackDuration); 29 } 30 }]; 31}

performFetchWithCompletionHandlerの中ではNSLogを空撃ちしているだけです。
これでしばらく放置してみました。

2016-10-19 19:48:46 test[8916] persistentID=1294602247482248857 2016-10-19 19:48:46 test[8916] 2016-10-19 10:30:22 +0000 [The Miracle (Of Joey Ramone)] [Songs of Innocence] [U2] 255.382000 2016-10-19 20:00:39 test[8916] persistentID=1294602247482248854 2016-10-19 20:00:39 test[8916] 2016-10-19 10:54:55 +0000 [Every Breaking Wave] [Songs of Innocence] [U2] 252.162000 2016-10-19 20:00:39 test[8916] [fetch] 2016-10-19 20:00:39 test[8916] persistentID=1294602247482248852 2016-10-19 20:00:39 test[8916] 2016-10-19 10:58:55 +0000 [California (There Is No End to Love)] [Songs of Innocence] [U2] 239.846000 2016-10-19 20:23:17 test[8916] persistentID=1294602247482248855 2016-10-19 20:23:17 test[8916] 2016-10-19 11:02:41 +0000 [Song for Someone] [Songs of Innocence] [U2] 226.763000 2016-10-19 20:23:17 test[8916] [fetch] 2016-10-19 20:23:17 test[8916] persistentID=1294602247482248860 2016-10-19 20:23:17 test[8916] 2016-10-19 11:08:01 +0000 [Iris (Hold Me Close)] [Songs of Innocence] [U2] 319.457000 2016-10-19 20:32:18 test[8916] persistentID=1294602247482248862 2016-10-19 20:32:18 test[8916] 2016-10-19 11:29:52 +0000 [This Is Where You Can Reach Me Now] [Songs of Innocence] [U2] 305.134000 2016-10-19 20:32:18 test[8916] [fetch] 2016-10-19 20:32:18 test[8916] persistentID=1294602247482248861 2016-10-19 20:32:18 test[8916] 2016-10-18 01:15:35 +0000 [The Troubles] [Songs of Innocence] [U2] 285.843000

うまくいってる?

投稿2016/10/18 01:58

編集2016/10/19 11:39
fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sont

2016/10/19 11:59

おー!ありがとうございます! 似たようなことをSwiftで再現しようとしてみました。 アプリ起動後、標準のミュージックアプリで適当な曲を数曲再生し(最初から最後まで再生しました)、 Xcodeから、Debug -> Simulate Background Fetchを実行しました。 performFetchWithCompletionHandlerは呼ばれていたのですが、NotificationCenterは呼ばれていなかったっぽいです。 fuzzballさんと何が違うんですかね。。。 fuzzballさんのログを見る限り、persistentID=xxxが出力されるのは、曲が変わったタイミングではなく、Background fetchが呼ばれたタイミングでしょうか? Background fetchが呼ばれるタイミングで、今まで溜まっていたNotificationCenterのコールバックが呼ばれているという感じですか?
fuzzball

2016/10/19 12:16 編集

>>Background fetchが呼ばれるタイミングで そうですね。ログのタイムスタンプを見ての通りです。ちなみに、Xcode7+iOS8+iPhone5です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問