15

First of, I have tried following both of these: https://vilimpoc.org/blog/2016/04/30/ubuntu-16-04-bluetooth-speakers/ and PulseAudio can not load bluetooth module 15.10/16.04/16.10

When I try to connect my Jaybird X2 (tried on both desktop and laptop, broadcom and intel) it pairs, connects for two seconds then disconnects.

Log form syslog (from my desktop with broadcom BT)

May 31 23:50:54 desktop pulseaudio[6247]: [pulseaudio] socket-server.c: bind(): Address already in use
May 31 23:50:54 desktop pulseaudio[6247]: [pulseaudio] module.c: Failed to load module "module-cli-protocol-unix" (argument: ""): initialization failed.
May 31 23:50:54 desktop pulseaudio[6247]: [pulseaudio] socket-server.c: bind(): Address already in use
May 31 23:50:54 desktop pulseaudio[6247]: [pulseaudio] module.c: Failed to load module "module-cli-protocol-unix" (argument: ""): initialization failed.

And other times:

a2dp-sink profile connect failed for xxxxxxx Protocol not available

EDIT.. IMPORTANT:

I have now found out that trying to connect to other devices work fine (Micropod BT and Samsung AirTrack) most of the time, but as soon as I try Jaybird X2, it disables/unloads module-bluetooth-discover and I have to pactl load-module module-bluetooth-discover for the other two to function again..

Now this happens with laptop:

May 31 17:02:58 vooze-x1 pulseaudio[3534]: [pulseaudio] backend-native.c: connect(): Function not implemented
May 31 17:02:58 vooze-x1 pulseaudio[3534]: [pulseaudio] volume.c: Assertion 'pa_channels_valid(channels)' failed at pulse/volume.c:74, function pa_cvolume_set(). Aborting.
May 31 17:02:58 vooze-x1 bluetoothd[865]: Endpoint unregistered: sender=:1.130 path=/MediaEndpoint/A2DPSource
May 31 17:02:58 vooze-x1 bluetoothd[865]: Endpoint unregistered: sender=:1.130 path=/MediaEndpoint/A2DPSink
May 31 17:03:00 vooze-x1 pulseaudio[3764]: [pulseaudio] main.c: User-configured server at {ddcf951d58914c47b9adca0056c50142}unix:/run/user/1000/pulse/native, which appears to be local. Probing deeper.
May 31 17:03:00 vooze-x1 pulseaudio[3767]: [pulseaudio] pid.c: Stale PID file, overwriting.

I was briefly able to connect it on my desktop before, but A2DP was not working most of the time..

Two different errors but same issue. What is going on?

Is bluetooth just broken in Ubuntu 16.04? It works in Windows and with my Android Phone.

Any help would be awesome! :) I somehow managed to make it work briefly, first it worked, then A2DP did not work.. so I suspect this have something to do with A2DP. Not sure.

Joakim Koed
  • 2,792
  • 3
  • 17
  • 29
  • Did you put your headset into pairing mode before connecting? From switched off device press & hold center key for 4 s until red/green flashing lights. Search for it in Ubuntu and connect. See http://askubuntu.com/questions/259354/how-do-i-connect-to-my-bluetooth-headset – Takkat May 31 '16 at 13:32
  • Yes, and it pairs just fine.. The problem is not paring, but "connecting" .. It connects and says "headphone connected" and then disconnects 2 seconds after. – Joakim Koed May 31 '16 at 14:03
  • Just asking because it is a symptom of not properly paired devices to appear to connect but then fail. It may be worth to retry (after removing device from known devices). – Takkat May 31 '16 at 14:05
  • Takket: I have probably done it 20 times. hard-reset headset, etc. etc. – Joakim Koed May 31 '16 at 14:44
  • Might be related to bug 1098959, where the power indicator interferes with mouse moves. Could also interfere with other bt streams too. Symptom is a second or less of connection then nothing. – ubfan1 May 31 '16 at 15:40
  • ubfan1: Seems unlikely, since It happens on my desktop as well, (no indicator) – Joakim Koed May 31 '16 at 16:07
  • I submitted a bug regarding this a while ago, but it hasn't caught the attention of anyone yet: https://bugs.launchpad.net/ubuntu/+source/pulseaudio/+bug/1574324 – Robert Ian Hawdon Jun 03 '16 at 14:50
  • 1
    Thank you for submitting the bug, @RobertIanHawdon. I have marked myself as affected. – Joakim Koed Jun 05 '16 at 18:38

