50 changes: 49 additions & 1 deletion mythplugins/mythzoneminder/mythzmserver/zmserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,44 @@ typedef struct
uint8_t control_state[256];
} SharedData26;

// shared data for ZM version 1.32.x
typedef struct
{
uint32_t size;
uint32_t last_write_index;
uint32_t last_read_index;
uint32_t state;
uint64_t last_event;
uint32_t action;
int32_t brightness;
int32_t hue;
int32_t colour;
int32_t contrast;
int32_t alarm_x;
int32_t alarm_y;
uint8_t valid;
uint8_t active;
uint8_t signal;
uint8_t format;
uint32_t imagesize;
uint32_t epadding1;
union {
time_t startup_time;
uint64_t extrapad1;
};
union {
time_t last_write_time;
uint64_t extrapad2;
};
union {
time_t last_read_time;
uint64_t extrapad3;
};
uint8_t control_state[256];

char alarm_cause[256];
} SharedData32;

typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;

// Triggerdata for ZM version 1.24.x and 1.25.x
Expand All @@ -137,7 +175,7 @@ typedef struct
char trigger_showtext[256];
} TriggerData;

// Triggerdata for ZM version 1.26.x
// Triggerdata for ZM version 1.26.x and 1.32.x
typedef struct
{
uint32_t size;
Expand All @@ -149,6 +187,15 @@ typedef struct
char trigger_showtext[256];
} TriggerData26;

// VideoStoreData for ZM version 1.32.x
typedef struct
{
uint32_t size;
uint64_t current_event;
char event_file[4096];
timeval recording;
} VideoStoreData;

class MONITOR
{
public:
Expand Down Expand Up @@ -186,6 +233,7 @@ class MONITOR
private:
SharedData *m_shared_data {nullptr};
SharedData26 *m_shared_data26 {nullptr};
SharedData32 *m_shared_data32 {nullptr};
string m_id {""};
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package org.mythtv.audio;

import android.media.AudioTrack;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTimestamp;
import java.nio.ByteBuffer;

public class AudioOutputAudioTrack
{
AudioTrack player;
AudioTimestamp timestamp = new AudioTimestamp();
long timelasttaken;
int samplerate;
long firstwritetime;
long lastwritetime;
int bufferedBytes;
Object syncBuffer;
int bufferSize;
int channels;
int bitsPer10Frames;
long bytesWritten;
int latency;
int latencyTot;
int latencyCount;
boolean isSettled;
boolean isBufferInFlux;
boolean isOutputThreadStarted;

public AudioOutputAudioTrack(int encoding, int sampleRate, int bufferSize, int channels)
{
syncBuffer = new Object();
this.bufferSize = bufferSize;
this.channels = channels;
AudioAttributes.Builder aab = new AudioAttributes.Builder();
aab.setUsage(AudioAttributes.USAGE_MEDIA);
aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);
AudioAttributes aa = aab.build();

AudioFormat.Builder afb = new AudioFormat.Builder();
afb.setEncoding (encoding);
afb.setSampleRate (sampleRate);
int channelMask = 0;
switch (channels)
{
case 8:
channelMask |= AudioFormat.CHANNEL_OUT_BACK_LEFT | AudioFormat.CHANNEL_OUT_BACK_RIGHT;
// fall through
case 6:
channelMask |= AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT
| AudioFormat.CHANNEL_OUT_FRONT_CENTER | AudioFormat.CHANNEL_OUT_LOW_FREQUENCY;
// fall through
case 2:
channelMask |= AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT;
break;
case 1:
channelMask |= AudioFormat.CHANNEL_OUT_FRONT_CENTER;
break;
default:
// default treated as 2 channel (stereo)
channelMask |= AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT;
break;
}
afb.setChannelMask(channelMask);
AudioFormat af = afb.build();
samplerate = sampleRate;
int state = 0;

for (int i = 0; i < 10; i++)
{
player = new AudioTrack(aa, af, bufferSize,
AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE);
state = player.getState();
if (state == AudioTrack.STATE_INITIALIZED)
break;
try
{
Thread.sleep(50);
}
catch (InterruptedException ex) { }
}

player.play();
}

public int write(byte[] audioData, int sizeInBytes)
{
isBufferInFlux = false;
if (player.getPlayState() != AudioTrack.PLAYSTATE_PLAYING)
player.play();
if (firstwritetime == 0)
firstwritetime = System.nanoTime();
ByteBuffer buf = ByteBuffer.wrap(audioData);
int written = 0;
int ret = 0;
int i;
while (buf.hasRemaining())
{
synchronized(syncBuffer)
{
// get out if we should not be here
if (player == null || !isOutputThreadStarted)
break;
ret = player.write(buf, buf.remaining(), AudioTrack.WRITE_NON_BLOCKING);
if (ret < 0)
{
break;
}
written += ret;
bytesWritten += ret;
lastwritetime = System.nanoTime();
// Note that only after this method returns is this data
// removed from the caller's buffer.
// bufferedBytes may be negative because actually some
// data still in the "Audio circular buffer" may have
// already played.
bufferedBytes = buf.remaining() - sizeInBytes;
}
try
{
Thread.sleep(10);
}
catch (InterruptedException ex) {}
}
synchronized(syncBuffer)
{
// After we return to caller, the data will be removed from
// the "Audio circular buffer", so the buffered bytes are now zero
isBufferInFlux = true;
bufferedBytes = 0;
}
return written;
}

public void resetFlux ()
{
isBufferInFlux = false;
}

public int getBufferedBytes ()
{
int ret;
synchronized(syncBuffer)
{
int i = 0;
if (isBufferInFlux)
{
try
{
// sleep 1 millisec to let code get from write to
// data removal from "Audio circular buffer".
// This is a crude way of doing it. A better way
// would be a call to resetFlux
// after "m_raud = next_raud" and a wait here
// to ensure this is only done after that resetFlux.
Thread.sleep(1);
}
catch (InterruptedException ex) {}
}
ret = bufferedBytes;
}
return ret;
}

// Get latency in milliseconds
// averaged over the first 10 seconds of playback plus
// at least 100 readings
public int getLatencyViaHeadPosition ()
{
if (!isSettled && player != null && bytesWritten > 0
&& bitsPer10Frames > 0)
{
int headPos = player.getPlaybackHeadPosition();
synchronized(syncBuffer)
{
long frameWritten = bytesWritten * 80 / bitsPer10Frames;
long framesInProg = frameWritten - headPos;
int new_latency = (int)(framesInProg * 1000 / samplerate);
if (new_latency >= 0 && new_latency < 1500)
{
latencyTot += new_latency;
latency = latencyTot / (++latencyCount);
}
}
if ((headPos > samplerate * 10 && latencyCount > 100)
|| latencyCount > 1000)
isSettled = true;
}
return latency;
}


public void setBitsPer10Frames (int bitsPer10Frames)
{
this.bitsPer10Frames = bitsPer10Frames;
}

public void pause (boolean doPause)
{
if (player == null)
return;

if (doPause)
{
if (player.getPlayState() == AudioTrack.PLAYSTATE_PLAYING)
player.pause();
}
else
{
if (player.getPlayState() != AudioTrack.PLAYSTATE_PLAYING)
player.play();
}
}

public void release ()
{
if (player == null)
return;

synchronized(syncBuffer)
{
pause(true);
player.flush();
player.release();
player = null;
}
}

public void setOutputThread (boolean isStarted)
{
isOutputThreadStarted = isStarted;
}

}
8 changes: 5 additions & 3 deletions mythtv/bindings/python/MythTV/services_api/send.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,6 @@ def _create_session(self):

# TODO: Problem with the BE not accepting postdata in the initial
# authorized query, Send a GET first as a workaround.
#
# Looks like a bug, Myth/version works for the backend.

