これは私が今のところ数日間把握しようとしてきた非常に奇妙な問題です。それについての奇妙なことは、特定のユーザーアカウントに対してのみこれを行うことです。これはWindowsのアプリケーションであり、ジュークボックスサンプルを出発点として使用しました。sp_session_process_eventsは、sp_session_loginを呼び出した後にアクセス違反を引き起こします。
- 私はsp_session_login()を呼び出します。
- logged_in()のコールバックを取得しました。
- notify_main_threadコールバックがあります。
- sp_session_process_events()は、アクセス違反の後にいくつかのイベントを処理します。
私はこれを何度もトレースしており、クラッシュを引き起こすアカウントでは、約8回userinfo_updated()コールバックを取得します。クラッシュしないアカウント私はそれを1〜2回しか見ません。私はコールバックで何もしていない。私はそれが私が提供していなかったコールバックの1つを必要とするかどうかを見ようとしていました。
クラッシュすることを防ぐ方法があります。私がlogged_in()コールバック(ジュークボックスサンプルのような)中にトラックをロードして再生すると、クラッシュしません。
このデバッグでは、どんな助力があれば幸いです。
-tommy
UPDATE:2016年4月7日 ここでは、ログファイルの出力です。誰かが私にこれを理解させるのを助けることを願っています。また、プレイリストに関連すると思います。私は再生リストのコールバックを設定しています。
14:57:57:
14 tgsourceユーザに47.735 sp_session_loginロギングセッションsp_session_login 47.788返さ:SP_ERROR_OK
14:57:47.859 LOG_MESSAGE 21:57:47.859 I [user_cache:135] UserCache 57:47.906 LOG_MESSAGE 21:57:私は[AP:1752] 47.906 APへの接続が
14:57をap.gslb.spotify.com:4070 :: initiateGetUsersは、(1人の)ユーザ
14を照会します:47.937 logged_in logged_inユーザーは:tgsource
14:57:47.937 LOG_MESSAGE 21:57:47.937 I [オフライン-MGR:2084]ストレージは
14洗浄された:57:47.984 LOG_MESSAGE 21:57:47.984 I [AP:1226]に接続しますAP:194.68.29.165:4070
14:57:48.405は
をcredentials_blob_updated credentials_blob_updated14:57:57:48.452 userinfo_updatedユーザー情報
と呼ばれるユーザー情報userinfo_updated 48.405は14が呼び出さ
14:57:57:プレイリストの48.920 PlaylistContainerLoaded数:8
14:57:49.107 LOG_MESSAGE 21時57分49秒48.452ユーザー情報userinfo_updatedは
14と呼ばれます。107 E [AP:4172] ChannelError(3、1、プレイリスト)
14:57:49.139 LOG_MESSAGE 21:57:49.139 W [コア/プレイリスト/ playlist.h:45]
を更新しながら観察を追加14:57:49.139 log_message 21:57:49.139 W [core/playlist/playlist.h:45]更新中のオブザーバーを追加する
14:57:49.154 log_message 21:57:49.154 W [コア/プレイリスト/プレイリスト] h:45]更新中にオブザーバを追加する
14:57:49.154 log_message 21:57:49.154 [core/playlist/playlist.h:45]更新時にオブザーバを追加する
14:57:49.295 LOG_MESSAGE 21:57:49.295 I [user_cache:135]:57:49.295 LOG_MESSAGE 21:57:49.295 W [コア/ UserCache :: initiateGetUsersは、(1人の)ユーザ
14を照会しますプレイリスト/ playlist.h:45]
14更新しながら観察者の追加:57:49.295 LOG_MESSAGE 21:57:49.295 W [コア/プレイリスト/ playlist.h:
夜02時57分を更新しながら45]は、観察者の追加:49.327 userinfo_updated Userinfoと呼ばれる
14:57:49.467 userinfo_updated Userinfo called
**クラッシュ**
アップデート2016年4月11日: ここでは、問題を再現するソースコードがあります。ジュークボックスの例のplaytrack.cファイルのポートです。私はトラックの読み込みを削除し、同じ問題を引き起こします。
/**
* Copyright (c) 2006-2010 Spotify Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*
* This example application is the most minimal way to just play a spotify URI.
*
* This file is part of the libspotify examples suite. Jukebox - playtrack.c
*/
#include <errno.h>
#include "stdint.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../libspotify/api.h"
#include "audio.h"
#include <windows.h>
/* --- Data --- */
extern const uint8_t g_appkey[];
extern const size_t g_appkey_size;
static audio_fifo_t g_audiofifo;
static HANDLE g_notify_mutex;
static HANDLE g_notify_cond;
static int g_notify_do;
int g_playback_done;
sp_session *g_sess;
static sp_track *g_currenttrack;
const char *g_trackurl;
static bool is_logged_out = FALSE;
static HANDLE events;
/* --------------------------- PLAYLIST CALLBACKS ------------------------- */
/**
* Playlist container callbacks.
* If some callbacks should not be of interest, set them to NULL.
*
*/
static void SP_CALLCONV PlaylistContainerLoaded(sp_playlistcontainer* pc, void* userdata)
{
int numPlaylists = sp_playlistcontainer_num_playlists(pc);
printf("Number of playlists: %d", numPlaylists);
}
static sp_playlistcontainer_callbacks pc_callbacks =
{
NULL,
NULL,
NULL,
&PlaylistContainerLoaded
};
/* --------------------------- SESSION CALLBACKS ------------------------- */
/**
* This callback is called when an attempt to login has succeeded or failed.
*
* @sa sp_session_callbacks#logged_in
*/
static void SP_CALLCONV logged_in(sp_session *sess, sp_error error)
{
sp_link *link;
if (SP_ERROR_OK != error) {
fprintf(stderr, "Login failed: %s\n",
sp_error_message(error));
exit(2);
}
sp_playlistcontainer *pc = sp_session_playlistcontainer(sess);
sp_playlistcontainer_add_callbacks(pc, &pc_callbacks, NULL);
// Note: Removing the track start will cause this to crash
#if 0
printf("Loading track\n");
link = sp_link_create_from_string("spotify:track:0W4Kpfp1w2xkY3PrV714B7");
sp_track_add_ref(g_currenttrack = sp_link_as_track(link));
sp_link_release(link);
if (sp_track_error(g_currenttrack) == SP_ERROR_OK) {
printf("Now playing \"%s\"...\n", sp_track_name(g_currenttrack));
printf("Duration: %d\n", sp_track_duration(g_currenttrack));
fflush(stdout);
sp_session_player_load(g_sess, g_currenttrack);
sp_session_player_play(g_sess, 1);
}
#endif
/* Track not loaded? Then we need to wait for the metadata to
load before we can start playback (see metadata_updated below) */
}
/**
* Callback called when libspotify has new metadata available
*
* @sa sp_session_callbacks#metadata_updated
*/
static void SP_CALLCONV metadata_updated(sp_session *sess)
{
puts("Metadata updated, trying to start playback");
if (sp_track_error(g_currenttrack) != SP_ERROR_OK)
return;
sp_session_player_load(g_sess, g_currenttrack);
sp_session_player_play(g_sess, 1);
}
/**
* This callback is called from an internal libspotify thread to ask
* us to reiterate the main loop.
*
* We notify the main thread using a condition variable and a protected variable.
*
* @sa sp_session_callbacks#notify_main_thread
*/
static void SP_CALLCONV notify_main_thread(sp_session *sess)
{
g_notify_do = 1;
SetEvent(events);
}
/**
* This callback is used from libspotify whenever there is PCM data available.
*
* @sa sp_session_callbacks#music_delivery
*/
static int SP_CALLCONV music_delivery(sp_session *sess, const sp_audioformat *format,
const void *frames, int num_frames)
{
audio_fifo_t *af = &g_audiofifo;
audio_fifo_data_t *afd;
size_t s;
if (num_frames == 0)
return 0; // Audio discontinuity, do nothing
WaitForSingleObject(af->mutex, INFINITE);
/* Buffer one second of audio */
if (af->qlen > format->sample_rate) {
ReleaseMutex(af->mutex);
return 0;
}
s = num_frames * sizeof(int16_t) * format->channels;
afd = malloc(sizeof(audio_fifo_data_t) + s);
memcpy(afd->samples, frames, s);
afd->nsamples = num_frames;
afd->rate = format->sample_rate;
afd->channels = format->channels;
TAILQ_INSERT_TAIL(&af->q, afd, link);
af->qlen += num_frames;
PulseEvent(af->cond);
ReleaseMutex(af->mutex);
return num_frames;
}
/**
* This callback is used from libspotify when the current track has ended
*
* @sa sp_session_callbacks#end_of_track
*/
static void SP_CALLCONV end_of_track(sp_session *sess)
{
printf("end_of_track\n");
g_playback_done = 1;
}
/**
* Notification that some other connection has started playing on this account.
* Playback has been stopped.
*
* @sa sp_session_callbacks#play_token_lost
*/
static void SP_CALLCONV play_token_lost(sp_session *sess)
{
printf("play_token_lost\n");
audio_fifo_flush(&g_audiofifo);
if (g_currenttrack != NULL) {
sp_session_player_unload(g_sess);
g_currenttrack = NULL;
}
}
static void SP_CALLCONV log_message(sp_session *session, const char *msg)
{
puts(msg);
}
static void SP_CALLCONV userinfo_updated(sp_session *session)
{
printf("Userinfo called.\n");
}
static void SP_CALLCONV offline_status_updated(sp_session *sess)
{
sp_offline_sync_status status;
sp_offline_sync_get_status(sess, &status);
if(status.syncing) {
printf("Offline status: queued:%d:%zd done:%d:%zd copied:%d:%zd nocopy:%d err:%d\n",
status.queued_tracks,
(size_t)status.queued_bytes,
status.done_tracks,
(size_t)status.done_bytes,
status.copied_tracks,
(size_t)status.copied_bytes,
status.willnotcopy_tracks,
status.error_tracks);
} else {
printf("Offline status: Idle\n");
}
}
/**
* The session callbacks
*/
static sp_session_callbacks session_callbacks = {
&logged_in,
NULL,
&metadata_updated,
NULL,
NULL,
¬ify_main_thread,
&music_delivery,
&play_token_lost,
&log_message,
&end_of_track,
NULL,
&userinfo_updated,
NULL,
NULL,
NULL,
&offline_status_updated,
NULL,
NULL,
NULL,
NULL,
NULL,
};
/**
* The session configuration. Note that application_key_size is an
* external, so we set it in main() instead.
*/
static sp_session_config spconfig = {
SPOTIFY_API_VERSION,
"tmp",
"tmp",
g_appkey,
0, // Set in main()
"spotify-jukebox-playtrack",
&session_callbacks,
NULL,
};
/* ------------------------- END SESSION CALLBACKS ----------------------- */
/**
* A track has ended. Remove it from the playlist.
*
* Called from the main loop when the music_delivery() callback has set g_playback_done.
*/
static void SP_CALLCONV track_ended(void)
{
printf("track_ended\n");
if (g_currenttrack) {
sp_track_release(g_currenttrack);
g_currenttrack = NULL;
sp_session_player_unload(g_sess);
exit(0);
}
}
/**
* Show usage information
*
* @param progname The program name
*/
static void usage(const char *progname)
{
fprintf(stderr, "usage: %s <username> <password> <trackurl>\n", progname);
}
/*************************************************
* app main
*
*************************************************/
int main(int argc, char **argv)
{
sp_session *sp;
sp_error err;
int next_timeout = 0;
DWORD ev;
const char* username_default = "username";
const char* password_default = "login";
const char *username = argc > 1 ? argv[1] : NULL;
const char *password = argc > 2 ? argv[2] : NULL;
g_trackurl = argc > 3 ? argv[3] : NULL;
if (!username || !password) {
username = username_default;
password = password_default;
}
events = CreateEvent(NULL, FALSE, FALSE, NULL);
audio_init(&g_audiofifo);
/* Create session */
spconfig.application_key_size = g_appkey_size;
err = sp_session_create(&spconfig, &sp);
if (SP_ERROR_OK != err) {
fprintf(stderr, "Unable to create session: %s\n",
sp_error_message(err));
exit(1);
}
g_sess = sp;
// Q: Why a mutex and cond here???
// pthread_mutex_init(&g_notify_mutex, NULL);
// pthread_cond_init(&g_notify_cond, NULL);
err = sp_session_login(sp, username, password, 0, NULL);
// pthread_mutex_lock(&g_notify_mutex);
if (SP_ERROR_OK != err) {
fprintf(stderr, "Unable to create session: %s\n",
sp_error_message(err));
exit(1);
}
while(!is_logged_out)
{
ev = WaitForSingleObject(events, next_timeout > 0 ? next_timeout : INFINITE);
switch (ev)
{
case WAIT_OBJECT_0:
case WAIT_TIMEOUT:
do {
sp_session_process_events(g_sess, &next_timeout);
} while (next_timeout == 0);
break;
}
if(g_playback_done)
{
track_ended();
break;
}
}
audio_free(&g_audiofifo);
printf("Logged out\n");
sp_session_release(g_sess);
printf("Exiting...\n");
return 0;
}