5 Answers5

12

It is a known bug. Try rmmod btusb ; modprobe btusb. I have had to do it up to four times.

I have seen this with my Lenovo P50 with Intel 8260 wifi/bluetooth. Sometimes the bluetooth firmware doesn't load correctly at boot. Other times it just doesn't work.

whtyger
  • 5,810
  • 3
  • 33
  • 46
3

I've had the same problem with Jaybird X2 and Bluebuds X, although other Bluetooth audio devices worked without problems. With the headphones, I got this error:

Assertion 'pa_channels_valid(channels)' failed at pulse/volume.c:74, function pa_cvolume_set(). Aborting.

and pulseaudio crashed. What solved it was installing pulseaudio from sources:

This will overwrite the default system installation, but it will work until packages get updated. To prevent updates, we can put the pulseaudio packages on hold:

sudo apt-mark hold libpulse-dev libpulse0 libpulse-mainloop-glib0 pulseaudio pulseaudio-module-bluetooth pulseaudio-utils libpulsedsp pulseaudio-module-x11

Note that I installed pulseaudio 9.0, but it is not the version that made it work. I tried using the packaged version of pulseaudio 9.0 from the PPA, but it also crashed with the same error.

2

I had this issue with my Bluedio T+3 headset and what I think is happening is that there is a connection timeout. You need to uncomment the line ; exit-idle-time = 20 in the file /etc/pulse/daemon.conf by removing the semicolon (;).

Change the value to -1 to become:

exit-idle-time = -1

After that use bluetoothctl again to try and connect to the device. See here for instructions:

Arch wiki: bluetooth headset

Zanna
  • 70,465
anandan111
  • 21
  • 1
1

Ensure the following:

  • Jaybird X2 is paired
  • it is marked as trusted (either via bluetoothctl and trust XX:XX:XX:XX (where XX:XX:XX:XX is the MAC address of your Jaybird) or via blueman-manager)
  • it is turned on

Press the power button of your Jaybird X2 once. This probably triggers an auto-connect to known devices. So you may need to ensure that other devices do not interfere here. From then on the connection was stable and it also connects automatically after a reboot.

Let me know whether this also solved your problem. I did also a lot of other things and was nearly giving up, when I accidently pressed the power button ;-) So it could also be that one of those other things solved the issue. (was already searching and trying things around bluetooth, pulseaudio, bluez, pactl load-module whatever; so I have still a lot of other hints :-D)

UPDATE (after having connection issues again)

After I connected Jaybird X2 to my android phone, I was not able to reconnect to my laptop again, even after I've dropped the connection from my android phone. Don't know yet, what exactly is the problem here, but to get the connection back, I had to do the following:

  1. disconnect Jaybird X2 from my android phone (or any other device)
  2. reboot ubuntu
  3. reconnecting works and the connection is stable (it usually did not work when pairing the first time... I also required a reboot after that)

I also tried some other things and it seems that at least pulseaudio-module-bluetooth is required. Also the wifi/bluetooth coexistence-configuration is required at least on my machine ( see: https://askubuntu.com/a/645072/558838 ). And last but not least: a reboot is always required to get the connection back in case I switched to another device.

To summarize: with that reboot-step I am able to reconnect the Jaybird X2 successfully and the connection is stable. If anyone knows an easier way to omit the reboot-step, please contribute :) /etc/init.d/bluetooth restart does not suffice.