try:
if self.opts['user'] and self.opts['pass']:
Expand All @@ -407,7 +405,11 @@ def _create_session(self):
if self.postdata:
saved_endpoint = self.endpoint
saved_postdata = self.postdata
self.send(endpoint='Myth/version', opts=self.opts)
# Need to adjust this if a service other than Frontend is
# added.
self.send(endpoint='{}/version'.format(
'Myth' if self.endpoint[:8] != 'Frontend'
else 'Frontend'), opts=self.opts)
self.endpoint = saved_endpoint
self.postdata = saved_postdata
except KeyError:
Expand Down
23 changes: 22 additions & 1 deletion mythtv/libs/libmyth/audio/audiooutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ using namespace std;
#endif
#ifdef Q_OS_ANDROID
#include "audiooutputopensles.h"
#include "audiooutputaudiotrack.h"
#endif
#ifdef USING_OPENMAX
#include "audiooutput_omx.h"
Expand Down Expand Up @@ -206,6 +207,16 @@ AudioOutput *AudioOutput::OpenAudio(AudioSettings &settings,
LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a OpenSLES "
"device but Android support is not compiled "
"in!");
#endif
}
else if (main_device.startsWith("AudioTrack:"))
{
#ifdef Q_OS_ANDROID
ret = new AudioOutputAudioTrack(settings);
#else
LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to AudioTrack "
"device but Android support is not compiled "
"in!");
#endif
}
else if (main_device.startsWith("OpenMAX:"))
Expand Down Expand Up @@ -564,7 +575,17 @@ AudioOutput::ADCVect* AudioOutput::GetOutputList(void)
#ifdef ANDROID
{
QString name = "OpenSLES:";
QString desc = tr("OpenSLES default output.");
QString desc = tr("OpenSLES default output. Stereo support only.");
adc = GetAudioDeviceConfig(name, desc);
if (adc)
{
list->append(*adc);
delete adc;
}
}
{
QString name = "AudioTrack:";
QString desc = tr("Android AudioTrack output. Supports surround sound.");
adc = GetAudioDeviceConfig(name, desc);
if (adc)
{
Expand Down
9 changes: 8 additions & 1 deletion mythtv/libs/libmyth/audio/audiooutput_omx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,13 @@ int AudioOutputOMX::GetBufferedOnSoundcard(void) const
}

#ifdef USING_BROADCOM
// output bits per 10 frames
int obpf;
if (m_passthru && !usesSpdif())
obpf = m_source_bitrate * 10 / m_source_samplerate;
else
obpf = m_output_bytes_per_frame * 80;

OMX_PARAM_U32TYPE u;
OMX_DATA_INIT(u);
u.nPortIndex = m_audiorender.Base();
Expand All @@ -586,7 +593,7 @@ int AudioOutputOMX::GetBufferedOnSoundcard(void) const
"GetConfig AudioRenderingLatency error %1").arg(Error2String(e)));
return 0;
}
return u.nU32 * m_output_bytes_per_frame;
return u.nU32 * obpf / 80;
#else
return m_pending;
#endif
Expand Down
320 changes: 320 additions & 0 deletions mythtv/libs/libmyth/audio/audiooutputaudiotrack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@

#include "config.h"

using namespace std;

#include <QAndroidJniObject>
#include <QAndroidJniEnvironment>
#include <android/log.h>

#include "mythlogging.h"
#include "audiooutputaudiotrack.h"

#define CHANNELS_MIN 1
#define CHANNELS_MAX 8

#define ANDROID_EXCEPTION_CHECK \
if (env->ExceptionCheck()) { \
env->ExceptionDescribe(); \
env->ExceptionClear(); \
exception=true; \
} else \
exception=false;
// clear exception without checking
#define ANDROID_EXCEPTION_CLEAR \
if (env->ExceptionCheck()) { \
env->ExceptionDescribe(); \
env->ExceptionClear(); \
}

#define LOC QString("AudioTrack: ")

// Constants from Android Java API
// class android.media.AudioFormat
#define AF_CHANNEL_OUT_MONO 4
#define AF_CHANNEL_OUT_STEREO 12
#define AF_CHANNEL_OUT_SURROUND 1052
#define AF_ENCODING_AC3 5
#define AF_ENCODING_E_AC3 6
#define AF_ENCODING_DTS 7
#define AF_ENCODING_DOLBY_TRUEHD 14
#define AF_ENCODING_PCM_8BIT 3
#define AF_ENCODING_PCM_16BIT 2
#define AF_ENCODING_PCM_FLOAT 4

// for debugging
#include <android/log.h>

AudioOutputAudioTrack::AudioOutputAudioTrack(const AudioSettings &settings) :
AudioOutputBase(settings)
{
InitSettings(settings);
if (settings.m_init)
Reconfigure(settings);
}

AudioOutputAudioTrack::~AudioOutputAudioTrack()
{
KillAudio();
}

bool AudioOutputAudioTrack::OpenDevice()
{
bool exception=false;
QAndroidJniEnvironment env;
jint encoding = 0;
jint sampleRate = m_samplerate;

// m_bitsPer10Frames = output bits per 10 frames
m_bitsPer10Frames = m_output_bytes_per_frame * 80;

if ((m_passthru || m_enc) && m_source_bitrate > 0)
m_bitsPer10Frames = m_source_bitrate * 10 / m_source_samplerate;

// 50 milliseconds
m_fragment_size = m_bitsPer10Frames * m_source_samplerate * 5 / 8000;

if (m_fragment_size < 1536)
m_fragment_size = 1536;


if (m_passthru || m_enc)
{
switch (m_codec)
{
case AV_CODEC_ID_AC3:
encoding = AF_ENCODING_AC3;
break;
case AV_CODEC_ID_DTS:
encoding = AF_ENCODING_DTS;
break;
case AV_CODEC_ID_EAC3:
encoding = AF_ENCODING_E_AC3;
break;
case AV_CODEC_ID_TRUEHD:
encoding = AF_ENCODING_DOLBY_TRUEHD;
break;

default:
LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" No support for audio passthru encoding %1").arg(m_codec));
return false;
}
}
else
{
switch (m_output_format)
{
case FORMAT_U8:
// This could be used to get the value from java instead // of haning these constants in pour header file.
// encoding = QAndroidJniObject::getStaticField<jint>
// ("android.media.AudioFormat","ENCODING_PCM_8BIT");
encoding = AF_ENCODING_PCM_8BIT;
break;
case FORMAT_S16:
encoding = AF_ENCODING_PCM_16BIT;
break;
case FORMAT_FLT:
encoding = AF_ENCODING_PCM_FLOAT;
break;
default:
LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" No support for audio format %1").arg(m_output_format));
return false;
}
}

jint minBufferSize = m_fragment_size * 4;
m_soundcard_buffer_size = minBufferSize;
jint channels = m_channels;

m_audioTrack = new QAndroidJniObject("org/mythtv/audio/AudioOutputAudioTrack",
"(IIII)V", encoding, sampleRate, minBufferSize, channels);
ANDROID_EXCEPTION_CHECK

if (exception)
{
LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" Java Exception when creating AudioTrack"));
m_audioTrack = nullptr;
return false;
}
if (!m_passthru && !m_enc)
{
jint bitsPer10Frames = m_bitsPer10Frames;
m_audioTrack->callMethod<void>("setBitsPer10Frames","(I)V",bitsPer10Frames);
}
return true;
}

void AudioOutputAudioTrack::CloseDevice()
{
QAndroidJniEnvironment env;
if (m_audioTrack)
{
m_audioTrack->callMethod<void>("release");
ANDROID_EXCEPTION_CLEAR
delete m_audioTrack;
m_audioTrack = nullptr;
}
}

