commit 13739fc1e7674a0be931e1f2aed7d30b7e3dad2c
Author: Mark Spieth <mspieth@digivation.com.au>
Date:   Wed Jan 5 10:49:43 2011 +1100

    nextgen comm detector with audio and subtitle data fusion

diff --git a/mythtv/programs/mythcommflag/AudioBuffer.cpp b/mythtv/programs/mythcommflag/AudioBuffer.cpp
new file mode 100644
index 0000000..10d15cd
--- /dev/null
+++ b/mythtv/programs/mythcommflag/AudioBuffer.cpp
@@ -0,0 +1,375 @@
+
+#include "AudioBuffer.h"
+
+AudioSample::AudioSample()
+{
+    time = 0;
+    duration = 0;
+    power = 0;
+    channels = 0;
+    changed = false;
+}
+
+QString AudioSample::toString() const
+{
+    return QString("%1 %2 %3 %4 %5")
+        .arg(time)
+        .arg(duration)
+        .arg(power)
+        .arg(channels)
+        .arg(changed);
+}
+
+
+AudioBuffer::AudioBuffer(const AudioSettings &settings) :
+    configured_channels(CHANNELS_MAX),
+    last_audiotime(0),
+    output_settings(0),
+    changed(false),
+    info_valid(false),
+    m_lock(),
+    verboseDebugging(false),
+    enabled(false)
+{
+    if (getenv("DEBUGCOMMFLAG"))
+        verboseDebugging = true;
+
+    Reset();
+    //AudioSettings orig_settings("", "", AudioFormat::,);
+    //Reconfigure(audio_bits, audio_channels, 0);
+    Reconfigure(settings);
+}
+
+AudioBuffer::~AudioBuffer()
+{
+    //delete [] audiobuffer;
+    if (output_settings)
+    {
+        delete output_settings;
+        output_settings = NULL;
+    }
+}
+
+AudioOutputSettings* AudioBuffer::GetOutputSettings(bool /*digital*/)
+{
+    AudioOutputSettings *settings = new AudioOutputSettings();
+
+    for (uint channels = CHANNELS_MIN; channels <= CHANNELS_MAX; channels++)
+    {
+        settings->AddSupportedChannels(channels);
+    }
+
+    LOG(VB_COMMFLAG, LOG_INFO,
+        QString("GetOutputSettings"));
+    return settings;
+}
+
+AudioOutputSettings* AudioBuffer::GetOutputSettingsUsers(bool digital)
+{
+    if (!output_settings)
+        output_settings = GetOutputSettings(digital);
+    return output_settings;
+}
+
+AudioOutputSettings* AudioBuffer::GetOutputSettingsCleaned(bool digital)
+{
+    if (!output_settings)
+        output_settings = GetOutputSettings(digital);
+    return output_settings;
+}
+
+void AudioBuffer::Enable()
+{
+    enabled = true;
+}
+
+// reconfigure sound out for new params
+void AudioBuffer::Reconfigure(const AudioSettings &orig_settings)
+{
+    ClearError();
+    changed = (orig_settings.format != settings.format)
+        | (orig_settings.channels != settings.channels)
+        | (orig_settings.samplerate != settings.samplerate);
+    settings = orig_settings;
+
+    source_bytes_per_frame = source_channels * AudioOutputSettings::SampleSize(settings.format);
+    LOG(VB_COMMFLAG, LOG_INFO,
+        QString("Audio Reconfigure changed %1.").arg(changed));
+
+    QMutexLocker locker(&m_lock);
+    audioSamples.clear();
+    last_audiotime = 0;
+}
+
+// dsprate is in 100 * samples/second
+void AudioBuffer::SetEffDsp(int /* dsprate */)
+{
+    //eff_audiorate = (dsprate / 100);
+}
+
+void AudioBuffer::SetBlocking(bool block) 
+{
+    (void)block; 
+}
+
+void AudioBuffer::Reset(void)
+{
+    if (!enabled)
+        return;
+    if (verboseDebugging)
+    {
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+            QString("audio reset"));
+    }
+    QMutexLocker locker(&m_lock);
+    audioSamples.clear();
+    last_audiotime = 0;
+}
+
+bool AudioBuffer::AddFrames(void *in_buffer, int in_frames,
+                                        int64_t timecode)
+{
+    if (!enabled)
+    {
+        last_audiotime = timecode;
+        return true;
+    }
+    return AddData(in_buffer, in_frames * source_bytes_per_frame, timecode,
+                                   in_frames);
+}
+
+bool AudioBuffer::AddData(void *in_buffer, int in_len,
+                     int64_t timecode, int in_frames)
+{
+    if (!enabled)
+    {
+        last_audiotime = timecode;
+        return true;
+    }
+    int i;
+    int f;
+    PAudioSample aSample(new AudioSample);
+    aSample->power = 0;
+    aSample->time = timecode;    // in ms
+    aSample->duration = (in_frames * 1000) / settings.samplerate; // in ms
+    aSample->channels = settings.channels;
+    if (changed)
+    {
+        aSample->changed = changed;
+        changed = false;
+    }
+    if (timecode < last_audiotime)
+    {
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+            QString("audio time reset %1").arg(timecode));
+        QMutexLocker locker(&m_lock);
+        audioSamples.clear();
+        last_audiotime = 0;
+    }
+    if (last_audiotime != 0)
+    {
+        int64_t nextAudiotime = last_audiotime + aSample->duration;
+        while (aSample->time < nextAudiotime)
+        {
+            PAudioSample nullSample(new AudioSample);
+            nullSample->power = -1;
+            nullSample->time = nextAudiotime;
+            nullSample->duration = aSample->duration;
+            {
+                QMutexLocker locker(&m_lock);
+                audioSamples.push_back(nullSample);
+            }
+#ifdef AUDIODEBUGGING
+            LOG(VB_COMMFLAG, LOG_DEBUG,
+                QString("Audio AddData invalid time %1").arg(nullSample->time));
+#endif
+        }
+    }
+    switch (settings.format)
+    {
+        case FORMAT_S16:
+            {
+                int64_t power = 0;
+                int16_t *p = (int16_t*)in_buffer;
+                for(f=in_frames;f>0;--f)
+                {
+                    // power as mono
+                    int64_t sum = 0;
+                    for(i=settings.channels; i>0; --i)
+                    {
+                        sum += *p++;
+                        p++;
+                    }
+                    power += sum * sum;
+                }
+                aSample->power += ((double)power)/(INT16_MAX * INT16_MAX)/in_frames;
+                info_valid = true;
+                QMutexLocker locker(&m_lock);
+                audioSamples.push_back(aSample);
+            } break;
+        case FORMAT_FLT:
+            {
+                double power = 0;
+                float *p = (float*)in_buffer;
+                for(f=in_frames;f>0;--f)
+                {
+                    // power as mono
+                    int64_t sum = 0;
+                    for(i=settings.channels; i>0; --i)
+                    {
+                        sum += *p++;
+                        p++;
+                    }
+                    power += sum * sum;
+                }
+                aSample->power += power/in_frames;
+                info_valid = true;
+                QMutexLocker locker(&m_lock);
+                audioSamples.push_back(aSample);
+            } break;
+        default:
+            break;
+    }
+#ifdef AUDIODEBUGGING
+    if (info_valid)
+    {
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+            QString("Audio AddData time %1 frames %2 power %3 changed %4").arg(timecode).arg(in_frames).arg(aSample->power).arg(aSample->changed));
+    }
+    else
+    {
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+            QString("Audio AddData time %1 frames %2").arg(timecode).arg(in_frames));
+    }
+#endif
+    last_audiotime = timecode;
+    return true;
+}
+
+const PAudioSample AudioBuffer::GetSample(int64_t time)
+{
+    if (!audioSamples.empty())
+    {
+        QMutexLocker locker(&m_lock);
+#ifdef USE_AUDIO_LIST
+        PAudioSample s;
+        do
+        {
+            if (audioSamples.empty())
+                break;
+            s = audioSamples.front();
+            if (!s)
+                break;
+            int64_t dtime = s->time - time;
+            if (dtime < 0)
+            {
+                // time already past
+                LOG(VB_COMMFLAG, LOG_DEBUG, QString("Audio GetSample past time %1 atime %2 duration %3 size %4").arg(time).arg(s->time).arg(s->duration).arg(audioSamples.size()));
+                audioSamples.pop_front();
+                continue;
+            }
+            if (dtime < s->duration)
+            {
+                LOG(VB_COMMFLAG, LOG_DEBUG, QString("Audio GetSample time %1 atime %2 duration %3 size %4").arg(time).arg(s->time).arg(s->duration).arg(audioSamples.size()));
+                //audioSamples.pop_front();
+                return s;
+            }
+            // not yet
+            break;
+        } while (true);
+#else
+        int64_t index = (time - audioSamples[0]->time)/audioSamples[0]->duration;
+        //LOG(VB_COMMFLAG, LOG_DEBUG, QString("Audio GetSample time %1 tine0 %2 duration %3 index %4 size %5").arg(time).arg(audioSamples[0].time).arg(audioSamples[0].duration).arg(index).arg(audioSamples.size()));
+        if (index >= 0 && index < audioSamples.size())
+            return audioSamples[index];
+#endif
+    }
+    return PAudioSample();
+}
+
+void AudioBuffer::SetTimecode(int64_t timecode)
+{
+    last_audiotime = timecode;
+}
+bool AudioBuffer::GetPause(void)
+{
+    return false;
+}
+void AudioBuffer::Pause(bool paused)
+{
+    (void)paused;
+}
+void AudioBuffer::Drain(void)
+{
+    // Do nothing
+    return;
+}
+bool AudioBuffer::IsPaused() const { return false; }
+void AudioBuffer::PauseUntilBuffered() { }
+bool AudioBuffer::IsUpmixing() { return false; }
+bool AudioBuffer::ToggleUpmix() { return false; }
+bool AudioBuffer::CanUpmix() { return false; }
+
+
+int64_t AudioBuffer::GetAudiotime(void)
+{
+    return last_audiotime;
+}
+
+int AudioBuffer::GetVolumeChannel(int) const
+{ 
+    // Do nothing
+    return 100;
+}
+void AudioBuffer::SetVolumeChannel(int, int) 
+{
+    // Do nothing
+}
+void AudioBuffer::SetVolumeAll(int)
+{
+    // Do nothing
+}
+int AudioBuffer::GetSWVolume(void)
+{
+    return 100;
+}
+void AudioBuffer::SetSWVolume(int, bool) {}
+
+
+int AudioBuffer::GetCurrentVolume(void)
+{ 
+    // Do nothing
+    return 100;
+}
+void AudioBuffer::SetCurrentVolume(int) 
+{
+    // Do nothing
+}
+void AudioBuffer::AdjustCurrentVolume(int) 
+{
+    // Do nothing
+}
+void AudioBuffer::SetMute(bool) 
+{
+    // Do nothing
+}
+void AudioBuffer::ToggleMute(void) 
+{
+    // Do nothing
+}
+MuteState AudioBuffer::GetMute(void) 
+{
+    // Do nothing
+    return kMuteOff;
+}
+MuteState AudioBuffer::IterateMutedChannels(void) 
+{
+    // Do nothing
+    return kMuteOff;
+}
+
+//  These are pure virtual in AudioOutput, but we don't need them here
+void AudioBuffer::bufferOutputData(bool){ return; }
+int AudioBuffer::readOutputData(unsigned char*, int ){ return 0; }
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/AudioBuffer.h b/mythtv/programs/mythcommflag/AudioBuffer.h
new file mode 100644
index 0000000..2689d40
--- /dev/null
+++ b/mythtv/programs/mythcommflag/AudioBuffer.h
@@ -0,0 +1,129 @@
+#ifndef _AUDIOBUFFER_H_
+#define _AUDIOBUFFER_H_
+
+#define USE_AUDIO_LIST
+
+// POSIX headers
+#include <stdint.h>
+
+// Qt headers
+#include <QObject>
+#include <QSharedPointer>
+
+// MythTV headers
+#include "mythcontext.h"
+#include <deque>
+
+#include "audiooutput.h"
+
+#define AUDIODEBUGGING
+
+class AudioSample : public QSharedData
+{
+public:
+    int64_t time;
+    int64_t duration;
+    double  power;
+    int     channels;
+    bool    changed;
+    AudioSample();
+    QString toString() const;
+};
+
+typedef QExplicitlySharedDataPointer< AudioSample > PAudioSample;
+
+
+#define CHANNELS_MIN 1
+#define CHANNELS_MAX 8
+class AudioBuffer : public AudioOutput
+{
+ public:
+    AudioBuffer(const AudioSettings &settings);
+   ~AudioBuffer();
+
+    AudioOutputSettings* GetOutputSettings(bool /*digital*/);
+    AudioOutputSettings* GetOutputSettingsUsers(bool digital);
+    AudioOutputSettings* GetOutputSettingsCleaned(bool digital);
+
+    // reconfigure sound out for new params
+    virtual void Reconfigure(const AudioSettings &orig_settings);
+
+    void Enable();
+
+    virtual void SetEffDsp(int /* dsprate */);
+    virtual void SetBlocking(bool block);
+    virtual void Reset(void);
+    virtual bool AddFrames(void *in_buffer, int in_frames, int64_t timecode);
+    virtual bool AddData(void *in_buffer, int in_len,
+                         int64_t timecode, int in_frames);
+    const PAudioSample GetSample(int64_t time);
+    virtual void SetTimecode(int64_t timecode);
+    virtual bool GetPause(void);
+    virtual void Pause(bool paused);
+    virtual void Drain(void);
+    virtual bool IsPaused() const;
+    virtual void PauseUntilBuffered();
+    virtual bool IsUpmixing();
+    virtual bool ToggleUpmix();
+    virtual bool CanUpmix();
+
+    virtual int64_t GetAudiotime(void);
+    virtual int GetVolumeChannel(int) const;
+    virtual void SetVolumeChannel(int, int);
+    virtual void SetVolumeAll(int);
+    virtual int GetSWVolume(void);
+    virtual void SetSWVolume(int, bool);
+    virtual int GetCurrentVolume(void);
+    virtual void SetCurrentVolume(int);
+    virtual void AdjustCurrentVolume(int);
+    virtual void SetMute(bool);
+    virtual void ToggleMute(void);
+    virtual MuteState GetMute(void);
+    virtual MuteState IterateMutedChannels(void);
+
+    //  These are pure virtual in AudioOutput, but we don't need them here
+    virtual void bufferOutputData(bool);
+    virtual int readOutputData(unsigned char*, int );
+
+    // Basic details about the audio stream
+    int channels;
+    int codec;
+    int bytes_per_frame;
+    int output_bytes_per_frame;
+    AudioFormat format; 
+    AudioFormat output_format;
+    int samplerate;
+    int source_channels;
+    int source_samplerate;
+    int source_bytes_per_frame;
+
+    int bufsize;
+    //unsigned char *audiobuffer;
+    //int audiobuffer_len;
+    //int channels, bits, bytes_per_sample, eff_audiorate;
+    int configured_channels;
+    AudioSettings settings;
+    int64_t last_audiotime;
+
+    AudioOutputSettings* output_settings;
+
+    bool changed;
+    bool info_valid;
+
+    int64_t first_audiotime;
+
+#ifdef USE_AUDIO_LIST
+    deque< PAudioSample > audioSamples;
+#else
+    vector< PAudioSample > audioSamples;
+#endif
+
+ private:
+    mutable QMutex m_lock;
+    bool verboseDebugging;
+    bool enabled;
+};
+
+#endif
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/AudioChangeDetector.cpp b/mythtv/programs/mythcommflag/AudioChangeDetector.cpp
new file mode 100644
index 0000000..63c8901
--- /dev/null
+++ b/mythtv/programs/mythcommflag/AudioChangeDetector.cpp
@@ -0,0 +1,13 @@
+using namespace std;
+
+#include "AudioChangeDetector.h"
+
+AudioChangeDetector::AudioChangeDetector()
+{
+}
+
+void AudioChangeDetector::processFrame(unsigned char *frame)
+{
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/AudioChangeDetector.h b/mythtv/programs/mythcommflag/AudioChangeDetector.h
new file mode 100644
index 0000000..4d24215
--- /dev/null
+++ b/mythtv/programs/mythcommflag/AudioChangeDetector.h
@@ -0,0 +1,24 @@
+/*
+ * AudioChangeDetector
+ *
+ * Detect audi changes
+ */
+
+#ifndef __AUDIOCHANGEDETECTOR_H__
+#define __AUDIOCHANGEDETECTOR_H__
+
+#include "AudioChangeDetectorBase.h"
+
+class AudioChangeDetector : public AudioChangeDetectorBase
+{
+public:
+    AudioChangeDetector();
+    virtual ~AudioChangeDetector() {}
+
+    void processFrame(unsigned char *frame);
+
+};
+
+#endif  /* !__AUDIOCHANGEDETECTOR_H__ */
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/AudioChangeDetectorBase.cpp b/mythtv/programs/mythcommflag/AudioChangeDetectorBase.cpp
new file mode 100644
index 0000000..a4dd42d
--- /dev/null
+++ b/mythtv/programs/mythcommflag/AudioChangeDetectorBase.cpp
@@ -0,0 +1,8 @@
+
+#include "AudioChangeDetectorBase.h"
+
+AudioChangeDetectorBase::AudioChangeDetectorBase()
+{
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/AudioChangeDetectorBase.h b/mythtv/programs/mythcommflag/AudioChangeDetectorBase.h
new file mode 100644
index 0000000..7966869
--- /dev/null
+++ b/mythtv/programs/mythcommflag/AudioChangeDetectorBase.h
@@ -0,0 +1,27 @@
+#ifndef _AUDIOCHANGEDETECTORBASE_H_
+#define _AUDIOCHANGEDETECTORBASE_H_
+
+#include <QObject>
+
+class AudioChangeDetectorBase : public QObject
+{
+    Q_OBJECT
+
+  public:
+    AudioChangeDetectorBase();
+
+    virtual void processFrame(unsigned char *frame) = 0;
+
+  signals:
+    void haveNewInformation(unsigned int framenum, bool audiochange,
+                            float amplitude,
+                            float debugValue = 0.0);
+
+  protected:
+    virtual ~AudioChangeDetectorBase() {}
+
+};
+
+#endif
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/ClassicCommDetector.cpp b/mythtv/programs/mythcommflag/ClassicCommDetector.cpp
index c5c40fc..28fd396 100644
--- a/mythtv/programs/mythcommflag/ClassicCommDetector.cpp
+++ b/mythtv/programs/mythcommflag/ClassicCommDetector.cpp
@@ -26,14 +26,14 @@ using namespace std;
 enum frameAspects {
     COMM_ASPECT_NORMAL = 0,
     COMM_ASPECT_WIDE
-} FrameAspects;
+};
 
 enum frameFormats {
     COMM_FORMAT_NORMAL = 0,
     COMM_FORMAT_LETTERBOX,
     COMM_FORMAT_PILLARBOX,
     COMM_FORMAT_MAX
-} FrameFormats;
+};
 
 static QString toStringFrameMaskValues(int mask, bool verbose)
 {
@@ -807,7 +807,7 @@ void ClassicCommDetector::ProcessFrame(VideoFrame *frame,
 
     if (commDetectMethod & COMM_DETECT_SCENE)
     {
-        sceneChangeDetector->processFrame(framePtr);
+        sceneChangeDetector->processFrame(framePtr, curFrameNumber);
     }
 
     stationLogoPresent = false;
diff --git a/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.cpp b/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.cpp
index c449353..b3f8c68 100644
--- a/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.cpp
+++ b/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.cpp
@@ -8,7 +8,6 @@ ClassicSceneChangeDetector::ClassicSceneChangeDetector(unsigned int width,
         unsigned int height, unsigned int commdetectborder_in,
         unsigned int xspacing_in, unsigned int yspacing_in):
     SceneChangeDetectorBase(width,height),
-    frameNumber(0),
     previousFrameWasSceneChange(false),
     xspacing(xspacing_in),
     yspacing(yspacing_in),
@@ -25,7 +24,7 @@ void ClassicSceneChangeDetector::deleteLater(void)
     SceneChangeDetectorBase::deleteLater();
 }
 
-void ClassicSceneChangeDetector::processFrame(unsigned char* frame)
+void ClassicSceneChangeDetector::processFrame(unsigned char* frame, long long frameNumber)
 {
     histogram->generateFromImage(frame, width, height, commdetectborder,
                                  width-commdetectborder, commdetectborder,
@@ -38,7 +37,6 @@ void ClassicSceneChangeDetector::processFrame(unsigned char* frame)
     previousFrameWasSceneChange = isSceneChange;
 
     std::swap(histogram,previousHistogram);
-    frameNumber++;
 }
 
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.h b/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.h
index f4d2200..45330f7 100644
--- a/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.h
+++ b/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.h
@@ -13,7 +13,7 @@ class ClassicSceneChangeDetector : public SceneChangeDetectorBase
         unsigned int yspacing);
     virtual void deleteLater(void);
 
-    void processFrame(unsigned char* frame);
+    void processFrame(unsigned char* frame, long long frameNumber);
 
   private:
     ~ClassicSceneChangeDetector() {}
@@ -21,7 +21,6 @@ class ClassicSceneChangeDetector : public SceneChangeDetectorBase
   private:
     Histogram* histogram;
     Histogram* previousHistogram;
-    unsigned int frameNumber;
     bool previousFrameWasSceneChange;
     unsigned int xspacing, yspacing;
     unsigned int commdetectborder;
diff --git a/mythtv/programs/mythcommflag/CommDetectorBase.h b/mythtv/programs/mythcommflag/CommDetectorBase.h
index 3d2b670..d26a631 100644
--- a/mythtv/programs/mythcommflag/CommDetectorBase.h
+++ b/mythtv/programs/mythcommflag/CommDetectorBase.h
@@ -11,6 +11,11 @@ using namespace std;
 
 #define MAX_BLANK_FRAMES 60
 
+#define COMM_DETECT_NG 0x400
+#define COMM_DETECT_AUDIO 0x08
+#define COMM_DETECT_SUBTITLES 0x10
+#define COMM_DETECT_NG_OLD 0x80
+
 typedef enum commMapValues {
     MARK_START   = 0,
     MARK_END     = 1,
diff --git a/mythtv/programs/mythcommflag/CommDetectorFactory.cpp b/mythtv/programs/mythcommflag/CommDetectorFactory.cpp
index 85f8df4..c5f5bfd 100644
--- a/mythtv/programs/mythcommflag/CommDetectorFactory.cpp
+++ b/mythtv/programs/mythcommflag/CommDetectorFactory.cpp
@@ -1,5 +1,6 @@
 #include "CommDetectorFactory.h"
 #include "ClassicCommDetector.h"
+#include "NextgenCommDetector.h"
 #include "CommDetector2.h"
 #include "PrePostRollFlagger.h"
 
@@ -33,6 +34,12 @@ CommDetectorFactory::makeCommDetector(
             recordingStartedAt, recordingStopsAt, useDB);
     }
 
+    if ((commDetectMethod & COMM_DETECT_NG))
+    {
+        return new NextgenCommDetector(commDetectMethod, showProgress, fullSpeed,
+                player, chanid, startedAt, stopsAt, recordingStartedAt, recordingStopsAt);
+    }
+
     return new ClassicCommDetector(commDetectMethod, showProgress, fullSpeed,
             player, startedAt, stopsAt, recordingStartedAt, recordingStopsAt);
 }