(additional steps I've tried):

I looked into my history. I tried also the following where one or the other might have contributed to the solution above:

  • apt-get install pulseaudio-module-bluetooth (on my system it wasn't installed)
  • logs mentioned something about missing ofono, so I also installed that
  • did a sudo chown -R $USER ~/*
  • also applied: https://askubuntu.com/a/691299/558838 (However I reverted it as it didn't help. But it might still have been active, when I tried the auto-connect)
  • did also purge/install the pulseaudio, blueman, bluetooth packag
Roland
  • 111
  • Thank for you for trying to help, just tested it, crash as soon as I tried. I then tried: pactl load-module module-bluetooth-discover and press power again, it once again crashed pulseaudio :/ – Joakim Koed Jun 19 '16 at 19:27
  • Added additional steps to the answer, which I've tried. If you need details on any, just ask. – Roland Jun 20 '16 at 07:56
  • OK. Found out something else today: after a sleep I couldn't get it to work again. Still it works after a reboot. Even worse: as soon as the bluetooth connection is established, my wireless connection does not work anymore. It stays connected but does not transfer anything. I have to investigate that one further. – Roland Jun 20 '16 at 15:16
  • Feel free to mark ourself as affected: https://bugs.launchpad.net/ubuntu/+source/pulseaudio/+bug/1574324 – Joakim Koed Jun 20 '16 at 15:19
  • I fixed the wifi/bluetooth coexistence problem with the following: http://askubuntu.com/a/645072/558838 :) Did a sleep again and it got the bluetooth connection back... so I may have to check what else I did, so that it now works... Maybe the coexistence bug was also the problem for the stable connection? At least it sounds more reasonable than the other things I've listed :-) Could you try that one too? – Roland Jun 20 '16 at 15:35
  • Sorry for the late reply. But I don't think this is the case since I use 5GHz WiFi only. But just to be sure I disabled WiFi during testing, same result. – Joakim Koed Jun 23 '16 at 08:58
  • Did you realize my last change of the post? At least for me it now always works after a reboot. I only have to assure that the headphones are not connected to any other device. And the pulseaudio-module-bluetooth package seems to be required. Without it I was not able to get a stable connection. I am still looking for an easier way to reconnect without reboot. Any suggestion? – Roland Jun 23 '16 at 09:10
1

Run the script here on GitHub

And the problem will vanish.

#! /usr/bin/env python3.5
"""

Fixing bluetooth stereo headphone/headset problem in ubuntu 16.04 and also debian jessie, with bluez5.

Workaround for bug: https://bugs.launchpad.net/ubuntu/+source/indicator-sound/+bug/1577197
Run it with python3.5 or higher after pairing/connecting the bluetooth stereo headphone.

This will be only fixes the bluez5 problem mentioned above .

Licence: Freeware

See ``python3.5 a2dp.py -h``.

Shorthands:

    $ alias speakers="a2dp.py 10:08:C1:44:AE:BC"
    $ alias headphones="a2dp.py 00:22:37:3D:DA:50"
    $ alias headset="a2dp.py 00:22:37:F8:A0:77 -p hsp"

    $ speakers



Check here for the latest updates: https://gist.github.com/pylover/d68be364adac5f946887b85e6ed6e7ae

Thanks to:

 * https://github.com/DominicWatson, for adding the ``-p/--profile`` argument.
 * https://github.com/IzzySoft, for mentioning wait before connecting again.
 * https://github.com/AmploDev, for v0.4.0

Change Log
----------

- 0.4.1
  * Sorting device list

- 0.4.0
  * Adding ignore_fail argument by @AmploDev.
  * Sending all available streams into selected sink, after successfull connection by @AmploDev.

- 0.3.3
  * Updating default sink before turning to ``off`` profile.

- 0.3.2
  * Waiting a bit: ``-w/--wait`` before connecting again.

- 0.3.0
  * Adding -p / --profile option for using the same script to switch between headset and A2DP audio profiles

- 0.2.5
  * Mentioning [mac] argument.

- 0.2.4
  * Removing duplicated devices in select device list.

- 0.2.3
  * Matching ANSI escape characters. Tested on 16.10 & 16.04

- 0.2.2
  * Some sort of code enhancements.

- 0.2.0
  * Adding `-V/--version`, `-w/--wait` and `-t/--tries` CLI arguments.

- 0.1.1
  * Supporting the `[NEW]` prefix for devices & controllers as advised by @wdullaer
  * Drying the code.

"""

import sys
import re
import asyncio
import subprocess as sb
import argparse


__version__ = '0.4.0'


HEX_DIGIT_PATTERN = '[0-9A-F]'
HEX_BYTE_PATTERN = '%s{2}' % HEX_DIGIT_PATTERN
MAC_ADDRESS_PATTERN = ':'.join((HEX_BYTE_PATTERN, ) * 6)
DEVICE_PATTERN = re.compile('^(?:.*\s)?Device\s(?P<mac>%s)\s(?P<name>.*)' % MAC_ADDRESS_PATTERN)
CONTROLLER_PATTERN = re.compile('^(?:.*\s)?Controller\s(?P<mac>%s)\s(?P<name>.*)' % MAC_ADDRESS_PATTERN)
WAIT_TIME = .75
TRIES = 4
PROFILE = 'a2dp'


_profiles = {
    'a2dp': 'a2dp_sink',
    'hsp': 'headset_head_unit',
    'off': 'off'
}

# CLI Arguments
parser = argparse.ArgumentParser(prog=sys.argv[0])
parser.add_argument('-e', '--echo', action='store_true', default=False,
                    help='If given, the subprocess stdout will be also printed on stdout.')
parser.add_argument('-w', '--wait', default=WAIT_TIME, type=float,
                    help='The seconds to wait for subprocess output, default is: %s' % WAIT_TIME)
parser.add_argument('-t', '--tries', default=TRIES, type=int,
                    help='The number of tries if subprocess is failed. default is: %s' % TRIES)
parser.add_argument('-p', '--profile', default=PROFILE,
                    help='The profile to switch to. available options are: hsp, a2dp. default is: %s' % PROFILE)
parser.add_argument('-V', '--version', action='store_true', help='Show the version.')
parser.add_argument('mac', nargs='?', default=None)


# Exceptions
class SubprocessError(Exception):
    pass


class RetryExceededError(Exception):
    pass


class BluetoothctlProtocol(asyncio.SubprocessProtocol):
    def __init__(self, exit_future, echo=True):
        self.exit_future = exit_future
        self.transport = None
        self.output = None
        self.echo = echo

    def listen_output(self):
        self.output = ''

    def not_listen_output(self):
        self.output = None

    def pipe_data_received(self, fd, raw):
        d = raw.decode()
        if self.echo:
            print(d, end='')

        if self.output is not None:
            self.output += d

    def process_exited(self):
        self.exit_future.set_result(True)

    def connection_made(self, transport):
        self.transport = transport
        print('Connection MADE')

    async def send_command(self, c):
        stdin_transport = self.transport.get_pipe_transport(0)
        # noinspection PyProtectedMember
        stdin_transport._pipe.write(('%s\n' % c).encode())

    async def search_in_output(self, expression, fail_expression=None):
        if self.output is None:
            return None

        for l in self.output.splitlines():
            if fail_expression and re.search(fail_expression, l, re.IGNORECASE):
                raise SubprocessError('Expression "%s" failed with fail pattern: "%s"' % (l, fail_expression))

            if re.search(expression, l, re.IGNORECASE):
                return True

    async def send_and_wait(self, cmd, wait_expression, fail_expression='fail'):
        try:
            self.listen_output()
            await self.send_command(cmd)
            while not await self.search_in_output(wait_expression.lower(), fail_expression=fail_expression):
                await wait()
        finally:
            self.not_listen_output()

    async def disconnect(self, mac):
        print('Disconnecting the device.')
        await self.send_and_wait('disconnect %s' % ':'.join(mac), 'Successful disconnected')

    async def connect(self, mac):
        print('Connecting again.')
        await self.send_and_wait('connect %s' % ':'.join(mac), 'Connection successful')

    async def trust(self, mac):
        await self.send_and_wait('trust %s' % ':'.join(mac), 'trust succeeded')

    async def quit(self):
        await self.send_command('quit')

    async def get_list(self, command, pattern):
        result = set()
        try:
            self.listen_output()
            await self.send_command(command)
            await wait()
            for l in self.output.splitlines():
                m = pattern.match(l)
                if m:
                    result.add(m.groups())
            return sorted(list(result), key=lambda i: i[1])
        finally:
            self.not_listen_output()

    async def list_devices(self):
        return await self.get_list('devices', DEVICE_PATTERN)

    async def list_paired_devices(self):
        return await self.get_list('paired-devices', DEVICE_PATTERN)

    async def list_controllers(self):
        return await self.get_list('list', CONTROLLER_PATTERN)

    async def select_paired_device(self):
        print('Selecting device:')
        devices = await self.list_paired_devices()
        count = len(devices)

        if count < 1:
            raise SubprocessError('There is no connected device.')
        elif count == 1:
            return devices[0]

        for i, d in enumerate(devices):
            print('%d. %s %s' % (i+1, d[0], d[1]))
        print('Select device[1]:')
        selected = input()
        return devices[0 if not selected.strip() else (int(selected) - 1)]


async def wait():
    return await asyncio.sleep(WAIT_TIME)


async def execute_command(cmd, ignore_fail=False):
    p = await asyncio.create_subprocess_shell(cmd, stdout=sb.PIPE, stderr=sb.PIPE)
    stdout, stderr = await p.communicate()
    stdout, stderr = \
        stdout.decode() if stdout is not None else '', \
        stderr.decode() if stderr is not None else ''
    if p.returncode != 0 or stderr.strip() != '':
        message = 'Command: %s failed with status: %s\nstderr: %s' % (cmd, p.returncode, stderr)
        if ignore_fail:
            print('Ignoring: %s' % message)
        else:
            raise SubprocessError(message)
    return stdout


async def execute_find(cmd, pattern, tries=0, fail_safe=False):
    tries = tries or TRIES

    message = 'Cannot find `%s` using `%s`.' % (pattern, cmd)
    retry_message = message + ' Retrying %d more times'
    while True:
        stdout = await execute_command(cmd)
        match = re.search(pattern, stdout)

        if match:
            return match.group()
        elif tries > 0:
            await wait()
            print(retry_message % tries)
            tries -= 1
            continue

        if fail_safe:
            return None

        raise RetryExceededError('Retry times exceeded: %s' % message)


async def find_dev_id(mac, **kw):
    return await execute_find('pactl list cards short', 'bluez_card.%s' % '_'.join(mac), **kw)


async def find_sink(mac, **kw):
    return await execute_find('pacmd list-sinks', 'bluez_sink.%s' % '_'.join(mac), **kw)


async def set_profile(device_id, profile):
    print('Setting the %s profile' % profile)
    try:
        return await execute_command('pactl set-card-profile %s %s' % (device_id, _profiles[profile]))
    except KeyError:
        print('Invalid profile: %s, please select one one of a2dp or hsp.' % profile, file=sys.stderr)
        raise SystemExit(1)


async def set_default_sink(sink):
    print('Updating default sink to %s' % sink)
    return await execute_command('pacmd set-default-sink %s' % sink)


async def move_streams_to_sink(sink):
    streams = await execute_command('pacmd list-sink-inputs | grep "index:"', True)
    for i in streams.split():
        i = ''.join(n for n in i if n.isdigit())
        if i != '':
            print('Moving stream %s to sink' % i)
            await execute_command('pacmd move-sink-input %s %s' % (i, sink))
    return sink


async def main(args):
    global WAIT_TIME, TRIES

    if args.version:
        print(__version__)
        return 0

    mac = args.mac

    # Hacking, Changing the constants!
    WAIT_TIME = args.wait
    TRIES = args.tries

    exit_future = asyncio.Future()
    transport, protocol = await asyncio.get_event_loop().subprocess_exec(
        lambda: BluetoothctlProtocol(exit_future, echo=args.echo), 'bluetoothctl'
    )

    try:

        if mac is None:
            mac, _ = await protocol.select_paired_device()

        mac = mac.split(':' if ':' in mac else '_')
        print('Device MAC: %s' % ':'.join(mac))

        device_id = await find_dev_id(mac, fail_safe=True)
        if device_id is None:
            print('It seems device: %s is not connected yet, trying to connect.' % ':'.join(mac))
            await protocol.trust(mac)
            await protocol.connect(mac)
            device_id = await find_dev_id(mac)

        sink = await find_sink(mac, fail_safe=True)
        if sink is None:
            await set_profile(device_id, args.profile)
            sink = await find_sink(mac)

        print('Device ID: %s' % device_id)
        print('Sink: %s' % sink)

        await set_default_sink(sink)
        await wait()

        await set_profile(device_id, 'off')

        if args.profile is 'a2dp':
            await protocol.disconnect(mac)
            await wait()
            await protocol.connect(mac)

        device_id = await find_dev_id(mac)
        print('Device ID: %s' % device_id)

        await set_profile(device_id, args.profile)
        await set_default_sink(sink)
        await move_streams_to_sink(sink)

    except (SubprocessError, RetryExceededError) as ex:
        print(str(ex), file=sys.stderr)
        return 1
    finally:
        print('Exiting bluetoothctl')
        await protocol.quit()
        await exit_future

        # Close the stdout pipe
        transport.close()

    if args.profile == 'a2dp':
        print('"Enjoy" the HiFi stereo music :)')
    else:
        print('"Enjoy" your headset audio :)')


if __name__ == '__main__':
    sys.exit(asyncio.get_event_loop().run_until_complete(main(parser.parse_args())))
muru
  • 197,895
  • 55
  • 485
  • 740