AudioOutputSettings* AudioOutputAudioTrack::GetOutputSettings(bool /* digital */)
{
bool exception=false;
QAndroidJniEnvironment env;
jint bufsize = 0;

AudioOutputSettings *settings = new AudioOutputSettings();

int supportedrate = 0;
while (int rate = settings->GetNextRate())
{
// Checking for valid rates using getMinBufferSize.
// See https://stackoverflow.com/questions/8043387/android-audiorecord-supported-sampling-rates/22317382
bufsize = QAndroidJniObject::callStaticMethod<jint>
("android/media/AudioTrack", "getMinBufferSize", "(III)I",
rate, AF_CHANNEL_OUT_MONO, AF_ENCODING_PCM_16BIT);
ANDROID_EXCEPTION_CHECK
if (bufsize > 0 && !exception)
{
settings->AddSupportedRate(rate);
// save any supported rate for later
supportedrate = rate;
}
}

// Checking for valid format using getMinBufferSize.
bufsize = QAndroidJniObject::callStaticMethod<jint>
("android/media/AudioTrack", "getMinBufferSize", "(III)I",
supportedrate, AF_CHANNEL_OUT_MONO, AF_ENCODING_PCM_8BIT);
ANDROID_EXCEPTION_CHECK
if (bufsize > 0 && !exception)
settings->AddSupportedFormat(FORMAT_U8);
// 16bit always supported
settings->AddSupportedFormat(FORMAT_S16);

bufsize = QAndroidJniObject::callStaticMethod<jint>
("android/media/AudioTrack", "getMinBufferSize", "(III)I",
supportedrate, AF_CHANNEL_OUT_MONO, AF_ENCODING_PCM_FLOAT);
ANDROID_EXCEPTION_CHECK
if (bufsize > 0 && !exception)
settings->AddSupportedFormat(FORMAT_FLT);

for (uint channels = CHANNELS_MIN; channels <= CHANNELS_MAX; channels++)
{
settings->AddSupportedChannels(channels);
}
settings->setPassthrough(0);

return settings;
}

void AudioOutputAudioTrack::WriteAudio(unsigned char* aubuf, int size)
{
bool exception=false;
QAndroidJniEnvironment env;
if (m_actually_paused)
{
jboolean param = true;
m_audioTrack->callMethod<void>("pause","(Z)V",param);
ANDROID_EXCEPTION_CLEAR
return;
}
// create a java byte array
jbyteArray arr = env->NewByteArray(size);
env->SetByteArrayRegion(arr, 0, size, reinterpret_cast<jbyte*>(aubuf));
jint ret = -99;
if (m_audioTrack)
{
ret = m_audioTrack->callMethod<jint>("write","([BI)I", arr, size);
ANDROID_EXCEPTION_CHECK
}
env->DeleteLocalRef(arr);
if (ret != size || exception)
LOG(VB_GENERAL, LOG_ERR, LOC + __func__
+ QString(" Audio Write failed, size %1 return %2 exception %3")
.arg(size).arg(ret).arg(exception));

LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO, LOC + __func__
+ QString(" WriteAudio size=%1 written=%2")
.arg(size).arg(ret));
}


int AudioOutputAudioTrack::GetBufferedOnSoundcard(void) const
{
bool exception=false;
QAndroidJniEnvironment env;
int buffered (0);
if (m_audioTrack)
{
// This may return a negative value, because there
// is data already played that is still in the "Audio circular buffer"
buffered
= m_audioTrack->callMethod<jint>("getBufferedBytes");
ANDROID_EXCEPTION_CHECK
if (exception)
buffered = 0;
int latency
= m_audioTrack->callMethod<jint>("getLatencyViaHeadPosition");
ANDROID_EXCEPTION_CHECK
if (exception)
latency = 0;
buffered += latency * m_samplerate / 1000 * m_bitsPer10Frames / 80 ;
}

return buffered;
}

bool AudioOutputAudioTrack::AddData(void *in_buffer, int in_len,
int64_t timecode, int in_frames)
{
bool ret = AudioOutputBase::AddData
(in_buffer, in_len, timecode,in_frames);

return ret;
}

void AudioOutputAudioTrack::Pause(bool paused)
{
AudioOutputBase::Pause(paused);
jboolean param = paused;
m_audioTrack->callMethod<void>("pause","(Z)V",param);
}

void AudioOutputAudioTrack::SetSourceBitrate(int rate)
{
AudioOutputBase::SetSourceBitrate(rate);
if (m_source_bitrate > 0)
{
if (m_passthru || m_enc)
{
m_bitsPer10Frames = m_source_bitrate * 10 / m_source_samplerate;
jint bitsPer10Frames = m_bitsPer10Frames;
m_audioTrack->callMethod<void>("setBitsPer10Frames","(I)V",bitsPer10Frames);

}
}
}

bool AudioOutputAudioTrack::StartOutputThread(void)
{
QAndroidJniEnvironment env;
if (m_audioTrack)
{
m_audioTrack->callMethod<void>("setOutputThread","(Z)V",true);
ANDROID_EXCEPTION_CLEAR
}

return AudioOutputBase::StartOutputThread();
}

void AudioOutputAudioTrack::StopOutputThread(void)
{
QAndroidJniEnvironment env;
if (m_audioTrack)
{
m_audioTrack->callMethod<void>("setOutputThread","(Z)V",false);
ANDROID_EXCEPTION_CLEAR
}

AudioOutputBase::StopOutputThread();
}
44 changes: 44 additions & 0 deletions mythtv/libs/libmyth/audio/audiooutputaudiotrack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef _AUDIOOUTPUTAUDIOTRACK_H_
#define _AUDIOOUTPUTAUDIOTRACK_H_

#include "audiooutputbase.h"

class QAndroidJniObject;
/*
Audio output for android based on android.media.AudioTrack.
This uses the java class org.mythtv.audio.AudioOutputAudioTrack
to invoke android media playback methods.
*/

class AudioOutputAudioTrack : public AudioOutputBase
{
public:
explicit AudioOutputAudioTrack(const AudioSettings &settings);
~AudioOutputAudioTrack() override;

bool AddData(void *buffer, int len, int64_t timecode, int frames) override; // AudioOutput

// Volume control
int GetVolumeChannel(int /* channel */) const override // VolumeBase
{ return 100; }
void SetVolumeChannel(int /* channel */, int /* volume */) override // VolumeBase
{}
void Pause(bool paused) override; // AudioOutput

protected:
bool OpenDevice(void) override; // AudioOutputBase
void CloseDevice(void) override; // AudioOutputBase
void WriteAudio(unsigned char *aubuf, int size) override; // AudioOutputBase
int GetBufferedOnSoundcard(void) const override; // AudioOutputBase
AudioOutputSettings* GetOutputSettings(bool digital) override; // AudioOutputBase
void SetSourceBitrate(int rate) override; // AudioOutputBase
bool StartOutputThread(void) override; // AudioOutputBase
void StopOutputThread(void) override; // AudioOutputBase
QAndroidJniObject *m_audioTrack {nullptr};
int m_bitsPer10Frames {0};
};

#endif //_AUDIOOUTPUTAUDIOTRACK_H_
57 changes: 40 additions & 17 deletions mythtv/libs/libmyth/audio/audiooutputbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ using namespace std;
#include "mythlogging.h"
#include "mythconfig.h"

// AC3 encode currently disabled for Android
#if defined(Q_OS_ANDROID)
#define DISABLE_AC3_ENCODE
#endif

#define LOC QString("AOBase: ")

#define WPOS (m_audiobuffer + org_waud)
Expand Down Expand Up @@ -61,6 +66,9 @@ AudioOutputBase::AudioOutputBase(const AudioSettings &settings) :
memset(m_src_in_buf, 0, sizeof(m_src_in_buf));
memset(m_audiobuffer, 0, sizeof(m_audiobuffer));