diff --git a/mythtv/programs/mythcommflag/NextgenCommDetector.cpp b/mythtv/programs/mythcommflag/NextgenCommDetector.cpp
new file mode 100644
index 0000000..c00d717
--- /dev/null
+++ b/mythtv/programs/mythcommflag/NextgenCommDetector.cpp
@@ -0,0 +1,3102 @@
+// POSIX headers
+#include <unistd.h>
+#include <sys/time.h> // for gettimeofday
+
+// ANSI C headers
+#include <cmath>
+
+// C++ headers
+#include <algorithm> // for min/max
+#include <iostream> // for cerr
+using namespace std;
+
+// Qt headers
+#include <QString>
+
+// MythTV headers
+#include "mythcontext.h"
+#include "programinfo.h"
+#include "mythplayer.h"
+
+#define USE_SUBTITLES
+
+// Commercial Flagging headers
+#include "NextgenCommDetector.h"
+#include "AudioChangeDetector.h"
+#ifdef USE_SUBTITLES
+#include "SubtitleChangeDetector.h"
+#endif
+#include "NextgenLogoDetector.h"
+#include "NextgenSceneChangeDetector.h"
+#include "AudioBuffer.h"
+
+enum frameAspects {
+    COMM_ASPECT_NORMAL = 0,
+    COMM_ASPECT_WIDE
+};
+
+enum frameFormats {
+    COMM_FORMAT_NORMAL = 0,
+    COMM_FORMAT_LETTERBOX,
+    COMM_FORMAT_PILLARBOX,
+    COMM_FORMAT_MAX
+};
+
+static QString toStringFrameMaskValues(int mask, bool verbose)
+{
+    QString msg;
+
+    if (verbose)
+    {
+        if (COMM_FRAME_SKIPPED & mask)
+            msg += "skipped,";
+        if (COMM_FRAME_BLANK & mask)
+            msg += "blank,";
+        if (COMM_FRAME_SCENE_CHANGE & mask)
+            msg += "scene,";
+        if (COMM_FRAME_LOGO_PRESENT & mask)
+            msg += "logo,";
+        if (COMM_FRAME_ASPECT_CHANGE & mask)
+            msg += "aspect,";
+        if (COMM_FRAME_RATING_SYMBOL & mask)
+            msg += "rating,";
+        if (COMM_FRAME_NO_AUDIO & mask)
+            msg += "silent,";
+        if (COMM_FRAME_AUDIO_CHANGE & mask)
+            msg += "audchg,";
+        if (COMM_FRAME_INVALID_AUDIO & mask)
+            msg += "noaud,";
+        if (COMM_FRAME_SUBTITLE_PRESENT & mask)
+            msg += "sub,";
+
+        if (msg.length())
+            msg = msg.left(msg.length() - 1);
+        else
+            msg = "noflags";
+    }
+    else
+    {
+        msg += (COMM_FRAME_SKIPPED       & mask) ? "s" : " ";
+        msg += (COMM_FRAME_BLANK         & mask) ? "B" : " ";
+        msg += (COMM_FRAME_SCENE_CHANGE  & mask) ? "S" : " ";
+        msg += (COMM_FRAME_LOGO_PRESENT  & mask) ? "L" : " ";
+        msg += (COMM_FRAME_ASPECT_CHANGE & mask) ? "A" : " ";
+        msg += (COMM_FRAME_RATING_SYMBOL & mask) ? "R" : " ";
+        msg += (COMM_FRAME_NO_AUDIO      & mask) ? "Q" : " ";
+        msg += (COMM_FRAME_AUDIO_CHANGE  & mask) ? "C" : " ";
+        msg += (COMM_FRAME_INVALID_AUDIO & mask) ? "I" : " ";
+        msg += (COMM_FRAME_SUBTITLE_PRESENT & mask) ? "c" : " ";
+    }
+
+    return msg;
+}
+
+static QString toStringFrameAspects(int aspect, bool verbose)
+{
+    if (verbose)
+        return (COMM_ASPECT_NORMAL == aspect) ? "normal" : " wide ";
+    else
+        return (COMM_ASPECT_NORMAL == aspect) ? "n" : "w";
+}
+
+static QString toStringFrameFormats(int format, bool verbose)
+{
+    switch (format)
+    {
+        case COMM_FORMAT_NORMAL:
+            return (verbose) ? "normal" : "N";
+        case COMM_FORMAT_LETTERBOX:
+            return (verbose) ? "letter" : "L";
+        case COMM_FORMAT_PILLARBOX:
+            return (verbose) ? "pillar" : "P";
+        case COMM_FORMAT_MAX:
+            return (verbose) ? " max  " : "M";
+    }
+
+    return (verbose) ? " null " : "n";
+}
+
+QString NGFrameInfoEntry::GetHeader(void)
+{
+    return QString("  frame     min/max/avg scene aspect format  apwr  achan flags");
+}
+
+QString NGFrameInfoEntry::toString(uint64_t frame, bool verbose) const
+{
+    return QString(
+        "%1: %2/%3/%4 %5%  %6 %7 %8 %9 %10")
+        .arg(frame,10)
+        .arg(minBrightness,3)
+        .arg(maxBrightness,3)
+        .arg(avgBrightness,3)
+        .arg(sceneChangePercent,3)
+        .arg(toStringFrameAspects(aspect, verbose))
+        .arg(toStringFrameFormats(format, verbose))
+        .arg(audioPower,8)
+        .arg(audioMode,6)
+        .arg(toStringFrameMaskValues(flagMask, verbose));
+}
+
+NextgenCommDetector::NextgenCommDetector(SkipType commDetectMethod_in,
+                                         bool showProgress_in,
+                                         bool fullSpeed_in,
+                                         MythPlayer* player_in,
+                                         int chanid,
+                                         const QDateTime& startedAt_in,
+                                         const QDateTime& stopsAt_in,
+                                         const QDateTime& recordingStartedAt_in,
+                                         const QDateTime& recordingStopsAt_in) :
+
+
+    commDetectMethod(commDetectMethod_in),
+    commBreakMapUpdateRequested(false),        sendCommBreakMapUpdates(false),
+    verboseDebugging(false),
+    lastFrameNumber(0),                        curFrameNumber(0),
+    width(0),                                  height(0),
+    horizSpacing(0),                           vertSpacing(0),
+    fpm(0.0),                                  blankFramesOnly(false),
+    blankFrameCount(0),                        currentAspect(0),
+    silentFrameCount(0),
+    totalMinBrightness(0),                     detectBlankFrames(false),
+    detectSceneChanges(false),                 detectStationLogo(false),
+    detectSilentFrames(false),
+    subtitleInfoAvailable(false),              subtitleFrameCount(0),
+    logoInfoAvailable(false),                  logoDetector(0),
+    framePtr(0),                               frameIsBlank(false),
+    sceneHasChanged(false),                    stationLogoPresent(false),
+    lastFrameWasBlank(false),                  lastFrameWasSceneChange(false),
+    decoderFoundAspectChanges(false),          sceneChangeDetector(0),
+    audioChangeDetector(0),                    
+#ifdef USE_SUBTITLES
+    subtitleChangeDetector(0),
+#endif
+    player(player_in),
+    startedAt(startedAt_in),                   stopsAt(stopsAt_in),
+    recordingStartedAt(recordingStartedAt_in),
+    recordingStopsAt(recordingStopsAt_in),     aggressiveDetection(false),
+    stillRecording(recordingStopsAt > QDateTime::currentDateTime()),
+    fullSpeed(fullSpeed_in),                   showProgress(showProgress_in),
+    fps(0.0),                                  framesProcessed(0),
+    preRoll(0),                                postRoll(0)
+{
+    commDetectBorder =
+        gCoreContext->GetNumSetting("CommDetectBorder", 20);
+    commDetectBlankFrameMaxDiff =
+        gCoreContext->GetNumSetting("CommDetectBlankFrameMaxDiff", 25);
+    commDetectDarkBrightness =
+        gCoreContext->GetNumSetting("CommDetectDarkBrightness", 80);
+    commDetectDimBrightness =
+        gCoreContext->GetNumSetting("CommDetectDimBrightness", 120);
+    commDetectBoxBrightness =
+        gCoreContext->GetNumSetting("CommDetectBoxBrightness", 30);
+    commDetectDimAverage =
+        gCoreContext->GetNumSetting("CommDetectDimAverage", 35);
+    commDetectMaxCommBreakLength =
+        gCoreContext->GetNumSetting("CommDetectMaxCommBreakLength", 395);
+    commDetectMinCommBreakLength =
+        gCoreContext->GetNumSetting("CommDetectMinCommBreakLength", 60);
+    commDetectMinShowLength =
+        gCoreContext->GetNumSetting("CommDetectMinShowLength", 65);
+    commDetectMaxCommLength =
+        gCoreContext->GetNumSetting("CommDetectMaxCommLength", 125);
+    commDetectLargeSceneChangeThreshold =
+        gCoreContext->GetNumSetting("CommDetectLargeSceneChangeThreshold", 60);
+    QStringList commDetectCommLengthsString =
+        gCoreContext->GetSetting("CommDetectCommLengths", "12,15,20,30,40,45,60").split(',', QString::SkipEmptyParts);
+    for (QStringList::iterator it = commDetectCommLengthsString.begin(); it != commDetectCommLengthsString.end(); ++it)
+        commDetectCommLengths.append((*it).toFloat());
+
+    commDetectBlankCanHaveLogo =
+        !!gCoreContext->GetNumSetting("CommDetectBlankCanHaveLogo", 1);
+
+    AudioSettings settings("", "");
+    AudioOutput *audioOutput = new AudioBuffer(settings);
+    audioBuffer = ((AudioBuffer*)audioOutput);
+
+}
+
+void NextgenCommDetector::Init()
+{
+    QSize video_disp_dim = player->GetVideoSize();
+    width  = video_disp_dim.width();
+    height = video_disp_dim.height();
+    fps = player->GetFrameRate();
+
+    preRoll  = (long long)(max(0,recordingStartedAt.secsTo(startedAt)) * fps);
+    postRoll = (long long)(max(0,stopsAt.secsTo(recordingStopsAt)) * fps);
+
+#ifdef SHOW_DEBUG_WIN
+    comm_debug_init(width, height);
+#endif
+
+    currentAspect = COMM_ASPECT_WIDE;
+
+    lastFrameNumber = -2;
+    curFrameNumber = -1;
+
+    if (getenv("DEBUGCOMMFLAG"))
+        verboseDebugging = true;
+    else
+        verboseDebugging = false;
+
+    LOG(VB_COMMFLAG, LOG_INFO,
+        QString("Commercial Detection initialized: "
+                "width = %1, height = %2, fps = %3, method = %4")
+            .arg(width).arg(height)
+            .arg(player->GetFrameRate()).arg(commDetectMethod));
+
+    if ((width * height) > 1000000)
+    {
+        horizSpacing = 10;
+        vertSpacing = 10;
+    }
+    else if ((width * height) > 800000)
+    {
+        horizSpacing = 8;
+        vertSpacing = 8;
+    }
+    else if ((width * height) > 400000)
+    {
+        horizSpacing = 6;
+        vertSpacing = 6;
+    }
+    else if ((width * height) > 300000)
+    {
+        horizSpacing = 6;
+        vertSpacing = 4;
+    }
+    else
+    {
+        horizSpacing = 4;
+        vertSpacing = 4;
+    }
+
+    LOG(VB_COMMFLAG, LOG_INFO,
+        QString("Using Sample Spacing of %1 horizontal & %2 vertical pixels.")
+            .arg(horizSpacing).arg(vertSpacing));
+
+    framesProcessed = 0;
+    totalMinBrightness = 0;
+    blankFrameCount = 0;
+
+    aggressiveDetection = true;
+    currentAspect = COMM_ASPECT_WIDE;
+    decoderFoundAspectChanges = false;
+
+    lastSentCommBreakMap.clear();
+
+    // Check if close to 4:3
+    if (fabs(((width*1.0)/height) - 1.333333) < 0.1)
+        currentAspect = COMM_ASPECT_NORMAL;
+
+    sceneChangeDetector = new NextgenSceneChangeDetector(width, height,
+        commDetectBorder, horizSpacing, vertSpacing);
+    connect(
+         sceneChangeDetector,
+         SIGNAL(haveNewInformation(unsigned int,bool,float)),
+         this,
+         SLOT(sceneChangeDetectorHasNewInformation(unsigned int,bool,float))
+    );
+
+    silentFrameCount = 0;
+    audioChangeDetector = new AudioChangeDetector();
+    connect(
+         audioChangeDetector,
+         SIGNAL(haveNewInformation(unsigned int,bool,float,float)),
+         this,
+         SLOT(audioDetectorHasNewInformation(unsigned int,bool,float,float))
+    );
+
+#ifdef USE_SUBTITLES
+    subtitleChangeDetector = new SubtitleChangeDetector(player);
+    connect(
+         subtitleChangeDetector,
+         SIGNAL(haveNewInformation(unsigned int,bool,float)),
+         this,
+         SLOT(subtitleDetectorHasNewInformation(unsigned int,bool,float))
+    );
+#endif
+
+    frameIsBlank = false;
+    stationLogoPresent = false;
+
+    framePtr = NULL;
+
+    logoInfoAvailable = false;
+
+    ClearAllMaps();
+
+    if (verboseDebugging)
+    {
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+            "       Fr #      Min Max Avg Scn F A Mask");
+        LOG(VB_COMMFLAG, LOG_DEBUG, 
+            "       ------    --- --- --- --- - - ----");
+    }
+}
+
+void NextgenCommDetector::deleteLater(void)
+{
+    if (audioBuffer)
+    {
+        if (player && player->GetAudio())
+            player->GetAudio()->SetAudioOutput(NULL);
+        delete audioBuffer;
+        audioBuffer = NULL;
+    }
+    if (audioChangeDetector)
+        audioChangeDetector->deleteLater();
+
+    if (sceneChangeDetector)
+        sceneChangeDetector->deleteLater();
+
+    if (logoDetector)
+        logoDetector->deleteLater();
+
+    CommDetectorBase::deleteLater();
+}
+
+bool NextgenCommDetector::go()
+{
+    player->GetAudio()->ReinitAudio();
+    int secsSince = 0;
+    int requiredBuffer = 30;
+    int requiredHeadStart = requiredBuffer;
+    bool wereRecording = stillRecording;
+
+    emit statusUpdate(QObject::tr("Building Head Start Buffer"));
+    secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
+    while (stillRecording && (secsSince < requiredHeadStart))
+    {
+        emit breathe();
+        if (m_bStop)
+            return false;
+
+        sleep(2);
+        secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
+    }
+
+    if (player->OpenFile() < 0)
+        return false;
+
+    Init();
+
+    if (commDetectMethod & COMM_DETECT_LOGO)
+    {
+        logoDetector = new NextgenLogoDetector(this, width, height,
+            commDetectBorder, horizSpacing, vertSpacing);
+
+        requiredHeadStart += max(0,recordingStartedAt.secsTo(startedAt));
+        requiredHeadStart += logoDetector->getRequiredAvailableBufferForSearch();
+
+        emit statusUpdate(QObject::tr("Building Logo Detection Buffer"));
+        secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
+        while (stillRecording && (secsSince < requiredHeadStart))
+        {
+            emit breathe();
+            if (m_bStop)
+                return false;
+
+            sleep(2);
+            secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
+        }
+    }
+
+    // Don't bother flagging short ~realtime recordings
+    if ((wereRecording) && (!stillRecording) && (secsSince < requiredHeadStart))
+        return false;
+
+    aggressiveDetection =
+        gCoreContext->GetNumSetting("AggressiveCommDetect", 1);
+
+    if (!player->InitVideo())
+    {
+        LOG(VB_GENERAL, LOG_ERR,
+            "NVP: Unable to initialize video for FlagCommercials.");
+        return false;
+    }
+#ifdef USE_SUBTITLES
+    //player->EnableSubtitles(false);
+#endif
+
+    if (commDetectMethod & COMM_DETECT_LOGO)
+    {
+        emit statusUpdate(QObject::tr("Searching for Logo"));
+
+        if (showProgress)
+        {
+            cerr << "Finding Logo";
+            cerr.flush();
+        }
+        LOG(VB_GENERAL, LOG_INFO, "Finding Logo");
+
+        logoInfoAvailable = logoDetector->searchForLogo(player);
+
+        if (showProgress)
+        {
+            cerr << "\b\b\b\b\b\b\b\b\b\b\b\b            "
+                    "\b\b\b\b\b\b\b\b\b\b\b\b";
+            cerr.flush();
+        }
+    }
+
+    emit breathe();
+    if (m_bStop)
+        return false;
+
+    QTime flagTime;
+    flagTime.start();
+
+    long long myTotalFrames;
+    if (recordingStopsAt < QDateTime::currentDateTime() )
+        myTotalFrames = player->GetTotalFrameCount();
+    else
+        myTotalFrames = (long long)(player->GetFrameRate() *
+                        (recordingStartedAt.secsTo(recordingStopsAt)));
+
+    if (showProgress)
+    {
+        if (myTotalFrames)
+            cerr << "\r  0%/          \r" << flush;
+        else
+            cerr << "\r     0/        \r" << flush;
+    }
+
+
+    float flagFPS;
+    long long  currentFrameNumber;
+    float aspect = player->GetVideoAspect();
+    float newAspect = aspect;
+    int prevpercent = -1;
+
+    SetVideoParams(aspect);
+
+    emit breathe();
+
+    audioBuffer->Enable();
+
+    // make sure we get a key frame before us otherwise reset doesnt happen correctly.
+    player->DiscardVideoFrame(player->GetRawVideoFrame(60));
+
+    emit breathe();
+
+    player->GetAudio()->SetAudioOutput(audioBuffer);
+    player->GetAudio()->ReinitAudio();
+
+    player->DiscardVideoFrame(player->GetRawVideoFrame(0));
+    player->ResetTotalDuration();
+
+#ifdef USE_SUBTITLES
+    player->GetSubReader()->EnableAVSubtitles(true);
+    player->GetSubReader()->EnableTextSubtitles(true);
+    player->GetSubReader()->EnableRawTextSubtitles(true);
+    //player->GetTeletextReader()->SetEnabled(true);
+    //player->EnableCaptions(kDisplayTeletextCaptions, false);
+    player->GetCC608Reader()->SetEnabled(true);
+    player->GetCC708Reader()->SetEnabled(true);
+    player->GetDecoder()->SetDecodeAllSubtitles(true);
+    LOG(VB_GENERAL, LOG_DEBUG, "enabling subtitles.");
+#endif
+
+    while (!player->GetEof())
+    {
+        struct timeval startTime;
+        if (stillRecording)
+            gettimeofday(&startTime, NULL);
+
+        VideoFrame* currentFrame = player->GetRawVideoFrame();
+        if (!vfQueue.isEmpty())
+        {
+            if (currentFrame)
+                vfQueue.enqueue(currentFrame);
+            currentFrame = vfQueue.dequeue();
+        }
+        currentFrameNumber = currentFrame->frameNumber;
+
+        //Lucas: maybe we should make the nuppelvideoplayer send out a signal
+        //when the aspect ratio changes.
+        //In order to not change too many things at a time, I"m using basic
+        //polling for now.
+        newAspect = currentFrame->aspect;
+        if (newAspect != aspect)
+        {
+            SetVideoParams(aspect);
+            aspect = newAspect;
+        }
+
+        if (((currentFrameNumber % 500) == 0) ||
+            (((currentFrameNumber % 100) == 0) &&
+             (stillRecording)))
+        {
+            emit breathe();
+            if (m_bStop)
+            {
+                player->DiscardVideoFrame(currentFrame);
+                return false;
+            }
+        }
+
+        if ((sendCommBreakMapUpdates) &&
+            ((commBreakMapUpdateRequested) ||
+             ((currentFrameNumber % 500) == 0)))
+        {
+            frm_dir_map_t commBreakMap;
+            frm_dir_map_t::iterator it;
+            frm_dir_map_t::iterator lastIt;
+            bool mapsAreIdentical = false;
+
+            GetCommercialBreakList(commBreakMap);
+
+            if ((commBreakMap.size() == 0) &&
+                (lastSentCommBreakMap.size() == 0))
+            {
+                mapsAreIdentical = true;
+            }
+            else if (commBreakMap.size() == lastSentCommBreakMap.size())
+            {
+                // assume true for now and set false if we find a difference
+                mapsAreIdentical = true;
+                for (it = commBreakMap.begin();
+                     it != commBreakMap.end() && mapsAreIdentical; ++it)
+                {
+                    lastIt = lastSentCommBreakMap.find(it.key());
+                    if ((lastIt == lastSentCommBreakMap.end()) ||
+                        (*lastIt != *it))
+                        mapsAreIdentical = false;
+                }
+            }
+
+            if (commBreakMapUpdateRequested || !mapsAreIdentical)
+            {
+                emit gotNewCommercialBreakList();
+                lastSentCommBreakMap = commBreakMap;
+            }
+
+            if (commBreakMapUpdateRequested)
+                commBreakMapUpdateRequested = false;
+        }
+
+        while (m_bPaused)
+        {
+            emit breathe();
+            sleep(1);
+        }
+
+        // sleep a little so we don't use all cpu even if we're niced
+        if (!fullSpeed && !stillRecording)
+            usleep(10000);
+
+        if (((currentFrameNumber % 500) == 0) ||
+            ((showProgress || stillRecording) &&
+             ((currentFrameNumber % 100) == 0)))
+        {
+            float elapsed = flagTime.elapsed() / 1000.0;
+
+            if (elapsed)
+                flagFPS = currentFrameNumber / elapsed;
+            else
+                flagFPS = 0.0;
+
+            int percentage;
+            if (myTotalFrames)
+                percentage = currentFrameNumber * 100 / myTotalFrames;
+            else
+                percentage = 0;
+
+            if (percentage > 100)
+                percentage = 100;
+
+            if (showProgress)
+            {
+                if (myTotalFrames)
+                {
+                    QString tmp = QString("\r%1%/%2fps  \r")
+                        .arg(percentage, 3).arg((int)flagFPS, 4);
+                    cerr << qPrintable(tmp) << flush;
+                }
+                else
+                {
+                    QString tmp = QString("\r%1/%2fps  \r")
+                        .arg(currentFrameNumber, 6).arg((int)flagFPS, 4);
+                    cerr << qPrintable(tmp) << flush;
+                }
+            }
+
+            if (myTotalFrames)
+                emit statusUpdate(QObject::tr("%1% Completed @ %2 fps.")
+                                  .arg(percentage).arg(flagFPS));
+            else
+                emit statusUpdate(QObject::tr("%1 Frames Completed @ %2 fps.")
+                                  .arg(currentFrameNumber).arg(flagFPS));
+
+            if (percentage % 10 == 0 && prevpercent != percentage)
+            {
+                prevpercent = percentage;
+                LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.")
+                    .arg(percentage) .arg(flagFPS));
+            }
+        }
+
+        ProcessFrame(currentFrame, currentFrameNumber);
+
+        if (stillRecording)
+        {
+            int secondsRecorded =
+                recordingStartedAt.secsTo(QDateTime::currentDateTime());
+            int secondsFlagged = (int)(framesProcessed / fps);
+            int secondsBehind = secondsRecorded - secondsFlagged;
+            long usecPerFrame = (long)(1.0 / player->GetFrameRate() * 1000000);
+
+            struct timeval endTime;
+            gettimeofday(&endTime, NULL);
+
+            long long usecSleep =
+                      usecPerFrame -
+                      (((endTime.tv_sec - startTime.tv_sec) * 1000000) +
+                       (endTime.tv_usec - startTime.tv_usec));
+
+            if (secondsBehind > requiredBuffer)
+            {
+                if (fullSpeed)
+                    usecSleep = 0;
+                else
+                    usecSleep = (long)(usecSleep * 0.25);
+            }
+            else if (secondsBehind < requiredBuffer)
+                usecSleep = (long)(usecPerFrame * 1.5);
+
+            if (usecSleep > 0)
+                usleep(usecSleep);
+        }
+
+        player->DiscardVideoFrame(currentFrame);
+    }
+
+    if (showProgress)
+    {
+        float elapsed = flagTime.elapsed() / 1000.0;
+
+        if (elapsed)
+            flagFPS = currentFrameNumber / elapsed;
+        else
+            flagFPS = 0.0;
+
+        if (myTotalFrames)
+            cerr << "\b\b\b\b\b\b      \b\b\b\b\b\b";
+        else
+            cerr << "\b\b\b\b\b\b\b\b\b\b\b\b\b             "
+                    "\b\b\b\b\b\b\b\b\b\b\b\b\b";
+        cerr.flush();
+    }
+
+    return true;
+}
+
+void NextgenCommDetector::sceneChangeDetectorHasNewInformation(
+    unsigned int framenum,bool isSceneChange,float debugValue)
+{
+    if (isSceneChange)
+    {
+        frameInfo[framenum].flagMask |= COMM_FRAME_SCENE_CHANGE;
+        sceneMap[framenum] = MARK_SCENE_CHANGE;
+    }
+    else
+    {
+        frameInfo[framenum].flagMask &= ~COMM_FRAME_SCENE_CHANGE;
+        sceneMap.remove(framenum);
+    }
+
+    frameInfo[framenum].sceneChangePercent = (int) (debugValue*100);
+    
+    if (verboseDebugging)
+    {
+        //LOG(VB_COMMFLAG, LOG_DEBUG, QString("Scene Change @%1 : %2 %3").arg(framenum).arg(isSceneChange).arg((int)(debugValue*100)));
+    }
+}
+
+void NextgenCommDetector::audioDetectorHasNewInformation(
+    unsigned int framenum, bool hasChanged, float amplitude, float debugValue)
+{
+    if (hasChanged)
+        frameInfo[framenum].flagMask |= COMM_FRAME_AUDIO_CHANGE;
+    else
+        frameInfo[framenum].flagMask &= ~COMM_FRAME_AUDIO_CHANGE;
+    frameInfo[framenum].audioMode = (int) (debugValue);
+}
+
+void NextgenCommDetector::subtitleDetectorHasNewInformation(
+    unsigned int framenum, bool hasChanged, float debugValue)
+{
+    if (hasChanged)
+    {
+        frameInfo[framenum].flagMask |= COMM_FRAME_SUBTITLE_PRESENT;
+        subtitleInfoAvailable = true;
+        subtitleFrameCount++;
+    }
+    else
+        frameInfo[framenum].flagMask &= ~COMM_FRAME_SUBTITLE_PRESENT;
+    frameInfo[framenum].subtitleMode = (int) (debugValue);
+}
+
+void NextgenCommDetector::GetCommercialBreakList(frm_dir_map_t &marks)
+{
+
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetCommBreakMap()");
+
+    marks.clear();
+
+    CleanupFrameInfo();
+
+    bool blank = COMM_DETECT_BLANK & commDetectMethod;
+    bool scene = COMM_DETECT_SCENE & commDetectMethod;
+    bool logo  = COMM_DETECT_LOGO  & commDetectMethod;
+    bool audio = COMM_DETECT_AUDIO & commDetectMethod;
+
+    if (COMM_DETECT_OFF == commDetectMethod)
+        return;
+
+    if (!blank && !scene && !logo && !audio)
+    {
+        LOG(VB_COMMFLAG, LOG_ERR, QString("Unexpected commDetectMethod: 0x%1")
+                .arg(commDetectMethod,0,16));
+        return;
+    }
+
+    if (blank && scene && logo)
+    {
+        BuildAllMethodsCommList();
+        marks = commBreakMap;
+        LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map");
+        return;
+    }
+
+    if (blank)
+    {
+        BuildBlankFrameCommList();
+        marks = blankCommBreakMap;
+    }
+
+    if (scene)
+    {
+        BuildSceneChangeCommList();
+        marks = sceneCommBreakMap;
+    }
+
+    if (logo)
+    {
+        BuildLogoCommList();
+        marks = logoCommBreakMap;
+    }
+
+    if (audio)
+    {
+        BuildAudioFrameCommList();
+        marks = audioCommBreakMap;
+    }
+
+    int cnt = ((blank) ? 1 : 0) + ((scene) ? 1 : 0) + ((logo) ? 1 : 0) + ((audio) ? 1 : 0);
+    if (cnt >= 2)
+    {
+        marks.clear();
+        if (blank)
+        {
+            marks = Combine2Maps(blankCommBreakMap, marks);
+        }
+        if (scene)
+        {
+            marks = Combine2Maps(sceneCommBreakMap, marks);
+        }
+        if (logo)
+        {
+            marks = Combine2Maps(logoCommBreakMap, marks);
+        }
+        if (audio)
+        {
+            marks = Combine2Maps(audioCommBreakMap, marks);
+        }
+        commBreakMap = marks;
+    }
+
+    LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map");
+}
+
+void NextgenCommDetector::recordingFinished(long long totalFileSize)
+{
+    (void)totalFileSize;
+
+    stillRecording = false;
+}
+
+void NextgenCommDetector::requestCommBreakMapUpdate(void)
+{
+    commBreakMapUpdateRequested = true;
+    sendCommBreakMapUpdates = true;
+}
+
+void NextgenCommDetector::SetVideoParams(float aspect)
+{
+    int newAspect = COMM_ASPECT_WIDE;
+
+    LOG(VB_COMMFLAG, LOG_INFO,
+        QString("CommDetect::SetVideoParams called with aspect = %1")
+            .arg(aspect));
+    // Default to Widescreen but use the same check as VideoOutput::MoveResize()
+    // to determine if is normal 4:3 aspect
+    if (fabs(aspect - 1.333333) < 0.1)
+        newAspect = COMM_ASPECT_NORMAL;
+
+    if (newAspect != currentAspect)
+    {
+        LOG(VB_COMMFLAG, LOG_INFO,
+            QString("Aspect Ratio changed from %1 to %2 at frame %3")
+                .arg(currentAspect).arg(newAspect)
+                .arg(curFrameNumber));
+
+        if (frameInfo.contains(curFrameNumber))
+        {
+            // pretend that this frame is blank so that we can create test
+            // blocks on real aspect ratio change boundaries.
+            frameInfo[curFrameNumber].flagMask |= COMM_FRAME_BLANK;
+            frameInfo[curFrameNumber].flagMask |= COMM_FRAME_ASPECT_CHANGE;
+            decoderFoundAspectChanges = true;
+        }
+        else if (curFrameNumber != -1)
+        {
+            LOG(VB_COMMFLAG, LOG_ERR,
+                QString("Unable to keep track of Aspect ratio change because "
+                        "frameInfo for frame number %1 does not exist.")
+                    .arg(curFrameNumber));
+        }
+        currentAspect = newAspect;
+    }
+}
+
+void NextgenCommDetector::ProcessFrame(VideoFrame *frame,
+                                       long long frame_number)
+{
+    int max = 0;
+    int min = 255;
+    int avg = 0;
+    unsigned char pixel;
+    int blankPixelsChecked = 0;
+    long long totBrightness = 0;
+    unsigned char *rowMax = new unsigned char[height];
+    unsigned char *colMax = new unsigned char[width];
+    memset(rowMax, 0, sizeof(*rowMax)*height);
+    memset(colMax, 0, sizeof(*colMax)*width);
+    int topDarkRow = commDetectBorder;
+    int bottomDarkRow = height - commDetectBorder - 1;
+    int leftDarkCol = commDetectBorder;
+    int rightDarkCol = width - commDetectBorder - 1;
+    NGFrameInfoEntry fInfo;
+
+    if (!frame || !(frame->buf) || frame_number == -1 ||
+        frame->codec != FMT_YV12)
+    {
+        LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Invalid video frame or codec, "
+                                  "unable to process frame.");
+        delete[] rowMax;
+        delete[] colMax;
+        return;
+    }
+
+    if (!width || !height)
+    {
+        LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Width or Height is 0, "
+                                  "unable to process frame.");
+        delete[] rowMax;
+        delete[] colMax;
+        return;
+    }
+
+    curFrameNumber = frame_number;
+    framePtr = frame->buf;
+
+    fInfo.minBrightness = -1;
+    fInfo.maxBrightness = -1;
+    fInfo.avgBrightness = -1;
+    fInfo.sceneChangePercent = -1;
+    fInfo.aspect = currentAspect;
+    fInfo.format = COMM_FORMAT_NORMAL;
+    fInfo.audioPower = 0;
+    fInfo.audioMode = 0;
+    fInfo.flagMask = 0;
+
+    int& flagMask = frameInfo[curFrameNumber].flagMask;
+
+    // Fill in dummy info records for skipped frames.
+    if (lastFrameNumber != (curFrameNumber - 1))
+    {
+        if (lastFrameNumber > 0)
+        {
+            fInfo.aspect = frameInfo[lastFrameNumber].aspect;
+            fInfo.format = frameInfo[lastFrameNumber].format;
+        }
+        fInfo.flagMask = COMM_FRAME_SKIPPED;
+
+        lastFrameNumber++;
+        while(lastFrameNumber < curFrameNumber)
+            frameInfo[lastFrameNumber++] = fInfo;
+
+        fInfo.flagMask = 0;
+    }
+    lastFrameNumber = curFrameNumber;
+
+    frameInfo[curFrameNumber] = fInfo;
+
+    if (commDetectMethod & COMM_DETECT_BLANKS)
+        frameIsBlank = false;
+
+    if (commDetectMethod & COMM_DETECT_SCENE)
+    {
+        sceneChangeDetector->processFrame(framePtr, curFrameNumber);
+    }
+
+#ifdef USE_SUBTITLES
+    subtitleChangeDetector->processFrame(player, frame);
+#endif
+
+    stationLogoPresent = false;
+
+    for(int y = commDetectBorder; y < (height - commDetectBorder);
+            y += vertSpacing)
+    {
+        for(int x = commDetectBorder; x < (width - commDetectBorder);
+                x += horizSpacing)
+        {
+            pixel = framePtr[y * width + x];
+
+            if (commDetectMethod & COMM_DETECT_BLANKS)
+            {
+                 bool checkPixel = false;
+                 if (!commDetectBlankCanHaveLogo)
+                     checkPixel = true;
+
+                 if (!logoInfoAvailable)
+                     checkPixel = true;
+                 else if (!logoDetector->pixelInsideLogo(x,y))
+                     checkPixel=true;
+
+                 if (checkPixel)
+                 {
+                     blankPixelsChecked++;
+                     totBrightness += pixel;
+
+                     if (pixel < min)
+                          min = pixel;
+
+                     if (pixel > max)
+                          max = pixel;
+
+                     if (pixel > rowMax[y])
+                         rowMax[y] = pixel;
+
+                     if (pixel > colMax[x])
+                         colMax[x] = pixel;
+                 }
+            }
+        }
+    }
+
+    if (commDetectMethod & COMM_DETECT_BLANKS)
+    {
+        for(int y = commDetectBorder; y < (height - commDetectBorder);
+                y += vertSpacing)
+        {
+            if (rowMax[y] > commDetectBoxBrightness)
+                break;
+            else
+                topDarkRow = y;
+        }
+
+        for(int y = commDetectBorder; y < (height - commDetectBorder);
+                y += vertSpacing)
+            if (rowMax[y] >= commDetectBoxBrightness)
+                bottomDarkRow = y;
+
+        delete[] rowMax;
+        rowMax = 0;
+
+        for(int x = commDetectBorder; x < (width - commDetectBorder);
+                x += horizSpacing)
+        {
+            if (colMax[x] > commDetectBoxBrightness)
+                break;
+            else
+                leftDarkCol = x;
+        }
+
+        for(int x = commDetectBorder; x < (width - commDetectBorder);
+                x += horizSpacing)
+            if (colMax[x] >= commDetectBoxBrightness)
+                rightDarkCol = x;
+
+        delete[] colMax;
+        colMax = 0;
+
+        if ((topDarkRow > commDetectBorder) &&
+            (topDarkRow < (height * .20)) &&
+            (bottomDarkRow < (height - commDetectBorder)) &&
+            (bottomDarkRow > (height * .80)))
+        {
+            frameInfo[curFrameNumber].format = COMM_FORMAT_LETTERBOX;
+        }
+        else if ((leftDarkCol > commDetectBorder) &&
+                 (leftDarkCol < (width * .20)) &&
+                 (rightDarkCol < (width - commDetectBorder)) &&
+                 (rightDarkCol > (width * .80)))
+        {
+            frameInfo[curFrameNumber].format = COMM_FORMAT_PILLARBOX;
+        }
+        else
+        {
+            frameInfo[curFrameNumber].format = COMM_FORMAT_NORMAL;
+        }
+
+        avg = totBrightness / blankPixelsChecked;
+
+        frameInfo[curFrameNumber].minBrightness = min;
+        frameInfo[curFrameNumber].maxBrightness = max;
+        frameInfo[curFrameNumber].avgBrightness = avg;
+
+        totalMinBrightness += min;
+        commDetectDimAverage = min + 10;
+
+        // Is the frame really dark
+        if (((max - min) <= commDetectBlankFrameMaxDiff) &&
+            (max < commDetectDimBrightness))
+            frameIsBlank = true;
+
+        // Are we non-strict and the frame is blank
+        if ((!aggressiveDetection) &&
+            ((max - min) <= commDetectBlankFrameMaxDiff))
+            frameIsBlank = true;
+
+        // Are we non-strict and the frame is dark
+        //                   OR the frame is dim and has a low avg brightness
+        if ((!aggressiveDetection) &&
+            ((max < commDetectDarkBrightness) ||
+             ((max < commDetectDimBrightness) && (avg < commDetectDimAverage))))
+            frameIsBlank = true;
+    }
+
+    if ((logoInfoAvailable) && (commDetectMethod & COMM_DETECT_LOGO))
+    {
+        stationLogoPresent =
+            logoDetector->doesThisFrameContainTheFoundLogo(framePtr);
+    }
+
+#if 0
+    if ((commDetectMethod == COMM_DETECT_ALL) &&
+        (CheckRatingSymbol()))
+    {
+        flagMask |= COMM_FRAME_RATING_SYMBOL;
+    }
+#endif
+
+    if (frameIsBlank)
+    {
+        blankFrameMap[curFrameNumber] = MARK_BLANK_FRAME;
+        flagMask |= COMM_FRAME_BLANK;
+        blankFrameCount++;
+    }
+
+    if (stationLogoPresent)
+        flagMask |= COMM_FRAME_LOGO_PRESENT;
+
+    if (commDetectMethod & COMM_DETECT_AUDIO)
+    {
+        if (audioBuffer)
+        {
+            static int max_loops = 100;
+            // process a number of frames worth of audio
+            PAudioSample audSample = audioBuffer->GetSample(frame->timecode);
+            int loops = max_loops;
+//#define USE_VFQUEUES
+#ifdef USE_VFQUEUES
+            int queued = 0;
+            int not_queued = 0;
+#endif
+            while (!audSample)
+            {
+#ifdef USE_VFQUEUES
+                if (vfQueue.count() < 5)
+                {
+                    VideoFrame* newFrame = player->GetRawVideoFrame();
+                    if (newFrame)
+                    {
+                        vfQueue.enqueue(newFrame);
+                        queued++;
+                    }
+                    else
+                        not_queued++;
+
+                    usleep(10000);
+                }
+                else
+                {
+                    usleep(1000);
+                }
+#else
+                usleep(1000);
+#endif
+                emit breathe();
+
+                audSample = audioBuffer->GetSample(frame->timecode);
+                if (--loops == 0)
+                    break;
+            }
+            if (loops < max_loops)
+            {
+                if (verboseDebugging)
+                {
+#ifdef USE_VFQUEUES
+                    if (loops == 0)
+                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample failed after max loops (%1,%2)").arg(queued).arg(not_queued));
+                    else
+                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample needed loops %1 (%2,%3)").arg(max_loops-loops).arg(queued).arg(not_queued));
+#else
+                    if (loops == 0)
+                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample failed after max loops"));
+                    else
+                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample needed loops %1").arg(max_loops-loops));
+#endif
+                }
+            }
+
+            if (audSample && (audSample->power >= 0))
+            {
+                frameInfo[curFrameNumber].audioPower = audSample->power;
+                frameInfo[curFrameNumber].audioMode = audSample->channels;
+                if (audSample->changed)
+                    flagMask |= COMM_FRAME_AUDIO_CHANGE;
+                if (audSample->power < 1e-6)
+                {
+                    flagMask |= COMM_FRAME_NO_AUDIO;
+                    silentFrameCount++;
+                }
+#ifdef AUDIODEBUGGING
+                if (verboseDebugging)
+                {
+                    LOG(VB_COMMFLAG, LOG_DEBUG, QString(" video %1 audio %3")
+                        .arg(frame->timecode)
+                        .arg(audSample->toString()));
+                }
+#endif
+            }
+            else
+            {
+#ifdef AUDIODEBUGGING
+                if (verboseDebugging)
+                {
+                    LOG(VB_COMMFLAG, LOG_DEBUG, QString(" video %1")
+                        .arg(frame->timecode));
+                }
+#endif
+                frameInfo[curFrameNumber].audioPower = 0;
+                flagMask |= COMM_FRAME_INVALID_AUDIO;
+            }
+        }
+    }
+
+    //TODO: move this debugging code out of the perframe loop, and do it after
+    // we've processed all frames. this is because a scenechangedetector can
+    // now use a few frames to determine whether the frame a few frames ago was
+    // a scene change or not.. due to this lookahead possibility the values
+    // that are currently in the frameInfo array, might be changed a few frames
+    // from now. The NextgenSceneChangeDetector doesn't use this though. future
+    // scenechangedetectors might.
+
+    if (verboseDebugging)
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+            QString().sprintf("Frame: %6ld -> %3d %3d %3d %3d %1d %1d %9.6f %1d %04x",
+                (long)curFrameNumber,
+                frameInfo[curFrameNumber].minBrightness,
+                frameInfo[curFrameNumber].maxBrightness,
+                frameInfo[curFrameNumber].avgBrightness,
+                frameInfo[curFrameNumber].sceneChangePercent,
+                frameInfo[curFrameNumber].format,
+                frameInfo[curFrameNumber].aspect,
+                frameInfo[curFrameNumber].audioPower,
+                frameInfo[curFrameNumber].audioMode,
+                frameInfo[curFrameNumber].flagMask ));
+
+#ifdef SHOW_DEBUG_WIN
+    comm_debug_show(frame->buf);
+    getchar();
+#endif
+
+    framesProcessed++;
+    delete[] rowMax;
+    delete[] colMax;
+}
+
+void NextgenCommDetector::ClearAllMaps(void)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::ClearAllMaps()");
+
+    frameInfo.clear();
+    blankFrameMap.clear();
+    blankCommMap.clear();
+    blankCommBreakMap.clear();
+    sceneMap.clear();
+    sceneCommBreakMap.clear();
+    audioCommBreakMap.clear();
+    commBreakMap.clear();
+}
+
+void NextgenCommDetector::GetBlankCommMap(frm_dir_map_t &comms)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommMap()");
+
+    if (blankCommMap.isEmpty())
+        BuildBlankFrameCommList();
+
+    comms = blankCommMap;
+}
+
+void NextgenCommDetector::GetBlankCommBreakMap(frm_dir_map_t &comms)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommBreakMap()");
+
+    if (blankCommBreakMap.isEmpty())
+        BuildBlankFrameCommList();
+
+    comms = blankCommBreakMap;
+}
+
+void NextgenCommDetector::GetAudioCommBreakMap(frm_dir_map_t &comms)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetAudioCommBreakMap()");
+
+    if (audioCommBreakMap.isEmpty())
+        BuildAudioFrameCommList();
+
+    comms = audioCommBreakMap;
+}
+
+void NextgenCommDetector::GetSceneChangeMap(frm_dir_map_t &scenes,
+                                            int64_t start_frame)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetSceneChangeMap()");
+
+    frm_dir_map_t::iterator it;
+
+    if (start_frame == -1)
+        scenes.clear();
+
+    for (it = sceneMap.begin(); it != sceneMap.end(); ++it)
+        if ((start_frame == -1) || ((int64_t)it.key() >= start_frame))
+            scenes[it.key()] = *it;
+}
+
+frm_dir_map_t NextgenCommDetector::Combine2Maps(const frm_dir_map_t &a,
+                                                const frm_dir_map_t &b) const
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildMasterCommList()");
+
+    frm_dir_map_t newMap;
+
+    if (a.size())
+    {
+        frm_dir_map_t::const_iterator it = a.begin();
+        for (; it != a.end(); ++it)
+            newMap[it.key()] = *it;
+    }
+
+    if ((a.size() > 1) &&
+        (b.size() > 1))
+    {
+        // see if beginning of the recording looks like a commercial
+        frm_dir_map_t::const_iterator it_a;
+        frm_dir_map_t::const_iterator it_b;
+
+        it_a = a.begin();
+        it_b = b.begin();
+
+        if ((it_b.key() < 2) && (it_a.key() > 2))
+        {
+            newMap.remove(it_a.key());
+            newMap[0] = MARK_COMM_START;
+        }
+
+
+        // see if ending of recording looks like a commercial
+        frm_dir_map_t::const_iterator it;
+        uint64_t max_a = 0;
+        uint64_t max_b = 0;
+
+        it = a.begin();
+        for (; it != a.end(); ++it)
+        {
+            if ((*it == MARK_COMM_END) && (it.key() > max_a))
+                max_a = it.key();
+        }
+
+        it = b.begin();
+        for (; it != b.end(); ++it)
+        {
+            if ((*it == MARK_COMM_END) && (it.key() > max_b))
+                max_b = it.key();
+        }
+
+        if ((max_a < (framesProcessed - 2)) &&
+            (max_b > (framesProcessed - 2)))
+        {
+            newMap.remove(max_a);
+            newMap[framesProcessed] = MARK_COMM_END;
+        }
+    }
+
+    if ((a.size() > 3) &&
+        (b.size() > 1))
+    {
+        frm_dir_map_t::const_iterator it_a;
+        frm_dir_map_t::const_iterator it_b;
+
+        it_a = a.begin();
+        ++it_a;
+        it_b = it_a;
+        ++it_b;
+        while (it_b != a.end())
+        {
+            uint64_t fdiff = it_b.key() - it_a.key();
+            bool allTrue = false;
+
+            if (fdiff < (62 * fps))
+            {
+                uint64_t f = it_a.key() + 1;
+
+                allTrue = true;
+
+                while ((f <= framesProcessed) && (f < it_b.key()) && (allTrue))
+                    allTrue = FrameIsInBreakMap(f++, b);
+            }
+
+            if (allTrue)
+            {
+                newMap.remove(it_a.key());
+                newMap.remove(it_b.key());
+            }
+
+            ++it_a; ++it_a;
+            ++it_b;
+            if (it_b != a.end())
+                ++it_b;
+        }
+    }
+
+    return newMap;
+}
+
+void NextgenCommDetector::UpdateFrameBlock(PFrameBlock fbp,
+                                           NGFrameInfoEntry& finfo,
+                                           int format, int aspect)
+{
+    int value = 0;
+
+    value = finfo.flagMask;
+
+    if (value & COMM_FRAME_BLANK)
+        fbp->bfCount++;
+
+    if (value & COMM_FRAME_LOGO_PRESENT)
+        fbp->logoCount++;
+
+    if (value & COMM_FRAME_RATING_SYMBOL)
+        fbp->ratingCount++;
+
+    if (value & COMM_FRAME_SCENE_CHANGE)
+        fbp->scCount++;
+
+    if (value & COMM_FRAME_NO_AUDIO)
+        fbp->saCount++;
+
+    if (value & COMM_FRAME_AUDIO_CHANGE)
+        fbp->acCount++;
+
+    if (value & COMM_FRAME_SUBTITLE_PRESENT)
+        fbp->subCount++;
+
+    fbp->aPowerAvg += finfo.audioPower;
+
+    if (finfo.format == format)
+        fbp->formatMatch++;
+
+    if (finfo.aspect == aspect)
+        fbp->aspectMatch++;
+}
+
+NextgenCommDetector::FrameBlock::FrameBlock()
+{
+    start = 0;
+    end = 0;
+    frames = 0;
+    length = 0;
+    bfCount = 0;
+    saCount = 0;
+    aPowerAvg = 0;
+    acCount = 0;
+    logoCount = 0;
+    subCount = 0;
+    ratingCount = 0;
+    scCount = 0;
+    scRate = 0.0;
+    formatMatch = 0;
+    aspectMatch = 0;
+    segmentScore = 0;
+    transitionScore = 0;
+}
+
+void NextgenCommDetector::FrameBlock::Merge(FrameBlock& o)
+{
+    if (start <= o.start)
+    {
+        end = o.end;
+        subBlock.insert(subBlock.end(), o.subBlock.begin(), o.subBlock.end());
+    }
+    else
+    {
+        start = o.start;
+        subBlock.insert(subBlock.begin(), o.subBlock.begin(), o.subBlock.end());
+    }
+
+    bfCount += o.bfCount;
+    saCount += o.saCount;
+    acCount += o.acCount;
+    logoCount += o.logoCount;
+    subCount += o.subCount;
+    ratingCount += o.ratingCount;
+    scCount += o.scCount;
+    formatMatch += o.formatMatch;
+    aspectMatch += o.aspectMatch;
+    segmentScore += o.segmentScore;
+    transitionScore += o.transitionScore;
+
+    aPowerAvg = aPowerAvg*frames + o.aPowerAvg*o.frames;
+    scRate = scRate*frames + o.scRate*o.frames;
+
+    frames += o.frames;
+    length += o.length;
+
+    aPowerAvg /= frames;
+    scRate    /= frames;
+
+}
+
+const char * NextgenCommDetector::FrameBlockHeader1() const
+{
+    return "Block StTime StFrm  EndFrm Frames Secs    "
+        "Bf  Lg Cnt ST Cnt RT Cnt SC Cnt SC Rt FmtMch AspMch SA Cnt AC Cnt AudPower TScore SScore";
+}
+
+const char * NextgenCommDetector::FrameBlockHeader2() const
+{
+    return "----- ------ ------ ------ ------ ------- "
+        "--- ------ ------ ------ ------ ----- ------ ------ ------ ------ -------- ------ ------";
+}
+
+const QString NextgenCommDetector::FrameBlockStr(int curBlock, PFrameBlock fbp, int *score) const
+{
+#define STATS_PRINT_SPEC " %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d %6d " \
+                         "%5.2f %6d %6d %6d %6d %8f %6d %6d"
+#define STATS_PRINT_PARAMS \
+                (int)(fbp->start / fps) / 60, \
+                (int)((fbp->start / fps )) % 60, \
+                fbp->start, fbp->end, fbp->frames, fbp->length, \
+                fbp->bfCount, fbp->logoCount, fbp->subCount, fbp->ratingCount, \
+                fbp->scCount, fbp->scRate, fbp->formatMatch, \
+                fbp->aspectMatch, \
+                fbp->saCount, fbp->acCount, fbp->aPowerAvg, \
+                fbp->transitionScore, score?*score:fbp->segmentScore
+
+    QString msg;
+    if (curBlock < 0)
+    {
+        msg.sprintf("  NOW" STATS_PRINT_SPEC ,
+                STATS_PRINT_PARAMS );
+    }
+    else
+    {
+        msg.sprintf("%5d" STATS_PRINT_SPEC ,
+                curBlock, STATS_PRINT_PARAMS );
+    }
+    return msg;
+#undef STATS_PRINT_SPEC
+#undef STATS_PRINT_PARAMS
+}
+
+void NextgenCommDetector::BuildAllMethodsCommList(void)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildAllMethodsCommList()");
+
+    vector< PFrameBlock > cfblock;
+    vector< PFrameBlock > fblock;
+    PFrameBlock fbp;
+    PFrameBlock cfbp;
+
+    int value = 0;
+    int curBlock = 0;
+    int maxBlock = 0;
+    int lastScore = 0;
+    int thisScore = 0;
+    int nextScore = 0;
+    uint64_t curFrame = 0;
+    int64_t  breakStart = 0;
+    uint64_t lastStart = 0;
+    uint64_t lastEnd = 0;
+    int64_t firstLogoFrame = -1;
+    bool lastFrameWasBlank = false;
+    bool lastFrameWasSilent = false;
+    uint64_t formatFrames = 0;
+    int format = COMM_FORMAT_NORMAL;
+    uint64_t aspectFrames = 0;
+    int aspect = COMM_ASPECT_NORMAL;
+    QString msg;
+    uint64_t formatCounts[COMM_FORMAT_MAX];
+    frm_dir_map_t tmpCommMap;
+    frm_dir_map_t::iterator it;
+
+    bool audioMethod = (commDetectMethod & COMM_DETECT_AUDIO);
+
+    commBreakMap.clear();
+
+    curBlock = 0;
+    curFrame = 0;
+
+#if 0
+    cfbp = PFrameBlock(new FrameBlock);
+    cfblock.push_back(cfbp);
+    fbp = PFrameBlock(new FrameBlock);
+    fbp->transitionScore = 5;   // first one is always a transition
+    cfbp->subBlock.push_back(fbp);
+    fblock.push_back(fbp);
+#endif
+
+    value = frameInfo[curFrame].flagMask;
+    lastFrameWasSilent = !(value & COMM_FRAME_NO_AUDIO);
+    lastFrameWasBlank = !(value & COMM_FRAME_BLANK);
+
+    if (decoderFoundAspectChanges)
+    {
+        for (int64_t i = preRoll;
+             i < ((int64_t)framesProcessed - (int64_t)postRoll); i++)
+        {
+            if ((frameInfo.contains(i)) &&
+                (frameInfo[i].aspect == COMM_ASPECT_NORMAL))
+                aspectFrames++;
+        }
+
+        if (aspectFrames < ((framesProcessed - preRoll - postRoll) / 2))
+        {
+            aspect = COMM_ASPECT_WIDE;
+            aspectFrames = framesProcessed - preRoll - postRoll - aspectFrames;
+        }
+    }
+    else
+    {
+        memset(&formatCounts, 0, sizeof(formatCounts));
+
+        for(int64_t i = preRoll;
+            i < ((int64_t)framesProcessed - (int64_t)postRoll); i++ )
+            if ((frameInfo.contains(i)) &&
+                (frameInfo[i].format >= 0) &&
+                (frameInfo[i].format < COMM_FORMAT_MAX))
+                formatCounts[frameInfo[i].format]++;
+
+        for(int i = 0; i < COMM_FORMAT_MAX; i++)
+        {
+            if (formatCounts[i] > formatFrames)
+            {
+                format = i;
+                formatFrames = formatCounts[i];
+            }
+        }
+    }
+
+    while (curFrame <= framesProcessed)
+    {
+        value = frameInfo[curFrame].flagMask;
+
+        int newBlockMask = 0;
+        bool lastSilentFrameState = lastFrameWasSilent;
+        lastFrameWasSilent = value & COMM_FRAME_NO_AUDIO;
+
+        bool lastBlankFrameState = lastFrameWasBlank;
+        lastFrameWasBlank = value & COMM_FRAME_BLANK;
+        if (audioMethod)
+        {
+            // transition only when both are set
+            newBlockMask = ((lastSilentFrameState & lastBlankFrameState) != (lastFrameWasSilent & lastFrameWasBlank))?4:0;
+
+            if ((frameInfo[curFrame].sceneChangePercent < 50) && lastFrameWasSilent && !lastFrameWasBlank)
+            {
+                newBlockMask |= 2;
+            }
+        }
+        else
+        {
+            newBlockMask |= (lastBlankFrameState != lastFrameWasBlank);
+        }
+
+        if (newBlockMask)
+        {
+            //LOG(VB_COMMFLAG, LOG_INFO, QString("nbm %1 %2 %3 %4").arg(curFrame).arg(lastSilentFrameState).arg(lastFrameWasBlank).arg(newBlockMask));
+            if (fbp)
+            {
+                fbp->end = curFrame - 1;
+                fbp->frames = fbp->end - fbp->start + 1;
+                fbp->length = fbp->frames / fps;
+
+                fbp->aPowerAvg /= fbp->frames;
+
+                if ((fbp->scCount) && (fbp->length > 1.05))
+                    fbp->scRate = fbp->scCount / fbp->length;
+            }
+
+            curBlock++;
+
+            fbp = PFrameBlock(new FrameBlock);
+            fblock.push_back(fbp);
+            if (((newBlockMask & 4) && (lastFrameWasSilent && lastFrameWasBlank)) || !cfbp)
+            {
+                if (cfbp)
+                {
+                    cfbp->end = curFrame - 1;
+                    cfbp->frames = cfbp->end - cfbp->start + 1;
+                    cfbp->length = cfbp->frames / fps;
+                    cfbp->aPowerAvg /= cfbp->frames;
+
+                    if ((cfbp->scCount) && (cfbp->length > 1.05))
+                        cfbp->scRate = cfbp->scCount / cfbp->length;
+                }
+
+                cfbp = PFrameBlock(new FrameBlock);
+                cfblock.push_back(cfbp);
+
+                //fbp->transitionScore = 10;
+                cfbp->start = curFrame;
+            }
+            cfbp->subBlock.push_back(fbp);
+#if 0
+            cfbp->frames += fbp->frames;
+            cfbp->aPowerAvg += fbp->aPowerAvg;
+            cfbp->bfCount += fbp->bfCount;
+            cfbp->scCount += fbp->scCount;
+            cfbp->saCount += fbp->saCount;
+            cfbp->acCount += fbp->acCount;
+            cfbp->logoCount += fbp->logoCount;
+            cfbp->ratingCount += fbp->ratingCount;
+            cfbp->formatMatch += fbp->formatMatch;
+            cfbp->aspectMatch += fbp->aspectMatch;
+#endif
+
+            fbp->start = curFrame;
+        }
+        UpdateFrameBlock(fbp, frameInfo[curFrame], format, aspect);
+        UpdateFrameBlock(cfbp, frameInfo[curFrame], format, aspect);
+
+        if ((value & COMM_FRAME_LOGO_PRESENT) &&
+            (firstLogoFrame == -1))
+            firstLogoFrame = curFrame;
+
+        curFrame++;
+    }
+
+    fbp->end = curFrame - 1;
+    fbp->frames = fbp->end - fbp->start + 1;
+    fbp->length = fbp->frames / fps;
+
+    fbp->aPowerAvg /= fbp->frames;
+
+    if ((fbp->scCount) && (fbp->length > 1.05))
+        fbp->scRate = fbp->scCount / fbp->length;
+
+    cfbp->end = curFrame - 1;
+    cfbp->frames = cfbp->end - cfbp->start + 1;
+    cfbp->length = cfbp->frames / fps;
+    cfbp->aPowerAvg /= cfbp->frames;
+
+    if ((cfbp->scCount) && (cfbp->length > 1.05))
+        cfbp->scRate = cfbp->scCount / cfbp->length;
+
+    maxBlock = curBlock;
+    curBlock = 0;
+    lastScore = 0;
+
+    LOG(VB_COMMFLAG, LOG_INFO, "Initial Block pass");
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
+    while (curBlock < maxBlock)
+    {
+        fbp = fblock[curBlock];
+
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp));
+
+        if ((fbp->bfCount == fbp->saCount) && (fbp->bfCount == fbp->frames) && (fbp->frames > 1))
+        {
+            fbp->transitionScore += 10;
+            goto endInitialPass;
+        }
+
+        if (fbp->frames > fps)
+        {
+            if (verboseDebugging)
+                LOG(VB_COMMFLAG, LOG_DEBUG,
+                    QString("      FRAMES > %1").arg(fps));
+
+            if (fbp->length > commDetectMaxCommLength)
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      length > max comm length, +20");
+                fbp->segmentScore += 20;
+            }
+
+            if (fbp->length > commDetectMaxCommBreakLength)
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      length > max comm break length, +20");
+                fbp->segmentScore += 20;
+            }
+
+            if ((fbp->length > 4) &&
+                (fbp->logoCount > (fbp->frames * 0.60)) &&
+                (fbp->bfCount < (fbp->frames * 0.10)))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      length > 4 && logoCount > frames * 0.60 && "
+                        "bfCount < frames * .10");
+                if (fbp->length > commDetectMaxCommBreakLength)
+                {
+                    if (verboseDebugging)
+                        LOG(VB_COMMFLAG, LOG_DEBUG,
+                            "      length > max comm break length, +20");
+                    fbp->segmentScore += 20;
+                }
+                else
+                {
+                    if (verboseDebugging)
+                        LOG(VB_COMMFLAG, LOG_DEBUG,
+                            "      length <= max comm break length, +10");
+                    fbp->segmentScore += 10;
+                }
+            }
+
+            if ((logoInfoAvailable) &&
+                (fbp->logoCount < (fbp->frames * 0.50)) &&
+                (fbp->bfCount < (fbp->frames * 0.10)))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      logoInfoAvailable && logoCount < frames * .50 && blanks < frames * 0.1, "
+                        "-10");
+                fbp->segmentScore -= 10;
+            }
+
+            if (fbp->ratingCount > (fbp->frames * 0.05))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      rating symbol present > 5% of time, +20");
+                fbp->segmentScore += 20;
+            }
+
+            if ((fbp->scRate > 1.0) &&
+                (fbp->bfCount < (fbp->frames * 0.50)) &&
+                (fbp->logoCount < (fbp->frames * .90)))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG, "      scRate > 1.0, -10");
+                fbp->segmentScore -= 10;
+
+                if (fbp->scRate > 2.0)
+                {
+                    if (verboseDebugging)
+                        LOG(VB_COMMFLAG, LOG_DEBUG, "      scRate > 2.0, -10");
+                    fbp->segmentScore -= 10;
+                }
+            }
+
+            if ((!decoderFoundAspectChanges) &&
+                (fbp->formatMatch < (fbp->frames * .10)))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      < 10% of frames match show letter/pillar-box "
+                        "format, -20");
+                fbp->segmentScore -= 20;
+            }
+
+            // check for common comm break lengths
+            for(QList<float>::iterator lit = commDetectCommLengths.begin(); lit != commDetectCommLengths.end(); ++lit)
+            {
+                if (abs(fbp->frames - ((*lit * fps) - fps/2 + 1)) <= (fps/2))
+                {
+                    if (verboseDebugging)
+                        LOG(VB_COMMFLAG, LOG_DEBUG,
+                            "      block appears to be standard comm length, -10");
+                    fbp->segmentScore -= 10;
+                    break;
+                }
+            }
+        }
+        else
+        {
+            if (verboseDebugging)
+                LOG(VB_COMMFLAG, LOG_DEBUG,
+                    QString("      FRAMES <= %1").arg(fps));
+
+            if ((logoInfoAvailable) &&
+                (fbp->start >= firstLogoFrame) &&
+                (fbp->logoCount == 0))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      logoInfoAvailable && logoCount == 0, -10");
+                fbp->segmentScore -= 10;
+            }
+
+            if ((!decoderFoundAspectChanges) &&
+                (fbp->formatMatch < (fbp->frames * .10)))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      < 10% of frames match show letter/pillar-box "
+                        "format, -10");
+                fbp->segmentScore -= 10;
+            }
+
+            if (fbp->ratingCount > (fbp->frames * 0.25))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      rating symbol present > 25% of time, +10");
+                fbp->segmentScore += 10;
+            }
+        }
+
+        if ((decoderFoundAspectChanges) &&
+            (fbp->aspectMatch < (fbp->frames * .10)))
+        {
+            if (verboseDebugging)
+                LOG(VB_COMMFLAG, LOG_DEBUG,
+                    "      < 10% of frames match show aspect, -20");
+            fbp->segmentScore -= 20;
+        }
+
+endInitialPass:
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, fbp));
+
+        lastScore = fbp->segmentScore;
+        curBlock++;
+    }
+
+    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
+    LOG(VB_COMMFLAG, LOG_INFO, "Initial Comm Block pass");
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
+    for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++)
+    {
+        cfbp = cfblock[curBlock];
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
+
+        if (cfbp->length >= commDetectMaxCommBreakLength)
+        {
+            // definitely program
+            cfbp->segmentScore += 100;
+        }
+        else if (cfbp->length >= (commDetectCommLengths[0] - 1))
+        {
+            // may be commercial. check subBlocks
+            vector<PFrameBlock>::iterator sbi;
+            for(sbi=cfbp->subBlock.begin(); sbi != cfbp->subBlock.end(); ++sbi)
+            {
+                int frames = (*sbi)->frames;
+                // check for common comm break lengths
+                for(QList<float>::iterator lit = commDetectCommLengths.begin(); lit != commDetectCommLengths.end(); ++lit)
+                {
+                    if (abs(frames - ((*lit * fps) - fps/2 + 1)) <= (fps/2))
+                    {
+                        if (verboseDebugging)
+                            LOG(VB_COMMFLAG, LOG_DEBUG,
+                                "      subblock appears to be standard comm length, -10");
+                        cfbp->segmentScore -= 10;
+                        break;
+                    }
+                }
+            }
+        }
+        else
+        {
+            // too short so try to coalesce
+            // probably silent credits (black with writing and no music)
+            cfbp->transitionScore += -10; // mark for merge
+        }
+        if (cfbp->subCount > 0)
+        {
+            if (verboseDebugging)
+                LOG(VB_COMMFLAG, LOG_DEBUG,
+                    "      subblock has subtitles, +5");
+            cfbp->segmentScore += 5;
+        }
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp));
+    }
+
+    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
+    LOG(VB_COMMFLAG, LOG_INFO, "Merge same Comm Block pass");
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
+    for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++)
+    {
+        cfbp = cfblock[curBlock];
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
+
+        // next one exists
+        if ((curBlock + 1) < (int)cfblock.size())
+        {
+            PFrameBlock cfbp2 = cfblock[curBlock+1];
+            if (curBlock == 0 && cfbp->segmentScore == 0 && cfbp2->segmentScore != 0)
+            {
+                cfbp->segmentScore += 5;
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      unlabelled first segment, set to opposite of 2nd for indexing");
+            }
+            if ((cfbp->segmentScore < 0 && cfbp2->segmentScore == 0 &&
+                 (cfbp->length + cfbp2->length) >= commDetectMaxCommBreakLength))
+            {
+                // cant be a comm as the length is over so mark next as program
+                cfbp2->segmentScore += 10;
+                LOG(VB_COMMFLAG, LOG_DEBUG,
+                    "      comm length + next length > commDetectMaxCommBreakLength, next +10");
+            }
+            if ((cfbp->segmentScore < 0 && cfbp2->segmentScore < 0) ||
+                (cfbp->segmentScore > 0 && cfbp2->segmentScore > 0) ||
+                (cfbp->segmentScore == 0 && cfbp2->segmentScore == 0 && 
+                 cfbp->transitionScore < 0 && cfbp2->transitionScore < 0) ||
+                (cfbp->segmentScore != 0 && cfbp2->segmentScore == 0 &&
+                 cfbp2->transitionScore < 0 && cfbp2->length <= 1) ||
+                (cfbp->segmentScore == 0 && cfbp2->segmentScore != 0 &&
+                 cfbp->transitionScore < 0 && cfbp->length <= 1)
+               )
+            {
+                if (verboseDebugging)
+                {
+                    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock+1, cfbp2));
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      merge with next");
+                }
+                cfbp->Merge(*cfbp2);
+                cfblock.erase(cfblock.begin()+curBlock+1);
+                // reprocess
+                curBlock--;
+            }
+        }
+        if (curBlock == (cfblock.size()-1) && curBlock > 0)
+        {
+            // last block has change of sign from previous block to fora a seek point
+            if (cfbp->segmentScore == 0 && cfblock[curBlock-1]->segmentScore != 0)
+                cfbp->segmentScore = - cfblock[curBlock-1]->segmentScore;
+        }
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp));
+    }
+
+    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
+    LOG(VB_COMMFLAG, LOG_INFO, "Merge Comm Block pass 2");
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
+    for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++)
+    {
+        cfbp = cfblock[curBlock];
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
+
+        // next one exists
+        if ((curBlock + 1) < (int)cfblock.size())
+        {
+        }
+
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp));
+    }
+
+    curBlock = 0;
+    lastScore = 0;
+
+    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
+    LOG(VB_COMMFLAG, LOG_INFO, "Second Block pass");
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
+    while (curBlock < maxBlock)
+    {
+        fbp = fblock[curBlock];
+
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp));
+
+        if ((curBlock >= 0) && (curBlock < (maxBlock-1)))
+        {
+            nextScore = fblock[curBlock + 1]->segmentScore;
+
+            if ((lastScore < 0) && (nextScore < 0) && (fbp->length < 35))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      lastScore < 0 && nextScore < 0 "
+                        "&& length < 35, setting -10");
+                fbp->segmentScore -= 10;
+            }
+
+            if ((fbp->bfCount > (fbp->frames * 0.95)) &&
+                (fbp->frames < (2*fps)) &&
+                (lastScore < 0 && nextScore < 0))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      blanks > frames * 0.95 && frames < 2*fps && "
+                        "lastScore < 0 && nextScore < 0, setting -10");
+                fbp->segmentScore -= 10;
+            }
+            if ((fbp->saCount > (fbp->frames * 0.95)) &&
+                (fbp->frames < (2*fps)) &&
+                (lastScore < 0 && nextScore < 0))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      silent > frames * 0.95 && frames < 2*fps && "
+                        "lastScore < 0 && nextScore < 0, setting -10");
+                fbp->segmentScore -= 10;
+            }
+
+            if ((fbp->frames < (120*fps)) &&
+                (lastScore < 0) &&
+                (fbp->segmentScore > 0) &&
+                (fbp->segmentScore < 20) &&
+                (nextScore < 0))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      frames < 120 * fps && (-20 < lastScore < 0) && "
+                        "thisScore > 0 && nextScore < 0, setting score = -10");
+                fbp->segmentScore = -10;
+            }
+
+            if ((fbp->frames < (30*fps)) &&
+                (lastScore > 0) &&
+                (fbp->segmentScore < 0) &&
+                (fbp->segmentScore > -20) &&
+                (nextScore > 0))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        "      frames < 30 * fps && (0 < lastScore < 20) && "
+                        "thisScore < 0 && nextScore > 0, setting score = 10");
+                fbp->segmentScore = 10;
+            }
+        }
+
+        if ((fbp->segmentScore == 0) && (lastScore > 30))
+        {
+            int offset = 1;
+            while(((curBlock + offset) < maxBlock) &&
+                    (fblock[curBlock + offset]->frames < (2 * fps)) &&
+                    (fblock[curBlock + offset]->segmentScore == 0))
+                offset++;
+
+            if ((curBlock + offset) < maxBlock)
+            {
+                offset--;
+                if (fblock[curBlock + offset + 1]->segmentScore > 0)
+                {
+                    for (; offset >= 0; offset--)
+                    {
+                        fblock[curBlock + offset]->segmentScore += 10;
+                        if (verboseDebugging)
+                            LOG(VB_COMMFLAG, LOG_DEBUG,
+                                QString("      Setting block %1 score +10")
+                                    .arg(curBlock+offset));
+                    }
+                }
+                else if (fblock[curBlock + offset + 1]->segmentScore < 0)
+                {
+                    for (; offset >= 0; offset--)
+                    {
+                        fblock[curBlock + offset]->segmentScore -= 10;
+                        if (verboseDebugging)
+                            LOG(VB_COMMFLAG, LOG_DEBUG,
+                                QString("      Setting block %1 score -10")
+                                    .arg(curBlock+offset));
+                    }
+                }
+            }
+        }
+
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, fbp));
+
+        lastScore = fbp->segmentScore;
+        curBlock++;
+    }
+
+    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
+    LOG(VB_COMMFLAG, LOG_INFO, "FINAL Block stats");
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
+    curBlock = 0;
+    lastScore = 0;
+    breakStart = -1;
+    while (curBlock < maxBlock)
+    {
+        fbp = fblock[curBlock];
+        thisScore = fbp->segmentScore;
+
+        if ((breakStart >= 0) &&
+            ((fbp->end - breakStart) > (commDetectMaxCommBreakLength * fps)))
+        {
+            if (((fbp->start - breakStart) >
+                (commDetectMinCommBreakLength * fps)) ||
+                (breakStart == 0))
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        QString("Closing commercial block at start of "
+                                "frame block %1 with length %2, frame "
+                                "block length of %3 frames would put comm "
+                                "block length over max of %4 seconds.")
+                            .arg(curBlock).arg(fbp->start - breakStart)
+                            .arg(fbp->frames)
+                            .arg(commDetectMaxCommBreakLength));
+
+                commBreakMap[breakStart] = MARK_COMM_START;
+                commBreakMap[fbp->start] = MARK_COMM_END;
+                lastStart = breakStart;
+                lastEnd = fbp->start;
+                breakStart = -1;
+            }
+            else
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        QString("Ignoring what appears to be commercial"
+                                " block at frame %1 with length %2, "
+                                "length of %3 frames would put comm "
+                                "block length under min of %4 seconds.")
+                            .arg(breakStart)
+                            .arg(fbp->start - breakStart)
+                            .arg(fbp->frames)
+                            .arg(commDetectMinCommBreakLength));
+                breakStart = -1;
+            }
+        }
+        if (thisScore == 0)
+        {
+            thisScore = lastScore;
+        }
+        else if (thisScore < 0)
+        {
+            if ((lastScore > 0) || (curBlock == 0))
+            {
+                if ((fbp->start - lastEnd) < (commDetectMinShowLength * fps))
+                {
+                    commBreakMap.remove(lastStart);
+                    commBreakMap.remove(lastEnd);
+                    breakStart = lastStart;
+
+                    if (verboseDebugging)
+                    {
+                        if (breakStart)
+                            LOG(VB_COMMFLAG, LOG_DEBUG,
+                                QString("ReOpening commercial block at "
+                                        "frame %1 because show less than "
+                                        "%2 seconds")
+                                    .arg(breakStart)
+                                    .arg(commDetectMinShowLength));
+                        else
+                            LOG(VB_COMMFLAG, LOG_DEBUG,
+                                "Opening initial commercial block "
+                                "at start of recording, block 0.");
+                    }
+                }
+                else
+                {
+                    breakStart = fbp->start;
+
+                    if (verboseDebugging)
+                        LOG(VB_COMMFLAG, LOG_DEBUG,
+                            QString("Starting new commercial block at "
+                                    "frame %1 from start of frame block %2")
+                                .arg(fbp->start).arg(curBlock));
+                }
+            }
+            else if (curBlock == (maxBlock-1))
+            {
+                if ((fbp->end - breakStart) >
+                    (commDetectMinCommBreakLength * fps))
+                {
+                    if (fbp->end <=
+                        ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2))
+                    {
+                        if (verboseDebugging)
+                            LOG(VB_COMMFLAG, LOG_DEBUG,
+                                QString("Closing final commercial block at "
+                                        "frame %1").arg(fbp->end));
+
+                        commBreakMap[breakStart] = MARK_COMM_START;
+                        commBreakMap[fbp->end] = MARK_COMM_END;
+                        lastStart = breakStart;
+                        lastEnd = fbp->end;
+                        breakStart = -1;
+                    }
+                }
+                else
+                {
+                    if (verboseDebugging)
+                        LOG(VB_COMMFLAG, LOG_DEBUG,
+                            QString("Ignoring what appears to be commercial"
+                                    " block at frame %1 with length %2, "
+                                    "length of %3 frames would put comm "
+                                    "block length under min of %4 seconds.")
+                                .arg(breakStart)
+                                .arg(fbp->start - breakStart)
+                                .arg(fbp->frames)
+                                .arg(commDetectMinCommBreakLength));
+                    breakStart = -1;
+                }
+            }
+        }
+        else if ((thisScore > 0) &&
+                 (lastScore < 0) &&
+                 (breakStart != -1))
+        {
+            if (((fbp->start - breakStart) >
+                (commDetectMinCommBreakLength * fps)) ||
+                (breakStart == 0))
+            {
+                commBreakMap[breakStart] = MARK_COMM_START;
+                commBreakMap[fbp->start] = MARK_COMM_END;
+                lastStart = breakStart;
+                lastEnd = fbp->start;
+
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        QString("Closing commercial block at frame %1")
+                            .arg(fbp->start));
+            }
+            else
+            {
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG,
+                        QString("Ignoring what appears to be commercial "
+                                "block at frame %1 with length %2, "
+                                "length of %3 frames would put comm block "
+                                "length under min of %4 seconds.")
+                            .arg(breakStart)
+                            .arg(fbp->start - breakStart)
+                            .arg(fbp->frames)
+                            .arg(commDetectMinCommBreakLength));
+            }
+            breakStart = -1;
+        }
+
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp, &thisScore));
+
+        lastScore = thisScore;
+        curBlock++;
+    }
+
+    if ((breakStart != -1) &&
+        (breakStart <= ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2)))
+    {
+        if (verboseDebugging)
+            LOG(VB_COMMFLAG, LOG_DEBUG,
+                QString("Closing final commercial block started at "
+                        "block %1 and going to end of program. length "
+                        "is %2 frames")
+                    .arg(curBlock)
+                    .arg((framesProcessed - breakStart - 1)));
+
+        commBreakMap[breakStart] = MARK_COMM_START;
+        // Create what is essentially an open-ended final skip region
+        // by setting the end point 10 seconds past the end of the
+        // recording.
+        commBreakMap[framesProcessed + (10 * fps)] = MARK_COMM_END;
+    }
+
+    // include/exclude blanks from comm breaks
+    tmpCommMap = commBreakMap;
+    commBreakMap.clear();
+
+    if (verboseDebugging)
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+            "Adjusting start/end marks according to blanks.");
+    for (it = tmpCommMap.begin(); it != tmpCommMap.end(); ++it)
+    {
+        if (*it == MARK_COMM_START)
+        {
+            uint64_t lastStartLower = it.key();
+            uint64_t lastStartUpper = it.key();
+            while ((lastStartLower > 0) &&
+                   (frameInfo[lastStartLower - 1].flagMask & COMM_FRAME_BLANK))
+                lastStartLower--;
+            while ((lastStartUpper < (framesProcessed - (2 * fps))) &&
+                   (frameInfo[lastStartUpper + 1].flagMask & COMM_FRAME_BLANK))
+                lastStartUpper++;
+            uint64_t adj = (lastStartUpper - lastStartLower) / 2;
+            if (adj > MAX_BLANK_FRAMES)
+                adj = MAX_BLANK_FRAMES;
+            lastStart = lastStartLower + adj;
+
+            if (verboseDebugging)
+                LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark: %1 -> %2")
+                        .arg(it.key()).arg(lastStart));
+
+            commBreakMap[lastStart] = MARK_COMM_START;
+        }
+        else
+        {
+            uint64_t lastEndLower = it.key();
+            uint64_t lastEndUpper = it.key();
+            while ((lastEndUpper < (framesProcessed - (2 * fps))) &&
+                   (frameInfo[lastEndUpper + 1].flagMask & COMM_FRAME_BLANK))
+                lastEndUpper++;
+            while ((lastEndLower > 0) &&
+                   (frameInfo[lastEndLower - 1].flagMask & COMM_FRAME_BLANK))
+                lastEndLower--;
+            uint64_t adj = (lastEndUpper - lastEndLower) / 2;
+            if (adj > MAX_BLANK_FRAMES)
+                adj = MAX_BLANK_FRAMES;
+            lastEnd = lastEndUpper - adj;
+
+            if (verboseDebugging)
+                LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark  : %1 -> %2")
+                        .arg(it.key()).arg(lastEnd));
+
+            commBreakMap[lastEnd] = MARK_COMM_END;
+        }
+    }
+
+    // new one
+    if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
+        commBreakMap.clear();
+    lastScore = 0;
+    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
+    LOG(VB_COMMFLAG, LOG_INFO, "Generate Comm Breaks pass");
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
+    cfbp = cfblock[0];
+    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(0, cfbp));
+    // is first one a comm
+    if ((lastScore = cfbp->segmentScore) < 0)
+    {
+        if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
+        commBreakMap[0] = MARK_COMM_START;
+        if (verboseDebugging)
+            LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark  : %1")
+                    .arg(0));
+    }
+    for(curBlock = 1; curBlock < (int)cfblock.size(); curBlock++)
+    {
+        cfbp = cfblock[curBlock];
+        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
+        if (cfbp->segmentScore != 0)
+        {
+            if (lastScore < 0 && cfbp->segmentScore > 0)
+            {
+                if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
+                    commBreakMap[cfbp->start + cfbp->subBlock[0]->frames/2] = MARK_COMM_END;
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark  : %1")
+                            .arg(cfbp->start + cfbp->subBlock[0]->frames/2));
+            }
+            if (lastScore > 0 && cfbp->segmentScore < 0) 
+            {
+                if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
+                    commBreakMap[cfbp->start + cfbp->subBlock[0]->frames/2] = MARK_COMM_START;
+                if (verboseDebugging)
+                    LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark  : %1")
+                            .arg(cfbp->start + cfbp->subBlock[0]->frames/2));
+            }
+            lastScore = cfbp->segmentScore;
+        }
+    }
+    if (lastScore < 0)
+    {
+        if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
+            commBreakMap[cfbp->end + fps * 2 + 1] = MARK_COMM_END;
+        if (verboseDebugging)
+            LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark  : %1")
+                    .arg(cfbp->end + fps * 2 + 1));
+    }
+}
+
+
+void NextgenCommDetector::BuildBlankFrameCommList(void)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildBlankFrameCommList()");
+
+    long long *bframes = new long long[blankFrameMap.count()*2];
+    long long *c_start = new long long[blankFrameMap.count()];
+    long long *c_end   = new long long[blankFrameMap.count()];
+    int frames = 0;
+    int commercials = 0;
+    int i, x;
+    frm_dir_map_t::iterator it;
+
+    blankCommMap.clear();
+
+    for (it = blankFrameMap.begin(); it != blankFrameMap.end(); ++it)
+        bframes[frames++] = it.key();
+
+    if (frames == 0)
+    {
+        delete[] c_start;
+        delete[] c_end;
+        delete[] bframes;
+        return;
+    }
+
+    // detect individual commercials from blank frames
+    // commercial end is set to frame right before ending blank frame to
+    //    account for instances with only a single blank frame between comms.
+    for(i = 0; i < frames; i++ )
+    {
+        for(x=i+1; x < frames; x++ )
+        {
+            // check for various length spots since some channels don't
+            // have blanks inbetween commercials just at the beginning and
+            // end of breaks
+            int gap_length = bframes[x] - bframes[i];
+            if (((aggressiveDetection) &&
+                ((abs((int)(gap_length - (5 * fps))) < 5 ) ||
+                 (abs((int)(gap_length - (10 * fps))) < 7 ) ||
+                 (abs((int)(gap_length - (15 * fps))) < 10 ) ||
+                 (abs((int)(gap_length - (20 * fps))) < 11 ) ||
+                 (abs((int)(gap_length - (30 * fps))) < 12 ) ||
+                 (abs((int)(gap_length - (40 * fps))) < 1 ) ||
+                 (abs((int)(gap_length - (45 * fps))) < 1 ) ||
+                 (abs((int)(gap_length - (60 * fps))) < 15 ) ||
+                 (abs((int)(gap_length - (90 * fps))) < 10 ) ||
+                 (abs((int)(gap_length - (120 * fps))) < 10 ))) ||
+                ((!aggressiveDetection) &&
+                 ((abs((int)(gap_length - (5 * fps))) < 11 ) ||
+                  (abs((int)(gap_length - (10 * fps))) < 13 ) ||
+                  (abs((int)(gap_length - (15 * fps))) < 16 ) ||
+                  (abs((int)(gap_length - (20 * fps))) < 17 ) ||
+                  (abs((int)(gap_length - (30 * fps))) < 18 ) ||
+                  (abs((int)(gap_length - (40 * fps))) < 3 ) ||
+                  (abs((int)(gap_length - (45 * fps))) < 3 ) ||
+                  (abs((int)(gap_length - (60 * fps))) < 20 ) ||
+                  (abs((int)(gap_length - (90 * fps))) < 20 ) ||
+                  (abs((int)(gap_length - (120 * fps))) < 20 ))))
+            {
+                c_start[commercials] = bframes[i];
+                c_end[commercials] = bframes[x] - 1;
+                commercials++;
+                i = x-1;
+                x = frames;
+            }
+
+            if ((!aggressiveDetection) &&
+                ((abs((int)(gap_length - (30 * fps))) < (int)(fps * 0.85)) ||
+                 (abs((int)(gap_length - (60 * fps))) < (int)(fps * 0.95)) ||
+                 (abs((int)(gap_length - (90 * fps))) < (int)(fps * 1.05)) ||
+                 (abs((int)(gap_length - (120 * fps))) < (int)(fps * 1.15))) &&
+                ((x + 2) < frames) &&
+                ((i + 2) < frames) &&
+                ((bframes[i] + 1) == bframes[i+1]) &&
+                ((bframes[x] + 1) == bframes[x+1]))
+            {
+                c_start[commercials] = bframes[i];
+                c_end[commercials] = bframes[x];
+                commercials++;
+                i = x;
+                x = frames;
+            }
+        }
+    }
+
+    i = 0;
+
+    // don't allow single commercial at head
+    // of show unless followed by another
+    if ((commercials > 1) &&
+        (c_end[0] < (33 * fps)) &&
+        (c_start[1] > (c_end[0] + 40 * fps)))
+        i = 1;
+
+    // eliminate any blank frames at end of commercials
+    bool first_comm = true;
+    for(; i < (commercials-1); i++)
+    {
+        long long r = c_start[i];
+        long long adjustment = 0;
+
+        if ((r < (30 * fps)) &&
+            (first_comm))
+            r = 1;
+
+        blankCommMap[r] = MARK_COMM_START;
+
+        r = c_end[i];
+        if ( i < (commercials-1))
+        {
+            for(x = 0; x < (frames-1); x++)
+                if (bframes[x] == r)
+                    break;
+            while((x < (frames-1)) &&
+                    ((bframes[x] + 1 ) == bframes[x+1]) &&
+                    (bframes[x+1] < c_start[i+1]))
+            {
+                r++;
+                x++;
+            }
+
+            while((blankFrameMap.contains(r+1)) &&
+                  (c_start[i+1] != (r+1)))
+                {
+                    r++;
+                    adjustment++;
+                }
+        }
+        else
+        {
+            while(blankFrameMap.contains(r+1))
+            {
+                r++;
+                adjustment++;
+            }
+        }
+
+        adjustment /= 2;
+        if (adjustment > MAX_BLANK_FRAMES)
+            adjustment = MAX_BLANK_FRAMES;
+        r -= adjustment;
+        blankCommMap[r] = MARK_COMM_END;
+        first_comm = false;
+    }
+
+    blankCommMap[c_start[i]] = MARK_COMM_START;
+    blankCommMap[c_end[i]] = MARK_COMM_END;
+
+    delete[] c_start;
+    delete[] c_end;
+    delete[] bframes;
+
+    LOG(VB_COMMFLAG, LOG_INFO, "Blank-Frame Commercial Map" );
+    for(it = blankCommMap.begin(); it != blankCommMap.end(); ++it)
+        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
+                .arg(it.key()).arg(*it));
+
+    MergeBlankCommList();
+
+    LOG(VB_COMMFLAG, LOG_INFO, "Merged Blank-Frame Commercial Break Map" );
+    for(it = blankCommBreakMap.begin(); it != blankCommBreakMap.end(); ++it)
+        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
+                .arg(it.key()).arg(*it));
+}
+
+void NextgenCommDetector::BuildAudioFrameCommList(void)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildBlankFrameCommList()");
+
+    audioCommBreakMap.clear();
+    // TODO
+}
+
+void NextgenCommDetector::BuildSceneChangeCommList(void)
+{
+    int section_start = -1;
+    int seconds = (int)(framesProcessed / fps);
+    int *sc_histogram = new int[seconds+1];
+
+    sceneCommBreakMap.clear();
+
+    memset(sc_histogram, 0, (seconds+1)*sizeof(int));
+    for (uint64_t f = 1; f <= framesProcessed; f++)
+    {
+        if (sceneMap.contains(f))
+            sc_histogram[(uint64_t)(f / fps)]++;
+    }
+
+    for(long long s = 0; s < (seconds + 1); s++)
+    {
+        if (sc_histogram[s] > 2)
+        {
+            if (section_start == -1)
+            {
+                long long f = (long long)(s * fps);
+                for(int i = 0; i < fps; i++, f++)
+                {
+                    if (sceneMap.contains(f))
+                    {
+                        sceneCommBreakMap[f] = MARK_COMM_START;
+                        i = (int)(fps) + 1;
+                    }
+                }
+            }
+
+            section_start = s;
+        }
+
+        if ((section_start >= 0) &&
+            (s > (section_start + 32)))
+        {
+            long long f = (long long)(section_start * fps);
+            bool found_end = false;
+
+            for(int i = 0; i < fps; i++, f++)
+            {
+                if (sceneMap.contains(f))
+                {
+                    frm_dir_map_t::iterator dit =  sceneCommBreakMap.find(f);
+                    if (dit != sceneCommBreakMap.end())
+                        sceneCommBreakMap.erase(dit);
+                    else
+                        sceneCommBreakMap[f] = MARK_COMM_END;
+                    i = (int)(fps) + 1;
+                    found_end = true;
+                }
+            }
+            section_start = -1;
+
+            if (!found_end)
+            {
+                f = (long long)(section_start * fps);
+                sceneCommBreakMap[f] = MARK_COMM_END;
+            }
+        }
+    }
+    delete[] sc_histogram;
+
+    if (section_start >= 0)
+        sceneCommBreakMap[framesProcessed] = MARK_COMM_END;
+
+    frm_dir_map_t deleteMap;
+    frm_dir_map_t::iterator it = sceneCommBreakMap.begin();
+    frm_dir_map_t::iterator prev = it;
+    if (it != sceneCommBreakMap.end())
+    {
+        ++it;
+        while (it != sceneCommBreakMap.end())
+        {
+            if ((*it == MARK_COMM_END) &&
+                (it.key() - prev.key()) < (30 * fps))
+            {
+                deleteMap[it.key()] = MARK_CUT_START;
+                deleteMap[prev.key()] = MARK_CUT_START;
+            }
+            ++prev;
+            if (it != sceneCommBreakMap.end())
+                ++it;
+        }
+
+        frm_dir_map_t::iterator dit;
+        for (dit = deleteMap.begin(); dit != deleteMap.end(); ++dit)
+            sceneCommBreakMap.remove(dit.key());
+    }
+
+    LOG(VB_COMMFLAG, LOG_INFO, "Scene-Change Commercial Break Map" );
+    for (it = sceneCommBreakMap.begin(); it != sceneCommBreakMap.end(); ++it)
+    {
+        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
+            .arg(it.key()).arg(*it));
+    }
+}
+
+
+void NextgenCommDetector::BuildLogoCommList()
+{
+    show_map_t showmap;
+    GetLogoCommBreakMap(showmap);
+    CondenseMarkMap(showmap, (int)(25 * fps), (int)(30 * fps));
+    ConvertShowMapToCommMap(logoCommBreakMap, showmap);
+
+    frm_dir_map_t::iterator it;
+    LOG(VB_COMMFLAG, LOG_INFO, "Logo Commercial Break Map" );
+    for(it = logoCommBreakMap.begin(); it != logoCommBreakMap.end(); ++it)
+        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
+                .arg(it.key()).arg(*it));
+}
+
+void NextgenCommDetector::MergeBlankCommList(void)
+{
+    frm_dir_map_t::iterator it;
+    frm_dir_map_t::iterator prev;
+    QMap<long long, long long> tmpMap;
+    QMap<long long, long long>::Iterator tmpMap_it;
+    QMap<long long, long long>::Iterator tmpMap_prev;
+
+    blankCommBreakMap.clear();
+
+    if (blankCommMap.isEmpty())
+        return;
+
+    for (it = blankCommMap.begin(); it != blankCommMap.end(); ++it)
+        blankCommBreakMap[it.key()] = *it;
+
+    if (blankCommBreakMap.isEmpty())
+        return;
+
+    it = blankCommMap.begin();
+    prev = it;
+    ++it;
+    for(; it != blankCommMap.end(); ++it, ++prev)
+    {
+        // if next commercial starts less than 15*fps frames away then merge
+        if ((((prev.key() + 1) == it.key()) ||
+            ((prev.key() + (15 * fps)) > it.key())) &&
+            (*prev == MARK_COMM_END) &&
+            (*it == MARK_COMM_START))
+        {
+            blankCommBreakMap.remove(prev.key());
+            blankCommBreakMap.remove(it.key());
+        }
+    }
+
+
+    // make temp copy of commercial break list
+    it = blankCommBreakMap.begin();
+    prev = it;
+    ++it;
+    tmpMap[prev.key()] = it.key();
+    for(; it != blankCommBreakMap.end(); ++it, ++prev)
+    {
+        if ((*prev == MARK_COMM_START) &&
+            (*it == MARK_COMM_END))
+            tmpMap[prev.key()] = it.key();
+    }
+
+    tmpMap_it = tmpMap.begin();
+    tmpMap_prev = tmpMap_it;
+    tmpMap_it++;
+    for(; tmpMap_it != tmpMap.end(); ++tmpMap_it, ++tmpMap_prev)
+    {
+        // if we find any segments less than 35 seconds between commercial
+        // breaks include those segments in the commercial break.
+        if (((*tmpMap_prev + (35 * fps)) > tmpMap_it.key()) &&
+            ((*tmpMap_prev - tmpMap_prev.key()) > (35 * fps)) &&
+            ((*tmpMap_it - tmpMap_it.key()) > (35 * fps)))
+        {
+            blankCommBreakMap.remove(*tmpMap_prev);
+            blankCommBreakMap.remove(tmpMap_it.key());
+        }
+    }
+}
+
+bool NextgenCommDetector::FrameIsInBreakMap(
+    uint64_t f, const frm_dir_map_t &breakMap) const
+{
+    for (uint64_t i = f; i < framesProcessed; i++)
+    {
+        if (breakMap.contains(i))
+        {
+            int type = breakMap[i];
+            if ((type == MARK_COMM_END) || (i == f))
+                return true;
+            if (type == MARK_COMM_START)
+                return false;
+        }
+    }
+
+    // We want from f down to 0, but without wrapping the counter to negative
+    // on an unsigned counter.
+    for (uint64_t i = (f + 1); i-- > 0; )
+    {
+        if (breakMap.contains(i))
+        {
+            int type = breakMap[i];
+            if ((type == MARK_COMM_START) || (i == f))
+                return true;
+            if (type == MARK_COMM_END)
+                return false;
+        }
+    }
+
+    return false;
+}
+
+void NextgenCommDetector::DumpMap(frm_dir_map_t &map)
+{
+    frm_dir_map_t::iterator it;
+    QString msg;
+
+    LOG(VB_COMMFLAG, LOG_INFO,
+        "---------------------------------------------------");
+    for (it = map.begin(); it != map.end(); ++it)
+    {
+        long long frame = it.key();
+        int flag = *it;
+        int my_fps = (int)ceil(fps);
+        int hour = (frame / my_fps) / 60 / 60;
+        int min = (frame / my_fps) / 60 - (hour * 60);
+        int sec = (frame / my_fps) - (min * 60) - (hour * 60 * 60);
+        int frm = frame - ((sec * my_fps) + (min * 60 * my_fps) +
+                           (hour * 60 * 60 * my_fps));
+        int my_sec = (int)(frame / my_fps);
+        msg.sprintf("%7ld : %d (%02d:%02d:%02d.%02d) (%d)",
+                    (long)frame, flag, hour, min, sec, frm, my_sec);
+        LOG(VB_COMMFLAG, LOG_INFO, msg);
+    }
+    LOG(VB_COMMFLAG, LOG_INFO,
+        "---------------------------------------------------");
+}
+
+void NextgenCommDetector::CondenseMarkMap(show_map_t &map, int spacing,
+                                          int length)
+{
+    show_map_t::iterator it;
+    show_map_t::iterator prev;
+    show_map_t tmpMap;
+
+    if (map.size() <= 2)
+        return;
+
+    // merge any segments less than 'spacing' frames apart from each other
+    LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map Before condense:" );
+    for (it = map.begin(); it != map.end(); ++it)
+    {
+        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
+                .arg(it.key()).arg(*it));
+        tmpMap[it.key()] = *it;
+    }
+
+    prev = tmpMap.begin();
+    it = prev;
+    ++it;
+    while (it != tmpMap.end())
+    {
+        if ((*it == MARK_START) &&
+            (*prev == MARK_END) &&
+            ((it.key() - prev.key()) < (uint64_t)spacing))
+        {
+            map.remove(prev.key());
+            map.remove(it.key());
+        }
+        ++prev;
+        ++it;
+    }
+
+    if (map.size() == 0)
+        return;
+
+    // delete any segments less than 'length' frames in length
+    tmpMap.clear();
+    for (it = map.begin(); it != map.end(); ++it)
+        tmpMap[it.key()] = *it;
+
+    prev = tmpMap.begin();
+    it = prev;
+    ++it;
+    while (it != tmpMap.end())
+    {
+        if ((*prev == MARK_START) &&
+            (*it == MARK_END) &&
+            ((it.key() - prev.key()) < (uint64_t)length))
+        {
+            map.remove(prev.key());
+            map.remove(it.key());
+        }
+        ++prev;
+        ++it;
+    }
+
+    LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map After condense:" );
+    for (it = map.begin(); it != map.end(); ++it)
+        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
+                .arg(it.key()).arg(*it));
+}
+
+void NextgenCommDetector::ConvertShowMapToCommMap(
+    frm_dir_map_t &out, const show_map_t &in)
+{
+    out.clear();
+    if (in.empty())
+        return;
+
+    show_map_t::const_iterator sit;
+    for (sit = in.begin(); sit != in.end(); ++sit)
+    {
+        if (*sit == MARK_START)
+            out[sit.key()] = MARK_COMM_END;
+        else
+            out[sit.key()] = MARK_COMM_START;
+    }
+
+    frm_dir_map_t::iterator it = out.begin();
+    if (it == out.end())
+        return;
+
+    switch (out[it.key()])
+    {
+        case MARK_COMM_END:
+            if (it.key() == 0)
+                out.remove(0);
+            else
+                out[0] = MARK_COMM_START;
+            break;
+        case MARK_COMM_START:
+            break;
+        default:
+            out.remove(0);
+            break;
+    }
+}
+
+
+/* ideas for this method ported back from comskip.c mods by Jere Jones
+ * which are partially mods based on Myth's original commercial skip
+ * code written by Chris Pinkham. */
+
+void NextgenCommDetector::CleanupFrameInfo(void)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::CleanupFrameInfo()");
+
+    int value;
+    int before, after;
+
+    // try to account for noisy signal causing blank frames to be undetected
+    if ((framesProcessed > (fps * 60)) &&
+        (blankFrameCount < (framesProcessed * 0.0004)))
+    {
+        int avgHistogram[256];
+        int minAvg = -1;
+        int newThreshold = -1;
+
+        LOG(VB_COMMFLAG, LOG_INFO,
+            QString("NextgenCommDetect: Only found %1 blank frames but "
+                    "wanted at least %2, rechecking data using higher "
+                    "threshold.")
+                .arg(blankFrameCount)
+                .arg((int)(framesProcessed * 0.0004)));
+        blankFrameMap.clear();
+        blankFrameCount = 0;
+
+        memset(avgHistogram, 0, sizeof(avgHistogram));
+
+        for (uint64_t i = 1; i <= framesProcessed; i++)
+            avgHistogram[clamp(frameInfo[i].avgBrightness, 0, 255)] += 1;
+
+        for (int i = 1; i <= 255 && minAvg == -1; i++)
+            if (avgHistogram[i] > (framesProcessed * 0.0004))
+                minAvg = i;
+
+        newThreshold = minAvg + 3;
+        LOG(VB_COMMFLAG, LOG_INFO,
+            QString("Minimum Average Brightness on a frame "
+                    "was %1, will use %2 as new threshold")
+                .arg(minAvg).arg(newThreshold));
+
+        for (uint64_t i = 1; i <= framesProcessed; i++)
+        {
+            value = frameInfo[i].flagMask;
+            frameInfo[i].flagMask = value & ~COMM_FRAME_BLANK;
+
+            if (( !(frameInfo[i].flagMask & COMM_FRAME_BLANK)) &&
+                (frameInfo[i].avgBrightness < newThreshold))
+            {
+                frameInfo[i].flagMask = value | COMM_FRAME_BLANK;
+                blankFrameMap[i] = MARK_BLANK_FRAME;
+                blankFrameCount++;
+            }
+        }
+
+        LOG(VB_COMMFLAG, LOG_INFO,
+            QString("Found %1 blank frames using new value")
+                .arg(blankFrameCount));
+    }
+
+    // try to account for fuzzy logo detection
+    for (uint64_t i = 1; i <= framesProcessed; i++)
+    {
+        if ((i < 10) || ((i+10) > framesProcessed))
+            continue;
+
+        before = 0;
+        for (int offset = 1; offset <= 10; offset++)
+            if (frameInfo[i - offset].flagMask & COMM_FRAME_LOGO_PRESENT)
+                before++;
+
+        after = 0;
+        for (int offset = 1; offset <= 10; offset++)
+            if (frameInfo[i + offset].flagMask & COMM_FRAME_LOGO_PRESENT)
+                after++;
+
+        value = frameInfo[i].flagMask;
+        if (value == -1)
+            frameInfo[i].flagMask = 0;
+
+        if (value & COMM_FRAME_LOGO_PRESENT)
+        {
+            if ((before < 4) && (after < 4))
+                frameInfo[i].flagMask = value & ~COMM_FRAME_LOGO_PRESENT;
+        }
+        else
+        {
+            if ((before > 6) && (after > 6))
+                frameInfo[i].flagMask = value | COMM_FRAME_LOGO_PRESENT;
+        }
+    }
+}
+
+void NextgenCommDetector::GetLogoCommBreakMap(show_map_t &map)
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetLogoCommBreakMap()");
+
+    map.clear();
+
+    bool PrevFrameLogo = false;
+
+    for (uint64_t curFrame = 1 ; curFrame <= framesProcessed; curFrame++)
+    {
+        bool CurrentFrameLogo =
+            (frameInfo[curFrame].flagMask & COMM_FRAME_LOGO_PRESENT);
+
+        if (!PrevFrameLogo && CurrentFrameLogo)
+            map[curFrame] = MARK_START;
+        else if (PrevFrameLogo && !CurrentFrameLogo)
+            map[curFrame] = MARK_END;
+
+        PrevFrameLogo = CurrentFrameLogo;
+    }
+}
+
+void NextgenCommDetector::logoDetectorBreathe()
+{
+    emit breathe();
+}
+
+void NextgenCommDetector::PrintFullMap(
+    ostream &out, const frm_dir_map_t *comm_breaks, bool verbose) const
+{
+    if (verbose)
+    {
+        QByteArray tmp = NGFrameInfoEntry::GetHeader().toAscii();
+        out << tmp.constData() << " mark" << endl;
+    }
+
+    for (long long i = 1; i < curFrameNumber; i++)
+    {
+        QMap<long long, NGFrameInfoEntry>::const_iterator it = frameInfo.find(i);
+        if (it == frameInfo.end())
+            continue;
+
+        QByteArray atmp = (*it).toString(i, verbose).toAscii();
+        out << atmp.constData() << " ";
+        if (comm_breaks)
+        {
+            frm_dir_map_t::const_iterator mit = comm_breaks->find(i);
+            if (mit != comm_breaks->end())
+            {
+                QString tmp = (verbose) ?
+                    toString((MarkTypes)*mit) : QString::number(*mit);
+                atmp = tmp.toAscii();
+
+                out << atmp.constData();
+            }
+        }
+        out << "\n";
+    }
+
+    out << flush;
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/NextgenCommDetector.h b/mythtv/programs/mythcommflag/NextgenCommDetector.h
new file mode 100644
index 0000000..373c0aa
--- /dev/null
+++ b/mythtv/programs/mythcommflag/NextgenCommDetector.h
@@ -0,0 +1,255 @@
+#ifndef _NEXTGEN_COMMDETECTOR_H_
+#define _NEXTGEN_COMMDETECTOR_H_
+
+// POSIX headers
+#include <stdint.h>
+
+// Qt headers
+#include <QObject>
+#include <QMap>
+#include <QQueue>
+#include <QDateTime>
+
+// MythTV headers
+#include "programinfo.h"
+#include "frame.h"
+
+// Commercial Flagging headers
+#include "CommDetectorBase.h"
+#include "ClassicCommDetector.h"
+
+class MythPlayer;
+class LogoDetectorBase;
+class SceneChangeDetectorBase;
+
+class AudioChangeDetectorBase;
+class AudioBuffer;
+class SubtitleChangeDetectorBase;
+
+// extends frameMaskValues
+enum extraFrameMaskValues {
+#if 0
+    COMM_FRAME_SKIPPED       = 0x0001,
+    COMM_FRAME_BLANK         = 0x0002,
+    COMM_FRAME_SCENE_CHANGE  = 0x0004,
+    COMM_FRAME_LOGO_PRESENT  = 0x0008,
+    COMM_FRAME_ASPECT_CHANGE = 0x0010,
+    COMM_FRAME_RATING_SYMBOL = 0x0020,
+#endif
+    COMM_FRAME_NO_AUDIO      = 0x0100,
+    COMM_FRAME_AUDIO_CHANGE  = 0x0200,
+    COMM_FRAME_INVALID_AUDIO = 0x0400,
+    COMM_FRAME_SUBTITLE_PRESENT = 0x1000,
+};
+
+class NGFrameInfoEntry
+{
+  public:
+    int minBrightness;
+    int maxBrightness;
+    int avgBrightness;
+    int sceneChangePercent;
+    int aspect;
+    int format;
+    double audioPower;
+    int audioMode;
+    int subtitleMode;
+    int flagMask;
+    static QString GetHeader(void);
+    QString toString(uint64_t frame, bool verbose) const;
+};
+
+class NextgenCommDetector : public CommDetectorBase
+{
+    Q_OBJECT
+
+    public:
+        NextgenCommDetector(SkipType commDetectMethod, bool showProgress,
+                            bool fullSpeed, MythPlayer* player,
+                            int chanid,
+                            const QDateTime& startedAt_in,
+                            const QDateTime& stopsAt_in,
+                            const QDateTime& recordingStartedAt_in,
+                            const QDateTime& recordingStopsAt_in);
+        virtual void deleteLater(void);
+
+        bool go();
+        void GetCommercialBreakList(frm_dir_map_t &comms);
+        void recordingFinished(long long totalFileSize);
+        void requestCommBreakMapUpdate(void);
+
+        void PrintFullMap(
+            ostream &out, const frm_dir_map_t *comm_breaks,
+            bool verbose) const;
+
+        void logoDetectorBreathe();
+
+        friend class NextgenLogoDetector;
+
+    protected:
+        virtual ~NextgenCommDetector() {}
+
+    private:
+        struct FrameBlock;
+        typedef QExplicitlySharedDataPointer<FrameBlock> PFrameBlock;
+        struct FrameBlock : QSharedData
+        {
+            long start;
+            long end;
+            long frames;
+            double length;
+            int bfCount;
+            int saCount;
+            int acCount;
+            double aPowerAvg;
+            int logoCount;
+            int ratingCount;
+            int scCount;
+            int subCount;
+            double scRate;
+            int formatMatch;
+            int aspectMatch;
+            int segmentScore;
+            int transitionScore;
+
+            vector<PFrameBlock> subBlock;
+
+            FrameBlock();
+
+            void Merge(FrameBlock& o);
+        };
+
+        void ClearAllMaps(void);
+        void GetBlankCommMap(frm_dir_map_t &comms);
+        void GetBlankCommBreakMap(frm_dir_map_t &comms);
+        void GetSilentCommMap(frm_dir_map_t &comms);
+        void GetAudioCommBreakMap(frm_dir_map_t &comms);
+        void GetSceneChangeMap(frm_dir_map_t &scenes,
+                               int64_t start_frame);
+        frm_dir_map_t Combine2Maps(
+            const frm_dir_map_t &a, const frm_dir_map_t &b) const;
+        void UpdateFrameBlock(PFrameBlock fbp, NGFrameInfoEntry& finfo,
+                              int format, int aspect);
+        void BuildAllMethodsCommList(void);
+        void BuildBlankFrameCommList(void);
+        void BuildAudioFrameCommList(void);
+        void BuildSceneChangeCommList(void);
+        void BuildLogoCommList();
+        void MergeBlankCommList(void);
+        bool FrameIsInBreakMap(uint64_t f, const frm_dir_map_t &breakMap) const;
+        void DumpMap(frm_dir_map_t &map);
+        void CondenseMarkMap(show_map_t &map, int spacing, int length);
+        void ConvertShowMapToCommMap(
+            frm_dir_map_t &out, const show_map_t &in);
+        void CleanupFrameInfo(void);
+        void GetLogoCommBreakMap(show_map_t &map);
+
+        const char * FrameBlockHeader1() const;
+        const char * FrameBlockHeader2() const;
+        const QString FrameBlockStr(int curBlock, PFrameBlock fbp, int *score = NULL) const;
+
+        enum SkipTypes commDetectMethod;
+        frm_dir_map_t lastSentCommBreakMap;
+        bool commBreakMapUpdateRequested;
+        bool sendCommBreakMapUpdates;
+
+        int commDetectBorder;
+        int commDetectBlankFrameMaxDiff;
+        int commDetectDarkBrightness;
+        int commDetectDimBrightness;
+        int commDetectBoxBrightness;
+        int commDetectDimAverage;
+        int commDetectMaxCommBreakLength;
+        int commDetectMinCommBreakLength;
+        int commDetectMinShowLength;
+        int commDetectMaxCommLength;
+        bool commDetectBlankCanHaveLogo;
+        int commDetectLargeSceneChangeThreshold;
+        QList<float> commDetectCommLengths;
+
+        bool verboseDebugging;
+
+        long long lastFrameNumber;
+        long long curFrameNumber;
+
+        int width;
+        int height;
+        int horizSpacing;
+        int vertSpacing;
+        double fpm;
+        bool blankFramesOnly;
+        int blankFrameCount;
+        int currentAspect;
+        int silentFrameCount;
+
+
+        int totalMinBrightness;
+
+        bool detectBlankFrames;
+        bool detectSceneChanges;
+        bool detectStationLogo;
+        bool detectSilentFrames;
+
+        bool subtitleInfoAvailable;
+        int subtitleFrameCount;
+
+        bool logoInfoAvailable;
+        LogoDetectorBase* logoDetector;
+
+        unsigned char *framePtr;
+
+        frm_dir_map_t blankFrameMap;
+        frm_dir_map_t blankCommMap;
+        frm_dir_map_t blankCommBreakMap;
+        frm_dir_map_t silentFrameMap;
+        frm_dir_map_t audioCommBreakMap;
+        frm_dir_map_t sceneMap;
+        frm_dir_map_t sceneCommBreakMap;
+        frm_dir_map_t commBreakMap;
+        frm_dir_map_t logoCommBreakMap;
+
+        bool frameIsBlank;
+        bool sceneHasChanged;
+        bool stationLogoPresent;
+
+        bool lastFrameWasBlank;
+        bool lastFrameWasSceneChange;
+        bool decoderFoundAspectChanges;
+
+        SceneChangeDetectorBase* sceneChangeDetector;
+        AudioChangeDetectorBase* audioChangeDetector;
+        SubtitleChangeDetectorBase* subtitleChangeDetector;
+
+protected:
+        MythPlayer *player;
+        QDateTime startedAt, stopsAt;
+        QDateTime recordingStartedAt, recordingStopsAt;
+        bool aggressiveDetection;
+        bool stillRecording;
+        bool fullSpeed;
+        bool showProgress;
+        double fps;
+        uint64_t framesProcessed;
+        long long preRoll;
+        long long postRoll;
+
+
+        void Init();
+        void SetVideoParams(float aspect);
+        void ProcessFrame(VideoFrame *frame, long long frame_number);
+        QMap<long long, NGFrameInfoEntry> frameInfo;
+        AudioBuffer *audioBuffer;
+        int64_t audioFrame;
+
+        QQueue<VideoFrame*> vfQueue;
+
+public slots:
+        void sceneChangeDetectorHasNewInformation(unsigned int framenum, bool isSceneChange,float debugValue);
+        void audioDetectorHasNewInformation(unsigned int framenum, bool hasChanged, float amplitude, float debugValue);
+        void subtitleDetectorHasNewInformation(unsigned int framenum, bool hasChanged, float debugValue);
+};
+
+#endif
+
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/NextgenLogoDetector.cpp b/mythtv/programs/mythcommflag/NextgenLogoDetector.cpp
new file mode 100644
index 0000000..286f08e
--- /dev/null
+++ b/mythtv/programs/mythcommflag/NextgenLogoDetector.cpp
@@ -0,0 +1,573 @@
+// POSIX headers
+#include <unistd.h>
+
+// ANSI C headers
+#include <cstdlib>
+
+// MythTV headers
+#include "mythcorecontext.h"
+#include "mythplayer.h"
+
+// Commercial Flagging headers
+#include "NextgenLogoDetector.h"
+#include "NextgenCommDetector.h"
+
+typedef struct edgemaskentry
+{
+    int isedge;
+    int horiz;
+    int vert;
+    int rdiag;
+    int ldiag;
+}
+EdgeMaskEntry;
+
+
+NextgenLogoDetector::NextgenLogoDetector(NextgenCommDetector* commdetector,
+                                         unsigned int w, unsigned int h,
+                                         unsigned int commdetectborder_in,
+                                         unsigned int xspacing_in,
+                                         unsigned int yspacing_in)
+    : LogoDetectorBase(w,h),
+      commDetector(commdetector),                       frameNumber(0),
+      previousFrameWasSceneChange(false),
+      xspacing(xspacing_in),                            yspacing(yspacing_in),
+      commDetectBorder(commdetectborder_in),            edgeMask(new EdgeMaskEntry[width * height]),
+      logoMaxValues(new unsigned char[width * height]), logoMinValues(new unsigned char[width * height]),
+      logoFrame(new unsigned char[width * height]),     logoMask(new unsigned char[width * height]),
+      logoCheckMask(new unsigned char[width * height]), tmpBuf(new unsigned char[width * height]),
+      logoEdgeDiff(0),                                  logoFrameCount(0),
+      logoMinX(0),                                      logoMaxX(0),
+      logoMinY(0),                                      logoMaxY(0),
+      logoInfoAvailable(false)
+{
+    commDetectLogoSamplesNeeded =
+        gCoreContext->GetNumSetting("CommDetectLogoSamplesNeeded", 240);
+    commDetectLogoSampleSpacing =
+        gCoreContext->GetNumSetting("CommDetectLogoSampleSpacing", 2);
+    commDetectLogoSecondsNeeded = (int)(1.3 * commDetectLogoSamplesNeeded *
+                                              commDetectLogoSampleSpacing);
+    commDetectLogoGoodEdgeThreshold =
+        gCoreContext->GetSetting("CommDetectLogoGoodEdgeThreshold", "0.75")
+        .toDouble();
+    commDetectLogoBadEdgeThreshold =
+        gCoreContext->GetSetting("CommDetectLogoBadEdgeThreshold", "0.85")
+        .toDouble();
+}
+
+unsigned int NextgenLogoDetector::getRequiredAvailableBufferForSearch()
+{
+    return commDetectLogoSecondsNeeded;
+}
+
+void NextgenLogoDetector::deleteLater(void)
+{
+    commDetector = 0;
+    if (edgeMask)
+        delete [] edgeMask;
+    if (logoFrame)
+        delete [] logoFrame;
+    if (logoMask)
+        delete [] logoMask;
+    if (logoCheckMask)
+        delete [] logoCheckMask;
+    if (logoMaxValues)
+        delete [] logoMaxValues;
+    if (logoMinValues)
+        delete [] logoMinValues;
+    if (tmpBuf)
+        delete [] tmpBuf;
+
+    LogoDetectorBase::deleteLater();
+}
+
+bool NextgenLogoDetector::searchForLogo(MythPlayer* player)
+{
+    int seekIncrement = 
+        (int)(commDetectLogoSampleSpacing * player->GetFrameRate());
+    long long seekFrame;
+    int loops;
+    int maxLoops = commDetectLogoSamplesNeeded;
+    EdgeMaskEntry *edgeCounts;
+    unsigned int pos, i, x, y, dx, dy;
+    int edgeDiffs[] = {5, 7, 10, 15, 20, 30, 40, 50, 60, 0 };
+
+
+    LOG(VB_COMMFLAG, LOG_INFO, "Searching for Station Logo");
+
+    logoInfoAvailable = false;
+
+    edgeCounts = new EdgeMaskEntry[width * height];
+
+    for (i = 0; edgeDiffs[i] != 0 && !logoInfoAvailable; i++)
+    {
+        int pixelsInMask = 0;
+
+        LOG(VB_COMMFLAG, LOG_INFO, QString("Trying with edgeDiff == %1")
+                .arg(edgeDiffs[i]));
+
+        memset(edgeCounts, 0, sizeof(EdgeMaskEntry) * width * height);
+        memset(edgeMask, 0, sizeof(EdgeMaskEntry) * width * height);
+
+        player->DiscardVideoFrame(player->GetRawVideoFrame(0));
+
+        loops = 0;
+        seekFrame = commDetector->preRoll + seekIncrement;
+        while(loops < maxLoops && !player->GetEof())
+        {
+            VideoFrame* vf = player->GetRawVideoFrame(seekFrame);
+
+            if ((loops % 50) == 0)
+                commDetector->logoDetectorBreathe();
+
+            if (commDetector->m_bStop)
+            {
+                player->DiscardVideoFrame(vf);
+                delete[] edgeCounts;
+                return false;
+            }
+
+            if (!commDetector->fullSpeed)
+                usleep(10000);
+
+            DetectEdges(vf, edgeCounts, edgeDiffs[i]);
+
+            seekFrame += seekIncrement;
+            loops++;
+
+            player->DiscardVideoFrame(vf);
+        }
+
+        LOG(VB_COMMFLAG, LOG_INFO, "Analyzing edge data");
+
+#ifdef SHOW_DEBUG_WIN
+        unsigned char *fakeFrame;
+        fakeFrame = new unsigned char[width * height * 3 / 2];
+        memset(fakeFrame, 0, width * height * 3 / 2);
+#endif
+
+        for (y = 0; y < height; y++)
+        {
+            if ((y > (height/4)) && (y < (height * 3 / 4)))
+                continue;
+
+            for (x = 0; x < width; x++)
+            {
+                if ((x > (width/4)) && (x < (width * 3 / 4)))
+                    continue;
+
+                pos = y * width + x;
+
+                if (edgeCounts[pos].isedge > (maxLoops * 0.66))
+                {
+                    edgeMask[pos].isedge = 1;
+                    pixelsInMask++;
+#ifdef SHOW_DEBUG_WIN
+                    fakeFrame[pos] = 0xff;
+#endif
+
+                }
+
+                if (edgeCounts[pos].horiz > (maxLoops * 0.66))
+                    edgeMask[pos].horiz = 1;
+
+                if (edgeCounts[pos].vert > (maxLoops * 0.66))
+                    edgeMask[pos].vert = 1;
+
+                if (edgeCounts[pos].ldiag > (maxLoops * 0.66))
+                    edgeMask[pos].ldiag = 1;
+                if (edgeCounts[pos].rdiag > (maxLoops * 0.66))
+                    edgeMask[pos].rdiag = 1;
+            }
+        }
+
+        SetLogoMaskArea();
+
+        for (y = logoMinY; y < logoMaxY; y++)
+        {
+            for (x = logoMinX; x < logoMaxX; x++)
+            {
+                int neighbors = 0;
+
+                if (!edgeMask[y * width + x].isedge)
+                    continue;
+
+                for (dy = y - 2; dy <= (y + 2); dy++ )
+                {
+                    for (dx = x - 2; dx <= (x + 2); dx++ )
+                    {
+                        if (edgeMask[dy * width + dx].isedge)
+                            neighbors++;
+                    }
+                }
+
+                if (neighbors < 5)
+                    edgeMask[y * width + x].isedge = 0;
+            }
+        }
+
+        SetLogoMaskArea();
+        LOG(VB_COMMFLAG, LOG_INFO,
+            QString("Testing Logo area: topleft (%1,%2), bottomright (%3,%4)")
+                .arg(logoMinX).arg(logoMinY)
+                .arg(logoMaxX).arg(logoMaxY));
+
+#ifdef SHOW_DEBUG_WIN
+        for (x = logoMinX; x < logoMaxX; x++)
+        {
+            pos = logoMinY * width + x;
+            fakeFrame[pos] = 0x7f;
+            pos = logoMaxY * width + x;
+            fakeFrame[pos] = 0x7f;
+        }
+        for (y = logoMinY; y < logoMaxY; y++)
+        {
+            pos = y * width + logoMinX;
+            fakeFrame[pos] = 0x7f;
+            pos = y * width + logoMaxX;
+            fakeFrame[pos] = 0x7f;
+        }
+
+        comm_debug_show(fakeFrame);
+        delete [] fakeFrame;
+
+        cerr << "Hit ENTER to continue" << endl;
+        getchar();
+#endif
+        if (((logoMaxX - logoMinX) < (width / 4)) &&
+            ((logoMaxY - logoMinY) < (height / 4)) &&
+            (pixelsInMask > 50))
+        {
+            logoInfoAvailable = true;
+            logoEdgeDiff = edgeDiffs[i];
+
+            LOG(VB_COMMFLAG, LOG_INFO, 
+                QString("Using Logo area: topleft (%1,%2), "
+                        "bottomright (%3,%4)")
+                    .arg(logoMinX).arg(logoMinY)
+                    .arg(logoMaxX).arg(logoMaxY));
+        }
+        else
+        {
+            LOG(VB_COMMFLAG, LOG_INFO, 
+                QString("Rejecting Logo area: topleft (%1,%2), "
+                        "bottomright (%3,%4), pixelsInMask (%5). "
+                        "Not within specified limits.")
+                    .arg(logoMinX).arg(logoMinY)
+                    .arg(logoMaxX).arg(logoMaxY)
+                    .arg(pixelsInMask));
+        }
+    }
+
+    delete [] edgeCounts;
+
+    if (!logoInfoAvailable)
+        LOG(VB_COMMFLAG, LOG_NOTICE, "No suitable logo area found.");
+
+    player->DiscardVideoFrame(player->GetRawVideoFrame(0));
+    return logoInfoAvailable;
+}
+
+
+void NextgenLogoDetector::SetLogoMaskArea()
+{
+    LOG(VB_COMMFLAG, LOG_INFO, "SetLogoMaskArea()");
+
+    logoMinX = width - 1;
+    logoMaxX = 0;
+    logoMinY = height - 1;
+    logoMaxY = 0;
+
+    for (unsigned int y = 0; y < height; y++)
+    {
+        for (unsigned int x = 0; x < width; x++)
+        {
+            if (edgeMask[y * width + x].isedge)
+            {
+                if (x < logoMinX)
+                    logoMinX = x;
+                if (y < logoMinY)
+                    logoMinY = y;
+                if (x > logoMaxX)
+                    logoMaxX = x;
+                if (y > logoMaxY)
+                    logoMaxY = y;
+            }
+        }
+    }
+
+    logoMinX -= 5;
+    logoMaxX += 5;
+    logoMinY -= 5;
+    logoMaxY += 5;
+
+    if (logoMinX < 4)
+        logoMinX = 4;
+    if (logoMaxX > (width-5))
+        logoMaxX = (width-5);
+    if (logoMinY < 4)
+        logoMinY = 4;
+    if (logoMaxY > (height-5))
+        logoMaxY = (height-5);
+}
+
+void NextgenLogoDetector::SetLogoMask(unsigned char *mask)
+{
+    int pixels = 0;
+
+    memcpy(logoMask, mask, width * height);
+
+    SetLogoMaskArea();
+
+    for(unsigned int y = logoMinY; y <= logoMaxY; y++)
+        for(unsigned int x = logoMinX; x <= logoMaxX; x++)
+            if (!logoMask[y * width + x] == 1)
+                pixels++;
+
+    if (pixels < 30)
+        return;
+
+    // set the pixels around our logo
+    for(unsigned int y = (logoMinY - 1); y <= (logoMaxY + 1); y++)
+    {
+        for(unsigned int x = (logoMinX - 1); x <= (logoMaxX + 1); x++)
+        {
+            if (!logoMask[y * width + x])
+            {
+                for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++)
+                {
+                    for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++)
+                    {
+                        if ((logoMask[y2 * width + x2] == 1) &&
+                            (!logoMask[y * width + x]))
+                        {
+                            logoMask[y * width + x] = 2;
+                            x2 = x + 2;
+                            y2 = y + 2;
+
+                            logoCheckMask[y2 * width + x2] = 1;
+                            logoCheckMask[y * width + x] = 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    for(unsigned int y = (logoMinY - 2); y <= (logoMaxY + 2); y++)
+    {
+        for(unsigned int x = (logoMinX - 2); x <= (logoMaxX + 2); x++)
+        {
+            if (!logoMask[y * width + x])
+            {
+                for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++)
+                {
+                    for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++)
+                    {
+                        if ((logoMask[y2 * width + x2] == 2) &&
+                            (!logoMask[y * width + x]))
+                        {
+                            logoMask[y * width + x] = 3;
+                            x2 = x + 2;
+                            y2 = y + 2;
+
+                            logoCheckMask[y * width + x] = 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+#ifdef SHOW_DEBUG_WIN
+    DumpLogo(true,framePtr);
+#endif
+
+    logoFrameCount = 0;
+    logoInfoAvailable = true;
+}
+
+
+void NextgenLogoDetector::DumpLogo(bool fromCurrentFrame,
+    unsigned char* framePtr)
+{
+    char scrPixels[] = " .oxX";
+
+    if (!logoInfoAvailable)
+        return;
+
+    cerr << "\nLogo Data ";
+    if (fromCurrentFrame)
+        cerr << "from current frame\n";
+
+    cerr << "\n     ";
+
+    for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++)
+        cerr << (x % 10);
+    cerr << "\n";
+
+    for(unsigned int y = logoMinY - 2; y <= (logoMaxY + 2); y++)
+    {
+        QString tmp = QString("%1: ").arg(y, 3);
+        QString ba = tmp.toAscii();
+        cerr << ba.constData();
+        for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++)
+        {
+            if (fromCurrentFrame)
+            {
+                cerr << scrPixels[framePtr[y * width + x] / 50];
+            }
+            else
+            {
+                switch (logoMask[y * width + x])
+                {
+                        case 0:
+                        case 2: cerr << " ";
+                        break;
+                        case 1: cerr << "*";
+                        break;
+                        case 3: cerr << ".";
+                        break;
+                }
+            }
+        }
+        cerr << "\n";
+    }
+    cerr.flush();
+}
+
+
+/* ideas for this method ported back from comskip.c mods by Jere Jones
+ * which are partially mods based on Myth's original commercial skip
+ * code written by Chris Pinkham. */
+bool NextgenLogoDetector::doesThisFrameContainTheFoundLogo(
+    unsigned char* framePtr)
+{
+    int radius = 2;
+    unsigned int x, y;
+    int pos1, pos2, pos3;
+    int pixel;
+    int goodEdges = 0;
+    int badEdges = 0;
+    int testEdges = 0;
+    int testNotEdges = 0;
+
+    for (y = logoMinY; y <= logoMaxY; y++ )
+    {
+        for (x = logoMinX; x <= logoMaxX; x++ )
+        {
+            pos1 = y * width + x;
+            pos2 = (y - radius) * width + x;
+            pos3 = (y + radius) * width + x;
+
+            pixel = framePtr[pos1];
+
+            if (edgeMask[pos1].horiz)
+            {
+                if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) ||
+                    (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff))
+                    goodEdges++;
+                testEdges++;
+            }
+            else
+            {
+                if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) ||
+                    (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff))
+                    badEdges++;
+                testNotEdges++;
+            }
+
+            if (edgeMask[pos1].vert)
+            {
+                if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) ||
+                    (abs(framePtr[pos3] - pixel) >= logoEdgeDiff))
+                    goodEdges++;
+                testEdges++;
+            }
+            else
+            {
+                if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) ||
+                    (abs(framePtr[pos3] - pixel) >= logoEdgeDiff))
+                    badEdges++;
+                testNotEdges++;
+            }
+        }
+    }
+
+    frameNumber++;
+    double goodEdgeRatio = (double)goodEdges / (double)testEdges;
+    double badEdgeRatio = (double)badEdges / (double)testNotEdges;
+    if ((goodEdgeRatio > commDetectLogoGoodEdgeThreshold) &&
+        (badEdgeRatio < commDetectLogoBadEdgeThreshold))
+        return true;
+    else
+        return false;
+}
+
+bool NextgenLogoDetector::pixelInsideLogo(unsigned int x, unsigned int y)
+{
+    if (!logoInfoAvailable)
+        return false;
+
+    return ((x > logoMinX) && (x < logoMaxX) &&
+            (y > logoMinY) && (y < logoMaxY));
+}
+
+void NextgenLogoDetector::DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges,
+                                      int edgeDiff)
+{
+    int r = 2;
+    unsigned char *buf = frame->buf;
+    unsigned char p;
+    unsigned int pos, x, y;
+
+    for (y = commDetectBorder + r; y < (height - commDetectBorder - r); y++)
+    {
+        if ((y > (height/4)) && (y < (height * 3 / 4)))
+            continue;
+
+        for (x = commDetectBorder + r; x < (width - commDetectBorder - r); x++)
+        {
+            int edgeCount = 0;
+
+            if ((x > (width/4)) && (x < (width * 3 / 4)))
+                continue;
+
+            pos = y * width + x;
+            p = buf[pos];
+
+            if (( abs(buf[y * width + (x - r)] - p) >= edgeDiff) ||
+                ( abs(buf[y * width + (x + r)] - p) >= edgeDiff))
+            {
+                edges[pos].horiz++;
+                edgeCount++;
+            }
+            if (( abs(buf[(y - r) * width + x] - p) >= edgeDiff) ||
+                ( abs(buf[(y + r) * width + x] - p) >= edgeDiff))
+            {
+                edges[pos].vert++;
+                edgeCount++;
+            }
+
+            if (( abs(buf[(y - r) * width + (x - r)] - p) >= edgeDiff) ||
+                ( abs(buf[(y + r) * width + (x + r)] - p) >= edgeDiff))
+            {
+                edges[pos].ldiag++;
+                edgeCount++;
+            }
+
+            if (( abs(buf[(y - r) * width + (x + r)] - p) >= edgeDiff) ||
+                ( abs(buf[(y + r) * width + (x - r)] - p) >= edgeDiff))
+            {
+                edges[pos].rdiag++;
+                edgeCount++;
+            }
+
+            if (edgeCount >= 3)
+                edges[pos].isedge++;
+        }
+    }
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
diff --git a/mythtv/programs/mythcommflag/NextgenLogoDetector.h b/mythtv/programs/mythcommflag/NextgenLogoDetector.h
new file mode 100644
index 0000000..30ad70f
--- /dev/null
+++ b/mythtv/programs/mythcommflag/NextgenLogoDetector.h
@@ -0,0 +1,67 @@
+#ifndef _NEXTGENLOGOGEDETECTOR_H_
+#define _NEXTGENLOGOGEDETECTOR_H_
+
+#include "LogoDetectorBase.h"
+
+typedef struct edgemaskentry EdgeMaskEntry;
+typedef struct VideoFrame_ VideoFrame;
+class NextgenCommDetector;
+
+class NextgenLogoDetector : public LogoDetectorBase
+{
+  public:
+    NextgenLogoDetector(NextgenCommDetector* commDetector,unsigned int width,
+        unsigned int height, unsigned int commdetectborder,
+        unsigned int xspacing, unsigned int yspacing);
+    virtual void deleteLater(void);
+
+    bool searchForLogo(MythPlayer* player);
+    bool doesThisFrameContainTheFoundLogo(unsigned char* frame);
+    bool pixelInsideLogo(unsigned int x, unsigned int y);
+
+    unsigned int getRequiredAvailableBufferForSearch();
+
+  protected:
+    virtual ~NextgenLogoDetector() {}
+
+  private:
+    void SetLogoMaskArea();
+    void SetLogoMask(unsigned char *mask);
+    void DumpLogo(bool fromCurrentFrame,unsigned char* framePtr);
+    void DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges, int edgeDiff);
+
+    NextgenCommDetector* commDetector;
+    unsigned int frameNumber;
+    bool previousFrameWasSceneChange;
+    unsigned int xspacing, yspacing;
+    unsigned int commDetectBorder;
+
+    int commDetectLogoSamplesNeeded;
+    int commDetectLogoSampleSpacing;
+    int commDetectLogoSecondsNeeded;
+    double commDetectLogoGoodEdgeThreshold;
+    double commDetectLogoBadEdgeThreshold;
+
+    EdgeMaskEntry *edgeMask;
+
+    unsigned char *logoMaxValues;
+    unsigned char *logoMinValues;
+    unsigned char *logoFrame;
+    unsigned char *logoMask;
+    unsigned char *logoCheckMask;
+    unsigned char *tmpBuf;
+
+    int logoEdgeDiff;
+    unsigned int logoFrameCount;
+    unsigned int logoMinX;
+    unsigned int logoMaxX;
+    unsigned int logoMinY;
+    unsigned int logoMaxY;
+
+    bool logoInfoAvailable;
+};
+
+#endif
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
diff --git a/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.cpp b/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.cpp
new file mode 100644
index 0000000..17b7806
--- /dev/null
+++ b/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.cpp
@@ -0,0 +1,49 @@
+#include <algorithm>
+using namespace std;
+
+// MythTV headers
+#include "mythcontext.h"
+
+#include "NextgenSceneChangeDetector.h"
+#include "Histogram.h"
+
+NextgenSceneChangeDetector::NextgenSceneChangeDetector(unsigned int width,
+        unsigned int height, unsigned int commdetectborder_in,
+        unsigned int xspacing_in, unsigned int yspacing_in):
+    SceneChangeDetectorBase(width,height),
+    frameNumber(0),
+    previousFrameWasSceneChange(false),
+    xspacing(xspacing_in),
+    yspacing(yspacing_in),
+    commdetectborder(commdetectborder_in)
+{
+    histogram = new Histogram;
+    previousHistogram = new Histogram;
+    commDetectSceneChangeThreshold =
+        gCoreContext->GetNumSetting("CommDetectSceneChangeThreshold", 70)/100.0;
+}
+
+void NextgenSceneChangeDetector::deleteLater(void)
+{
+    delete histogram;
+    delete previousHistogram;
+    SceneChangeDetectorBase::deleteLater();
+}
+
+void NextgenSceneChangeDetector::processFrame(unsigned char* frame, long long frameNumber)
+{
+    histogram->generateFromImage(frame, width, height, commdetectborder,
+                                 width-commdetectborder, commdetectborder,
+                                 height-commdetectborder, xspacing, yspacing);
+    float similar = histogram->calculateSimilarityWith(*previousHistogram);
+
+    bool isSceneChange = (similar < commDetectSceneChangeThreshold && !previousFrameWasSceneChange);
+
+    emit(haveNewInformation(frameNumber,isSceneChange,similar));
+    previousFrameWasSceneChange = isSceneChange;
+
+    std::swap(histogram,previousHistogram);
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
diff --git a/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.h b/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.h
new file mode 100644
index 0000000..23a6638
--- /dev/null
+++ b/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.h
@@ -0,0 +1,34 @@
+#ifndef _NEXTGENSCENECHANGEDETECTOR_H_
+#define _NEXTGENSCENECHANGEDETECTOR_H_
+
+#include "SceneChangeDetectorBase.h"
+
+class Histogram;
+
+class NextgenSceneChangeDetector : public SceneChangeDetectorBase
+{
+  public:
+    NextgenSceneChangeDetector(unsigned int width, unsigned int height,
+        unsigned int commdetectborder, unsigned int xspacing,
+        unsigned int yspacing);
+    virtual void deleteLater(void);
+
+    void processFrame(unsigned char* frame, long long frameNumber);
+
+  private:
+    ~NextgenSceneChangeDetector() {}
+
+  private:
+    Histogram* histogram;
+    Histogram* previousHistogram;
+    unsigned int frameNumber;
+    bool previousFrameWasSceneChange;
+    unsigned int xspacing, yspacing;
+    unsigned int commdetectborder;
+    double commDetectSceneChangeThreshold;
+};
+
+#endif
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
diff --git a/mythtv/programs/mythcommflag/SceneChangeDetectorBase.h b/mythtv/programs/mythcommflag/SceneChangeDetectorBase.h
index 67296d5..f901407 100644
--- a/mythtv/programs/mythcommflag/SceneChangeDetectorBase.h
+++ b/mythtv/programs/mythcommflag/SceneChangeDetectorBase.h
@@ -11,7 +11,7 @@ class SceneChangeDetectorBase : public QObject
     SceneChangeDetectorBase(unsigned int w, unsigned int h) :
         width(w), height(h) {}
 
-    virtual void processFrame(unsigned char *frame) = 0;
+    virtual void processFrame(unsigned char *frame, long long frameNumber) = 0;
 
   signals:
     void haveNewInformation(unsigned int framenum, bool scenechange,
diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetector.cpp b/mythtv/programs/mythcommflag/SubtitleChangeDetector.cpp
new file mode 100644
index 0000000..b3cf10d
--- /dev/null
+++ b/mythtv/programs/mythcommflag/SubtitleChangeDetector.cpp
@@ -0,0 +1,285 @@
+using namespace std;
+
+// MythTV headers
+#include "frame.h"
+#include "mythplayer.h"
+
+extern "C" {
+#include "libavcodec/avcodec.h"
+}
+
+#include "SubtitleChangeDetector.h"
+
+const int    kTeletextColumns = 40;
+const int    kTeletextRows    = 26;
+
+SubtitleChangeDetector::SubtitleChangeDetector(MythPlayer* player)
+    : 
+        m_teletextIsBlank(true),
+        m_cc608IsBlank(true),
+        m_cc708IsBlank(true)
+{
+    TeletextReader * teletextReader = player->GetTeletextReader();
+    if (teletextReader)
+    {
+        int page = player->GetDecoder()->GetTrackLanguageIndex(
+                            kTrackTypeTeletextCaptions,
+                            player->GetDecoder()->GetTrack(kTrackTypeTeletextCaptions));
+        teletextReader->SetPage(page, -1);
+    }    
+}
+
+SubtitleChangeDetector::SubtitleTiming::SubtitleTiming(int start_, int end_)
+    : start(start_), end(end_)
+{
+}
+
+void SubtitleChangeDetector::Add(int start, int end)
+{
+    LOG(VB_COMMFLAG, LOG_DEBUG,
+            QString("subtitle start %1 end %2")
+            .arg(start).arg(end));
+
+    if (!subtitleTimingList.empty())
+    {
+        MythDeque<SubtitleTiming>::iterator sit;
+        for(sit = subtitleTimingList.begin(); sit != subtitleTimingList.end(); ++sit)
+        {
+            if (end < (*sit).start)
+            {
+                // wholy before current one so insert it
+                subtitleTimingList.insert(sit, SubtitleTiming(start, end));
+                break;
+            }
+            else if (start > (*sit).end)
+            {
+                // wholly after this one
+            }
+            else
+            {
+                if (start < (*sit).start)
+                    (*sit).start = start;
+                if (end > (*sit).end)
+                    (*sit).end = end;
+                break;
+            }
+        }
+        if (sit == subtitleTimingList.end())
+        {
+            // wholly at the end
+            subtitleTimingList.push_back(SubtitleTiming(start, end));
+        }
+    }
+    else
+    {
+        subtitleTimingList.push_back(SubtitleTiming(start, end));
+    }
+}
+
+void SubtitleChangeDetector::processFrame(MythPlayer* player, VideoFrame * frame)
+{
+#if 1
+    SubtitleReader * reader = player->GetSubReader();
+    int frameNumber = frame->frameNumber;
+    double fps = player->GetFrameRate();
+    //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle frame %1").arg(frameNumber));
+#if 0
+    uint64_t duration = 0;
+    const QStringList rawSubs = reader->GetRawTextSubtitles(duration);
+
+    if (!rawSubs.isEmpty())
+    {
+        LOG(VB_COMMFLAG, LOG_DEBUG,
+                QString("There are also %1 raw text subtitles with duration %2")
+                .arg(rawSubs.size()).arg(duration));
+    }
+
+    reader->ClearRawTextSubtitles();
+
+#endif
+    AVSubtitles *subtitles = reader->GetAVSubtitles();
+    if (subtitles && !subtitles->buffers.empty())
+    {
+        QMutexLocker locker(&(subtitles->lock));
+        while (!subtitles->buffers.empty())
+        {
+            const AVSubtitle subtitle = subtitles->buffers.front();
+            subtitles->buffers.pop_front();
+
+            //subtitle.start_display_time;    /* relative to packet pts, in ms */
+            //subtitle.end_display_time;  /* relative to packet pts, in ms */
+            //subtitle.pts;               /* in AV_TIME_BASE, use frame number * fps */
+
+            int start = frameNumber + subtitle.start_display_time * fps / 1000;
+            int end   = frameNumber + subtitle.end_display_time * fps / 1000;
+            Add(start, end);
+            reader->FreeAVSubtitle(subtitle);
+
+            reader->ClearRawTextSubtitles();
+        }
+    }
+
+    TeletextReader * teletextReader = player->GetTeletextReader();
+    if (teletextReader)
+    {
+        if (teletextReader->PageChanged())
+        {
+            LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext changed 1"));
+            TeletextSubPage *subpage = teletextReader->FindSubPage();
+            if (subpage)
+            {
+                LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext subpage found %1 %2").arg((uint)subpage,0,16).arg(subpage->subpagenum));
+                teletextReader->SetSubPage(subpage->subpagenum);
+                int a = 0;
+                if ((subpage->subtitle) ||
+                        (subpage->flags & (TP_SUPPRESS_HEADER | TP_NEWSFLASH | TP_SUBTITLE)))
+                {
+                    LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext %1").arg(teletextReader->IsSubtitle()));
+                    a = 1;
+                    teletextReader->SetShowHeader(false);
+                    teletextReader->SetIsSubtitle(true);
+                    // check text is not blank
+                    if (teletextReader->IsSubtitle() || teletextReader->IsTransparent())
+                    {
+                        QString caption;
+                        bool isBlank = true;
+                        unsigned char ch;
+                        for (int y = kTeletextRows - a; y >= 2; y--)
+                        {
+                            for (uint i = (y == 1 ? 8 : 0); i < (uint) kTeletextColumns; i++)
+                            {
+                                ch = subpage->data[y-1][i] & 0x7F;
+                                caption += ch;
+                                if (ch != ' ')
+                                {
+                                    isBlank = false;
+                                    //goto done_char_scan;
+                                }
+                            }
+                        }
+//done_char_scan:
+                        m_teletextIsBlank = isBlank;
+                        caption = caption.simplified();
+                        LOG(VB_COMMFLAG, LOG_DEBUG, QString("new teletext subtitle string '%1' %2").arg(caption).arg(isBlank));
+                    }
+                    else
+                    {
+                        m_teletextIsBlank = true;
+                    }
+
+                }
+                else
+                {
+                    teletextReader->SetShowHeader(true);
+                    teletextReader->SetIsSubtitle(false);
+                    teletextReader->SetHeaderChanged(false);
+                    m_teletextIsBlank = true;
+                }
+            }
+            teletextReader->SetPageChanged(false);
+        }
+        if (!m_teletextIsBlank)
+        {
+            //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext present"));
+            haveNewInformation(frameNumber, true, 0);
+        }
+    }
+    CC608Reader * cc608Reader = player->GetCC608Reader();
+    if (cc608Reader)
+    {
+        bool changed = false;
+        CC608Buffer* textlist = cc608Reader->GetOutputText(changed);
+        if (changed && textlist)
+        {
+            textlist->lock.lock();
+            bool isBlank = true;
+            QString caption;
+            if (!textlist->buffers.empty())
+            {
+                for(vector<CC608Text*>::const_iterator it = textlist->buffers.begin();
+                        it != textlist->buffers.end();
+                        ++it)
+                {
+                    if (!(*it)->text.isEmpty())
+                    {
+                        isBlank = false;
+                        caption += " " + (*it)->text;
+                    }
+                }
+            }
+            textlist->lock.unlock();
+            m_cc608IsBlank = isBlank;
+            caption = caption.simplified();
+            LOG(VB_COMMFLAG, LOG_DEBUG, QString("new cc608 subtitle string '%1' %2").arg(caption).arg(isBlank));
+        }
+        if (!m_cc608IsBlank)
+        {
+            //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle cc608 present"));
+            haveNewInformation(frameNumber, true, 0);
+        }
+    }
+    CC708Reader * cc708Reader = player->GetCC708Reader();
+    if (cc708Reader)
+    {
+        int cc708ChangedMask = 0;
+        bool isBlank = true;
+        QString caption;
+        CC708Service *cc708service = cc708Reader->GetCurrentService();
+        for (uint i = 0; i < 8; i++)
+        {
+            CC708Window &win = cc708service->windows[i];
+            if (win.exists && win.visible && !win.changed)
+                continue;
+            if (!win.exists || !win.visible)
+                continue;
+
+            QMutexLocker locker(&win.lock);
+            cc708ChangedMask |= 1<<i;
+            vector<CC708String*> list = win.GetStrings();
+            if (!list.empty())
+            {
+                for(vector<CC708String*>::const_iterator it = list.begin();
+                        it != list.end();
+                        ++it)
+                {
+                    if (!(*it)->str.isEmpty())
+                    {
+                        isBlank = false;
+                        caption += " " + (*it)->str;
+                    }
+                }
+            }
+            for (uint j = 0; j < list.size(); j++)
+                delete list[j];
+            win.changed = false;
+        }
+        if (cc708ChangedMask != 0)
+        {
+            m_cc708IsBlank = isBlank;
+            caption = caption.simplified();
+            LOG(VB_COMMFLAG, LOG_DEBUG, QString("new cc708 subtitle string '%1' %2").arg(caption).arg(isBlank).arg(cc708ChangedMask,2,16,QChar('0')));
+        }
+        if (!m_cc708IsBlank)
+        {
+            //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle cc708 present"));
+            haveNewInformation(frameNumber, true, 0);
+        }
+    }
+
+    if (!subtitleTimingList.empty())
+    {
+        SubtitleTiming& currentSub = subtitleTimingList.front();
+        if (currentSub.start <= frameNumber && frameNumber <= currentSub.end)
+        {
+            currentSub.start = frameNumber + 1;
+            haveNewInformation(frameNumber, true, 0);
+        }
+        if (frameNumber >= currentSub.end)
+        {
+            subtitleTimingList.pop_front();
+        }
+    }
+#endif
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetector.h b/mythtv/programs/mythcommflag/SubtitleChangeDetector.h
new file mode 100644
index 0000000..89c69c3
--- /dev/null
+++ b/mythtv/programs/mythcommflag/SubtitleChangeDetector.h
@@ -0,0 +1,39 @@
+/*
+ * SubtitleChangeDetector
+ *
+ * Detect subtitle changes
+ */
+
+#ifndef __SUBTITLECHANGEDETECTOR_H__
+#define __SUBTITLECHANGEDETECTOR_H__
+
+#include "SubtitleChangeDetectorBase.h"
+#include "subtitlereader.h"
+
+class SubtitleChangeDetector : public SubtitleChangeDetectorBase
+{
+public:
+    SubtitleChangeDetector(MythPlayer* player);
+    virtual ~SubtitleChangeDetector() {}
+
+    void processFrame(MythPlayer* player, VideoFrame * frame);
+
+protected:
+    void Add(int start, int end);
+
+    struct SubtitleTiming 
+    {
+        int start;
+        int end;
+        SubtitleTiming(int start, int end);
+    };
+    MythDeque<SubtitleTiming> subtitleTimingList;
+
+    bool m_teletextIsBlank;
+    bool m_cc608IsBlank;
+    bool m_cc708IsBlank;
+};
+
+#endif  /* !__SUBTITLECHANGEDETECTOR_H__ */
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.cpp b/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.cpp
new file mode 100644
index 0000000..f61e131
--- /dev/null
+++ b/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.cpp
@@ -0,0 +1,8 @@
+
+#include "SubtitleChangeDetectorBase.h"
+
+SubtitleChangeDetectorBase::SubtitleChangeDetectorBase()
+{
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.h b/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.h
new file mode 100644
index 0000000..70861f3
--- /dev/null
+++ b/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.h
@@ -0,0 +1,29 @@
+#ifndef _SUBTITLECHANGEDETECTORBASE_H_
+#define _SUBTITLECHANGEDETECTORBASE_H_
+
+#include <QObject>
+
+#include "frame.h"
+class MythPlayer;
+
+class SubtitleChangeDetectorBase : public QObject
+{
+    Q_OBJECT
+
+  public:
+    SubtitleChangeDetectorBase();
+
+    virtual void processFrame(MythPlayer* player, VideoFrame * frame) = 0;
+
+  signals:
+    void haveNewInformation(unsigned int framenum, bool subtitleState,
+                            float debugValue = 0.0);
+
+  protected:
+    virtual ~SubtitleChangeDetectorBase() {}
+
+};
+
+#endif
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythcommflag/main.cpp b/mythtv/programs/mythcommflag/main.cpp
index bbba950..66a7f54 100644
--- a/mythtv/programs/mythcommflag/main.cpp
+++ b/mythtv/programs/mythcommflag/main.cpp
@@ -44,6 +44,7 @@ using namespace std;
 #include "CommDetectorFactory.h"
 #include "SlotRelayer.h"
 #include "CustomEventRelayer.h"
+//#include "AudioBuffer.h"
 
 #define LOC      QString("MythCommFlag: ")
 #define LOC_WARN QString("MythCommFlag, Warning: ")
@@ -107,12 +108,21 @@ static QMap<QString,SkipTypes> *init_skip_types(void)
     (*tmp)["blankscene"]  = COMM_DETECT_BLANK_SCENE;
     (*tmp)["blank_scene"] = COMM_DETECT_BLANK_SCENE;
     (*tmp)["logo"]        = COMM_DETECT_LOGO;
+    (*tmp)["audio"]       = (SkipTypes)COMM_DETECT_AUDIO;
+    (*tmp)["sub"]         = (SkipTypes)COMM_DETECT_SUBTITLES;
     (*tmp)["all"]         = COMM_DETECT_ALL;
     (*tmp)["d2"]          = COMM_DETECT_2;
     (*tmp)["d2_logo"]     = COMM_DETECT_2_LOGO;
     (*tmp)["d2_blank"]    = COMM_DETECT_2_BLANK;
     (*tmp)["d2_scene"]    = COMM_DETECT_2_SCENE;
     (*tmp)["d2_all"]      = COMM_DETECT_2_ALL;
+    (*tmp)["ng"]          = (SkipTypes)(COMM_DETECT_NG);
+    (*tmp)["ng_logo"]     = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_LOGO);
+    (*tmp)["ng_blank"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_BLANK);
+    (*tmp)["ng_scene"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_SCENE);
+    (*tmp)["ng_audio"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_AUDIO);
+    (*tmp)["ng_all"]      = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_ALL | COMM_DETECT_AUDIO | COMM_DETECT_SUBTITLES);
+    (*tmp)["ng_old"]      = (SkipTypes)(COMM_DETECT_NG_OLD);
     return tmp;
 }
 
diff --git a/mythtv/programs/mythcommflag/mythcommflag.pro b/mythtv/programs/mythcommflag/mythcommflag.pro
index b7242cf..ec6abfb 100644
--- a/mythtv/programs/mythcommflag/mythcommflag.pro
+++ b/mythtv/programs/mythcommflag/mythcommflag.pro
@@ -31,6 +31,13 @@ HEADERS += LogoDetectorBase.h SceneChangeDetectorBase.h
 HEADERS += SlotRelayer.h CustomEventRelayer.h
 HEADERS += commandlineparser.h
 
+HEADERS += NextgenCommDetector.h
+HEADERS += NextgenLogoDetector.h
+HEADERS += NextgenSceneChangeDetector.h
+HEADERS += AudioChangeDetectorBase.h AudioChangeDetector.h
+HEADERS += AudioBuffer.h
+HEADERS += SubtitleChangeDetectorBase.h SubtitleChangeDetector.h
+
 SOURCES += CommDetectorFactory.cpp CommDetectorBase.cpp
 SOURCES += ClassicLogoDetector.cpp
 SOURCES += ClassicSceneChangeDetector.cpp
@@ -47,6 +54,18 @@ SOURCES += HistogramAnalyzer.cpp
 SOURCES += BlankFrameDetector.cpp
 SOURCES += SceneChangeDetector.cpp
 SOURCES += PrePostRollFlagger.cpp
+SOURCES += AudioChangeDetectorBase.cpp AudioChangeDetector.cpp
+SOURCES += AudioBuffer.cpp
+SOURCES += SubtitleChangeDetectorBase.cpp SubtitleChangeDetector.cpp
+SOURCES += NextgenLogoDetector.cpp
+SOURCES += NextgenSceneChangeDetector.cpp
+SOURCES += NextgenCommDetector.cpp
+
+SOURCES += ../../libs/libmythtv/subtitlereader.cpp
+SOURCES += ../../libs/libmythtv/textsubtitleparser.cpp
+SOURCES += ../../libs/libmythtv/xine_demux_sputext.cpp
+SOURCES += ../../libs/libmythtv/teletextreader.cpp
+SOURCES += ../../libs/libmythtv/vbilut.cpp
 
 SOURCES += main.cpp commandlineparser.cpp
 
