sp_session_player_load()上的AccessViolationException

时间:2015-02-24 11:57:02

标签: c# naudio libspotify

我正在尝试基于Spotify的libspotify SDK创建流应用程序。

要在C#中实现这一点,请使用ohLibspotify绑定和包装器。这只是一个很薄的抽象层,因此大部分都是libspotify SDK的1:1映射。使用NAudio库播放传入的PCM数据。

大多数时候我都可以演奏第一首曲目。然后,当我加载第二个时,我会在尝试呼叫AccessViolationException时获得sp_session_player_load()。这有时也会在我第一次尝试播放曲目时发生,有时它会在第三次发生。

这是我用来播放曲目的功能。

public void playTrack(string track, string juke)
{
    new Thread(new ThreadStart(() =>
    {
        var playable = Link.CreateFromString(string.Format("spotify:track:{0}", track)).AsTrack();

        if (playing)
        {
            player.Pause();
            App.Logic.spotify.sp_session.PlayerUnload();
        }

        buffer = new BufferedWaveProvider(new WaveFormat())
        {
            BufferDuration = TimeSpan.FromSeconds(120),
            DiscardOnBufferOverflow = false
        };

        var waitEvent = new AutoResetEvent(false);
        while (!playable.IsLoaded())
        {
            waitEvent.WaitOne(30);
        }

        App.Logic.spotify.sp_session.PlayerLoad(playable);
        App.Logic.spotify.sp_session.PlayerPlay(true);

        player = new WaveOut();
        player.Init(buffer);
        player.Play();
        playing = true;
    })).Start();
}

AccessViolationException发生在包装器库中以下代码段的第6行。

    [DllImport("libspotify")]
    internal static extern SpotifyError sp_session_player_load(IntPtr @session, IntPtr @track);
    public void PlayerLoad(Track @track)
    {
        SpotifyError errorValue;
        errorValue = NativeMethods.sp_session_player_load(this._handle, track._handle);
        SpotifyMarshalling.CheckError(errorValue);
    }

流媒体回调:

        public override void GetAudioBufferStats(SpotifySession session, out AudioBufferStats stats)
        {
            stats = new AudioBufferStats()
            {
                samples = App.Logic.spotify.player.buffer.BufferedBytes / 2,
                stutter = 0
            };
        }

        public override int MusicDelivery(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames) {
            int incoming_size = num_frames * format.channels * 2;
            try
            {
                if (incoming_size > sample_buffer.Length)
                {
                    short rendered_frames = Convert.ToInt16(Math.Floor((sample_buffer.Length / format.channels / 2d)));
                    short rendered_size = Convert.ToInt16(rendered_frames * format.channels * 2);
                    Marshal.Copy(frames, sample_buffer, 0, rendered_size);
                    App.Logic.spotify.player.buffer.AddSamples(sample_buffer, 0, rendered_size);
                    return rendered_frames;
                }
                else
                {
                    Marshal.Copy(frames, sample_buffer, 0, incoming_size);
                    App.Logic.spotify.player.buffer.AddSamples(sample_buffer, 0, incoming_size);
                    return num_frames;
                }
            }
            catch (Exception e)
            {
                return 0;
            }
        }

1 个答案:

答案 0 :(得分:0)

正如@Weeble在对我的问题的评论中所说。诊断AccessViolationException的可能来源非常困难。在我的情况下,它是线程,有多个线程一次访问Spotify会话对象。

如果这也是您的问题,您可能需要查看this article。它讨论了C#和VB.Net中的线程同步。这真的很容易。

lock(sessionLock)
{
    App.Logic.spotify.sp_session.PlayerLoad(playable);
    App.Logic.spotify.sp_session.PlayerPlay(true);
}

sessionLock对象可以是Object类型的简单实例化。虽然不建议使用任何“实际”对象。就我而言,我做了以下事情:

public class Player
{
    private object sessionLock = new object();

    // Rest of code with locks
}

这样,每次要访问sessionLock对象时,都可以锁定SpotifySession对象。因此,当线程1加载歌曲并且线程2想要同时处理事件时,线程2将被阻止,直到sessionLock上的锁定被解除为止。

正如@Weeble建议的那样,如果线程不是问题,你可能还需要考虑其他一些事情。

  1. 您可能无法正确处理spotify的重新计数,并且意外地过早地减少了重新计数,或者忘记在必要时在某处增加重新计数。
  2. 你可能会破坏处理本机指针的音乐回调中的内存。
  3. ohLibspotify或libspotify中可能存在错误。如果您认为是这种情况,请转到ohLibspotify repoopenhome forum报告您的问题。