Exemple #1
0
void ELD::show()
{
    int i;

    if (!isValid())
    {
        VBAUDIO("Invalid ELD");
        return;
    }
    VBAUDIO(QString("Detected monitor %1 at connection type %2")
            .arg(product_name().simplified())
            .arg(connection_name()));

    if (m_e.spk_alloc)
    {
        VBAUDIO(QString("available speakers:%1")
                .arg(channel_allocation_desc()));
    }
    VBAUDIO(QString("max LPCM channels = %1").arg(maxLPCMChannels()));
    VBAUDIO(QString("max channels = %1").arg(maxChannels()));
    VBAUDIO(QString("supported codecs = %1").arg(codecs_desc()));
    for (i = 0; i < m_e.sad_count; i++)
    {
        VBAUDIO(sad_desc(i));
    }
}
Exemple #2
0
void AudioOutputPulseAudio::ServerInfoCallback(
    pa_context *context, const pa_server_info *inf, void *arg)
{
    QString fn_log_tag = "ServerInfoCallback, ";

    VBAUDIO(fn_log_tag +
            QString("PulseAudio server info - host name: %1, server version: "
                    "%2, server name: %3, default sink: %4")
            .arg(inf->host_name).arg(inf->server_version)
            .arg(inf->server_name).arg(inf->default_sink_name));
}
Exemple #3
0
char *AudioOutputPulseAudio::ChooseHost(void)
{
    QString fn_log_tag = "ChooseHost, ";
    char *pulse_host = NULL;
    char *device = strdup(main_device.toAscii().constData());
    const char *host;

    for (host=device; host && *host != ':' && *host != 0; host++);

    if (host && *host != 0)
        host++;

    if ( !(!host || *host == 0 || strcmp(host,"default") == 0))
    {
        if ((pulse_host = new char[strlen(host)]))
            strcpy(pulse_host, host);
        else
            VBERROR(fn_log_tag +
                    QString("allocation of pulse host '%1' char[%2] failed")
                    .arg(host).arg(strlen(host) + 1));
    }

    if (!pulse_host && strcmp(host,"default") != 0)
    {
        char *env_pulse_host = getenv("PULSE_SERVER");
        if (env_pulse_host && (*env_pulse_host != '\0'))
        {
            int host_len = strlen(env_pulse_host) + 1;

            if ((pulse_host = new char[host_len]))
                strcpy(pulse_host, env_pulse_host);
            else
            {
                VBERROR(fn_log_tag +
                        QString("allocation of pulse host '%1' char[%2] failed")
                        .arg(env_pulse_host).arg(host_len));
            }
        }
    }

    VBAUDIO(fn_log_tag + QString("chosen PulseAudio server: %1")
            .arg((pulse_host != NULL) ? pulse_host : "default"));

    free(device);

    return pulse_host;
}
Exemple #4
0
bool AudioOutputPulseAudio::ConnectPlaybackStream(void)
{
    QString fn_log_tag = "ConnectPlaybackStream, ";
    pstream = pa_stream_new(pcontext, "MythTV playback", &sample_spec,
                            &channel_map);
    if (!pstream)
    {
        VBERROR(fn_log_tag + QString("failed to create new playback stream"));
        return false;
    }
    pa_stream_set_state_callback(pstream, StreamStateCallback, this);
    pa_stream_set_write_callback(pstream, WriteCallback, this);
    pa_stream_set_overflow_callback(pstream, BufferFlowCallback, (char*)"over");
    pa_stream_set_underflow_callback(pstream, BufferFlowCallback,
                                     (char*)"under");
    if (set_initial_vol)
    {
        int volume = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
        pa_cvolume_set(&volume_control, channels,
                       (float)volume * (float)PA_VOLUME_NORM / 100.0f);
    }
    else
        pa_cvolume_reset(&volume_control, channels);

    fragment_size = (samplerate * 25 * output_bytes_per_frame) / 1000;

    buffer_settings.maxlength = (uint32_t)-1;
    buffer_settings.tlength = fragment_size * 4;
    buffer_settings.prebuf = (uint32_t)-1;
    buffer_settings.minreq = (uint32_t)-1;

    int flags = PA_STREAM_INTERPOLATE_TIMING
                | PA_STREAM_ADJUST_LATENCY
                | PA_STREAM_AUTO_TIMING_UPDATE
                | PA_STREAM_NO_REMIX_CHANNELS;

    pa_stream_connect_playback(pstream, NULL, &buffer_settings,
                               (pa_stream_flags_t)flags, &volume_control, NULL);

    pa_context_state_t cstate;
    pa_stream_state_t sstate;
    bool connected = false, failed = false;

    while (!(connected || failed))
    {
        switch (cstate = pa_context_get_state(pcontext))
        {
        case PA_CONTEXT_FAILED:
        case PA_CONTEXT_TERMINATED:
            VERBOSE(VB_IMPORTANT, LOC_ERR + fn_log_tag +
                    QString("context is stuffed, %1")
                    .arg(pa_strerror(pa_context_errno(
                                         pcontext))));
            failed = true;
            break;
        default:
            switch (sstate = pa_stream_get_state(pstream))
            {
            case PA_STREAM_READY:
                connected = true;
                break;
            case PA_STREAM_FAILED:
            case PA_STREAM_TERMINATED:
                VBERROR(fn_log_tag +
                        QString("stream failed or was terminated, "
                                "context state %1, stream state %2")
                        .arg(cstate).arg(sstate));
                failed = true;
                break;
            default:
                pa_threaded_mainloop_wait(mainloop);
                break;
            }
        }
    }

    const pa_buffer_attr *buf_attr = pa_stream_get_buffer_attr(pstream);
    fragment_size = buf_attr->tlength >> 2;
    soundcard_buffer_size = buf_attr->maxlength;

    VBAUDIO(fn_log_tag + QString("fragment size %1, soundcard buffer size %2")
            .arg(fragment_size).arg(soundcard_buffer_size));

    return (connected && !failed);
}
Exemple #5
0
bool AudioOutputPulseAudio::ContextConnect(void)
{
    QString fn_log_tag = "ContextConnect, ";
    if (pcontext)
    {
        VBERROR(fn_log_tag + "context appears to exist, but shouldn't (yet)");
        pa_context_unref(pcontext);
        pcontext = NULL;
        return false;
    }
    pcontext = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "MythTV");
    if (!pcontext)
    {
        VBERROR(fn_log_tag + "failed to acquire new context");
        return false;
    }
    pa_context_set_state_callback(pcontext, ContextStateCallback, this);

    char *pulse_host = ChooseHost();
    int chk = pa_context_connect(
                  pcontext, pulse_host, (pa_context_flags_t)0, NULL);

    delete(pulse_host);

    if (chk < 0)
    {
        VBERROR(fn_log_tag + QString("context connect failed: %1")
                .arg(pa_strerror(pa_context_errno(pcontext))));
        return false;
    }
    bool connected = false;
    pa_context_state_t state = pa_context_get_state(pcontext);
    for (; !connected; state = pa_context_get_state(pcontext))
    {
        switch(state)
        {
        case PA_CONTEXT_READY:
            VBAUDIO(fn_log_tag +"context connection ready");
            connected = true;
            continue;

        case PA_CONTEXT_FAILED:
        case PA_CONTEXT_TERMINATED:
            VBERROR(fn_log_tag +
                    QString("context connection failed or terminated: %1")
                    .arg(pa_strerror(pa_context_errno(pcontext))));
            return false;

        default:
            VBAUDIO(fn_log_tag + "waiting for context connection ready");
            pa_threaded_mainloop_wait(mainloop);
            break;
        }
    }

    pa_operation *op =
        pa_context_get_server_info(pcontext, ServerInfoCallback, this);

    if (op)
        pa_operation_unref(op);
    else
        VBERROR(fn_log_tag + "failed to get PulseAudio server info");

    return true;
}
Exemple #6
0
bool AudioOutputPulseAudio::OpenDevice()
{
    QString fn_log_tag = "OpenDevice, ";
    if (channels > PULSE_MAX_CHANNELS )
    {
        VBERROR(fn_log_tag + QString("audio channel limit %1, but %2 requested")
                .arg(PULSE_MAX_CHANNELS).arg(channels));
        return false;
    }

    sample_spec.rate = samplerate;
    sample_spec.channels = volume_control.channels = channels;
    switch (output_format)
    {
    case FORMAT_U8:
        sample_spec.format = PA_SAMPLE_U8;
        break;
    case FORMAT_S16:
        sample_spec.format = PA_SAMPLE_S16NE;
        break;
// define from PA 0.9.15 only
#ifdef PA_MAJOR
    case FORMAT_S24LSB:
        sample_spec.format = PA_SAMPLE_S24_32NE;
        break;
#endif
    case FORMAT_S32:
        sample_spec.format = PA_SAMPLE_S32NE;
        break;
    case FORMAT_FLT:
        sample_spec.format = PA_SAMPLE_FLOAT32NE;
        break;
        break;
    default:
        VBERROR(fn_log_tag + QString("unsupported sample format %1")
                .arg(output_format));
        return false;
    }

    if (!pa_sample_spec_valid(&sample_spec))
    {
        VBERROR(fn_log_tag + "invalid sample spec");
        return false;
    }
    else
    {
        char spec[PA_SAMPLE_SPEC_SNPRINT_MAX];
        pa_sample_spec_snprint(spec, sizeof(spec), &sample_spec);
        VBAUDIO(fn_log_tag + QString("using sample spec %1").arg(spec));
    }

    pa_channel_map *pmap = NULL;

    if(!(pmap = pa_channel_map_init_auto(&channel_map, channels,
                                         PA_CHANNEL_MAP_WAVEEX)) < 0)
    {
        VBERROR(fn_log_tag + "failed to init channel map");
        return false;
    }

    channel_map = *pmap;

    mainloop = pa_threaded_mainloop_new();
    if (!mainloop)
    {
        VBERROR(fn_log_tag + "failed to get new threaded mainloop");
        return false;
    }

    pa_threaded_mainloop_start(mainloop);
    pa_threaded_mainloop_lock(mainloop);

    if (!ContextConnect())
    {
        pa_threaded_mainloop_unlock(mainloop);
        pa_threaded_mainloop_stop(mainloop);
        return false;
    }

    if (!ConnectPlaybackStream())
    {
        pa_threaded_mainloop_unlock(mainloop);
        pa_threaded_mainloop_stop(mainloop);
        return false;
    }

    pa_threaded_mainloop_unlock(mainloop);
    return true;
}
Exemple #7
0
int ELD::update_eld(const char *buf, int size)
{
    int mnl;
    int i;

    m_e.eld_ver = GRAB_BITS(buf, 0, 3, 5);
    if (m_e.eld_ver != ELD_VER_CEA_861D &&
        m_e.eld_ver != ELD_VER_PARTIAL)
    {
        VBAUDIO(QString("Unknown ELD version %1").arg(m_e.eld_ver));
        goto out_fail;
    }

    m_e.eld_size = size;
    m_e.baseline_len    = GRAB_BITS(buf, 2, 0, 8);
    mnl                = GRAB_BITS(buf, 4, 0, 5);
    m_e.cea_edid_ver    = GRAB_BITS(buf, 4, 5, 3);

    m_e.support_hdcp    = GRAB_BITS(buf, 5, 0, 1);
    m_e.support_ai      = GRAB_BITS(buf, 5, 1, 1);
    m_e.conn_type       = GRAB_BITS(buf, 5, 2, 2);
    m_e.sad_count       = GRAB_BITS(buf, 5, 4, 4);

    m_e.aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
    m_e.spk_alloc       = GRAB_BITS(buf, 7, 0, 7);

    m_e.port_id         = LE_INT64(buf + 8);

    /* not specified, but the spec's tendency is little endian */
    m_e.manufacture_id  = LE_SHORT(buf + 16);
    m_e.product_id      = LE_SHORT(buf + 18);

    if (mnl > ELD_MAX_MNL)
    {
        VBAUDIO(QString("MNL is reserved value %1").arg(mnl));
        goto out_fail;
    }
    else if (ELD_FIXED_BYTES + mnl > size)
    {
        VBAUDIO(QString("out of range MNL %1").arg(mnl));
        goto out_fail;
    }
    else
    {
        strncpy(m_e.monitor_name, (char *)buf + ELD_FIXED_BYTES, mnl + 1);
        m_e.monitor_name[mnl] = '\0';
    }

    for (i = 0; i < m_e.sad_count; i++)
    {
        if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size)
        {
            VBAUDIO(QString("out of range SAD %1").arg(i));
            goto out_fail;
        }
        update_sad(i, buf + ELD_FIXED_BYTES + mnl + 3 * i);
    }

    /*
     * Assume the highest speakers configuration
     */
    if (!m_e.spk_alloc)
        m_e.spk_alloc = 0xffff;

    m_e.eld_valid = true;
    return 0;

  out_fail:
    m_e.eld_valid = false;
    return -1;
}
Exemple #8
0
void ELD::update_sad(int index,
                     const char *buf)
{
    int val;

    cea_sad *a = m_e.sad + index;

    val = GRAB_BITS(buf, 1, 0, 7);
    a->rates = 0;
    for (int i = 0; i < 7; i++)
        if ((val & (1 << i)) != 0)
            a->rates |= cea_sampling_frequencies[i + 1];

    a->channels = GRAB_BITS(buf, 0, 0, 3);
    a->channels++;

    a->sample_bits = 0;
    a->max_bitrate = 0;

    a->format = GRAB_BITS(buf, 0, 3, 4);
    m_e.formats |= 1 << a->format;
    switch (a->format)
    {
        case TYPE_REF_STREAM_HEADER:
            VBAUDIO("audio coding type 0 not expected");
            break;

        case TYPE_LPCM:
            a->sample_bits = GRAB_BITS(buf, 2, 0, 3);
            break;

        case TYPE_AC3:
        case TYPE_MPEG1:
        case TYPE_MP3:
        case TYPE_MPEG2:
        case TYPE_AACLC:
        case TYPE_DTS:
        case TYPE_ATRAC:
            a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
            a->max_bitrate *= 8000;
            break;

        case TYPE_SACD:
            break;

        case TYPE_EAC3:
            break;

        case TYPE_DTS_HD:
            break;

        case TYPE_MLP:
            break;

        case TYPE_DST:
            break;

        case TYPE_WMAPRO:
            a->profile = GRAB_BITS(buf, 2, 0, 3);
            break;

        case TYPE_REF_CXT:
            a->format = GRAB_BITS(buf, 2, 3, 5);
            if (a->format == XTYPE_HE_REF_CT ||
                a->format >= XTYPE_FIRST_RESERVED)
            {
                VBAUDIO(QString("audio coding xtype %1 not expected")
                        .arg(a->format));
                a->format = 0;
            }
            else
            {
                a->format += TYPE_HE_AAC - XTYPE_HE_AAC;
            }
            break;
    }
}