if (m_main_device.startsWith("OpenMAX:")
|| m_main_device.startsWith("AudioTrack:"))
m_usesSpdif = false;
// Handle override of SRC quality settings
if (gCoreContext->GetBoolSetting("SRCQualityOverride", false))
{
Expand Down Expand Up @@ -313,7 +321,7 @@ void AudioOutputBase::SetStretchFactorLocked(float lstretchfactor)
m_pSoundStretch->setSampleRate(m_samplerate);
m_pSoundStretch->setChannels(channels);
m_pSoundStretch->setTempo(m_stretchfactor);
#if ARCH_ARM
#if ARCH_ARM || defined(Q_OS_ANDROID)
// use less demanding settings for Raspberry pi
m_pSoundStretch->setSetting(SETTING_SEQUENCE_MS, 82);
m_pSoundStretch->setSetting(SETTING_USE_AA_FILTER, 0);
Expand Down Expand Up @@ -415,11 +423,11 @@ bool AudioOutputBase::SetupPassthrough(AVCodecID codec, int codec_profile,

delete m_spdifenc;

// No spdif encoder if using openmax audio
if (m_main_device.startsWith("OpenMAX:"))
m_spdifenc = nullptr;
else
// No spdif encoder needed for certain devices
if (m_usesSpdif)
m_spdifenc = new SPDIFEncoder("spdif", codec);
else
m_spdifenc = nullptr;
if (m_spdifenc && m_spdifenc->Succeeded() && codec == AV_CODEC_ID_DTS)
{
switch(codec_profile)
Expand Down Expand Up @@ -476,10 +484,11 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
m_output_settings->IsSupportedChannels(lconfigured_channels);

// check if the number of channels could be transmitted via AC3 encoding
#ifndef DISABLE_AC3_ENCODE
lenc = m_output_settingsdigital->canFeature(FEATURE_AC3) &&
(!m_output_settings->canFeature(FEATURE_LPCM) &&
lconfigured_channels > 2 && lconfigured_channels <= 6);

#endif
if (!lenc && !cando_channels)
{
// if hardware doesn't support source audio configuration
Expand Down Expand Up @@ -517,11 +526,11 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
and we have more than 2 channels but multichannel PCM is not
supported or if the device just doesn't support the number of
channels */
#ifndef DISABLE_AC3_ENCODE
lenc = m_output_settingsdigital->canFeature(FEATURE_AC3) &&
((!m_output_settings->canFeature(FEATURE_LPCM) &&
lconfigured_channels > 2) ||
!m_output_settings->IsSupportedChannels(lconfigured_channels));

/* Might we reencode a bitstream that's been decoded for timestretch?
If the device doesn't support the number of channels - see below */
if (m_output_settingsdigital->canFeature(FEATURE_AC3) &&
Expand All @@ -530,7 +539,7 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
{
lreenc = true;
}

#endif
// Enough channels? Upmix if not, but only from mono/stereo/5.0 to 5.1
if (IS_VALID_UPMIX_CHANNEL(settings.m_channels) &&
settings.m_channels < lconfigured_channels)
Expand Down Expand Up @@ -968,7 +977,7 @@ void AudioOutputBase::SetEffDsp(int dsprate)
/**
* Get the number of bytes in the audiobuffer
*/
inline int AudioOutputBase::audiolen()
inline int AudioOutputBase::audiolen() const
{
if (m_waud >= m_raud)
return m_waud - m_raud;
Expand All @@ -978,7 +987,7 @@ inline int AudioOutputBase::audiolen()
/**
* Get the free space in the audiobuffer in bytes
*/
int AudioOutputBase::audiofree()
int AudioOutputBase::audiofree() const
{
return kAudioRingBufferSize - audiolen() - 1;
/* There is one wasted byte in the buffer. The case where waud = raud is
Expand All @@ -993,7 +1002,7 @@ int AudioOutputBase::audiofree()
* This value can differ from that returned by audiolen if samples are
* being converted to floats and the output sample format is not 32 bits
*/
int AudioOutputBase::audioready()
int AudioOutputBase::audioready() const
{
if (m_passthru || m_enc || m_bytes_per_frame == m_output_bytes_per_frame)
return audiolen();
Expand All @@ -1008,7 +1017,20 @@ int64_t AudioOutputBase::GetAudiotime(void)
if (m_audbuf_timecode == 0 || !m_configure_succeeded)
return 0;

int obpf = m_output_bytes_per_frame;
// output bits per 10 frames
int64_t obpf;

if (m_passthru && !usesSpdif())
obpf = m_source_bitrate * 10 / m_source_samplerate;
else
if (m_enc && !usesSpdif())
{
// re-encode bitrate is hardcoded at 448000
obpf = 448000 * 10 / m_source_samplerate;
}
else
obpf = m_output_bytes_per_frame * 80;

int64_t oldaudiotime;

/* We want to calculate 'audiotime', which is the timestamp of the audio
Expand All @@ -1029,21 +1051,22 @@ int64_t AudioOutputBase::GetAudiotime(void)

QMutexLocker lockav(&m_avsync_lock);

int soundcard_buffer = GetBufferedOnSoundcard(); // bytes
int64_t soundcard_buffer = GetBufferedOnSoundcard(); // bytes

/* audioready tells us how many bytes are in audiobuffer
scaled appropriately if output format != internal format */
int main_buffer = audioready();
int64_t main_buffer = audioready();

oldaudiotime = m_audiotime;

/* timecode is the stretch adjusted version
of major post-stretched buffer contents
processing latencies are catered for in AddData/SetAudiotime
to eliminate race */
m_audiotime = m_audbuf_timecode - (m_effdsp && obpf ? (
((int64_t)(main_buffer + soundcard_buffer) * m_eff_stretchfactor) /
(m_effdsp * obpf)) : 0);

m_audiotime = m_audbuf_timecode - (m_effdsp && obpf ?
((main_buffer + soundcard_buffer) * int64_t(m_eff_stretchfactor)
* 80 / int64_t(m_effdsp) / obpf) : 0);

/* audiotime should never go backwards, but we might get a negative
value if GetBufferedOnSoundcard() isn't updated by the driver very
Expand Down
9 changes: 6 additions & 3 deletions mythtv/libs/libmyth/audio/audiooutputbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,17 @@ class AudioOutputBase : public AudioOutput, public MThread

int CheckFreeSpace(int &frames);

inline int audiolen(); // number of valid bytes in audio buffer
int audiofree(); // number of free bytes in audio buffer
int audioready(); // number of bytes ready to be written
inline int audiolen() const; // number of valid bytes in audio buffer
int audiofree() const; // number of free bytes in audio buffer
int audioready() const; // number of bytes ready to be written

void SetStretchFactorLocked(float factor);

// For audiooutputca
int GetBaseAudBufTimeCode() const { return m_audbuf_timecode; }

bool usesSpdif() const { return m_usesSpdif; }

protected:
// Basic details about the audio stream
int m_channels {-1};
Expand Down Expand Up @@ -295,6 +297,7 @@ class AudioOutputBase : public AudioOutput, public MThread
int64_t m_length_last_data {0};

// SPDIF Encoder for digital passthrough
bool m_usesSpdif {true};
SPDIFEncoder *m_spdifenc {nullptr};

// Flag indicating if SetStretchFactor enabled audio float processing
Expand Down
2 changes: 2 additions & 0 deletions mythtv/libs/libmyth/libmyth.pro
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ unix:!cygwin {

android {
SOURCES += audio/audiooutputopensles.cpp
SOURCES += audio/audiooutputaudiotrack.cpp
HEADERS += audio/audiooutputopensles.h
HEADERS += audio/audiooutputaudiotrack.h
}

linux:DEFINES += linux
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmyth/standardsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ bool StandardSetting::haveChanged()
if (m_haveChanged)
{
LOG(VB_GENERAL, LOG_DEBUG,
QString("Setting %1 changed to %2").arg(getLabel())
QString("Setting '%1' changed to %2").arg(getLabel())
.arg(getValue()));
return true;
}
Expand Down
9 changes: 1 addition & 8 deletions mythtv/libs/libmythmetadata/imagemetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,7 @@
#include "exitcodes.h" // for ffprobe

// libexiv2 for Exif metadata
//#include <exiv2/exiv2.hpp>
// Note: Older versions of Exiv2 don't have the exiv2.hpp include
// file. Using image.hpp instead seems to work.
#ifdef _MSC_VER
#include <exiv2/src/image.hpp>
#else
#include <exiv2/image.hpp>
#endif
#include <exiv2/exiv2.hpp>

// To read FFMPEG Metadata
extern "C" {
Expand Down
108 changes: 61 additions & 47 deletions mythtv/libs/libmythtv/cardutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,6 @@ QStringList CardUtil::ProbeDeliverySystems(const QString &device)
// Get the list of all supported delivery systems from the card
QStringList CardUtil::ProbeDeliverySystems(int fd_frontend)
{
Q_UNUSED(fd_frontend);
QStringList delsyslist;

#ifdef USING_DVB
Expand Down Expand Up @@ -658,6 +657,24 @@ QStringList CardUtil::ProbeDeliverySystems(int fd_frontend)
return delsyslist;
}

QString CardUtil::ProbeDefaultDeliverySystem(const QString &device)
{
DTVModulationSystem delsys;

#ifdef USING_DVB
int fd = OpenVideoDevice(device);
if (fd >= 0)
{
delsys = ProbeBestDeliverySystem(fd);
close(fd);
}
#else
Q_UNUSED(device);
#endif // USING_DVB

return delsys.toString();
}

QString CardUtil::ProbeDVBType(const QString &device)
{
QString ret = "ERROR_UNKNOWN";
Expand All @@ -684,19 +701,24 @@ QString CardUtil::ProbeDVBFrontendName(const QString &device)
QString ret = "ERROR_UNKNOWN";

#ifdef USING_DVB
int fd_frontend = OpenVideoDevice(device);
if (fd_frontend > 0)
QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
QByteArray dev = dvbdev.toLatin1();
int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
if (fd_frontend < 0)
return "ERROR_OPEN";

struct dvb_frontend_info info;
memset(&info, 0, sizeof(info));
int err = ioctl(fd_frontend, FE_GET_INFO, &info);
if (err < 0)
{
struct dvb_frontend_info info;
memset(&info, 0, sizeof(info));
int err = ioctl(fd_frontend, FE_GET_INFO, &info);
ret = err < 0 ? "ERROR_PROBE" : info.name;
close(fd_frontend);
return "ERROR_PROBE";
}
else
{
ret = "ERROR_OPEN";
}

ret = info.name;

close(fd_frontend);
#else
Q_UNUSED(device);
#endif // USING_DVB
Expand Down Expand Up @@ -890,35 +912,31 @@ QString CardUtil::ProbeSubTypeName(uint inputid)

DTVTunerType tunertype;
int fd_frontend = OpenVideoDevice(inputid);
if (fd_frontend > 0)
if (fd_frontend < 0)
return "ERROR_OPEN";

DTVModulationSystem delsys = GetDeliverySystem(inputid);
if (DTVModulationSystem::kModulationSystem_UNDEFINED == delsys)
{
DTVModulationSystem delsys = GetDeliverySystem(inputid);
if (DTVModulationSystem::kModulationSystem_UNDEFINED == delsys)
delsys = ProbeBestDeliverySystem(fd_frontend);
if (DTVModulationSystem::kModulationSystem_UNDEFINED != delsys)
{
delsys = ProbeBestDeliverySystem(inputid, fd_frontend);
if (DTVModulationSystem::kModulationSystem_UNDEFINED != delsys)
{
// Update database
set_on_input("inputname", inputid, delsys.toString()); // Update DB capturecard
LOG(VB_GENERAL, LOG_INFO,
QString("CardUtil[%1]: ").arg(inputid) +
QString("Update capturecard delivery system: %1").arg(delsys.toString()));
}
else
{
LOG(VB_GENERAL, LOG_ERR,
QString("CardUtil[%1]: Error probing best delivery system").arg(inputid));
return "ERROR_UNKNOWN";
}
// Update database
set_on_input("inputname", inputid, delsys.toString()); // Update DB capturecard
LOG(VB_GENERAL, LOG_INFO,
QString("CardUtil[%1]: ").arg(inputid) +
QString("Update capturecard delivery system: %1").arg(delsys.toString()));
}
else
{
LOG(VB_GENERAL, LOG_ERR,
QString("CardUtil[%1]: Error probing best delivery system").arg(inputid));
return "ERROR_UNKNOWN";
}
SetDeliverySystem(inputid, delsys, fd_frontend);
tunertype = ConvertToTunerType(delsys);
close(fd_frontend);
}
else
{
return "ERROR_OPEN";
}
SetDeliverySystem(inputid, delsys, fd_frontend);
tunertype = ConvertToTunerType(delsys);
close(fd_frontend);

QString subtype = "ERROR_UNKNOWN";
if (DTVTunerType::kTunerTypeUnknown != tunertype)
Expand All @@ -945,15 +963,14 @@ bool CardUtil::IsDVBInputType(const QString &inputType)
// If the current delivery system is DVB-T and DVB-T2 is supported then select DVB-T2
// If the current delivery system is DVB-S and DVB-S2 is supported then select DVB-S2
//
DTVModulationSystem CardUtil::ProbeBestDeliverySystem(uint inputid, int fd)
DTVModulationSystem CardUtil::ProbeBestDeliverySystem(int fd)
{
DTVModulationSystem delsys;

#ifdef USING_DVB
// Get the current delivery system from the card
delsys = ProbeCurrentDeliverySystem(fd);
LOG(VB_GENERAL, LOG_INFO,
QString("CardUtil[%1]: ").arg(inputid) +
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Current delivery system: %1").arg(delsys.toString()));

// Get all supported delivery systems from the card
Expand All @@ -965,16 +982,15 @@ DTVModulationSystem CardUtil::ProbeBestDeliverySystem(uint inputid, int fd)
msg += " ";
msg += *it;
}
LOG(VB_GENERAL, LOG_INFO, QString("CardUtil[%1]: ").arg(inputid) + msg);
LOG(VB_GENERAL, LOG_INFO, LOC + msg);

// If the current delivery system is DVB-T and DVB-T2 is supported then select DVB-T2
if (DTVModulationSystem::kModulationSystem_DVBT == delsys)
{
DTVModulationSystem newdelsys(DTVModulationSystem::kModulationSystem_DVBT2);
if (delsyslist.contains(newdelsys.toString()))
{
LOG(VB_GENERAL, LOG_INFO,
QString("CardUtil[%1]: ").arg(inputid) +
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Changing delivery system from %1 to %2")
.arg(delsys.toString()).arg(newdelsys.toString()));
delsys = newdelsys;
Expand All @@ -987,15 +1003,13 @@ DTVModulationSystem CardUtil::ProbeBestDeliverySystem(uint inputid, int fd)
DTVModulationSystem newdelsys(DTVModulationSystem::kModulationSystem_DVBS2);
if (delsyslist.contains(newdelsys.toString()))
{
LOG(VB_GENERAL, LOG_INFO,
QString("CardUtil[%1]: ").arg(inputid) +
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Changing delivery system from %1 to %2")
.arg(delsys.toString()).arg(newdelsys.toString()));
delsys = newdelsys;
}
}
#else
Q_UNUSED(inputid);
Q_UNUSED(fd);
#endif

Expand All @@ -1019,7 +1033,7 @@ DTVModulationSystem CardUtil::GetOrProbeDeliverySystem(uint inputid, int fd)
}

// Nothing in the database, get default and use that
delsys = ProbeBestDeliverySystem(inputid, fd);
delsys = ProbeBestDeliverySystem(fd);
LOG(VB_GENERAL, LOG_INFO,
QString("CardUtil[%1]: ").arg(inputid) +
QString("No capturecard delivery system in database, using: %1").arg(delsys.toString()));
Expand Down Expand Up @@ -1134,7 +1148,7 @@ int CardUtil::SetDeliverySystem(uint inputid, DTVModulationSystem delsys, int fd
#ifdef USING_DVB
LOG(VB_GENERAL, LOG_INFO,
QString("CardUtil[%1]: ").arg(inputid) +
QString("Using delivery system: %1").arg(delsys.toString()));
QString("Set delivery system: %1").arg(delsys.toString()));

struct dtv_property prop;
struct dtv_properties cmd;
Expand Down
3 changes: 2 additions & 1 deletion mythtv/libs/libmythtv/cardutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ class MTV_PUBLIC CardUtil
static bool IsDVBInputType(const QString &inputType);
static QStringList ProbeDeliverySystems(const QString &device);
static QStringList ProbeDeliverySystems(int fd_frontend);
static QString ProbeDefaultDeliverySystem(const QString &device);
static QString ProbeDVBType(const QString &device);
static QString ProbeDVBFrontendName(const QString &device);
static bool HasDVBCRCBug(const QString &device);
Expand All @@ -387,7 +388,7 @@ class MTV_PUBLIC CardUtil
static DTVModulationSystem GetDeliverySystem(uint inputid);
static DTVModulationSystem ProbeCurrentDeliverySystem(const QString &device);
static DTVModulationSystem ProbeCurrentDeliverySystem(int fd_frontend);
static DTVModulationSystem ProbeBestDeliverySystem(uint inputid, int fd);
static DTVModulationSystem ProbeBestDeliverySystem(int fd);
static DTVModulationSystem GetOrProbeDeliverySystem(uint inputid, int fd);
static int SetDefaultDeliverySystem(uint inputid, int fd);
static int SetDeliverySystem(uint inputid);
Expand Down
13 changes: 10 additions & 3 deletions mythtv/libs/libmythtv/channelscan/channelimporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,9 @@ ScanDTVTransportList ChannelImporter::InsertChannels(
{
ChannelInsertInfo chan = transports[i].m_channels[j];

bool filter = false, handle = false;
bool asked = false;
bool filter = false;
bool handle = false;
if (!chan.m_channel_id && (kInsertIgnoreAll == action) &&
IsType(info, chan, type))
{
Expand Down Expand Up @@ -432,6 +434,10 @@ ScanDTVTransportList ChannelImporter::InsertChannels(
{
handle = false;
}
else if (kOCTOk == rc)
{
asked = true;
}
}

if (handle)
Expand Down Expand Up @@ -470,7 +476,8 @@ ScanDTVTransportList ChannelImporter::InsertChannels(
chan.m_chan_num, chan.m_source_id);
}

if (m_is_interactive &&
// Only ask if not already asked before with kInsertManual
if (m_is_interactive && !asked &&
(conflicting || (kChannelTypeConflictingFirst <= type)))
{
OkCancelType rc =
Expand Down Expand Up @@ -1292,7 +1299,7 @@ void ChannelImporter::CountChannels(
* Compute a suggested channel number based on various aspects of the
* channel information. Check to see if this channel number conflicts
* with an existing channel number. If so, fall back to incrementing a
* per-source number to fund an unused value.
* per-source number to find an unused value.
*
* \param info Unused.
* \param transport Unused.
Expand Down
11 changes: 8 additions & 3 deletions mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ bool ChannelScanSM::UpdateChannelInfo(bool wait_until_complete)
m_scanMonitor->ScanAppendTextToLog(msg_tr);
}

QString msg = LOC + QString("Program %1").arg(it.key());
QString msg = QString("Program %1").arg(it.key());
if (kEncEncrypted == *it)
msg = msg + " -- Encrypted";
else if (kEncDecrypted == *it)
Expand Down Expand Up @@ -1448,7 +1448,8 @@ ChannelScanSM::GetChannelList(transport_scan_items_it_t trans_info,
}

LOG(VB_CHANSCAN, LOG_INFO, LOC +
QString("GetChannelList: set chan_num '%1'").arg(info.m_chan_num));
QString("GetChannelList: set chan_num '%1' for '%2'")
.arg(info.m_chan_num).arg(info.m_callsign));
}

// Get QAM/SCTE/MPEG channel numbers
Expand Down Expand Up @@ -1522,6 +1523,10 @@ ScanDTVTransportList ChannelScanSM::GetChannelList(bool addFullTS) const
dbchan_it = pnum_to_dbchan.begin();
ChannelInsertInfo info = *dbchan_it;

// Use transport stream ID as (fake) service ID
// to use in callsign and as channel number
info.m_service_id = info.m_pat_tsid;

if (tuner_type == DTVTunerType::kTunerTypeASI)
info.m_callsign = QString("MPTS_%1")
.arg(CardUtil::GetDisplayName(cardid));
Expand All @@ -1540,9 +1545,9 @@ ScanDTVTransportList ChannelScanSM::GetChannelList(bool addFullTS) const
info.m_callsign = "MPTS_UNKNOWN";

info.m_service_name = info.m_callsign;
info.m_service_id = 0;
info.m_atsc_minor_channel = 0;
info.m_format = "MPTS";
info.m_use_on_air_guide = false;
item.m_channels.push_back(info);
}

Expand Down
4 changes: 2 additions & 2 deletions mythtv/libs/libmythtv/channelscan/paneexistingscanimport.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class PaneExistingScanImport : public GroupSetting
setting->addTargetedChildren(target, {this, m_scanSelect});
}

virtual void load(void)
void Load(void) override // GroupSetting
{
m_scanSelect->clearSelections();
if (!m_sourceid)
Expand All @@ -77,7 +77,7 @@ class PaneExistingScanImport : public GroupSetting
void SetSourceID(uint sourceid)
{
m_sourceid = sourceid;
load();
Load();
}

uint GetScanID(void) const { return m_scanSelect->getValue().toUInt(); }
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/dbcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3432,7 +3432,7 @@ nullptr
if (dbver == "1349")
{
const char *updates[] = {
"DELETE FROM settings WHERE value='AltClearSavedPosition';",
// Incorrect DB update removed
nullptr
};
if (!performActualUpdate(updates, "1350", dbver))
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/eitscanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include "tv_rec.h"

#define LOC QString("EITScanner: ")
#define LOC_ID QString("EITScanner (%1): ").arg(m_cardnum)
#define LOC_ID QString("EITScanner[%1]: ").arg(m_cardnum)

/** \class EITScanner
* \brief Acts as glue between ChannelBase, EITSource, and EITHelper.
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/mpeg/mpegstreamdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace std;
#include "atsctables.h"

//#define DEBUG_MPEG_RADIO // uncomment to strip video streams from TS stream
#define LOC QString("MPEGStream[%1](0x%2): ").arg(_cardid).arg((intptr_t)this, QT_POINTER_SIZE, 16)
#define LOC QString("MPEGStream[%1](0x%2): ").arg(_cardid).arg((intptr_t)this, QT_POINTER_SIZE, 16, QChar('0'))

/** \class MPEGStreamData
* \brief Encapsulates data about MPEG stream and emits events for each table.
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/mpeg/tsstreamdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "tsstreamdata.h"

#define LOC QString("TSStream[%1](0x%2): ").arg(_cardid).arg((intptr_t)this, QT_POINTER_SIZE, 16)
#define LOC QString("TSStream[%1](0x%2): ").arg(_cardid).arg((intptr_t)this, QT_POINTER_SIZE, 16, QChar('0'))

/** \class TSStreamData
* \brief Specialized version of MPEGStreamData which is used to 'blindly'
Expand Down
6 changes: 3 additions & 3 deletions mythtv/libs/libmythtv/recorders/dvbchannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ bool DVBChannel::Open(DVBChannel *who)
m_tunerType = CardUtil::ProbeTunerType(m_fd_frontend);

LOG(VB_RECORD, LOG_INFO, LOC +
QString("Frontend '%2' tunertype:%3 %4")
.arg(m_frontend_name).arg(m_tunerType).arg(m_tunerType.toString()));
QString("Frontend '%2' tunertype: %3")
.arg(m_frontend_name).arg(m_tunerType.toString()));

// Turn on the power to the LNB
if (m_tunerType.IsDiSEqCSupported())
Expand Down Expand Up @@ -834,7 +834,7 @@ bool DVBChannel::Tune(const DTVMultiplex &tuning,

SetSIStandard(tuning.m_sistandard);

LOG(VB_CHANNEL, LOG_INFO, LOC + "Tune(): Frequency tuning successful.");
LOG(VB_CHANNEL, LOG_INFO, LOC + "Tune(): Frequency tuning successful");

return true;
}
Expand Down
5 changes: 0 additions & 5 deletions mythtv/libs/libmythtv/recorders/dvbsignalmonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@ DVBSignalMonitor::DVBSignalMonitor(int db_cardnum, DVBChannel* _channel,
// in practice, however this is correct for the 4.0 DVB API
m_signalStrength.SetRange(0, 65535);

bool ok;
_channel->HasLock(&ok);
if (!ok)
LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot read DVB status" + ENO);

uint64_t rmflags = 0;

#define DVB_IO(FLAG, METHOD, MSG) \
Expand Down
31 changes: 29 additions & 2 deletions mythtv/libs/libmythtv/tv_play.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,10 +563,37 @@ void TV::InitKeys(void)
"Change Group View"), "");
REG_KEY("TV Frontend", ACTION_LISTRECORDEDEPISODES, QT_TRANSLATE_NOOP("MythControls",
"List recorded episodes"), "");
/*
* TODO DB update needs to perform the necessary conversion and delete
* the following upgrade code and replace bkmKeys and togBkmKeys with "" in the
* REG_KEY for ACTION_SETBOOKMARK and ACTION_TOGGLEBOOKMARK.
*/
// Bookmarks - Instead of SELECT to add or toggle,
// Use separate bookmark actions. This code is to convert users
// who may already be using SELECT. If they are not already using
// this frontend then nothing will be assigned to bookmark actions.
QString bkmKeys;
QString togBkmKeys;
// Check if this is a new frontend - if PAUSE returns
// "?" then frontend is new, never used before, so we will not assign
// any default bookmark keys
QString testKey = GetMythMainWindow()->GetKey("TV Playback", ACTION_PAUSE);
if (testKey != "?")
{
int alternate = gCoreContext->GetNumSetting("AltClearSavedPosition",0);
QString selectKeys = GetMythMainWindow()->GetKey("Global", ACTION_SELECT);
if (selectKeys != "?")
{
if (alternate)
togBkmKeys = selectKeys;
else
bkmKeys = selectKeys;
}
}
REG_KEY("TV Playback", ACTION_SETBOOKMARK, QT_TRANSLATE_NOOP("MythControls",
"Add Bookmark"), "");
"Add Bookmark"), bkmKeys);
REG_KEY("TV Playback", ACTION_TOGGLEBOOKMARK, QT_TRANSLATE_NOOP("MythControls",
"Toggle Bookmark"), "");
"Toggle Bookmark"), togBkmKeys);
REG_KEY("TV Playback", "BACK", QT_TRANSLATE_NOOP("MythControls",
"Exit or return to DVD menu"), "Esc");
REG_KEY("TV Playback", ACTION_MENUCOMPACT, QT_TRANSLATE_NOOP("MythControls",
Expand Down
6 changes: 5 additions & 1 deletion mythtv/libs/libmythtv/tv_rec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1734,7 +1734,7 @@ QString TVRec::GetStartChannel(uint inputid)
startchan = query.value(0).toString();
if (!startchan.isEmpty())
{
LOG(VB_CHANNEL, LOG_INFO, LOC2 + QString("Start channel: %1.")
LOG(VB_CHANNEL, LOG_INFO, LOC2 + QString("Start channel: %1")
.arg(startchan));
return startchan;
}
Expand Down Expand Up @@ -3738,6 +3738,10 @@ void TVRec::TuningFrequency(const TuningRequest &request)
{
// Not using a signal monitor, so just set the status to recording
SetRecordingStatus(RecStatus::Recording, __LINE__);
if (m_curRecording)
{
m_curRecording->SetRecordingStatus(RecStatus::Recording);
}
}


Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/tvremoteutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ void RemoteCancelNextRecording(uint inputid, bool cancel)
vector<InputInfo> RemoteRequestFreeInputInfo(uint excluded_input)
{
LOG(VB_CHANNEL, LOG_INFO,
QString("RemoteRequestFreeInputInfo exluding input %1")
QString("RemoteRequestFreeInputInfo excluding input %1")
.arg(excluded_input));

vector<InputInfo> inputs;
Expand Down
19 changes: 11 additions & 8 deletions mythtv/libs/libmythtv/videosource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ class DVBNoSeqStart : public MythUICheckBoxSetting
MythUICheckBoxSetting(
new CaptureCardDBStorage(this, parent, "dvb_wait_for_seqstart"))
{
setLabel(QObject::tr("Wait for SEQ start header."));
setLabel(QObject::tr("Wait for SEQ start header"));
setValue(true);
setHelpText(
QObject::tr("If enabled, drop packets from the start of a DVB "
Expand Down Expand Up @@ -3733,19 +3733,22 @@ void DVBConfigurationGroup::probeCard(const QString &videodevice)
// Create selection list of all delivery systems of this card
{
m_cardType->clearSelections();
QStringList delsys = CardUtil::ProbeDeliverySystems(videodevice);
QStringList::iterator it = delsys.begin();
if (it != delsys.end())
{
m_cardType->setValue(*it);
}
for (; it != delsys.end(); it++)
QStringList delsyslist = CardUtil::ProbeDeliverySystems(videodevice);
QStringList::iterator it = delsyslist.begin();
for (; it != delsyslist.end(); it++)
{
LOG(VB_GENERAL, LOG_DEBUG, QString("DVBCardType: add deliverysystem:%1")
.arg(*it));

m_cardType->addSelection(*it, *it);
}

// Default value, used if not already defined in capturecard/inputname
QString delsys = CardUtil::ProbeDefaultDeliverySystem(videodevice);
if (!delsys.isEmpty())
{
m_cardType->setValue(delsys);
}
}
#
#else
Expand Down
6 changes: 3 additions & 3 deletions mythtv/libs/libmythui/mythnotificationcenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ bool NCPrivate::Queue(const MythNotification &notification)
{
LOG(VB_GENERAL, LOG_DEBUG, LOC +
QString("Queue: 0x%1, not registered for id (%2)")
.arg((size_t)parent, QT_POINTER_SIZE, 16)
.arg((size_t)parent, QT_POINTER_SIZE, 16, QChar('0'))
.arg(id));
id = -1;
}
Expand Down Expand Up @@ -1038,7 +1038,7 @@ void NCPrivate::UnRegister(void *from, int id, bool closeimemdiately)
{
LOG(VB_GENERAL, LOG_ERR, LOC +
QString("UnRegister: 0x%1, no such registration (%2)")
.arg((size_t)from, QT_POINTER_SIZE, 16)
.arg((size_t)from, QT_POINTER_SIZE, 16, QChar('0'))
.arg(id));
return;
}
Expand All @@ -1047,7 +1047,7 @@ void NCPrivate::UnRegister(void *from, int id, bool closeimemdiately)
{
LOG(VB_GENERAL, LOG_ERR, LOC +
QString("UnRegister: 0x%1, not registered for id (%2")
.arg((size_t)from, QT_POINTER_SIZE, 16)
.arg((size_t)from, QT_POINTER_SIZE, 16, QChar('0'))
.arg(id));
}

Expand Down
2 changes: 2 additions & 0 deletions mythtv/libs/libmythui/mythuihelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ class MythUIHelperPrivate
QAtomicInt m_cacheSize {0};
QAtomicInt m_maxCacheSize {30 * 1024 * 1024};
#else
// This change is because of the QImage change from byteCount() to
// sizeInBytes(), the latter returning a 64bit value.
QAtomicInteger<qint64> m_cacheSize {0};
QAtomicInteger<qint64> m_maxCacheSize {30 * 1024 * 1024};
#endif
Expand Down
25 changes: 11 additions & 14 deletions mythtv/programs/mythbackend/httpstatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
// Qt headers
#include <QTextStream>
#include <QRegExp>
#include <QLocale>

// MythTV headers
#include "httpstatus.h"
Expand Down Expand Up @@ -1264,38 +1263,38 @@ int HttpStatus::PrintMachineInfo( QTextStream &os, const QDomElement& info )

os << " <li>Total Disk Space:\r\n"
<< " <ul>\r\n";
QLocale c(QLocale::C);

os << " <li>Total Space: ";
sRep = c.toString(nTotal) + " MB";
sRep = QString("%L1").arg(nTotal) + " MB";
os << sRep << "</li>\r\n";

os << " <li>Space Used: ";
sRep = c.toString(nUsed) + " MB";
sRep = QString("%L1").arg(nUsed) + " MB";
os << sRep << "</li>\r\n";

os << " <li>Space Free: ";
sRep = c.toString(nFree) + " MB";
sRep = QString("%L1").arg(nFree) + " MB";
os << sRep << "</li>\r\n";

if ((nLiveTV + nDeleted + nExpirable) > 0)
{
os << " <li>Space Available "
"After Auto-expire: ";
sRep = c.toString(nFree + nLiveTV +
sRep = QString("%L1").arg(nUsed) + " MB";
sRep = QString("%L1").arg(nFree + nLiveTV +
nDeleted + nExpirable) + " MB";
os << sRep << "\r\n";
os << " <ul>\r\n";
os << " <li>Space Used by LiveTV: ";
sRep = c.toString(nLiveTV) + " MB";
sRep = QString("%L1").arg(nLiveTV) + " MB";
os << sRep << "</li>\r\n";
os << " <li>Space Used by "
"Deleted Recordings: ";
sRep = c.toString(nDeleted) + " MB";
sRep = QString("%L1").arg(nDeleted) + " MB";
os << sRep << "</li>\r\n";
os << " <li>Space Used by "
"Auto-expirable Recordings: ";
sRep = c.toString(nExpirable) + " MB";
sRep = QString("%L1").arg(nExpirable) + " MB";
os << sRep << "</li>\r\n";
os << " </ul>\r\n";
os << " </li>\r\n";
Expand Down Expand Up @@ -1348,18 +1347,16 @@ int HttpStatus::PrintMachineInfo( QTextStream &os, const QDomElement& info )

os << nDir << "</li>\r\n";

QLocale c(QLocale::C);

os << " <li>Total Space: ";
sRep = c.toString(nTotal) + " MB";
sRep = QString("%L1").arg(nTotal) + " MB";
os << sRep << "</li>\r\n";

os << " <li>Space Used: ";
sRep = c.toString(nUsed) + " MB";
sRep = QString("%L1").arg(nUsed) + " MB";
os << sRep << "</li>\r\n";

os << " <li>Space Free: ";
sRep = c.toString(nFree) + " MB";
sRep = QString("%L1").arg(nFree) + " MB";
os << sRep << "</li>\r\n";

os << " </ul>\r\n"
Expand Down
30 changes: 15 additions & 15 deletions mythtv/programs/mythbackend/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2648,6 +2648,7 @@ bool Scheduler::HandleRecording(

QDateTime curtime = MythDate::current();
QDateTime nextrectime = ri.GetRecordingStartTime();
int origprerollseconds = prerollseconds;

if (ri.GetRecordingStatus() != RecStatus::WillRecord &&
ri.GetRecordingStatus() != RecStatus::Pending)
Expand Down Expand Up @@ -2819,21 +2820,18 @@ bool Scheduler::HandleRecording(

if (ri.GetRecordingStatus() != RecStatus::Pending)
{
if (m_sinputinfomap[ri.GetInputID()].m_schedgroup)
if (!AssignGroupInput(tempri, origprerollseconds))
{
if (!AssignGroupInput(tempri))
{
// We failed to assign an input. Keep asking the main
// server to add one until we get one.
MythEvent me(QString("ADD_CHILD_INPUT %1")
.arg(tempri.GetInputID()));
gCoreContext->dispatch(me);
nextWakeTime = min(nextWakeTime, curtime.addSecs(1));
return m_reclist_changed;
}
ri.SetInputID(tempri.GetInputID());
nexttv = (*m_tvList)[ri.GetInputID()];
// We failed to assign an input. Keep asking the main
// server to add one until we get one.
MythEvent me(QString("ADD_CHILD_INPUT %1")
.arg(tempri.GetInputID()));
gCoreContext->dispatch(me);
nextWakeTime = min(nextWakeTime, curtime.addSecs(1));
return m_reclist_changed;
}
ri.SetInputID(tempri.GetInputID());
nexttv = (*m_tvList)[ri.GetInputID()];

ri.SetRecordingStatus(RecStatus::Pending);
tempri.SetRecordingStatus(RecStatus::Pending);
Expand Down Expand Up @@ -2928,7 +2926,8 @@ void Scheduler::HandleRecordingStatusChange(
}
}

bool Scheduler::AssignGroupInput(RecordingInfo &ri)
bool Scheduler::AssignGroupInput(RecordingInfo &ri,
int prerollseconds)
{
if (!m_sinputinfomap[ri.GetInputID()].m_schedgroup)
return true;
Expand Down Expand Up @@ -2956,7 +2955,8 @@ bool Scheduler::AssignGroupInput(RecordingInfo &ri)
for (RecIter j = m_reclist.begin(); j != m_reclist.end(); ++j)
{
RecordingInfo *p = (*j);
if (now.secsTo(p->GetRecordingStartTime()) > 300)
if (now.secsTo(p->GetRecordingStartTime()) >
prerollseconds + 60)
break;
if (p->GetInputID() != inputid)
continue;
Expand Down
2 changes: 1 addition & 1 deletion mythtv/programs/mythbackend/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class Scheduler : public MThread, public MythScheduler
int prerollseconds);
void HandleRecordingStatusChange(
RecordingInfo &ri, RecStatus::Type recStatus, const QString &details);
bool AssignGroupInput(RecordingInfo &ri);
bool AssignGroupInput(RecordingInfo &ri, int prerollseconds);
void HandleIdleShutdown(
bool &blockShutdown, QDateTime &idleSince, int prerollseconds,
int idleTimeoutSecs, int idleWaitForRecordingTime,
Expand Down
7 changes: 6 additions & 1 deletion mythtv/programs/mythtv-setup/checksetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ bool checkChannelPresets(QStringList &probs)

MSqlQuery query(MSqlQuery::InitCon());

query.prepare("SELECT cardid, startchan, sourceid, inputname"
query.prepare("SELECT cardid, startchan, sourceid, inputname, parentid"
" FROM capturecard;");

if (!query.exec() || !query.isActive())
Expand All @@ -191,6 +191,11 @@ bool checkChannelPresets(QStringList &probs)
int cardid = query.value(0).toInt();
QString startchan = query.value(1).toString();
int sourceid = query.value(2).toInt();
int parentid = query.value(4).toInt();

// Warnings only for real devices
if (parentid != 0)
continue;

if (0 == sourceid)
{
Expand Down