Ticket #10793: 20141111-29-comm-detector-nextgen.patch

File 20141111-29-comm-detector-nextgen.patch, 205.9 KB (added by Mark Spieth, 5 years ago)
  • new file mythtv/programs/mythcommflag/AudioBuffer.cpp

    nextgen comm detector with audio and subtitle data fusion
    
    From: Mark Spieth <mspieth@digivation.com.au>
    
    also with updates from faginbagin.
    uses existing logo detector by default.
    ---
     mythtv/programs/mythcommflag/AudioBuffer.cpp       |  443 +++
     mythtv/programs/mythcommflag/AudioBuffer.h         |  133 +
     .../programs/mythcommflag/AudioChangeDetector.cpp  |   13 
     mythtv/programs/mythcommflag/AudioChangeDetector.h |   24 
     .../mythcommflag/AudioChangeDetectorBase.cpp       |    8 
     .../mythcommflag/AudioChangeDetectorBase.h         |   27 
     mythtv/programs/mythcommflag/CommDetectorBase.h    |    1 
     .../programs/mythcommflag/CommDetectorFactory.cpp  |    7 
     .../programs/mythcommflag/NextgenCommDetector.cpp  | 3223 ++++++++++++++++++++
     mythtv/programs/mythcommflag/NextgenCommDetector.h |  252 ++
     .../programs/mythcommflag/NextgenLogoDetector.cpp  |  603 ++++
     mythtv/programs/mythcommflag/NextgenLogoDetector.h |   66 
     .../programs/mythcommflag/NextgenLogoDetector2.cpp |  790 +++++
     .../programs/mythcommflag/NextgenLogoDetector2.h   |  103 +
     .../mythcommflag/NextgenSceneChangeDetector.cpp    |   51 
     .../mythcommflag/NextgenSceneChangeDetector.h      |   34 
     .../mythcommflag/SubtitleChangeDetector.cpp        |  285 ++
     .../programs/mythcommflag/SubtitleChangeDetector.h |   39 
     .../mythcommflag/SubtitleChangeDetectorBase.cpp    |    8 
     .../mythcommflag/SubtitleChangeDetectorBase.h      |   29 
     mythtv/programs/mythcommflag/commandlineparser.cpp |    2 
     mythtv/programs/mythcommflag/main.cpp              |   20 
     mythtv/programs/mythcommflag/mythcommflag.pro      |   21 
     23 files changed, 6181 insertions(+), 1 deletion(-)
     create mode 100644 mythtv/programs/mythcommflag/AudioBuffer.cpp
     create mode 100644 mythtv/programs/mythcommflag/AudioBuffer.h
     create mode 100644 mythtv/programs/mythcommflag/AudioChangeDetector.cpp
     create mode 100644 mythtv/programs/mythcommflag/AudioChangeDetector.h
     create mode 100644 mythtv/programs/mythcommflag/AudioChangeDetectorBase.cpp
     create mode 100644 mythtv/programs/mythcommflag/AudioChangeDetectorBase.h
     create mode 100644 mythtv/programs/mythcommflag/NextgenCommDetector.cpp
     create mode 100644 mythtv/programs/mythcommflag/NextgenCommDetector.h
     create mode 100644 mythtv/programs/mythcommflag/NextgenLogoDetector.cpp
     create mode 100644 mythtv/programs/mythcommflag/NextgenLogoDetector.h
     create mode 100644 mythtv/programs/mythcommflag/NextgenLogoDetector2.cpp
     create mode 100644 mythtv/programs/mythcommflag/NextgenLogoDetector2.h
     create mode 100644 mythtv/programs/mythcommflag/NextgenSceneChangeDetector.cpp
     create mode 100644 mythtv/programs/mythcommflag/NextgenSceneChangeDetector.h
     create mode 100644 mythtv/programs/mythcommflag/SubtitleChangeDetector.cpp
     create mode 100644 mythtv/programs/mythcommflag/SubtitleChangeDetector.h
     create mode 100644 mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.cpp
     create mode 100644 mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.h
    
    diff --git a/mythtv/programs/mythcommflag/AudioBuffer.cpp b/mythtv/programs/mythcommflag/AudioBuffer.cpp
    new file mode 100644
    index 0000000..2f1252d
    - +  
     1
     2#include "AudioBuffer.h"
     3
     4#define USE_PEAK 0
     5
     6AudioSample::AudioSample()
     7{
     8    time = 0;
     9    duration = 0;
     10    power = 0;
     11    channels = 0;
     12    changed = false;
     13}
     14
     15QString AudioSample::toString() const
     16{
     17    return QString("%1 %2 %3 %4 %5")
     18        .arg(time)
     19        .arg(duration)
     20        .arg(power)
     21        .arg(channels)
     22        .arg(changed);
     23}
     24
     25
     26AudioBuffer::AudioBuffer(const AudioSettings &settings) :
     27    configured_channels(CHANNELS_MAX),
     28    last_audiotime(0),
     29    output_settings(0),
     30    changed(false),
     31    info_valid(false),
     32    m_lock(),
     33    verboseDebugging(false),
     34    enabled(false)
     35{
     36    if (getenv("DEBUGCOMMFLAG"))
     37        verboseDebugging = true;
     38
     39    Reset();
     40    //AudioSettings orig_settings("", "", AudioFormat::,);
     41    //Reconfigure(audio_bits, audio_channels, 0);
     42    Reconfigure(settings);
     43}
     44
     45AudioBuffer::~AudioBuffer()
     46{
     47    //delete [] audiobuffer;
     48    if (output_settings)
     49    {
     50        delete output_settings;
     51        output_settings = NULL;
     52    }
     53}
     54
     55AudioOutputSettings* AudioBuffer::GetOutputSettings(bool /*digital*/)
     56{
     57    AudioOutputSettings *settings = new AudioOutputSettings();
     58
     59    for (uint channels = CHANNELS_MIN; channels <= CHANNELS_MAX; channels++)
     60    {
     61        settings->AddSupportedChannels(channels);
     62    }
     63    settings->AddSupportedFormat(FORMAT_S16);
     64    settings->AddSupportedFormat(FORMAT_FLT);
     65
     66    LOG(VB_COMMFLAG, LOG_INFO,
     67        QString("GetOutputSettings"));
     68    return settings;
     69}
     70
     71AudioOutputSettings* AudioBuffer::GetOutputSettingsUsers(bool digital)
     72{
     73    if (!output_settings)
     74        output_settings = GetOutputSettings(digital);
     75    return output_settings;
     76}
     77
     78AudioOutputSettings* AudioBuffer::GetOutputSettingsCleaned(bool digital)
     79{
     80    if (!output_settings)
     81        output_settings = GetOutputSettings(digital);
     82    return output_settings;
     83}
     84
     85void AudioBuffer::Enable()
     86{
     87    enabled = true;
     88}
     89
     90// reconfigure sound out for new params
     91void AudioBuffer::Reconfigure(const AudioSettings &orig_settings)
     92{
     93    ClearError();
     94    changed = (orig_settings.format != settings.format)
     95        | (orig_settings.channels != settings.channels)
     96        | (orig_settings.samplerate != settings.samplerate);
     97    settings = orig_settings;
     98
     99    source_bytes_per_frame = source_channels * AudioOutputSettings::SampleSize(settings.format);
     100    LOG(VB_COMMFLAG, LOG_INFO,
     101        QString("Audio Reconfigure changed %1, Settings fmt=%2 ch=%3 sr=%4.")
     102                .arg(changed)
     103                .arg(settings.format)
     104                .arg(settings.channels)
     105                .arg(settings.samplerate));
     106
     107    QMutexLocker locker(&m_lock);
     108    audioSamples.clear();
     109    last_audiotime = 0;
     110}
     111
     112bool AudioBuffer::CanProcess(AudioFormat fmt)
     113{
     114    return fmt == FORMAT_S16 || fmt == FORMAT_FLT;
     115}
     116
     117uint32_t AudioBuffer::CanProcess(void)
     118{
     119    return (1 << FORMAT_S16) | (1 << FORMAT_FLT);
     120}
     121
     122// dsprate is in 100 * samples/second
     123void AudioBuffer::SetEffDsp(int /* dsprate */)
     124{
     125    //eff_audiorate = (dsprate / 100);
     126}
     127
     128void AudioBuffer::SetBlocking(bool block)
     129{
     130    (void)block;
     131}
     132
     133void AudioBuffer::Reset(void)
     134{
     135    if (!enabled)
     136        return;
     137    if (verboseDebugging)
     138    {
     139        LOG(VB_COMMFLAG, LOG_DEBUG,
     140            QString("audio reset"));
     141    }
     142    QMutexLocker locker(&m_lock);
     143    audioSamples.clear();
     144    last_audiotime = 0;
     145}
     146
     147bool AudioBuffer::AddFrames(void *in_buffer, int in_frames,
     148                                        int64_t timecode)
     149{
     150    if (!enabled)
     151    {
     152        last_audiotime = timecode;
     153        return true;
     154    }
     155    return AddData(in_buffer, in_frames * source_bytes_per_frame, timecode,
     156                                   in_frames);
     157}
     158
     159bool AudioBuffer::AddData(void *in_buffer, int in_len,
     160                     int64_t timecode, int in_frames)
     161{
     162    if (!enabled)
     163    {
     164        last_audiotime = timecode;
     165        return true;
     166    }
     167    int i;
     168    int f;
     169    PAudioSample aSample(new AudioSample);
     170    aSample->power = 0;
     171    aSample->time = timecode;    // in ms
     172    aSample->duration = (in_frames * 1000) / settings.samplerate; // in ms
     173    aSample->channels = settings.channels;
     174    if (changed)
     175    {
     176        aSample->changed = changed;
     177        changed = false;
     178    }
     179    if (timecode < last_audiotime)
     180    {
     181        LOG(VB_COMMFLAG, LOG_DEBUG,
     182            QString("audio time reset %1").arg(timecode));
     183        QMutexLocker locker(&m_lock);
     184        audioSamples.clear();
     185        last_audiotime = 0;
     186    }
     187    if (last_audiotime != 0)
     188    {
     189        int64_t nextAudiotime = last_audiotime + aSample->duration;
     190        while (aSample->time < nextAudiotime)
     191        {
     192            PAudioSample nullSample(new AudioSample);
     193            nullSample->power = -1;
     194            nullSample->time = nextAudiotime;
     195            nullSample->duration = aSample->duration;
     196            {
     197                QMutexLocker locker(&m_lock);
     198                audioSamples.push_back(nullSample);
     199            }
     200#ifdef AUDIODEBUGGING
     201            LOG(VB_COMMFLAG, LOG_DEBUG,
     202                QString("Audio AddData invalid time %1").arg(nullSample->time));
     203#endif
     204        }
     205    }
     206    switch (settings.format)
     207    {
     208        case FORMAT_S16:
     209            {
     210                int64_t power = 0;
     211                int16_t *p = (int16_t*)in_buffer;
     212                for(f=in_frames;f>0;--f)
     213                {
     214#if USE_PEAK
     215                    for(i=settings.channels; i>0; --i)
     216                    {
     217                        int32_t v = *p++;
     218                        if (v > power)
     219                        {
     220                            power = v;
     221                        }
     222                        else if (-v > power)
     223                        {
     224                            power = -v;
     225                        }
     226                    }
     227#else
     228                    // power as mono
     229                    int64_t sum = 0;
     230                    for(i=settings.channels; i>0; --i)
     231                    {
     232                        int32_t v = *p++;
     233                        sum += v;
     234                        //p++;
     235                    }
     236                    power += sum * sum;
     237#endif
     238                }
     239#if USE_PEAK
     240                aSample->power += ((double)power)/(INT16_MAX);
     241#else
     242                aSample->power += sqrt(((double)power)/(INT16_MAX * INT16_MAX)/in_frames);
     243#endif
     244                info_valid = true;
     245                QMutexLocker locker(&m_lock);
     246                audioSamples.push_back(aSample);
     247            } break;
     248        case FORMAT_FLT:
     249            {
     250                double power = 0;
     251                float *p = (float*)in_buffer;
     252#if USE_PEAK
     253                for(f=in_frames;f>0;--f)
     254                {
     255                    float v = *p++;
     256                    if (v > power)
     257                    {
     258                        power = v;
     259                    }
     260                    else if (-v > power)
     261                    {
     262                        power = -v;
     263                    }
     264                }
     265#else
     266                double ss[8];
     267                double s[8];
     268                for(i=0; i < settings.channels; i++)
     269                {
     270                    ss[i] = 0;
     271                    s[i] = 0;
     272                }
     273                for(f=in_frames;f>0;--f)
     274                {
     275                    // discrete power with dc cancelled using stddev calc
     276                    for(i=0; i < settings.channels; i++)
     277                    {
     278                        float v = *p++;
     279                        s[i] += v;
     280                        ss[i] += v * v;
     281                    }
     282                }
     283                for(i=0; i < settings.channels; i++)
     284                {
     285                    power += (ss[i] - (s[i] * s[i] / in_frames)) / in_frames;
     286                }
     287                power += sqrt(power);
     288#endif
     289                aSample->power += power;
     290                info_valid = true;
     291                QMutexLocker locker(&m_lock);
     292                audioSamples.push_back(aSample);
     293            } break;
     294        default:
     295            break;
     296    }
     297#ifdef AUDIODEBUGGING
     298    if (info_valid)
     299    {
     300        LOG(VB_COMMFLAG, LOG_DEBUG,
     301            QString("Audio AddData time %1 frames %2 power %3 changed %4").arg(timecode).arg(in_frames).arg(aSample->power).arg(aSample->changed));
     302    }
     303    else
     304    {
     305        LOG(VB_COMMFLAG, LOG_DEBUG,
     306            QString("Audio AddData time %1 frames %2").arg(timecode).arg(in_frames));
     307    }
     308#endif
     309    last_audiotime = timecode;
     310    return true;
     311}
     312
     313const PAudioSample AudioBuffer::GetSample(int64_t time)
     314{
     315    if (!audioSamples.empty())
     316    {
     317        QMutexLocker locker(&m_lock);
     318#ifdef USE_AUDIO_LIST
     319        PAudioSample s;
     320        do
     321        {
     322            if (audioSamples.empty())
     323                break;
     324            s = audioSamples.front();
     325            if (!s)
     326                break;
     327            int64_t dtime = s->time - time;
     328            if (dtime < 0)
     329            {
     330                // time already past
     331#ifdef AUDIODEBUGGING2
     332                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()));
     333#endif
     334                audioSamples.pop_front();
     335                continue;
     336            }
     337            if (dtime < s->duration)
     338            {
     339#ifdef AUDIODEBUGGING2
     340                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()));
     341#endif
     342                //audioSamples.pop_front();
     343                return s;
     344            }
     345            // not yet
     346            break;
     347        } while (true);
     348#else
     349        int64_t index = (time - audioSamples[0]->time)/audioSamples[0]->duration;
     350        //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()));
     351        if (index >= 0 && index < audioSamples.size())
     352            return audioSamples[index];
     353#endif
     354    }
     355    return PAudioSample();
     356}
     357
     358void AudioBuffer::SetTimecode(int64_t timecode)
     359{
     360    last_audiotime = timecode;
     361}
     362bool AudioBuffer::GetPause(void)
     363{
     364    return false;
     365}
     366void AudioBuffer::Pause(bool paused)
     367{
     368    (void)paused;
     369}
     370void AudioBuffer::Drain(void)
     371{
     372    // Do nothing
     373    return;
     374}
     375bool AudioBuffer::IsPaused() const { return false; }
     376void AudioBuffer::PauseUntilBuffered() { }
     377bool AudioBuffer::IsUpmixing() { return false; }
     378bool AudioBuffer::ToggleUpmix() { return false; }
     379bool AudioBuffer::CanUpmix() { return false; }
     380
     381
     382int64_t AudioBuffer::GetAudiotime(void)
     383{
     384    return last_audiotime;
     385}
     386
     387int AudioBuffer::GetVolumeChannel(int) const
     388{
     389    // Do nothing
     390    return 100;
     391}
     392void AudioBuffer::SetVolumeChannel(int, int)
     393{
     394    // Do nothing
     395}
     396void AudioBuffer::SetVolumeAll(int)
     397{
     398    // Do nothing
     399}
     400int AudioBuffer::GetSWVolume(void)
     401{
     402    return 100;
     403}
     404void AudioBuffer::SetSWVolume(int, bool) {}
     405
     406
     407uint AudioBuffer::GetCurrentVolume(void) const
     408{
     409    // Do nothing
     410    return 100;
     411}
     412void AudioBuffer::SetCurrentVolume(int)
     413{
     414    // Do nothing
     415}
     416void AudioBuffer::AdjustCurrentVolume(int)
     417{
     418    // Do nothing
     419}
     420void AudioBuffer::SetMute(bool)
     421{
     422    // Do nothing
     423}
     424void AudioBuffer::ToggleMute(void)
     425{
     426    // Do nothing
     427}
     428MuteState AudioBuffer::GetMute(void)
     429{
     430    // Do nothing
     431    return kMuteOff;
     432}
     433MuteState AudioBuffer::IterateMutedChannels(void)
     434{
     435    // Do nothing
     436    return kMuteOff;
     437}
     438
     439//  These are pure virtual in AudioOutput, but we don't need them here
     440void AudioBuffer::bufferOutputData(bool){ return; }
     441int AudioBuffer::readOutputData(unsigned char*, int ){ return 0; }
     442
     443/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/AudioBuffer.h

    diff --git a/mythtv/programs/mythcommflag/AudioBuffer.h b/mythtv/programs/mythcommflag/AudioBuffer.h
    new file mode 100644
    index 0000000..36ab6e0
    - +  
     1#ifndef _AUDIOBUFFER_H_
     2#define _AUDIOBUFFER_H_
     3
     4#define USE_AUDIO_LIST
     5
     6// POSIX headers
     7#include <stdint.h>
     8
     9// Qt headers
     10#include <QObject>
     11#include <QSharedPointer>
     12
     13// MythTV headers
     14#include "mythcontext.h"
     15#include <deque>
     16
     17#include "audiooutput.h"
     18
     19//#define AUDIODEBUGGING
     20//#define AUDIODEBUGGING2
     21//#define AUDIODEBUGGING3
     22
     23class AudioSample : public QSharedData
     24{
     25public:
     26    int64_t time;
     27    int64_t duration;
     28    double  power;
     29    int     channels;
     30    bool    changed;
     31    AudioSample();
     32    QString toString() const;
     33};
     34
     35typedef QExplicitlySharedDataPointer< AudioSample > PAudioSample;
     36
     37
     38#define CHANNELS_MIN 1
     39#define CHANNELS_MAX 8
     40class AudioBuffer : public AudioOutput
     41{
     42 public:
     43    AudioBuffer(const AudioSettings &settings);
     44   ~AudioBuffer();
     45
     46    AudioOutputSettings* GetOutputSettings(bool /*digital*/);
     47    AudioOutputSettings* GetOutputSettingsUsers(bool digital);
     48    AudioOutputSettings* GetOutputSettingsCleaned(bool digital);
     49
     50    // reconfigure sound out for new params
     51    virtual void Reconfigure(const AudioSettings &orig_settings);
     52
     53    void Enable();
     54
     55    virtual void SetEffDsp(int /* dsprate */);
     56    virtual void SetBlocking(bool block);
     57    virtual void Reset(void);
     58    virtual bool AddFrames(void *in_buffer, int in_frames, int64_t timecode);
     59    virtual bool AddData(void *in_buffer, int in_len,
     60                         int64_t timecode, int in_frames);
     61    const PAudioSample GetSample(int64_t time);
     62    virtual void SetTimecode(int64_t timecode);
     63    virtual bool GetPause(void);
     64    virtual void Pause(bool paused);
     65    virtual void Drain(void);
     66    virtual bool IsPaused() const;
     67    virtual void PauseUntilBuffered();
     68    virtual bool IsUpmixing();
     69    virtual bool ToggleUpmix();
     70    virtual bool CanUpmix();
     71    virtual bool CanProcess(AudioFormat fmt);
     72    virtual uint32_t CanProcess(void);
     73
     74    virtual int64_t GetAudiotime(void);
     75    virtual int GetVolumeChannel(int) const;
     76    virtual void SetVolumeChannel(int, int);
     77    virtual void SetVolumeAll(int);
     78    virtual int GetSWVolume(void);
     79    virtual void SetSWVolume(int, bool);
     80    virtual uint GetCurrentVolume(void) const;
     81    virtual void SetCurrentVolume(int);
     82    virtual void AdjustCurrentVolume(int);
     83    virtual void SetMute(bool);
     84    virtual void ToggleMute(void);
     85    virtual MuteState GetMute(void);
     86    virtual MuteState IterateMutedChannels(void);
     87
     88    //  These are pure virtual in AudioOutput, but we don't need them here
     89    virtual void bufferOutputData(bool);
     90    virtual int readOutputData(unsigned char*, int );
     91
     92    // Basic details about the audio stream
     93    int channels;
     94    int codec;
     95    int bytes_per_frame;
     96    int output_bytes_per_frame;
     97    AudioFormat format;
     98    AudioFormat output_format;
     99    int samplerate;
     100    int source_channels;
     101    int source_samplerate;
     102    int source_bytes_per_frame;
     103
     104    int bufsize;
     105    //unsigned char *audiobuffer;
     106    //int audiobuffer_len;
     107    //int channels, bits, bytes_per_sample, eff_audiorate;
     108    int configured_channels;
     109    AudioSettings settings;
     110    int64_t last_audiotime;
     111
     112    AudioOutputSettings* output_settings;
     113
     114    bool changed;
     115    bool info_valid;
     116
     117    int64_t first_audiotime;
     118
     119#ifdef USE_AUDIO_LIST
     120    std::deque< PAudioSample > audioSamples;
     121#else
     122    std::vector< PAudioSample > audioSamples;
     123#endif
     124
     125 private:
     126    mutable QMutex m_lock;
     127    bool verboseDebugging;
     128    bool enabled;
     129};
     130
     131#endif
     132
     133/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/AudioChangeDetector.cpp

    diff --git a/mythtv/programs/mythcommflag/AudioChangeDetector.cpp b/mythtv/programs/mythcommflag/AudioChangeDetector.cpp
    new file mode 100644
    index 0000000..63c8901
    - +  
     1using namespace std;
     2
     3#include "AudioChangeDetector.h"
     4
     5AudioChangeDetector::AudioChangeDetector()
     6{
     7}
     8
     9void AudioChangeDetector::processFrame(unsigned char *frame)
     10{
     11}
     12
     13/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/AudioChangeDetector.h

    diff --git a/mythtv/programs/mythcommflag/AudioChangeDetector.h b/mythtv/programs/mythcommflag/AudioChangeDetector.h
    new file mode 100644
    index 0000000..4d24215
    - +  
     1/*
     2 * AudioChangeDetector
     3 *
     4 * Detect audi changes
     5 */
     6
     7#ifndef __AUDIOCHANGEDETECTOR_H__
     8#define __AUDIOCHANGEDETECTOR_H__
     9
     10#include "AudioChangeDetectorBase.h"
     11
     12class AudioChangeDetector : public AudioChangeDetectorBase
     13{
     14public:
     15    AudioChangeDetector();
     16    virtual ~AudioChangeDetector() {}
     17
     18    void processFrame(unsigned char *frame);
     19
     20};
     21
     22#endif  /* !__AUDIOCHANGEDETECTOR_H__ */
     23
     24/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/AudioChangeDetectorBase.cpp

    diff --git a/mythtv/programs/mythcommflag/AudioChangeDetectorBase.cpp b/mythtv/programs/mythcommflag/AudioChangeDetectorBase.cpp
    new file mode 100644
    index 0000000..a4dd42d
    - +  
     1
     2#include "AudioChangeDetectorBase.h"
     3
     4AudioChangeDetectorBase::AudioChangeDetectorBase()
     5{
     6}
     7
     8/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/AudioChangeDetectorBase.h

    diff --git a/mythtv/programs/mythcommflag/AudioChangeDetectorBase.h b/mythtv/programs/mythcommflag/AudioChangeDetectorBase.h
    new file mode 100644
    index 0000000..7966869
    - +  
     1#ifndef _AUDIOCHANGEDETECTORBASE_H_
     2#define _AUDIOCHANGEDETECTORBASE_H_
     3
     4#include <QObject>
     5
     6class AudioChangeDetectorBase : public QObject
     7{
     8    Q_OBJECT
     9
     10  public:
     11    AudioChangeDetectorBase();
     12
     13    virtual void processFrame(unsigned char *frame) = 0;
     14
     15  signals:
     16    void haveNewInformation(unsigned int framenum, bool audiochange,
     17                            float amplitude,
     18                            float debugValue = 0.0);
     19
     20  protected:
     21    virtual ~AudioChangeDetectorBase() {}
     22
     23};
     24
     25#endif
     26
     27/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • mythtv/programs/mythcommflag/CommDetectorBase.h

    diff --git a/mythtv/programs/mythcommflag/CommDetectorBase.h b/mythtv/programs/mythcommflag/CommDetectorBase.h
    index 7faefa1..f257694 100644
    a b using namespace std; 
    1515#define COMM_DETECT_AUDIO 0x08
    1616#define COMM_DETECT_SUBTITLES 0x10
    1717#define COMM_DETECT_NG_OLD 0x80
     18#define COMM_DETECT_LOGO_EXPERIMENTAL 0x100
    1819
    1920typedef enum commMapValues {
    2021    MARK_START   = 0,
  • mythtv/programs/mythcommflag/CommDetectorFactory.cpp

    diff --git a/mythtv/programs/mythcommflag/CommDetectorFactory.cpp b/mythtv/programs/mythcommflag/CommDetectorFactory.cpp
    index 85f8df4..c5f5bfd 100644
    a b  
    11#include "CommDetectorFactory.h"
    22#include "ClassicCommDetector.h"
     3#include "NextgenCommDetector.h"
    34#include "CommDetector2.h"
    45#include "PrePostRollFlagger.h"
    56
    CommDetectorFactory::makeCommDetector( 
    3334            recordingStartedAt, recordingStopsAt, useDB);
    3435    }
    3536
     37    if ((commDetectMethod & COMM_DETECT_NG))
     38    {
     39        return new NextgenCommDetector(commDetectMethod, showProgress, fullSpeed,
     40                player, chanid, startedAt, stopsAt, recordingStartedAt, recordingStopsAt);
     41    }
     42
    3643    return new ClassicCommDetector(commDetectMethod, showProgress, fullSpeed,
    3744            player, startedAt, stopsAt, recordingStartedAt, recordingStopsAt);
    3845}
  • new file mythtv/programs/mythcommflag/NextgenCommDetector.cpp

    diff --git a/mythtv/programs/mythcommflag/NextgenCommDetector.cpp b/mythtv/programs/mythcommflag/NextgenCommDetector.cpp
    new file mode 100644
    index 0000000..806eabd
    - +  
     1// POSIX headers
     2#include <unistd.h>
     3#include <sys/time.h> // for gettimeofday
     4
     5// ANSI C headers
     6#include <cmath>
     7
     8// C++ headers
     9#include <algorithm> // for min/max
     10#include <iostream> // for cerr
     11using namespace std;
     12
     13// Qt headers
     14#include <QString>
     15
     16// MythTV headers
     17#include "mythcontext.h"
     18#include "mythmiscutil.h"
     19#include "programinfo.h"
     20#include "mythplayer.h"
     21
     22// Commercial Flagging headers
     23#include "NextgenCommDetector.h"
     24#include "AudioChangeDetector.h"
     25#include "SubtitleChangeDetector.h"
     26#include "NextgenLogoDetector.h"
     27#include "NextgenLogoDetector2.h"
     28#include "NextgenSceneChangeDetector.h"
     29#include "AudioBuffer.h"
     30
     31typedef enum frameAspects {
     32    COMM_ASPECT_NORMAL = 0,
     33    COMM_ASPECT_WIDE
     34} FrameAspects;
     35
     36typedef enum frameFormats {
     37    COMM_FORMAT_NORMAL    = 0,
     38    COMM_FORMAT_LETTERBOX = 1,
     39    COMM_FORMAT_PILLARBOX = 2,
     40    COMM_FORMAT_MAX       = 4,
     41} FrameFormats;
     42
     43static QString toStringFrameMaskValues(int mask, bool verbose)
     44{
     45    QString msg;
     46
     47    if (verbose)
     48    {
     49        if (COMM_FRAME_SKIPPED & mask)
     50            msg += "skipped,";
     51        if (COMM_FRAME_BLANK & mask)
     52            msg += "blank,";
     53        if (COMM_FRAME_SCENE_CHANGE & mask)
     54            msg += "scene,";
     55        if (COMM_FRAME_LOGO_PRESENT & mask)
     56            msg += "logo,";
     57        if (COMM_FRAME_ASPECT_CHANGE & mask)
     58            msg += "aspect,";
     59        if (COMM_FRAME_RATING_SYMBOL & mask)
     60            msg += "rating,";
     61        if (COMM_FRAME_NO_AUDIO & mask)
     62            msg += "silent,";
     63        if (COMM_FRAME_AUDIO_CHANGE & mask)
     64            msg += "audchg,";
     65        if (COMM_FRAME_INVALID_AUDIO & mask)
     66            msg += "noaud,";
     67        if (COMM_FRAME_SUBTITLE_PRESENT & mask)
     68            msg += "sub,";
     69
     70        if (msg.length())
     71            msg = msg.left(msg.length() - 1);
     72        else
     73            msg = "noflags";
     74    }
     75    else
     76    {
     77        msg += (COMM_FRAME_SKIPPED       & mask) ? "s" : " ";
     78        msg += (COMM_FRAME_BLANK         & mask) ? "B" : " ";
     79        msg += (COMM_FRAME_SCENE_CHANGE  & mask) ? "S" : " ";
     80        msg += (COMM_FRAME_LOGO_PRESENT  & mask) ? "L" : " ";
     81        msg += (COMM_FRAME_ASPECT_CHANGE & mask) ? "A" : " ";
     82        msg += (COMM_FRAME_RATING_SYMBOL & mask) ? "R" : " ";
     83        msg += (COMM_FRAME_NO_AUDIO      & mask) ? "Q" : " ";
     84        msg += (COMM_FRAME_AUDIO_CHANGE  & mask) ? "C" : " ";
     85        msg += (COMM_FRAME_INVALID_AUDIO & mask) ? "I" : " ";
     86        msg += (COMM_FRAME_SUBTITLE_PRESENT & mask) ? "c" : " ";
     87    }
     88
     89    return msg;
     90}
     91
     92static QString toStringFrameAspects(int aspect, bool verbose)
     93{
     94    if (verbose)
     95        return (COMM_ASPECT_NORMAL == aspect) ? "normal" : " wide ";
     96    else
     97        return (COMM_ASPECT_NORMAL == aspect) ? "n" : "w";
     98}
     99
     100static QString toStringFrameFormats(int format, bool verbose)
     101{
     102    switch (format)
     103    {
     104        case COMM_FORMAT_NORMAL:
     105            return (verbose) ? "normal" : " N ";
     106        case COMM_FORMAT_LETTERBOX:
     107            return (verbose) ? "letter" : " L ";
     108        case COMM_FORMAT_PILLARBOX:
     109            return (verbose) ? "pillar" : " P ";
     110        case COMM_FORMAT_LETTERBOX | COMM_FORMAT_PILLARBOX:
     111            return (verbose) ? "letter,pillar" : "L,P";
     112        case COMM_FORMAT_MAX:
     113            return (verbose) ? " max  " : " M ";
     114    }
     115
     116    return (verbose) ? "unknown" : " U ";
     117}
     118
     119QString NGFrameInfoEntry::GetHeader(void)
     120{
     121    return QString("  frame     min/max/avg scene aspect format  apwr  achan flags");
     122}
     123
     124QString NGFrameInfoEntry::toString(uint64_t frame, bool verbose) const
     125{
     126    return QString(
     127        "%1: %2/%3/%4 %5%  %6 %7 %8 %9 %10")
     128        .arg(frame,10)
     129        .arg(minBrightness,3)
     130        .arg(maxBrightness,3)
     131        .arg(avgBrightness,3)
     132        .arg(sceneChangePercent,3)
     133        .arg(toStringFrameAspects(aspect, verbose))
     134        .arg(toStringFrameFormats(format, verbose))
     135        .arg(audioPower,8)
     136        .arg(audioMode,6)
     137        .arg(toStringFrameMaskValues(flagMask, verbose));
     138}
     139
     140NextgenCommDetector::NextgenCommDetector(SkipType commDetectMethod_in,
     141                                         bool showProgress_in,
     142                                         bool fullSpeed_in,
     143                                         MythPlayer* player_in,
     144                                         int chanid,
     145                                         const QDateTime& startedAt_in,
     146                                         const QDateTime& stopsAt_in,
     147                                         const QDateTime& recordingStartedAt_in,
     148                                         const QDateTime& recordingStopsAt_in) :
     149
     150
     151    commDetectMethod(commDetectMethod_in),
     152    commBreakMapUpdateRequested(false),        sendCommBreakMapUpdates(false),
     153    verboseDebugging(false),
     154    lastFrameNumber(0),                        curFrameNumber(0),
     155    width(0),                                  height(0),
     156    horizSpacing(0),                           vertSpacing(0),
     157    fpm(0.0),                                  blankFramesOnly(false),
     158    blankFrameCount(0),                        currentAspect(0),
     159    silentFrameCount(0),
     160    totalMinBrightness(0),                     detectBlankFrames(false),
     161    detectSceneChanges(false),                 detectStationLogo(false),
     162    detectSilentFrames(false),
     163    subtitleInfoAvailable(false),              subtitleFrameCount(0),
     164    logoInfoAvailable(false),                  logoDetector(0),
     165    frameIsBlank(false),
     166    sceneHasChanged(false),                    stationLogoPresent(false),
     167    lastFrameWasBlank(false),                  lastFrameWasSceneChange(false),
     168    decoderFoundAspectChanges(false),          sceneChangeDetector(0),
     169    audioChangeDetector(0),                   
     170    subtitleChangeDetector(0),
     171    player(player_in),
     172    startedAt(startedAt_in),                   stopsAt(stopsAt_in),
     173    recordingStartedAt(recordingStartedAt_in),
     174    recordingStopsAt(recordingStopsAt_in),     aggressiveDetection(false),
     175    stillRecording(recordingStopsAt > MythDate::current()),
     176    fullSpeed(fullSpeed_in),                   showProgress(showProgress_in),
     177    fps(0.0),                                  framesProcessed(0),
     178    preRoll(0),                                postRoll(0)
     179{
     180    commDetectBorder =
     181        gCoreContext->GetNumSetting("CommDetectBorder", 20);
     182    commDetectBlankFrameMaxDiff =
     183        gCoreContext->GetNumSetting("CommDetectBlankFrameMaxDiff", 25);
     184    commDetectDarkBrightness =
     185        gCoreContext->GetNumSetting("CommDetectDarkBrightness", 80);
     186    commDetectDimBrightness =
     187        gCoreContext->GetNumSetting("CommDetectDimBrightness", 120);
     188    commDetectBoxBrightness =
     189        gCoreContext->GetNumSetting("CommDetectBoxBrightness", 30);
     190    commDetectDimAverage =
     191        gCoreContext->GetNumSetting("CommDetectDimAverage", 35);
     192    commDetectMaxCommBreakLength =
     193        gCoreContext->GetNumSetting("CommDetectMaxCommBreakLength", 395);
     194    commDetectMinCommBreakLength =
     195        gCoreContext->GetNumSetting("CommDetectMinCommBreakLength", 60);
     196    commDetectMinShowLength =
     197        gCoreContext->GetNumSetting("CommDetectMinShowLength", 65);
     198    commDetectMaxCommLength =
     199        gCoreContext->GetNumSetting("CommDetectMaxCommLength", 125);
     200    commDetectLargeSceneChangeThreshold =
     201        gCoreContext->GetNumSetting("CommDetectLargeSceneChangeThreshold", 60);
     202    QStringList commDetectCommLengthsString =
     203        gCoreContext->GetSetting("CommDetectCommLengths", "12,15,20,30,40,45,60").split(',', QString::SkipEmptyParts);
     204    for (QStringList::iterator it = commDetectCommLengthsString.begin(); it != commDetectCommLengthsString.end(); ++it)
     205        commDetectCommLengths.append((*it).toFloat());
     206
     207    commDetectBlankCanHaveLogo =
     208        !!gCoreContext->GetNumSetting("CommDetectBlankCanHaveLogo", 1);
     209
     210    AudioSettings settings("", "");
     211    AudioOutput *audioOutput = new AudioBuffer(settings);
     212    audioBuffer = ((AudioBuffer*)audioOutput);
     213
     214}
     215
     216void NextgenCommDetector::Init()
     217{
     218    QSize video_disp_dim = player->GetVideoSize();
     219    width  = video_disp_dim.width();
     220    height = video_disp_dim.height();
     221    // decoder not set yet so use this one for now
     222    // even though its wrong
     223    fps = player->GetFrameRate();
     224
     225    preRoll  = (long long)(
     226        max(int64_t(0), int64_t(recordingStartedAt.secsTo(startedAt))) * fps);
     227    postRoll = (long long)(
     228        max(int64_t(0), int64_t(stopsAt.secsTo(recordingStopsAt))) * fps);
     229
     230    // CommDetectBorder's default value of 20 predates the change to use
     231    // ffmpeg's lowres decoding capability by 5 years.
     232    // I believe it should be adjusted based on the height of the lowres video
     233    // CommDetectBorder * height / 720 seems to produce reasonable results.
     234    // source height =  480 gives border = 20 *  480 / 4 / 720 = 2
     235    // source height =  720 gives border = 20 *  720 / 4 / 720 = 5
     236    // source height = 1080 gives border = 20 * 1080 / 4 / 720 = 7
     237    commDetectBorder =
     238        gCoreContext->GetNumSetting("CommDetectBorder", 20) * height / 720;
     239
     240#ifdef SHOW_DEBUG_WIN
     241    comm_debug_init(width, height);
     242#endif
     243
     244    currentAspect = COMM_ASPECT_WIDE;
     245
     246    lastFrameNumber = -2;
     247    curFrameNumber = -1;
     248
     249    if (getenv("DEBUGCOMMFLAG"))
     250        verboseDebugging = true;
     251    else
     252        verboseDebugging = false;
     253
     254    LOG(VB_COMMFLAG, LOG_INFO,
     255        QString("Commercial Detection initialized: "
     256                "width = %1, height = %2, fps = %3, method = %4")
     257            .arg(width).arg(height)
     258            .arg(player->GetFrameRate()).arg(commDetectMethod));
     259
     260    if ((width * height) > 1000000)
     261    {
     262        horizSpacing = 10;
     263        vertSpacing = 10;
     264    }
     265    else if ((width * height) > 800000)
     266    {
     267        horizSpacing = 8;
     268        vertSpacing = 8;
     269    }
     270    else if ((width * height) > 400000)
     271    {
     272        horizSpacing = 6;
     273        vertSpacing = 6;
     274    }
     275    else if ((width * height) > 300000)
     276    {
     277        horizSpacing = 6;
     278        vertSpacing = 4;
     279    }
     280    else
     281    {
     282        horizSpacing = 4;
     283        vertSpacing = 4;
     284    }
     285
     286    LOG(VB_COMMFLAG, LOG_INFO,
     287        QString("Using Sample Spacing of %1 horizontal & %2 vertical pixels.")
     288            .arg(horizSpacing).arg(vertSpacing));
     289
     290    framesProcessed = 0;
     291    totalMinBrightness = 0;
     292    blankFrameCount = 0;
     293
     294    aggressiveDetection = true;
     295    currentAspect = COMM_ASPECT_WIDE;
     296    decoderFoundAspectChanges = false;
     297
     298    lastSentCommBreakMap.clear();
     299
     300    // Check if close to 4:3
     301    if (fabs(((width*1.0)/height) - 1.333333) < 0.1)
     302        currentAspect = COMM_ASPECT_NORMAL;
     303
     304    sceneChangeDetector = new NextgenSceneChangeDetector(width, height,
     305        commDetectBorder, horizSpacing, vertSpacing);
     306    connect(
     307         sceneChangeDetector,
     308         SIGNAL(haveNewInformation(unsigned int,bool,float)),
     309         this,
     310         SLOT(sceneChangeDetectorHasNewInformation(unsigned int,bool,float))
     311    );
     312
     313    silentFrameCount = 0;
     314    audioChangeDetector = new AudioChangeDetector();
     315    connect(
     316         audioChangeDetector,
     317         SIGNAL(haveNewInformation(unsigned int,bool,float,float)),
     318         this,
     319         SLOT(audioDetectorHasNewInformation(unsigned int,bool,float,float))
     320    );
     321
     322    subtitleChangeDetector = new SubtitleChangeDetector(player);
     323    connect(
     324         subtitleChangeDetector,
     325         SIGNAL(haveNewInformation(unsigned int,bool,float)),
     326         this,
     327         SLOT(subtitleDetectorHasNewInformation(unsigned int,bool,float))
     328    );
     329
     330    frameIsBlank = false;
     331    stationLogoPresent = false;
     332
     333    logoInfoAvailable = false;
     334
     335    ClearAllMaps();
     336
     337    if (verboseDebugging)
     338    {
     339        LOG(VB_COMMFLAG, LOG_DEBUG,
     340            "       Fr #      Min Max Avg Scn F A Mask");
     341        LOG(VB_COMMFLAG, LOG_DEBUG,
     342            "       ------    --- --- --- --- - - ----");
     343    }
     344}
     345
     346void NextgenCommDetector::deleteLater(void)
     347{
     348    if (audioBuffer)
     349    {
     350        if (player && player->GetAudio())
     351            player->GetAudio()->SetAudioOutput(NULL);
     352        delete audioBuffer;
     353        audioBuffer = NULL;
     354    }
     355    if (audioChangeDetector)
     356        audioChangeDetector->deleteLater();
     357
     358    if (sceneChangeDetector)
     359        sceneChangeDetector->deleteLater();
     360
     361    if (logoDetector)
     362        logoDetector->deleteLater();
     363
     364    CommDetectorBase::deleteLater();
     365}
     366
     367bool NextgenCommDetector::go()
     368{
     369    player->GetAudio()->SetAudioOutput(audioBuffer);
     370    player->GetAudio()->ReinitAudio();
     371    int secsSince = 0;
     372    int requiredBuffer = 30;
     373    int requiredHeadStart = requiredBuffer;
     374    bool wereRecording = stillRecording;
     375
     376    emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
     377        "Building Head Start Buffer"));
     378    secsSince = recordingStartedAt.secsTo(MythDate::current());
     379    while (stillRecording && (secsSince < requiredHeadStart))
     380    {
     381        emit breathe();
     382        if (m_bStop)
     383            return false;
     384
     385        sleep(2);
     386        secsSince = recordingStartedAt.secsTo(MythDate::current());
     387    }
     388
     389    if (player->OpenFile() < 0)
     390        return false;
     391
     392    Init();
     393
     394    if (commDetectMethod & COMM_DETECT_LOGO)
     395    {
     396        // Use a different border for logo detection.
     397        // If we try to detect logos in letterboxed areas,
     398        // chances are we won't detect the logo.
     399        // Generally speaking, SD video is likely to be letter boxed
     400        // and HD video is not likely to be letter boxed.
     401        // To detect logos, try to exclude letterboxed area from SD video
     402        // but exclude too much from HD video and you'll miss the logo.
     403        // Using the same border for both with no scaling seems to be
     404        // a good compromise.
     405        int logoDetectBorder =
     406            gCoreContext->GetNumSetting("CommDetectLogoBorder", 16);
     407        if (commDetectMethod & COMM_DETECT_LOGO_EXPERIMENTAL)
     408        {
     409            logoDetector = new NextgenLogoDetector2(this, width, height,
     410                logoDetectBorder, horizSpacing, vertSpacing);
     411        }
     412        else
     413        {
     414            logoDetector = new NextgenLogoDetector(this, width, height,
     415                logoDetectBorder, horizSpacing, vertSpacing);
     416        }
     417
     418        requiredHeadStart += max(
     419            int64_t(0), int64_t(recordingStartedAt.secsTo(startedAt)));
     420        requiredHeadStart += logoDetector->getRequiredAvailableBufferForSearch();
     421
     422        emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
     423            "Building Logo Detection Buffer"));
     424        secsSince = recordingStartedAt.secsTo(MythDate::current());
     425        while (stillRecording && (secsSince < requiredHeadStart))
     426        {
     427            emit breathe();
     428            if (m_bStop)
     429                return false;
     430
     431            sleep(2);
     432            secsSince = recordingStartedAt.secsTo(MythDate::current());
     433        }
     434    }
     435
     436    // Don't bother flagging short ~realtime recordings
     437    if ((wereRecording) && (!stillRecording) && (secsSince < requiredHeadStart))
     438        return false;
     439
     440    aggressiveDetection =
     441        gCoreContext->GetNumSetting("AggressiveCommDetect", 1);
     442
     443    if (!player->InitVideo())
     444    {
     445        LOG(VB_GENERAL, LOG_ERR,
     446            "NVP: Unable to initialize video for FlagCommercials.");
     447        return false;
     448    }
     449    //player->EnableSubtitles(false);
     450
     451    if (commDetectMethod & COMM_DETECT_LOGO)
     452    {
     453        emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
     454            "Searching for Logo"));
     455
     456        if (showProgress)
     457        {
     458            cerr << "Finding Logo";
     459            cerr.flush();
     460        }
     461        LOG(VB_GENERAL, LOG_INFO, "Finding Logo");
     462
     463        logoInfoAvailable = logoDetector->searchForLogo(player);
     464
     465        if (showProgress)
     466        {
     467            cerr << "\b\b\b\b\b\b\b\b\b\b\b\b            "
     468                    "\b\b\b\b\b\b\b\b\b\b\b\b";
     469            cerr.flush();
     470        }
     471    }
     472
     473    emit breathe();
     474    if (m_bStop)
     475        return false;
     476
     477    QTime flagTime;
     478    flagTime.start();
     479
     480
     481    float flagFPS;
     482    long long  currentFrameNumber = 0LL;
     483    float aspect = player->GetVideoAspect();
     484    float newAspect = aspect;
     485    int prevpercent = -1;
     486
     487    SetVideoParams(aspect);
     488
     489    emit breathe();
     490
     491    audioBuffer->Enable();
     492
     493    // make sure we get a key frame before us otherwise reset doesnt happen correctly.
     494    player->DiscardVideoFrame(player->GetRawVideoFrame(60));
     495    player->DiscardVideoFrame(player->GetRawVideoFrame(60));
     496
     497    // fps is only correct after the decoder has done some work
     498    // players fps is wrong so use that one from now on
     499    fps = player->GetDecoder()->GetFPS();
     500
     501    LOG(VB_COMMFLAG, LOG_INFO,
     502        QString("Commercial Detection frame rate updated: fps = %1")
     503            .arg(fps));
     504
     505    // update all dependencies now
     506    preRoll  = (long long)(
     507        max(int64_t(0), int64_t(recordingStartedAt.secsTo(startedAt))) * fps);
     508    postRoll = (long long)(
     509        max(int64_t(0), int64_t(stopsAt.secsTo(recordingStopsAt))) * fps);
     510
     511    long long myTotalFrames;
     512    if (recordingStopsAt < MythDate::current() )
     513        myTotalFrames = player->GetTotalFrameCount();
     514    else
     515        myTotalFrames = (long long)(fps *
     516                        (recordingStartedAt.secsTo(recordingStopsAt)));
     517
     518    if (showProgress)
     519    {
     520        if (myTotalFrames)
     521            cerr << "\r  0%/          \r" << flush;
     522        else
     523            cerr << "\r     0/        \r" << flush;
     524    }
     525
     526    emit breathe();
     527
     528    //player->GetAudio()->SetAudioOutput(audioBuffer);
     529    //player->GetAudio()->ReinitAudio();
     530
     531    player->DiscardVideoFrame(player->GetRawVideoFrame(0));
     532    player->ResetTotalDuration();
     533
     534    player->GetSubReader()->EnableAVSubtitles(true);
     535    player->GetSubReader()->EnableTextSubtitles(true);
     536    player->GetSubReader()->EnableRawTextSubtitles(true);
     537    //player->GetTeletextReader()->SetEnabled(true);
     538    //player->EnableCaptions(kDisplayTeletextCaptions, false);
     539    player->GetCC608Reader()->SetEnabled(true);
     540    player->GetCC708Reader()->SetEnabled(true);
     541    player->GetDecoder()->SetDecodeAllSubtitles(true);
     542    LOG(VB_GENERAL, LOG_DEBUG, "enabling subtitles.");
     543
     544    while (player->GetEof() == kEofStateNone)
     545    {
     546        struct timeval startTime;
     547        if (stillRecording)
     548            gettimeofday(&startTime, NULL);
     549
     550        VideoFrame* currentFrame = player->GetRawVideoFrame();
     551        //if (!vfQueue.isEmpty())
     552        //{
     553        //    if (currentFrame)
     554        //        vfQueue.enqueue(currentFrame);
     555        //    currentFrame = vfQueue.dequeue();
     556        //}
     557        currentFrameNumber = currentFrame->frameNumber;
     558
     559        //Lucas: maybe we should make the nuppelvideoplayer send out a signal
     560        //when the aspect ratio changes.
     561        //In order to not change too many things at a time, I"m using basic
     562        //polling for now.
     563        newAspect = currentFrame->aspect;
     564        if (newAspect != aspect)
     565        {
     566            SetVideoParams(aspect);
     567            aspect = newAspect;
     568        }
     569
     570        if (((currentFrameNumber % 500) == 0) ||
     571            (((currentFrameNumber % 100) == 0) &&
     572             (stillRecording)))
     573        {
     574            emit breathe();
     575            if (m_bStop)
     576            {
     577                player->DiscardVideoFrame(currentFrame);
     578                return false;
     579            }
     580        }
     581
     582        if ((sendCommBreakMapUpdates) &&
     583            ((commBreakMapUpdateRequested) ||
     584             ((currentFrameNumber % 500) == 0)))
     585        {
     586            frm_dir_map_t commBreakMap;
     587            frm_dir_map_t::iterator it;
     588            frm_dir_map_t::iterator lastIt;
     589            bool mapsAreIdentical = false;
     590
     591            GetCommercialBreakList(commBreakMap);
     592
     593            if ((commBreakMap.size() == 0) &&
     594                (lastSentCommBreakMap.size() == 0))
     595            {
     596                mapsAreIdentical = true;
     597            }
     598            else if (commBreakMap.size() == lastSentCommBreakMap.size())
     599            {
     600                // assume true for now and set false if we find a difference
     601                mapsAreIdentical = true;
     602                for (it = commBreakMap.begin();
     603                     it != commBreakMap.end() && mapsAreIdentical; ++it)
     604                {
     605                    lastIt = lastSentCommBreakMap.find(it.key());
     606                    if ((lastIt == lastSentCommBreakMap.end()) ||
     607                        (*lastIt != *it))
     608                        mapsAreIdentical = false;
     609                }
     610            }
     611
     612            if (commBreakMapUpdateRequested || !mapsAreIdentical)
     613            {
     614                emit gotNewCommercialBreakList();
     615                lastSentCommBreakMap = commBreakMap;
     616            }
     617
     618            if (commBreakMapUpdateRequested)
     619                commBreakMapUpdateRequested = false;
     620        }
     621
     622        while (m_bPaused)
     623        {
     624            emit breathe();
     625            sleep(1);
     626        }
     627
     628        // sleep a little so we don't use all cpu even if we're niced
     629        if (!fullSpeed && !stillRecording)
     630            usleep(10000);
     631
     632        if (((currentFrameNumber % 500) == 0) ||
     633            ((showProgress || stillRecording) &&
     634             ((currentFrameNumber % 100) == 0)))
     635        {
     636            float elapsed = flagTime.elapsed() / 1000.0;
     637
     638            if (elapsed)
     639                flagFPS = currentFrameNumber / elapsed;
     640            else
     641                flagFPS = 0.0;
     642
     643            int percentage;
     644            if (myTotalFrames)
     645                percentage = currentFrameNumber * 100 / myTotalFrames;
     646            else
     647                percentage = 0;
     648
     649            if (percentage > 100)
     650                percentage = 100;
     651
     652            if (showProgress)
     653            {
     654                if (myTotalFrames)
     655                {
     656                    QString tmp = QString("\r%1%/%2fps  \r")
     657                        .arg(percentage, 3).arg((int)flagFPS, 4);
     658                    cerr << qPrintable(tmp) << flush;
     659                }
     660                else
     661                {
     662                    QString tmp = QString("\r%1/%2fps  \r")
     663                        .arg(currentFrameNumber, 6).arg((int)flagFPS, 4);
     664                    cerr << qPrintable(tmp) << flush;
     665                }
     666            }
     667
     668            if (myTotalFrames)
     669                emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
     670                    "%1% Completed @ %2 fps.")
     671                                  .arg(percentage).arg(flagFPS));
     672            else
     673                emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
     674                    "%1 Frames Completed @ %2 fps.")
     675                                  .arg(currentFrameNumber).arg(flagFPS));
     676
     677            if (percentage % 10 == 0 && prevpercent != percentage)
     678            {
     679                prevpercent = percentage;
     680                LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.")
     681                    .arg(percentage) .arg(flagFPS));
     682            }
     683        }
     684
     685        ProcessFrame(currentFrame, currentFrameNumber);
     686
     687        if (stillRecording)
     688        {
     689            int secondsRecorded =
     690                recordingStartedAt.secsTo(MythDate::current());
     691            int secondsFlagged = (int)(framesProcessed / fps);
     692            int secondsBehind = secondsRecorded - secondsFlagged;
     693            long usecPerFrame = (long)(1.0 / player->GetDecoder()->GetFPS() * 1000000);
     694
     695            struct timeval endTime;
     696            gettimeofday(&endTime, NULL);
     697
     698            long long usecSleep =
     699                      usecPerFrame -
     700                      (((endTime.tv_sec - startTime.tv_sec) * 1000000) +
     701                       (endTime.tv_usec - startTime.tv_usec));
     702
     703            if (secondsBehind > requiredBuffer)
     704            {
     705                if (fullSpeed)
     706                    usecSleep = 0;
     707                else
     708                    usecSleep = (long)(usecSleep * 0.25);
     709            }
     710            else if (secondsBehind < requiredBuffer)
     711                usecSleep = (long)(usecPerFrame * 1.5);
     712
     713            if (usecSleep > 0)
     714                usleep(usecSleep);
     715        }
     716
     717        player->DiscardVideoFrame(currentFrame);
     718    }
     719
     720    if (showProgress)
     721    {
     722        float elapsed = flagTime.elapsed() / 1000.0;
     723
     724        if (elapsed)
     725            flagFPS = currentFrameNumber / elapsed;
     726        else
     727            flagFPS = 0.0;
     728
     729        if (myTotalFrames)
     730            cerr << "\b\b\b\b\b\b      \b\b\b\b\b\b";
     731        else
     732            cerr << "\b\b\b\b\b\b\b\b\b\b\b\b\b             "
     733                    "\b\b\b\b\b\b\b\b\b\b\b\b\b";
     734        cerr.flush();
     735    }
     736
     737    return true;
     738}
     739
     740void NextgenCommDetector::sceneChangeDetectorHasNewInformation(
     741    unsigned int framenum,bool isSceneChange,float debugValue)
     742{
     743    if (isSceneChange)
     744    {
     745        frameInfo[framenum].flagMask |= COMM_FRAME_SCENE_CHANGE;
     746        sceneMap[framenum] = MARK_SCENE_CHANGE;
     747    }
     748    else
     749    {
     750        frameInfo[framenum].flagMask &= ~COMM_FRAME_SCENE_CHANGE;
     751        sceneMap.remove(framenum);
     752    }
     753
     754    frameInfo[framenum].sceneChangePercent = (int) (debugValue*100);
     755   
     756    if (verboseDebugging)
     757    {
     758        //LOG(VB_COMMFLAG, LOG_DEBUG, QString("Scene Change @%1 : %2 %3").arg(framenum).arg(isSceneChange).arg((int)(debugValue*100)));
     759    }
     760}
     761
     762void NextgenCommDetector::audioDetectorHasNewInformation(
     763    unsigned int framenum, bool hasChanged, float amplitude, float debugValue)
     764{
     765    if (hasChanged)
     766        frameInfo[framenum].flagMask |= COMM_FRAME_AUDIO_CHANGE;
     767    else
     768        frameInfo[framenum].flagMask &= ~COMM_FRAME_AUDIO_CHANGE;
     769    frameInfo[framenum].audioMode = (int) (debugValue);
     770}
     771
     772void NextgenCommDetector::subtitleDetectorHasNewInformation(
     773    unsigned int framenum, bool hasChanged, float debugValue)
     774{
     775    if (hasChanged)
     776    {
     777        frameInfo[framenum].flagMask |= COMM_FRAME_SUBTITLE_PRESENT;
     778        subtitleInfoAvailable = true;
     779        subtitleFrameCount++;
     780    }
     781    else
     782        frameInfo[framenum].flagMask &= ~COMM_FRAME_SUBTITLE_PRESENT;
     783    frameInfo[framenum].subtitleMode = (int) (debugValue);
     784}
     785
     786void NextgenCommDetector::GetCommercialBreakList(frm_dir_map_t &marks)
     787{
     788
     789    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetCommBreakMap()");
     790
     791    marks.clear();
     792
     793    CleanupFrameInfo();
     794
     795    bool blank = COMM_DETECT_BLANK & commDetectMethod;
     796    bool scene = COMM_DETECT_SCENE & commDetectMethod;
     797    bool logo  = COMM_DETECT_LOGO  & commDetectMethod;
     798    bool audio = COMM_DETECT_AUDIO & commDetectMethod;
     799
     800    if (COMM_DETECT_OFF == commDetectMethod)
     801        return;
     802
     803    if (!blank && !scene && !logo && !audio)
     804    {
     805        LOG(VB_COMMFLAG, LOG_ERR, QString("Unexpected commDetectMethod: 0x%1")
     806                .arg(commDetectMethod,0,16));
     807        return;
     808    }
     809
     810    if (blank && scene && logo)
     811    {
     812        BuildAllMethodsCommList();
     813        marks = commBreakMap;
     814        LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map");
     815        return;
     816    }
     817
     818    if (blank)
     819    {
     820        BuildBlankFrameCommList();
     821        marks = blankCommBreakMap;
     822    }
     823
     824    if (scene)
     825    {
     826        BuildSceneChangeCommList();
     827        marks = sceneCommBreakMap;
     828    }
     829
     830    if (logo)
     831    {
     832        BuildLogoCommList();
     833        marks = logoCommBreakMap;
     834    }
     835
     836    if (audio)
     837    {
     838        BuildAudioFrameCommList();
     839        marks = audioCommBreakMap;
     840    }
     841
     842    int cnt = ((blank) ? 1 : 0) + ((scene) ? 1 : 0) + ((logo) ? 1 : 0) + ((audio) ? 1 : 0);
     843    if (cnt >= 2)
     844    {
     845        marks.clear();
     846        if (blank)
     847        {
     848            marks = Combine2Maps(blankCommBreakMap, marks);
     849        }
     850        if (scene)
     851        {
     852            marks = Combine2Maps(sceneCommBreakMap, marks);
     853        }
     854        if (logo)
     855        {
     856            marks = Combine2Maps(logoCommBreakMap, marks);
     857        }
     858        if (audio)
     859        {
     860            marks = Combine2Maps(audioCommBreakMap, marks);
     861        }
     862        commBreakMap = marks;
     863    }
     864
     865    LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map");
     866}
     867
     868void NextgenCommDetector::recordingFinished(long long totalFileSize)
     869{
     870    (void)totalFileSize;
     871
     872    stillRecording = false;
     873}
     874
     875void NextgenCommDetector::requestCommBreakMapUpdate(void)
     876{
     877    commBreakMapUpdateRequested = true;
     878    sendCommBreakMapUpdates = true;
     879}
     880
     881void NextgenCommDetector::SetVideoParams(float aspect)
     882{
     883    int newAspect = COMM_ASPECT_WIDE;
     884
     885    LOG(VB_COMMFLAG, LOG_INFO,
     886        QString("CommDetect::SetVideoParams called with aspect = %1")
     887            .arg(aspect));
     888    // Default to Widescreen but use the same check as VideoOutput::MoveResize()
     889    // to determine if is normal 4:3 aspect
     890    if (fabs(aspect - 1.333333) < 0.1)
     891        newAspect = COMM_ASPECT_NORMAL;
     892
     893    if (newAspect != currentAspect)
     894    {
     895        LOG(VB_COMMFLAG, LOG_INFO,
     896            QString("Aspect Ratio changed from %1 to %2 at frame %3")
     897                .arg(currentAspect).arg(newAspect)
     898                .arg(curFrameNumber));
     899
     900        if (frameInfo.contains(curFrameNumber))
     901        {
     902            // pretend that this frame is blank so that we can create test
     903            // blocks on real aspect ratio change boundaries.
     904            frameInfo[curFrameNumber].flagMask |= COMM_FRAME_BLANK;
     905            frameInfo[curFrameNumber].flagMask |= COMM_FRAME_ASPECT_CHANGE;
     906            decoderFoundAspectChanges = true;
     907        }
     908        else if (curFrameNumber != -1)
     909        {
     910            LOG(VB_COMMFLAG, LOG_ERR,
     911                QString("Unable to keep track of Aspect ratio change because "
     912                        "frameInfo for frame number %1 does not exist.")
     913                    .arg(curFrameNumber));
     914        }
     915        currentAspect = newAspect;
     916    }
     917}
     918
     919void NextgenCommDetector::ProcessFrame(VideoFrame *frame,
     920                                       long long frame_number)
     921{
     922    int max = 0;
     923    int min = 255;
     924    int avg = 0;
     925    unsigned char pixel;
     926    int blankPixelsChecked = 0;
     927    long long totBrightness = 0;
     928    unsigned char *rowMax = new unsigned char[height];
     929    unsigned char *colMax = new unsigned char[width];
     930    memset(rowMax, 0, sizeof(*rowMax)*height);
     931    memset(colMax, 0, sizeof(*colMax)*width);
     932    int topDarkRow = commDetectBorder;
     933    int bottomDarkRow = height - commDetectBorder - 1;
     934    int leftDarkCol = commDetectBorder;
     935    int rightDarkCol = width - commDetectBorder - 1;
     936    NGFrameInfoEntry fInfo;
     937
     938    if (!frame || !(frame->buf) || frame_number == -1 ||
     939        frame->codec != FMT_YV12)
     940    {
     941        LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Invalid video frame or codec, "
     942                                  "unable to process frame.");
     943        delete[] rowMax;
     944        delete[] colMax;
     945        return;
     946    }
     947
     948    if (!width || !height)
     949    {
     950        LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Width or Height is 0, "
     951                                  "unable to process frame.");
     952        delete[] rowMax;
     953        delete[] colMax;
     954        return;
     955    }
     956
     957    curFrameNumber = frame_number;
     958    unsigned char* framePtr = frame->buf;
     959    int bytesPerLine = frame->pitches[0];
     960
     961    fInfo.minBrightness = -1;
     962    fInfo.maxBrightness = -1;
     963    fInfo.avgBrightness = -1;
     964    fInfo.sceneChangePercent = -1;
     965    fInfo.aspect = currentAspect;
     966    fInfo.format = COMM_FORMAT_NORMAL;
     967    fInfo.audioPower = 0;
     968    fInfo.audioMode = 0;
     969    fInfo.flagMask = 0;
     970
     971    int& flagMask = frameInfo[curFrameNumber].flagMask;
     972
     973    // Fill in dummy info records for skipped frames.
     974    if (lastFrameNumber != (curFrameNumber - 1))
     975    {
     976        if (lastFrameNumber > 0)
     977        {
     978            fInfo.aspect = frameInfo[lastFrameNumber].aspect;
     979            fInfo.format = frameInfo[lastFrameNumber].format;
     980        }
     981        fInfo.flagMask = COMM_FRAME_SKIPPED;
     982
     983        lastFrameNumber++;
     984        while(lastFrameNumber < curFrameNumber)
     985            frameInfo[lastFrameNumber++] = fInfo;
     986
     987        fInfo.flagMask = 0;
     988    }
     989    lastFrameNumber = curFrameNumber;
     990
     991    frameInfo[curFrameNumber] = fInfo;
     992
     993    if (commDetectMethod & COMM_DETECT_BLANKS)
     994        frameIsBlank = false;
     995
     996    if (commDetectMethod & COMM_DETECT_SCENE)
     997    {
     998        sceneChangeDetector->processFrame(frame);
     999    }
     1000
     1001    subtitleChangeDetector->processFrame(player, frame);
     1002
     1003    stationLogoPresent = false;
     1004
     1005    for(int y = commDetectBorder; y < (height - commDetectBorder);
     1006            y += vertSpacing)
     1007    {
     1008        for(int x = commDetectBorder; x < (width - commDetectBorder);
     1009                x += horizSpacing)
     1010        {
     1011            pixel = framePtr[y * bytesPerLine + x];
     1012
     1013            if (commDetectMethod & COMM_DETECT_BLANKS)
     1014            {
     1015                 bool checkPixel = false;
     1016                 if (!commDetectBlankCanHaveLogo)
     1017                     checkPixel = true;
     1018
     1019                 if (!logoInfoAvailable)
     1020                     checkPixel = true;
     1021                 else if (!logoDetector->pixelInsideLogo(x,y))
     1022                     checkPixel = true;
     1023
     1024                 if (checkPixel)
     1025                 {
     1026                     blankPixelsChecked++;
     1027                     totBrightness += pixel;
     1028
     1029                     if (pixel < min)
     1030                          min = pixel;
     1031
     1032                     if (pixel > max)
     1033                          max = pixel;
     1034
     1035                     if (pixel > rowMax[y])
     1036                         rowMax[y] = pixel;
     1037
     1038                     if (pixel > colMax[x])
     1039                         colMax[x] = pixel;
     1040                 }
     1041            }
     1042        }
     1043    }
     1044
     1045    if ((commDetectMethod & COMM_DETECT_BLANKS) && blankPixelsChecked)
     1046    {
     1047        for(int y = commDetectBorder; y < (height - commDetectBorder);
     1048                y += vertSpacing)
     1049        {
     1050            if (rowMax[y] > commDetectBoxBrightness)
     1051                break;
     1052            else
     1053                topDarkRow = y;
     1054        }
     1055
     1056        for(int y = commDetectBorder; y < (height - commDetectBorder);
     1057                y += vertSpacing)
     1058            if (rowMax[y] >= commDetectBoxBrightness)
     1059                bottomDarkRow = y;
     1060
     1061        delete[] rowMax;
     1062        rowMax = 0;
     1063
     1064        for(int x = commDetectBorder; x < (width - commDetectBorder);
     1065                x += horizSpacing)
     1066        {
     1067            if (colMax[x] > commDetectBoxBrightness)
     1068                break;
     1069            else
     1070                leftDarkCol = x;
     1071        }
     1072
     1073        for(int x = commDetectBorder; x < (width - commDetectBorder);
     1074                x += horizSpacing)
     1075            if (colMax[x] >= commDetectBoxBrightness)
     1076                rightDarkCol = x;
     1077
     1078        delete[] colMax;
     1079        colMax = 0;
     1080
     1081        frameInfo[curFrameNumber].format = COMM_FORMAT_NORMAL;
     1082        if ((topDarkRow > commDetectBorder) &&
     1083            (topDarkRow < (height * .20)) &&
     1084            (bottomDarkRow < (height - commDetectBorder)) &&
     1085            (bottomDarkRow > (height * .80)))
     1086        {
     1087            frameInfo[curFrameNumber].format |= COMM_FORMAT_LETTERBOX;
     1088        }
     1089        if ((leftDarkCol > commDetectBorder) &&
     1090                 (leftDarkCol < (width * .20)) &&
     1091                 (rightDarkCol < (width - commDetectBorder)) &&
     1092                 (rightDarkCol > (width * .80)))
     1093        {
     1094            frameInfo[curFrameNumber].format |= COMM_FORMAT_PILLARBOX;
     1095        }
     1096
     1097        avg = totBrightness / blankPixelsChecked;
     1098
     1099        frameInfo[curFrameNumber].minBrightness = min;
     1100        frameInfo[curFrameNumber].maxBrightness = max;
     1101        frameInfo[curFrameNumber].avgBrightness = avg;
     1102
     1103        totalMinBrightness += min;
     1104        commDetectDimAverage = min + 10;
     1105
     1106        // Is the frame really dark
     1107        if (((max - min) <= commDetectBlankFrameMaxDiff) &&
     1108            (max < commDetectDimBrightness))
     1109            frameIsBlank = true;
     1110
     1111        // Are we non-strict and the frame is blank
     1112        if ((!aggressiveDetection) &&
     1113            ((max - min) <= commDetectBlankFrameMaxDiff))
     1114            frameIsBlank = true;
     1115
     1116        // Are we non-strict and the frame is dark
     1117        //                   OR the frame is dim and has a low avg brightness
     1118        if ((!aggressiveDetection) &&
     1119            ((max < commDetectDarkBrightness) ||
     1120             ((max < commDetectDimBrightness) && (avg < commDetectDimAverage))))
     1121            frameIsBlank = true;
     1122    }
     1123
     1124    if ((logoInfoAvailable) && (commDetectMethod & COMM_DETECT_LOGO))
     1125    {
     1126        stationLogoPresent =
     1127            logoDetector->doesThisFrameContainTheFoundLogo(frame);
     1128    }
     1129
     1130#if 0
     1131    if ((commDetectMethod == COMM_DETECT_ALL) &&
     1132        (CheckRatingSymbol()))
     1133    {
     1134        flagMask |= COMM_FRAME_RATING_SYMBOL;
     1135    }
     1136#endif
     1137
     1138    if (frameIsBlank)
     1139    {
     1140        blankFrameMap[curFrameNumber] = MARK_BLANK_FRAME;
     1141        flagMask |= COMM_FRAME_BLANK;
     1142        blankFrameCount++;
     1143    }
     1144
     1145    if (stationLogoPresent)
     1146        flagMask |= COMM_FRAME_LOGO_PRESENT;
     1147
     1148    if (commDetectMethod & COMM_DETECT_AUDIO)
     1149    {
     1150        if (audioBuffer)
     1151        {
     1152            static int max_loops = 100;
     1153            // process a number of frames worth of audio
     1154            PAudioSample audSample = audioBuffer->GetSample(frame->timecode);
     1155            int loops = max_loops;
     1156//#define USE_VFQUEUES
     1157#ifdef USE_VFQUEUES
     1158            int queued = 0;
     1159            int not_queued = 0;
     1160#endif
     1161            while (!audSample)
     1162            {
     1163#ifdef USE_VFQUEUES
     1164                if (vfQueue.count() < 5)
     1165                {
     1166                    VideoFrame* newFrame = player->GetRawVideoFrame();
     1167                    if (newFrame)
     1168                    {
     1169                        vfQueue.enqueue(newFrame);
     1170                        queued++;
     1171                    }
     1172                    else
     1173                        not_queued++;
     1174
     1175                    usleep(10000);
     1176                }
     1177                else
     1178                {
     1179                    usleep(1000);
     1180                }
     1181#else
     1182                usleep(1000);
     1183#endif
     1184                emit breathe();
     1185
     1186                audSample = audioBuffer->GetSample(frame->timecode);
     1187                if (--loops == 0)
     1188                    break;
     1189            }
     1190            if (loops < max_loops)
     1191            {
     1192#ifdef AUDIODEBUGGING3
     1193                if (verboseDebugging)
     1194                {
     1195#ifdef USE_VFQUEUES
     1196                    if (loops == 0)
     1197                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample failed after max loops (%1,%2)").arg(queued).arg(not_queued));
     1198                    else
     1199                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample needed loops %1 (%2,%3)").arg(max_loops-loops).arg(queued).arg(not_queued));
     1200#else
     1201                    if (loops == 0)
     1202                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample failed after max loops"));
     1203                    else
     1204                        LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample needed loops %1").arg(max_loops-loops));
     1205#endif
     1206                }
     1207#endif
     1208            }
     1209
     1210            if (audSample && (audSample->power >= 0))
     1211            {
     1212                frameInfo[curFrameNumber].audioPower = audSample->power;
     1213                frameInfo[curFrameNumber].audioMode = audSample->channels;
     1214                if (audSample->changed)
     1215                    flagMask |= COMM_FRAME_AUDIO_CHANGE;
     1216                if (audSample->power < 1e-3)
     1217                {
     1218                    flagMask |= COMM_FRAME_NO_AUDIO;
     1219                    silentFrameCount++;
     1220                }
     1221#ifdef AUDIODEBUGGING
     1222                if (verboseDebugging)
     1223                {
     1224                    LOG(VB_COMMFLAG, LOG_DEBUG, QString(" video %1 audio %3")
     1225                        .arg(frame->timecode)
     1226                        .arg(audSample->toString()));
     1227                }
     1228#endif
     1229            }
     1230            else
     1231            {
     1232#ifdef AUDIODEBUGGING
     1233                if (verboseDebugging)
     1234                {
     1235                    LOG(VB_COMMFLAG, LOG_DEBUG, QString(" video %1")
     1236                        .arg(frame->timecode));
     1237                }
     1238#endif
     1239                frameInfo[curFrameNumber].audioPower = 0;
     1240                flagMask |= COMM_FRAME_INVALID_AUDIO;
     1241            }
     1242        }
     1243    }
     1244
     1245    //TODO: move this debugging code out of the perframe loop, and do it after
     1246    // we've processed all frames. this is because a scenechangedetector can
     1247    // now use a few frames to determine whether the frame a few frames ago was
     1248    // a scene change or not.. due to this lookahead possibility the values
     1249    // that are currently in the frameInfo array, might be changed a few frames
     1250    // from now. The NextgenSceneChangeDetector doesn't use this though. future
     1251    // scenechangedetectors might.
     1252
     1253    if (verboseDebugging)
     1254        LOG(VB_COMMFLAG, LOG_DEBUG,
     1255            QString().sprintf("Frame: %6ld -> %3d %3d %3d %3d %1d %1d %9.6f %1d %04x",
     1256                (long)curFrameNumber,
     1257                frameInfo[curFrameNumber].minBrightness,
     1258                frameInfo[curFrameNumber].maxBrightness,
     1259                frameInfo[curFrameNumber].avgBrightness,
     1260                frameInfo[curFrameNumber].sceneChangePercent,
     1261                frameInfo[curFrameNumber].format,
     1262                frameInfo[curFrameNumber].aspect,
     1263                frameInfo[curFrameNumber].audioPower,
     1264                frameInfo[curFrameNumber].audioMode,
     1265                frameInfo[curFrameNumber].flagMask ));
     1266
     1267#ifdef SHOW_DEBUG_WIN
     1268    comm_debug_show(frame->buf);
     1269    getchar();
     1270#endif
     1271
     1272    framesProcessed++;
     1273    delete[] rowMax;
     1274    delete[] colMax;
     1275}
     1276
     1277void NextgenCommDetector::ClearAllMaps(void)
     1278{
     1279    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::ClearAllMaps()");
     1280
     1281    frameInfo.clear();
     1282    blankFrameMap.clear();
     1283    blankCommMap.clear();
     1284    blankCommBreakMap.clear();
     1285    sceneMap.clear();
     1286    sceneCommBreakMap.clear();
     1287    audioCommBreakMap.clear();
     1288    commBreakMap.clear();
     1289}
     1290
     1291void NextgenCommDetector::GetBlankCommMap(frm_dir_map_t &comms)
     1292{
     1293    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommMap()");
     1294
     1295    if (blankCommMap.isEmpty())
     1296        BuildBlankFrameCommList();
     1297
     1298    comms = blankCommMap;
     1299}
     1300
     1301void NextgenCommDetector::GetBlankCommBreakMap(frm_dir_map_t &comms)
     1302{
     1303    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommBreakMap()");
     1304
     1305    if (blankCommBreakMap.isEmpty())
     1306        BuildBlankFrameCommList();
     1307
     1308    comms = blankCommBreakMap;
     1309}
     1310
     1311void NextgenCommDetector::GetAudioCommBreakMap(frm_dir_map_t &comms)
     1312{
     1313    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetAudioCommBreakMap()");
     1314
     1315    if (audioCommBreakMap.isEmpty())
     1316        BuildAudioFrameCommList();
     1317
     1318    comms = audioCommBreakMap;
     1319}
     1320
     1321void NextgenCommDetector::GetSceneChangeMap(frm_dir_map_t &scenes,
     1322                                            int64_t start_frame)
     1323{
     1324    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetSceneChangeMap()");
     1325
     1326    frm_dir_map_t::iterator it;
     1327
     1328    if (start_frame == -1)
     1329        scenes.clear();
     1330
     1331    for (it = sceneMap.begin(); it != sceneMap.end(); ++it)
     1332        if ((start_frame == -1) || ((int64_t)it.key() >= start_frame))
     1333            scenes[it.key()] = *it;
     1334}
     1335
     1336frm_dir_map_t NextgenCommDetector::Combine2Maps(const frm_dir_map_t &a,
     1337                                                const frm_dir_map_t &b) const
     1338{
     1339    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildMasterCommList()");
     1340
     1341    frm_dir_map_t newMap;
     1342
     1343    if (a.size())
     1344    {
     1345        frm_dir_map_t::const_iterator it = a.begin();
     1346        for (; it != a.end(); ++it)
     1347            newMap[it.key()] = *it;
     1348    }
     1349
     1350    if ((a.size() > 1) &&
     1351        (b.size() > 1))
     1352    {
     1353        // see if beginning of the recording looks like a commercial
     1354        frm_dir_map_t::const_iterator it_a;
     1355        frm_dir_map_t::const_iterator it_b;
     1356
     1357        it_a = a.begin();
     1358        it_b = b.begin();
     1359
     1360        if ((it_b.key() < 2) && (it_a.key() > 2))
     1361        {
     1362            newMap.remove(it_a.key());
     1363            newMap[0] = MARK_COMM_START;
     1364        }
     1365
     1366
     1367        // see if ending of recording looks like a commercial
     1368        frm_dir_map_t::const_iterator it;
     1369        uint64_t max_a = 0;
     1370        uint64_t max_b = 0;
     1371
     1372        it = a.begin();
     1373        for (; it != a.end(); ++it)
     1374        {
     1375            if ((*it == MARK_COMM_END) && (it.key() > max_a))
     1376                max_a = it.key();
     1377        }
     1378
     1379        it = b.begin();
     1380        for (; it != b.end(); ++it)
     1381        {
     1382            if ((*it == MARK_COMM_END) && (it.key() > max_b))
     1383                max_b = it.key();
     1384        }
     1385
     1386        if ((max_a < (framesProcessed - 2)) &&
     1387            (max_b > (framesProcessed - 2)))
     1388        {
     1389            newMap.remove(max_a);
     1390            newMap[framesProcessed] = MARK_COMM_END;
     1391        }
     1392    }
     1393
     1394    if ((a.size() > 3) &&
     1395        (b.size() > 1))
     1396    {
     1397        frm_dir_map_t::const_iterator it_a;
     1398        frm_dir_map_t::const_iterator it_b;
     1399
     1400        it_a = a.begin();
     1401        ++it_a;
     1402        it_b = it_a;
     1403        ++it_b;
     1404        while (it_b != a.end())
     1405        {
     1406            uint64_t fdiff = it_b.key() - it_a.key();
     1407            bool allTrue = false;
     1408
     1409            if (fdiff < (62 * fps))
     1410            {
     1411                uint64_t f = it_a.key() + 1;
     1412
     1413                allTrue = true;
     1414
     1415                while ((f <= framesProcessed) && (f < it_b.key()) && (allTrue))
     1416                    allTrue = FrameIsInBreakMap(f++, b);
     1417            }
     1418
     1419            if (allTrue)
     1420            {
     1421                newMap.remove(it_a.key());
     1422                newMap.remove(it_b.key());
     1423            }
     1424
     1425            ++it_a; ++it_a;
     1426            ++it_b;
     1427            if (it_b != a.end())
     1428                ++it_b;
     1429        }
     1430    }
     1431
     1432    return newMap;
     1433}
     1434
     1435void NextgenCommDetector::UpdateFrameBlock(PFrameBlock fbp,
     1436                                           NGFrameInfoEntry& finfo,
     1437                                           int format, int aspect)
     1438{
     1439    int value = 0;
     1440
     1441    value = finfo.flagMask;
     1442
     1443    if (value & COMM_FRAME_BLANK)
     1444        fbp->bfCount++;
     1445
     1446    if (value & COMM_FRAME_LOGO_PRESENT)
     1447        fbp->logoCount++;
     1448
     1449    if (value & COMM_FRAME_RATING_SYMBOL)
     1450        fbp->ratingCount++;
     1451
     1452    if (value & COMM_FRAME_SCENE_CHANGE)
     1453        fbp->scCount++;
     1454
     1455    if (value & COMM_FRAME_NO_AUDIO)
     1456        fbp->saCount++;
     1457
     1458    if (value & COMM_FRAME_AUDIO_CHANGE)
     1459        fbp->acCount++;
     1460
     1461    if (value & COMM_FRAME_SUBTITLE_PRESENT)
     1462        fbp->subCount++;
     1463
     1464    fbp->aPowerAvg += finfo.audioPower;
     1465
     1466    if (finfo.format == format)
     1467        fbp->formatMatch++;
     1468
     1469    if (finfo.aspect == aspect)
     1470        fbp->aspectMatch++;
     1471}
     1472
     1473NextgenCommDetector::FrameBlock::FrameBlock()
     1474{
     1475    start = 0;
     1476    end = 0;
     1477    frames = 0;
     1478    length = 0;
     1479    bfCount = 0;
     1480    saCount = 0;
     1481    aPowerAvg = 0;
     1482    acCount = 0;
     1483    logoCount = 0;
     1484    subCount = 0;
     1485    ratingCount = 0;
     1486    scCount = 0;
     1487    scRate = 0.0;
     1488    formatMatch = 0;
     1489    aspectMatch = 0;
     1490    segmentScore = 0;
     1491    transitionScore = 0;
     1492}
     1493
     1494void NextgenCommDetector::FrameBlock::Merge(FrameBlock& o)
     1495{
     1496    if (start <= o.start)
     1497    {
     1498        end = o.end;
     1499        subBlock.insert(subBlock.end(), o.subBlock.begin(), o.subBlock.end());
     1500    }
     1501    else
     1502    {
     1503        start = o.start;
     1504        subBlock.insert(subBlock.begin(), o.subBlock.begin(), o.subBlock.end());
     1505    }
     1506
     1507    bfCount += o.bfCount;
     1508    saCount += o.saCount;
     1509    acCount += o.acCount;
     1510    logoCount += o.logoCount;
     1511    subCount += o.subCount;
     1512    ratingCount += o.ratingCount;
     1513    scCount += o.scCount;
     1514    formatMatch += o.formatMatch;
     1515    aspectMatch += o.aspectMatch;
     1516    segmentScore += o.segmentScore;
     1517    transitionScore += o.transitionScore;
     1518
     1519    aPowerAvg = aPowerAvg*frames + o.aPowerAvg*o.frames;
     1520    scRate = scRate*frames + o.scRate*o.frames;
     1521
     1522    frames += o.frames;
     1523    length += o.length;
     1524
     1525    aPowerAvg /= frames;
     1526    scRate    /= frames;
     1527
     1528}
     1529
     1530const char * NextgenCommDetector::FrameBlockHeader1() const
     1531{
     1532    return "Block StTime StFrm  EndFrm Frames Secs    "
     1533        "Bf  Lg Cnt ST Cnt RT Cnt SC Cnt SC Rt FmtMch AspMch SA Cnt AC Cnt AudPower TScore SScore";
     1534}
     1535
     1536const char * NextgenCommDetector::FrameBlockHeader2() const
     1537{
     1538    return "----- ------ ------ ------ ------ ------- "
     1539        "--- ------ ------ ------ ------ ----- ------ ------ ------ ------ -------- ------ ------";
     1540}
     1541
     1542const QString NextgenCommDetector::FrameBlockStr(int curBlock, PFrameBlock fbp, int *score) const
     1543{
     1544#define STATS_PRINT_SPEC " %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d %6d " \
     1545                         "%5.2f %6d %6d %6d %6d %8f %6d %6d"
     1546#define STATS_PRINT_PARAMS \
     1547                (int)(fbp->start / fps) / 60, \
     1548                (int)((fbp->start / fps )) % 60, \
     1549                fbp->start, fbp->end, fbp->frames, fbp->length, \
     1550                fbp->bfCount, fbp->logoCount, fbp->subCount, fbp->ratingCount, \
     1551                fbp->scCount, fbp->scRate, fbp->formatMatch, \
     1552                fbp->aspectMatch, \
     1553                fbp->saCount, fbp->acCount, fbp->aPowerAvg, \
     1554                fbp->transitionScore, score?*score:fbp->segmentScore
     1555
     1556    QString msg;
     1557    if (curBlock < 0)
     1558    {
     1559        msg.sprintf("  NOW" STATS_PRINT_SPEC ,
     1560                STATS_PRINT_PARAMS );
     1561    }
     1562    else
     1563    {
     1564        msg.sprintf("%5d" STATS_PRINT_SPEC ,
     1565                curBlock, STATS_PRINT_PARAMS );
     1566    }
     1567    return msg;
     1568#undef STATS_PRINT_SPEC
     1569#undef STATS_PRINT_PARAMS
     1570}
     1571
     1572void NextgenCommDetector::BuildAllMethodsCommList(void)
     1573{
     1574    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildAllMethodsCommList()");
     1575
     1576    vector< PFrameBlock > cfblock;
     1577    vector< PFrameBlock > fblock;
     1578    PFrameBlock fbp;
     1579    PFrameBlock cfbp;
     1580
     1581    int value = 0;
     1582    int curBlock = 0;
     1583    int maxBlock = 0;
     1584    int lastScore = 0;
     1585    int thisScore = 0;
     1586    int nextScore = 0;
     1587    uint64_t curFrame = 0;
     1588    int64_t  breakStart = 0;
     1589    uint64_t lastStart = 0;
     1590    uint64_t lastEnd = 0;
     1591    int64_t firstLogoFrame = -1;
     1592    bool lastFrameWasBlank = false;
     1593    bool lastFrameWasSilent = false;
     1594    uint64_t formatFrames = 0;
     1595    int format = COMM_FORMAT_NORMAL;
     1596    uint64_t aspectFrames = 0;
     1597    int aspect = COMM_ASPECT_NORMAL;
     1598    QString msg;
     1599    uint64_t formatCounts[COMM_FORMAT_MAX];
     1600    frm_dir_map_t tmpCommMap;
     1601    frm_dir_map_t::iterator it;
     1602
     1603    bool audioMethod = (commDetectMethod & COMM_DETECT_AUDIO);
     1604
     1605    commBreakMap.clear();
     1606
     1607    curBlock = 0;
     1608    curFrame = 0;
     1609
     1610#if 0
     1611    cfbp = PFrameBlock(new FrameBlock);
     1612    cfblock.push_back(cfbp);
     1613    fbp = PFrameBlock(new FrameBlock);
     1614    fbp->transitionScore = 5;   // first one is always a transition
     1615    cfbp->subBlock.push_back(fbp);
     1616    fblock.push_back(fbp);
     1617#endif
     1618
     1619    value = frameInfo[curFrame].flagMask;
     1620    lastFrameWasSilent = !(value & COMM_FRAME_NO_AUDIO);
     1621    lastFrameWasBlank = !(value & COMM_FRAME_BLANK);
     1622
     1623    if (decoderFoundAspectChanges)
     1624    {
     1625        for (int64_t i = preRoll;
     1626             i < ((int64_t)framesProcessed - (int64_t)postRoll); i++)
     1627        {
     1628            if ((frameInfo.contains(i)) &&
     1629                (frameInfo[i].aspect == COMM_ASPECT_NORMAL))
     1630                aspectFrames++;
     1631        }
     1632
     1633        if (aspectFrames < ((framesProcessed - preRoll - postRoll) / 2))
     1634        {
     1635            aspect = COMM_ASPECT_WIDE;
     1636            aspectFrames = framesProcessed - preRoll - postRoll - aspectFrames;
     1637        }
     1638    }
     1639    else
     1640    {
     1641        memset(&formatCounts, 0, sizeof(formatCounts));
     1642
     1643        for(int64_t i = preRoll;
     1644            i < ((int64_t)framesProcessed - (int64_t)postRoll); i++ )
     1645            if ((frameInfo.contains(i)) &&
     1646                (frameInfo[i].format >= 0) &&
     1647                (frameInfo[i].format < COMM_FORMAT_MAX))
     1648                formatCounts[frameInfo[i].format]++;
     1649
     1650        for(int i = 0; i < COMM_FORMAT_MAX; i++)
     1651        {
     1652            if (formatCounts[i] > formatFrames)
     1653            {
     1654                format = i;
     1655                formatFrames = formatCounts[i];
     1656            }
     1657        }
     1658    }
     1659
     1660    while (curFrame <= framesProcessed)
     1661    {
     1662        value = frameInfo[curFrame].flagMask;
     1663
     1664        int newBlockMask = 0;
     1665        bool lastSilentFrameState = lastFrameWasSilent;
     1666        lastFrameWasSilent = value & COMM_FRAME_NO_AUDIO;
     1667
     1668        bool lastBlankFrameState = lastFrameWasBlank;
     1669        lastFrameWasBlank = value & COMM_FRAME_BLANK;
     1670        if (audioMethod)
     1671        {
     1672            // transition only when both are set
     1673            newBlockMask = ((lastSilentFrameState & lastBlankFrameState) != (lastFrameWasSilent & lastFrameWasBlank))?4:0;
     1674
     1675            if ((frameInfo[curFrame].sceneChangePercent < 50) && lastFrameWasSilent && !lastFrameWasBlank)
     1676            {
     1677                newBlockMask |= 2;
     1678            }
     1679        }
     1680        else
     1681        {
     1682            newBlockMask |= (lastBlankFrameState != lastFrameWasBlank);
     1683        }
     1684
     1685        if (newBlockMask)
     1686        {
     1687            //LOG(VB_COMMFLAG, LOG_INFO, QString("nbm %1 %2 %3 %4").arg(curFrame).arg(lastSilentFrameState).arg(lastFrameWasBlank).arg(newBlockMask));
     1688            if (fbp)
     1689            {
     1690                fbp->end = curFrame - 1;
     1691                fbp->frames = fbp->end - fbp->start + 1;
     1692                fbp->length = fbp->frames / fps;
     1693
     1694                fbp->aPowerAvg /= fbp->frames;
     1695
     1696                if ((fbp->scCount) && (fbp->length > 1.05))
     1697                    fbp->scRate = fbp->scCount / fbp->length;
     1698            }
     1699
     1700            curBlock++;
     1701
     1702            fbp = PFrameBlock(new FrameBlock);
     1703            fblock.push_back(fbp);
     1704            if (((newBlockMask & 4) && (lastFrameWasSilent && lastFrameWasBlank)) || !cfbp)
     1705            {
     1706                if (cfbp)
     1707                {
     1708                    cfbp->end = curFrame - 1;
     1709                    cfbp->frames = cfbp->end - cfbp->start + 1;
     1710                    cfbp->length = cfbp->frames / fps;
     1711                    cfbp->aPowerAvg /= cfbp->frames;
     1712
     1713                    if ((cfbp->scCount) && (cfbp->length > 1.05))
     1714                        cfbp->scRate = cfbp->scCount / cfbp->length;
     1715                }
     1716
     1717                cfbp = PFrameBlock(new FrameBlock);
     1718                cfblock.push_back(cfbp);
     1719
     1720                //fbp->transitionScore = 10;
     1721                cfbp->start = curFrame;
     1722            }
     1723            cfbp->subBlock.push_back(fbp);
     1724
     1725            fbp->start = curFrame;
     1726        }
     1727        UpdateFrameBlock(fbp, frameInfo[curFrame], format, aspect);
     1728        UpdateFrameBlock(cfbp, frameInfo[curFrame], format, aspect);
     1729
     1730        if ((value & COMM_FRAME_LOGO_PRESENT) &&
     1731            (firstLogoFrame == -1))
     1732            firstLogoFrame = curFrame;
     1733
     1734        curFrame++;
     1735    }
     1736
     1737    fbp->end = curFrame - 1;
     1738    fbp->frames = fbp->end - fbp->start + 1;
     1739    fbp->length = fbp->frames / fps;
     1740
     1741    fbp->aPowerAvg /= fbp->frames;
     1742
     1743    if ((fbp->scCount) && (fbp->length > 1.05))
     1744        fbp->scRate = fbp->scCount / fbp->length;
     1745
     1746    cfbp->end = curFrame - 1;
     1747    cfbp->frames = cfbp->end - cfbp->start + 1;
     1748    cfbp->length = cfbp->frames / fps;
     1749    cfbp->aPowerAvg /= cfbp->frames;
     1750
     1751    if ((cfbp->scCount) && (cfbp->length > 1.05))
     1752        cfbp->scRate = cfbp->scCount / cfbp->length;
     1753
     1754    maxBlock = curBlock;
     1755    curBlock = 0;
     1756    lastScore = 0;
     1757
     1758    LOG(VB_COMMFLAG, LOG_INFO, "Initial Block pass");
     1759    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
     1760    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
     1761    while (curBlock < maxBlock)
     1762    {
     1763        fbp = fblock[curBlock];
     1764
     1765        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp));
     1766
     1767        if ((fbp->bfCount == fbp->saCount) && (fbp->bfCount == fbp->frames) && (fbp->frames > 1))
     1768        {
     1769            fbp->transitionScore += 10;
     1770            goto endInitialPass;
     1771        }
     1772
     1773        if (fbp->frames > fps)
     1774        {
     1775            if (verboseDebugging)
     1776                LOG(VB_COMMFLAG, LOG_DEBUG,
     1777                    QString("      FRAMES > %1").arg(fps));
     1778
     1779            if (fbp->length > commDetectMaxCommLength)
     1780            {
     1781                if (verboseDebugging)
     1782                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1783                        "      length > max comm length, +20");
     1784                fbp->segmentScore += 20;
     1785            }
     1786
     1787            if (fbp->length > commDetectMaxCommBreakLength)
     1788            {
     1789                if (verboseDebugging)
     1790                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1791                        "      length > max comm break length, +20");
     1792                fbp->segmentScore += 20;
     1793            }
     1794
     1795            if ((fbp->length > 4) &&
     1796                (!logoInfoAvailable || fbp->logoCount > (fbp->frames * 0.60)) &&
     1797                (fbp->bfCount < (fbp->frames * 0.10)))
     1798            {
     1799                if (verboseDebugging)
     1800                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1801                        "      length > 4 && logoCount > frames * 0.60 && "
     1802                        "bfCount < frames * .10");
     1803                if (logoInfoAvailable)
     1804                {
     1805                    if (verboseDebugging)
     1806                        LOG(VB_COMMFLAG, LOG_DEBUG,
     1807                            "      logoInfoAvailable, +10");
     1808                    fbp->segmentScore += 10;
     1809                    if (fbp->logoCount > (fbp->frames * 0.90))
     1810                    {
     1811                        if (verboseDebugging)
     1812                            LOG(VB_COMMFLAG, LOG_DEBUG,
     1813                                "      logoCount > frames * 0.90, +10");
     1814                        fbp->segmentScore += 10;
     1815                    }
     1816                }
     1817                if (fbp->length > commDetectMaxCommBreakLength)
     1818                {
     1819                    if (verboseDebugging)
     1820                        LOG(VB_COMMFLAG, LOG_DEBUG,
     1821                            "      length > max comm break length, +20");
     1822                    fbp->segmentScore += 20;
     1823                }
     1824                else
     1825                {
     1826                    if (verboseDebugging)
     1827                        LOG(VB_COMMFLAG, LOG_DEBUG,
     1828                            "      length <= max comm break length, +10");
     1829                    fbp->segmentScore += 10;
     1830                }
     1831            }
     1832
     1833            if ((logoInfoAvailable) &&
     1834                (fbp->logoCount < (fbp->frames * 0.50)) &&
     1835                (fbp->bfCount < (fbp->frames * 0.10)))
     1836            {
     1837                if (verboseDebugging)
     1838                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1839                        "      logoInfoAvailable && logoCount < frames * .50 && blanks < frames * 0.1, "
     1840                        "-10");
     1841                fbp->segmentScore -= 10;
     1842            }
     1843
     1844            if (fbp->ratingCount > (fbp->frames * 0.05))
     1845            {
     1846                if (verboseDebugging)
     1847                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1848                        "      rating symbol present > 5% of time, +20");
     1849                fbp->segmentScore += 20;
     1850            }
     1851
     1852            if ((fbp->scRate > 1.0) &&
     1853                (fbp->bfCount < (fbp->frames * 0.50)) &&
     1854                (fbp->logoCount < (fbp->frames * .90)))
     1855            {
     1856                if (verboseDebugging)
     1857                    LOG(VB_COMMFLAG, LOG_DEBUG, "      scRate > 1.0, -10");
     1858                fbp->segmentScore -= 10;
     1859
     1860                if (fbp->scRate > 2.0)
     1861                {
     1862                    if (verboseDebugging)
     1863                        LOG(VB_COMMFLAG, LOG_DEBUG, "      scRate > 2.0, -10");
     1864                    fbp->segmentScore -= 10;
     1865                }
     1866            }
     1867
     1868            if ((!decoderFoundAspectChanges) &&
     1869                (fbp->formatMatch < (fbp->frames * .10)))
     1870            {
     1871                if (verboseDebugging)
     1872                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1873                        "      < 10% of frames match show letter/pillar-box "
     1874                        "format, -20");
     1875                fbp->segmentScore -= 20;
     1876            }
     1877
     1878            // check for common comm break lengths
     1879            for(QList<float>::iterator lit = commDetectCommLengths.begin(); lit != commDetectCommLengths.end(); ++lit)
     1880            {
     1881                if (abs(fbp->frames - ((*lit * fps) - fps/2 + 1)) <= (fps/2))
     1882                {
     1883#if 0
     1884                    if (subtitleInfoAvailable && (fbp->subCount > 0))
     1885                    {
     1886                        if (fbp->segmentScore < 0)
     1887                        {
     1888                            if (verboseDebugging)
     1889                                LOG(VB_COMMFLAG, LOG_DEBUG,
     1890                                    "      block has subtitles and seems to be standard comm length and comm marked, set to -5");
     1891                            fbp->segmentScore = -5;
     1892                        }
     1893                        break;
     1894                    }
     1895#endif
     1896                    if (verboseDebugging)
     1897                        LOG(VB_COMMFLAG, LOG_DEBUG,
     1898                            "      block appears to be standard comm length, -10");
     1899                    fbp->segmentScore -= 10;
     1900                    break;
     1901                }
     1902            }
     1903
     1904#if 0
     1905            // subtitle stuff
     1906            if (subtitleInfoAvailable)
     1907            {
     1908                if (fbp->subCount > 0)
     1909                {
     1910                    if (verboseDebugging)
     1911                        LOG(VB_COMMFLAG, LOG_DEBUG,
     1912                            "      block has subtitles, +5");
     1913                    fbp->segmentScore += 5;
     1914                }
     1915#if 0
     1916                else
     1917                {
     1918                    if (verboseDebugging)
     1919                        LOG(VB_COMMFLAG, LOG_DEBUG,
     1920                            "      subblock has no subtitles, -5");
     1921                    fbp->segmentScore -= 5;
     1922                }
     1923#endif
     1924            }
     1925#endif
     1926        }
     1927        else
     1928        {
     1929            if (verboseDebugging)
     1930                LOG(VB_COMMFLAG, LOG_DEBUG,
     1931                    QString("      FRAMES <= %1").arg(fps));
     1932
     1933            if ((logoInfoAvailable) &&
     1934                (fbp->start >= firstLogoFrame) &&
     1935                (fbp->logoCount == 0))
     1936            {
     1937                if (verboseDebugging)
     1938                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1939                        "      logoInfoAvailable && logoCount == 0, -10");
     1940                fbp->segmentScore -= 10;
     1941            }
     1942
     1943            if ((!decoderFoundAspectChanges) &&
     1944                (fbp->formatMatch < (fbp->frames * .10)))
     1945            {
     1946                if (verboseDebugging)
     1947                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1948                        "      < 10% of frames match show letter/pillar-box "
     1949                        "format, -10");
     1950                fbp->segmentScore -= 10;
     1951            }
     1952
     1953            if (fbp->ratingCount > (fbp->frames * 0.25))
     1954            {
     1955                if (verboseDebugging)
     1956                    LOG(VB_COMMFLAG, LOG_DEBUG,
     1957                        "      rating symbol present > 25% of time, +10");
     1958                fbp->segmentScore += 10;
     1959            }
     1960#if 0
     1961            if (subtitleInfoAvailable)
     1962            {
     1963                if (fbp->subCount > 0)
     1964                {
     1965                    if (verboseDebugging)
     1966                        LOG(VB_COMMFLAG, LOG_DEBUG,
     1967                            "      subblock has subtitles, +5");
     1968                    fbp->segmentScore += 25;
     1969                }
     1970            }
     1971#endif
     1972        }
     1973
     1974        if ((decoderFoundAspectChanges) &&
     1975            (fbp->aspectMatch < (fbp->frames * .10)))
     1976        {
     1977            if (verboseDebugging)
     1978                LOG(VB_COMMFLAG, LOG_DEBUG,
     1979                    "      < 10% of frames match show aspect, -20");
     1980            fbp->segmentScore -= 20;
     1981        }
     1982
     1983endInitialPass:
     1984        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, fbp));
     1985
     1986        lastScore = fbp->segmentScore;
     1987        curBlock++;
     1988    }
     1989
     1990    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
     1991    LOG(VB_COMMFLAG, LOG_INFO, "Initial Comm Block pass");
     1992    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
     1993    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
     1994    for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++)
     1995    {
     1996        cfbp = cfblock[curBlock];
     1997        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
     1998
     1999        vector<PFrameBlock>::iterator sbi;
     2000        cfbp->segmentScore = 0;
     2001        int64_t score = 0;
     2002        for(sbi=cfbp->subBlock.begin(); sbi != cfbp->subBlock.end(); ++sbi)
     2003        {
     2004            score += (*sbi)->segmentScore * (*sbi)->frames;
     2005        }
     2006        cfbp->segmentScore = (score + cfbp->frames / 2) / cfbp->frames;
     2007        if (cfbp->length >= commDetectMaxCommBreakLength)
     2008        {
     2009            // definitely program
     2010            //cfbp->segmentScore += 100;
     2011        }
     2012        else if (cfbp->length >= (commDetectCommLengths[0] - 1))
     2013        {
     2014            // may be commercial. check subBlocks
     2015            int subBlockLengthMatchCount = 0;
     2016            for(sbi=cfbp->subBlock.begin(); sbi != cfbp->subBlock.end(); ++sbi)
     2017            {
     2018                int frames = (*sbi)->frames;
     2019                // check for common comm break lengths
     2020                for(QList<float>::iterator lit = commDetectCommLengths.begin(); lit != commDetectCommLengths.end(); ++lit)
     2021                {
     2022                    if (abs(frames - ((*lit * fps) - fps/2 + 1)) <= (fps/2))
     2023                    {
     2024                        if (verboseDebugging)
     2025                            LOG(VB_COMMFLAG, LOG_DEBUG,
     2026                                "      subblock appears to be standard comm length, -10");
     2027                        cfbp->segmentScore -= 10;
     2028                        subBlockLengthMatchCount++;
     2029                        break;
     2030                    }
     2031                }
     2032            }
     2033            if (subBlockLengthMatchCount == 0)
     2034            {
     2035                // no matches so
     2036                // try to split this block a bit more with modified scene cut thresh.
     2037                for(int frame = cfbp->start; frame < cfbp->end; frame++)
     2038                {
     2039                   
     2040                }
     2041            }
     2042        }
     2043        else
     2044        {
     2045            // too short so try to coalesce
     2046            // probably silent credits (black with writing and no music)
     2047            cfbp->transitionScore += -10; // mark for merge
     2048        }
     2049#if 0
     2050        if ((subtitleFrameCount > (0.5 * framesProcessed)) &&
     2051            (cfbp->subCount == 0))
     2052        {
     2053            if (verboseDebugging)
     2054                LOG(VB_COMMFLAG, LOG_DEBUG,
     2055                    "      subblock has no subtitles but they were expected, -1");
     2056            cfbp->segmentScore -= 1;
     2057        }
     2058#endif
     2059        if (cfbp->subCount > 0)
     2060        {
     2061            if (verboseDebugging)
     2062                LOG(VB_COMMFLAG, LOG_DEBUG,
     2063                    "      subblock has subtitles, +5");
     2064            cfbp->segmentScore += 5;
     2065        }
     2066        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp));
     2067    }
     2068
     2069    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
     2070    LOG(VB_COMMFLAG, LOG_INFO, "Merge same Comm Block pass");
     2071    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
     2072    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
     2073    for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++)
     2074    {
     2075        cfbp = cfblock[curBlock];
     2076        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
     2077
     2078        // next one exists
     2079        if ((curBlock + 1) < (int)cfblock.size())
     2080        {
     2081            PFrameBlock cfbp2 = cfblock[curBlock+1];
     2082            if (curBlock == 0 && cfbp->segmentScore == 0 && cfbp2->segmentScore != 0)
     2083            {
     2084                cfbp->segmentScore += 5;
     2085                if (verboseDebugging)
     2086                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2087                        "      unlabelled first segment, set to opposite of 2nd for indexing");
     2088            }
     2089            if ((cfbp->segmentScore < 0 && cfbp2->segmentScore == 0 &&
     2090                 (cfbp->length + cfbp2->length) >= commDetectMaxCommBreakLength))
     2091            {
     2092                // cant be a comm as the length is over so mark next as program
     2093                cfbp2->segmentScore += 10;
     2094                LOG(VB_COMMFLAG, LOG_DEBUG,
     2095                    "      comm length + next length > commDetectMaxCommBreakLength, next +10");
     2096            }
     2097            if ((cfbp->segmentScore < 0 && cfbp2->segmentScore < 0) ||
     2098                (cfbp->segmentScore > 0 && cfbp2->segmentScore > 0) ||
     2099                (cfbp->segmentScore == 0 && cfbp2->segmentScore == 0 &&
     2100                 cfbp->transitionScore < 0 && cfbp2->transitionScore < 0) ||
     2101                (cfbp->segmentScore != 0 && cfbp2->segmentScore == 0 &&
     2102                 cfbp2->transitionScore < 0 && cfbp2->length <= 1) ||
     2103                (cfbp->segmentScore == 0 && cfbp2->segmentScore != 0 &&
     2104                 cfbp->transitionScore < 0 && cfbp->length <= 1)
     2105               )
     2106            {
     2107                if (verboseDebugging)
     2108                {
     2109                    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock+1, cfbp2));
     2110                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2111                        "      merge with next");
     2112                }
     2113                cfbp->Merge(*cfbp2);
     2114                cfblock.erase(cfblock.begin()+curBlock+1);
     2115                // reprocess
     2116                curBlock--;
     2117            }
     2118        }
     2119        if (curBlock == ((int)cfblock.size()-1) && curBlock > 0)
     2120        {
     2121            // last block has change of sign from previous block to fora a seek point
     2122            if (cfbp->segmentScore == 0 && cfblock[curBlock-1]->segmentScore != 0)
     2123                cfbp->segmentScore = - cfblock[curBlock-1]->segmentScore;
     2124        }
     2125        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp));
     2126    }
     2127
     2128    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
     2129    LOG(VB_COMMFLAG, LOG_INFO, "Merge Comm Block pass 2");
     2130    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
     2131    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
     2132    for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++)
     2133    {
     2134        cfbp = cfblock[curBlock];
     2135        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
     2136
     2137        // next one exists
     2138        if ((curBlock + 1) < (int)cfblock.size())
     2139        {
     2140        }
     2141
     2142        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp));
     2143    }
     2144
     2145    curBlock = 0;
     2146    lastScore = 0;
     2147
     2148    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
     2149    LOG(VB_COMMFLAG, LOG_INFO, "Second Block pass");
     2150    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
     2151    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
     2152    while (curBlock < maxBlock)
     2153    {
     2154        fbp = fblock[curBlock];
     2155
     2156        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp));
     2157
     2158        if ((curBlock >= 0) && (curBlock < (maxBlock-1)))
     2159        {
     2160            nextScore = fblock[curBlock + 1]->segmentScore;
     2161
     2162            if ((lastScore < 0) && (nextScore < 0) && (fbp->length < 35))
     2163            {
     2164                if (verboseDebugging)
     2165                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2166                        "      lastScore < 0 && nextScore < 0 "
     2167                        "&& length < 35, setting -10");
     2168                fbp->segmentScore -= 10;
     2169            }
     2170
     2171            if ((fbp->bfCount > (fbp->frames * 0.95)) &&
     2172                (fbp->frames < (2*fps)) &&
     2173                (lastScore < 0 && nextScore < 0))
     2174            {
     2175                if (verboseDebugging)
     2176                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2177                        "      blanks > frames * 0.95 && frames < 2*fps && "
     2178                        "lastScore < 0 && nextScore < 0, setting -10");
     2179                fbp->segmentScore -= 10;
     2180            }
     2181            if ((fbp->saCount > (fbp->frames * 0.95)) &&
     2182                (fbp->frames < (2*fps)) &&
     2183                (lastScore < 0 && nextScore < 0))
     2184            {
     2185                if (verboseDebugging)
     2186                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2187                        "      silent > frames * 0.95 && frames < 2*fps && "
     2188                        "lastScore < 0 && nextScore < 0, setting -10");
     2189                fbp->segmentScore -= 10;
     2190            }
     2191
     2192            if ((fbp->frames < (120*fps)) &&
     2193                (lastScore < 0) &&
     2194                (fbp->segmentScore > 0) &&
     2195                (fbp->segmentScore < 20) &&
     2196                (nextScore < 0))
     2197            {
     2198                if (verboseDebugging)
     2199                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2200                        "      frames < 120 * fps && (-20 < lastScore < 0) && "
     2201                        "thisScore > 0 && nextScore < 0, setting score = -10");
     2202                fbp->segmentScore = -10;
     2203            }
     2204
     2205            if ((fbp->frames < (30*fps)) &&
     2206                (lastScore > 0) &&
     2207                (fbp->segmentScore < 0) &&
     2208                (fbp->segmentScore > -20) &&
     2209                (nextScore > 0))
     2210            {
     2211                if (verboseDebugging)
     2212                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2213                        "      frames < 30 * fps && (0 < lastScore < 20) && "
     2214                        "thisScore < 0 && nextScore > 0, setting score = 10");
     2215                fbp->segmentScore = 10;
     2216            }
     2217        }
     2218
     2219        if ((fbp->segmentScore == 0) && (lastScore > 30))
     2220        {
     2221            int offset = 1;
     2222            while(((curBlock + offset) < maxBlock) &&
     2223                    (fblock[curBlock + offset]->frames < (2 * fps)) &&
     2224                    (fblock[curBlock + offset]->segmentScore == 0))
     2225                offset++;
     2226
     2227            if ((curBlock + offset) < maxBlock)
     2228            {
     2229                offset--;
     2230                if (fblock[curBlock + offset + 1]->segmentScore > 0)
     2231                {
     2232                    for (; offset >= 0; offset--)
     2233                    {
     2234                        fblock[curBlock + offset]->segmentScore += 10;
     2235                        if (verboseDebugging)
     2236                            LOG(VB_COMMFLAG, LOG_DEBUG,
     2237                                QString("      Setting block %1 score +10")
     2238                                    .arg(curBlock+offset));
     2239                    }
     2240                }
     2241                else if (fblock[curBlock + offset + 1]->segmentScore < 0)
     2242                {
     2243                    for (; offset >= 0; offset--)
     2244                    {
     2245                        fblock[curBlock + offset]->segmentScore -= 10;
     2246                        if (verboseDebugging)
     2247                            LOG(VB_COMMFLAG, LOG_DEBUG,
     2248                                QString("      Setting block %1 score -10")
     2249                                    .arg(curBlock+offset));
     2250                    }
     2251                }
     2252            }
     2253        }
     2254
     2255        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, fbp));
     2256
     2257        lastScore = fbp->segmentScore;
     2258        curBlock++;
     2259    }
     2260
     2261    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
     2262    LOG(VB_COMMFLAG, LOG_INFO, "FINAL Block stats");
     2263    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
     2264    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
     2265    curBlock = 0;
     2266    lastScore = 0;
     2267    breakStart = -1;
     2268    while (curBlock < maxBlock)
     2269    {
     2270        fbp = fblock[curBlock];
     2271        thisScore = fbp->segmentScore;
     2272
     2273        if ((breakStart >= 0) &&
     2274            ((fbp->end - breakStart) > (commDetectMaxCommBreakLength * fps)))
     2275        {
     2276            if (((fbp->start - breakStart) >
     2277                (commDetectMinCommBreakLength * fps)) ||
     2278                (breakStart == 0))
     2279            {
     2280                if (verboseDebugging)
     2281                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2282                        QString("Closing commercial block at start of "
     2283                                "frame block %1 with length %2, frame "
     2284                                "block length of %3 frames would put comm "
     2285                                "block length over max of %4 seconds.")
     2286                            .arg(curBlock).arg(fbp->start - breakStart)
     2287                            .arg(fbp->frames)
     2288                            .arg(commDetectMaxCommBreakLength));
     2289
     2290                commBreakMap[breakStart] = MARK_COMM_START;
     2291                commBreakMap[fbp->start] = MARK_COMM_END;
     2292                lastStart = breakStart;
     2293                lastEnd = fbp->start;
     2294                breakStart = -1;
     2295            }
     2296            else
     2297            {
     2298                if (verboseDebugging)
     2299                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2300                        QString("Ignoring what appears to be commercial"
     2301                                " block at frame %1 with length %2, "
     2302                                "length of %3 frames would put comm "
     2303                                "block length under min of %4 seconds.")
     2304                            .arg(breakStart)
     2305                            .arg(fbp->start - breakStart)
     2306                            .arg(fbp->frames)
     2307                            .arg(commDetectMinCommBreakLength));
     2308                breakStart = -1;
     2309            }
     2310        }
     2311        if (thisScore == 0)
     2312        {
     2313            thisScore = lastScore;
     2314        }
     2315        else if (thisScore < 0)
     2316        {
     2317            if ((lastScore > 0) || (curBlock == 0))
     2318            {
     2319                if ((fbp->start - lastEnd) < (commDetectMinShowLength * fps))
     2320                {
     2321                    commBreakMap.remove(lastStart);
     2322                    commBreakMap.remove(lastEnd);
     2323                    breakStart = lastStart;
     2324
     2325                    if (verboseDebugging)
     2326                    {
     2327                        if (breakStart)
     2328                            LOG(VB_COMMFLAG, LOG_DEBUG,
     2329                                QString("ReOpening commercial block at "
     2330                                        "frame %1 because show less than "
     2331                                        "%2 seconds")
     2332                                    .arg(breakStart)
     2333                                    .arg(commDetectMinShowLength));
     2334                        else
     2335                            LOG(VB_COMMFLAG, LOG_DEBUG,
     2336                                "Opening initial commercial block "
     2337                                "at start of recording, block 0.");
     2338                    }
     2339                }
     2340                else
     2341                {
     2342                    breakStart = fbp->start;
     2343
     2344                    if (verboseDebugging)
     2345                        LOG(VB_COMMFLAG, LOG_DEBUG,
     2346                            QString("Starting new commercial block at "
     2347                                    "frame %1 from start of frame block %2")
     2348                                .arg(fbp->start).arg(curBlock));
     2349                }
     2350            }
     2351            else if (curBlock == (maxBlock-1))
     2352            {
     2353                if ((fbp->end - breakStart) >
     2354                    (commDetectMinCommBreakLength * fps))
     2355                {
     2356                    if (fbp->end <=
     2357                        ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2))
     2358                    {
     2359                        if (verboseDebugging)
     2360                            LOG(VB_COMMFLAG, LOG_DEBUG,
     2361                                QString("Closing final commercial block at "
     2362                                        "frame %1").arg(fbp->end));
     2363
     2364                        commBreakMap[breakStart] = MARK_COMM_START;
     2365                        commBreakMap[fbp->end] = MARK_COMM_END;
     2366                        lastStart = breakStart;
     2367                        lastEnd = fbp->end;
     2368                        breakStart = -1;
     2369                    }
     2370                }
     2371                else
     2372                {
     2373                    if (verboseDebugging)
     2374                        LOG(VB_COMMFLAG, LOG_DEBUG,
     2375                            QString("Ignoring what appears to be commercial"
     2376                                    " block at frame %1 with length %2, "
     2377                                    "length of %3 frames would put comm "
     2378                                    "block length under min of %4 seconds.")
     2379                                .arg(breakStart)
     2380                                .arg(fbp->start - breakStart)
     2381                                .arg(fbp->frames)
     2382                                .arg(commDetectMinCommBreakLength));
     2383                    breakStart = -1;
     2384                }
     2385            }
     2386        }
     2387        else if ((thisScore > 0) &&
     2388                 (lastScore < 0) &&
     2389                 (breakStart != -1))
     2390        {
     2391            if (((fbp->start - breakStart) >
     2392                (commDetectMinCommBreakLength * fps)) ||
     2393                (breakStart == 0))
     2394            {
     2395                commBreakMap[breakStart] = MARK_COMM_START;
     2396                commBreakMap[fbp->start] = MARK_COMM_END;
     2397                lastStart = breakStart;
     2398                lastEnd = fbp->start;
     2399
     2400                if (verboseDebugging)
     2401                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2402                        QString("Closing commercial block at frame %1")
     2403                            .arg(fbp->start));
     2404            }
     2405            else
     2406            {
     2407                if (verboseDebugging)
     2408                    LOG(VB_COMMFLAG, LOG_DEBUG,
     2409                        QString("Ignoring what appears to be commercial "
     2410                                "block at frame %1 with length %2, "
     2411                                "length of %3 frames would put comm block "
     2412                                "length under min of %4 seconds.")
     2413                            .arg(breakStart)
     2414                            .arg(fbp->start - breakStart)
     2415                            .arg(fbp->frames)
     2416                            .arg(commDetectMinCommBreakLength));
     2417            }
     2418            breakStart = -1;
     2419        }
     2420
     2421        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp, &thisScore));
     2422
     2423        lastScore = thisScore;
     2424        curBlock++;
     2425    }
     2426
     2427    if ((breakStart != -1) &&
     2428        (breakStart <= ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2)))
     2429    {
     2430        if (verboseDebugging)
     2431            LOG(VB_COMMFLAG, LOG_DEBUG,
     2432                QString("Closing final commercial block started at "
     2433                        "block %1 and going to end of program. length "
     2434                        "is %2 frames")
     2435                    .arg(curBlock)
     2436                    .arg((framesProcessed - breakStart - 1)));
     2437
     2438        commBreakMap[breakStart] = MARK_COMM_START;
     2439        // Create what is essentially an open-ended final skip region
     2440        // by setting the end point 10 seconds past the end of the
     2441        // recording.
     2442        commBreakMap[framesProcessed + (10 * fps)] = MARK_COMM_END;
     2443    }
     2444
     2445    // include/exclude blanks from comm breaks
     2446    tmpCommMap = commBreakMap;
     2447    commBreakMap.clear();
     2448
     2449    if (verboseDebugging)
     2450        LOG(VB_COMMFLAG, LOG_DEBUG,
     2451            "Adjusting start/end marks according to blanks.");
     2452    for (it = tmpCommMap.begin(); it != tmpCommMap.end(); ++it)
     2453    {
     2454        if (*it == MARK_COMM_START)
     2455        {
     2456            uint64_t lastStartLower = it.key();
     2457            uint64_t lastStartUpper = it.key();
     2458            while ((lastStartLower > 0) &&
     2459                   (frameInfo[lastStartLower - 1].flagMask & COMM_FRAME_BLANK))
     2460                lastStartLower--;
     2461            while ((lastStartUpper < (framesProcessed - (2 * fps))) &&
     2462                   (frameInfo[lastStartUpper + 1].flagMask & COMM_FRAME_BLANK))
     2463                lastStartUpper++;
     2464            uint64_t adj = (lastStartUpper - lastStartLower) / 2;
     2465            if (adj > MAX_BLANK_FRAMES)
     2466                adj = MAX_BLANK_FRAMES;
     2467            lastStart = lastStartLower + adj;
     2468
     2469            if (verboseDebugging)
     2470                LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark: %1 -> %2")
     2471                        .arg(it.key()).arg(lastStart));
     2472
     2473            commBreakMap[lastStart] = MARK_COMM_START;
     2474        }
     2475        else
     2476        {
     2477            uint64_t lastEndLower = it.key();
     2478            uint64_t lastEndUpper = it.key();
     2479            while ((lastEndUpper < (framesProcessed - (2 * fps))) &&
     2480                   (frameInfo[lastEndUpper + 1].flagMask & COMM_FRAME_BLANK))
     2481                lastEndUpper++;
     2482            while ((lastEndLower > 0) &&
     2483                   (frameInfo[lastEndLower - 1].flagMask & COMM_FRAME_BLANK))
     2484                lastEndLower--;
     2485            uint64_t adj = (lastEndUpper - lastEndLower) / 2;
     2486            if (adj > MAX_BLANK_FRAMES)
     2487                adj = MAX_BLANK_FRAMES;
     2488            lastEnd = lastEndUpper - adj;
     2489
     2490            if (verboseDebugging)
     2491                LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark  : %1 -> %2")
     2492                        .arg(it.key()).arg(lastEnd));
     2493
     2494            commBreakMap[lastEnd] = MARK_COMM_END;
     2495        }
     2496    }
     2497
     2498    // new one
     2499    if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
     2500        commBreakMap.clear();
     2501    lastScore = 0;
     2502    LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
     2503    LOG(VB_COMMFLAG, LOG_INFO, "Generate Comm Breaks pass");
     2504    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1());
     2505    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2());
     2506    cfbp = cfblock[0];
     2507    LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(0, cfbp));
     2508    // is first one a comm
     2509    if ((lastScore = cfbp->segmentScore) < 0)
     2510    {
     2511        if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
     2512        commBreakMap[0] = MARK_COMM_START;
     2513        if (verboseDebugging)
     2514            LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark  : %1")
     2515                    .arg(0));
     2516    }
     2517    for(curBlock = 1; curBlock < (int)cfblock.size(); curBlock++)
     2518    {
     2519        cfbp = cfblock[curBlock];
     2520        LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp));
     2521        if (cfbp->segmentScore != 0)
     2522        {
     2523            if (lastScore < 0 && cfbp->segmentScore > 0)
     2524            {
     2525                if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
     2526                    commBreakMap[cfbp->start + cfbp->subBlock[0]->frames/2] = MARK_COMM_END;
     2527                if (verboseDebugging)
     2528                    LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark  : %1")
     2529                            .arg(cfbp->start + cfbp->subBlock[0]->frames/2));
     2530            }
     2531            if (lastScore > 0 && cfbp->segmentScore < 0)
     2532            {
     2533                if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
     2534                    commBreakMap[cfbp->start + cfbp->subBlock[0]->frames/2] = MARK_COMM_START;
     2535                if (verboseDebugging)
     2536                    LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark  : %1")
     2537                            .arg(cfbp->start + cfbp->subBlock[0]->frames/2));
     2538            }
     2539            lastScore = cfbp->segmentScore;
     2540        }
     2541    }
     2542    if (lastScore < 0)
     2543    {
     2544        if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0)
     2545            commBreakMap[cfbp->end + fps * 2 + 1] = MARK_COMM_END;
     2546        if (verboseDebugging)
     2547            LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark  : %1")
     2548                    .arg(cfbp->end + fps * 2 + 1));
     2549    }
     2550}
     2551
     2552
     2553void NextgenCommDetector::BuildBlankFrameCommList(void)
     2554{
     2555    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildBlankFrameCommList()");
     2556
     2557    long long *bframes = new long long[blankFrameMap.count()*2];
     2558    long long *c_start = new long long[blankFrameMap.count()];
     2559    long long *c_end   = new long long[blankFrameMap.count()];
     2560    int frames = 0;
     2561    int commercials = 0;
     2562    int i, x;
     2563    frm_dir_map_t::iterator it;
     2564
     2565    blankCommMap.clear();
     2566
     2567    for (it = blankFrameMap.begin(); it != blankFrameMap.end(); ++it)
     2568        bframes[frames++] = it.key();
     2569
     2570    if (frames == 0)
     2571    {
     2572        delete[] c_start;
     2573        delete[] c_end;
     2574        delete[] bframes;
     2575        return;
     2576    }
     2577
     2578    // detect individual commercials from blank frames
     2579    // commercial end is set to frame right before ending blank frame to
     2580    //    account for instances with only a single blank frame between comms.
     2581    for(i = 0; i < frames; i++ )
     2582    {
     2583        for(x=i+1; x < frames; x++ )
     2584        {
     2585            // check for various length spots since some channels don't
     2586            // have blanks inbetween commercials just at the beginning and
     2587            // end of breaks
     2588            int gap_length = bframes[x] - bframes[i];
     2589            if (((aggressiveDetection) &&
     2590                ((abs((int)(gap_length - (5 * fps))) < 5 ) ||
     2591                 (abs((int)(gap_length - (10 * fps))) < 7 ) ||
     2592                 (abs((int)(gap_length - (15 * fps))) < 10 ) ||
     2593                 (abs((int)(gap_length - (20 * fps))) < 11 ) ||
     2594                 (abs((int)(gap_length - (30 * fps))) < 12 ) ||
     2595                 (abs((int)(gap_length - (40 * fps))) < 1 ) ||
     2596                 (abs((int)(gap_length - (45 * fps))) < 1 ) ||
     2597                 (abs((int)(gap_length - (60 * fps))) < 15 ) ||
     2598                 (abs((int)(gap_length - (90 * fps))) < 10 ) ||
     2599                 (abs((int)(gap_length - (120 * fps))) < 10 ))) ||
     2600                ((!aggressiveDetection) &&
     2601                 ((abs((int)(gap_length - (5 * fps))) < 11 ) ||
     2602                  (abs((int)(gap_length - (10 * fps))) < 13 ) ||
     2603                  (abs((int)(gap_length - (15 * fps))) < 16 ) ||
     2604                  (abs((int)(gap_length - (20 * fps))) < 17 ) ||
     2605                  (abs((int)(gap_length - (30 * fps))) < 18 ) ||
     2606                  (abs((int)(gap_length - (40 * fps))) < 3 ) ||
     2607                  (abs((int)(gap_length - (45 * fps))) < 3 ) ||
     2608                  (abs((int)(gap_length - (60 * fps))) < 20 ) ||
     2609                  (abs((int)(gap_length - (90 * fps))) < 20 ) ||
     2610                  (abs((int)(gap_length - (120 * fps))) < 20 ))))
     2611            {
     2612                c_start[commercials] = bframes[i];
     2613                c_end[commercials] = bframes[x] - 1;
     2614                commercials++;
     2615                i = x-1;
     2616                x = frames;
     2617            }
     2618
     2619            if ((!aggressiveDetection) &&
     2620                ((abs((int)(gap_length - (30 * fps))) < (int)(fps * 0.85)) ||
     2621                 (abs((int)(gap_length - (60 * fps))) < (int)(fps * 0.95)) ||
     2622                 (abs((int)(gap_length - (90 * fps))) < (int)(fps * 1.05)) ||
     2623                 (abs((int)(gap_length - (120 * fps))) < (int)(fps * 1.15))) &&
     2624                ((x + 2) < frames) &&
     2625                ((i + 2) < frames) &&
     2626                ((bframes[i] + 1) == bframes[i+1]) &&
     2627                ((bframes[x] + 1) == bframes[x+1]))
     2628            {
     2629                c_start[commercials] = bframes[i];
     2630                c_end[commercials] = bframes[x];
     2631                commercials++;
     2632                i = x;
     2633                x = frames;
     2634            }
     2635        }
     2636    }
     2637
     2638    i = 0;
     2639
     2640    // don't allow single commercial at head
     2641    // of show unless followed by another
     2642    if ((commercials > 1) &&
     2643        (c_end[0] < (33 * fps)) &&
     2644        (c_start[1] > (c_end[0] + 40 * fps)))
     2645        i = 1;
     2646
     2647    // eliminate any blank frames at end of commercials
     2648    bool first_comm = true;
     2649    for(; i < (commercials-1); i++)
     2650    {
     2651        long long r = c_start[i];
     2652        long long adjustment = 0;
     2653
     2654        if ((r < (30 * fps)) &&
     2655            (first_comm))
     2656            r = 1;
     2657
     2658        blankCommMap[r] = MARK_COMM_START;
     2659
     2660        r = c_end[i];
     2661        if ( i < (commercials-1))
     2662        {
     2663            for(x = 0; x < (frames-1); x++)
     2664                if (bframes[x] == r)
     2665                    break;
     2666            while((x < (frames-1)) &&
     2667                    ((bframes[x] + 1 ) == bframes[x+1]) &&
     2668                    (bframes[x+1] < c_start[i+1]))
     2669            {
     2670                r++;
     2671                x++;
     2672            }
     2673
     2674            while((blankFrameMap.contains(r+1)) &&
     2675                  (c_start[i+1] != (r+1)))
     2676                {
     2677                    r++;
     2678                    adjustment++;
     2679                }
     2680        }
     2681        else
     2682        {
     2683            while(blankFrameMap.contains(r+1))
     2684            {
     2685                r++;
     2686                adjustment++;
     2687            }
     2688        }
     2689
     2690        adjustment /= 2;
     2691        if (adjustment > MAX_BLANK_FRAMES)
     2692            adjustment = MAX_BLANK_FRAMES;
     2693        r -= adjustment;
     2694        blankCommMap[r] = MARK_COMM_END;
     2695        first_comm = false;
     2696    }
     2697
     2698    blankCommMap[c_start[i]] = MARK_COMM_START;
     2699    blankCommMap[c_end[i]] = MARK_COMM_END;
     2700
     2701    delete[] c_start;
     2702    delete[] c_end;
     2703    delete[] bframes;
     2704
     2705    LOG(VB_COMMFLAG, LOG_INFO, "Blank-Frame Commercial Map" );
     2706    for(it = blankCommMap.begin(); it != blankCommMap.end(); ++it)
     2707        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
     2708                .arg(it.key()).arg(*it));
     2709
     2710    MergeBlankCommList();
     2711
     2712    LOG(VB_COMMFLAG, LOG_INFO, "Merged Blank-Frame Commercial Break Map" );
     2713    for(it = blankCommBreakMap.begin(); it != blankCommBreakMap.end(); ++it)
     2714        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
     2715                .arg(it.key()).arg(*it));
     2716}
     2717
     2718void NextgenCommDetector::BuildAudioFrameCommList(void)
     2719{
     2720    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildBlankFrameCommList()");
     2721
     2722    audioCommBreakMap.clear();
     2723    // TODO
     2724}
     2725
     2726void NextgenCommDetector::BuildSceneChangeCommList(void)
     2727{
     2728    int section_start = -1;
     2729    int seconds = (int)(framesProcessed / fps);
     2730    int *sc_histogram = new int[seconds+1];
     2731
     2732    sceneCommBreakMap.clear();
     2733
     2734    memset(sc_histogram, 0, (seconds+1)*sizeof(int));
     2735    for (uint64_t f = 1; f <= framesProcessed; f++)
     2736    {
     2737        if (sceneMap.contains(f))
     2738            sc_histogram[(uint64_t)(f / fps)]++;
     2739    }
     2740
     2741    for(long long s = 0; s < (seconds + 1); s++)
     2742    {
     2743        if (sc_histogram[s] > 2)
     2744        {
     2745            if (section_start == -1)
     2746            {
     2747                long long f = (long long)(s * fps);
     2748                for(int i = 0; i < fps; i++, f++)
     2749                {
     2750                    if (sceneMap.contains(f))
     2751                    {
     2752                        sceneCommBreakMap[f] = MARK_COMM_START;
     2753                        i = (int)(fps) + 1;
     2754                    }
     2755                }
     2756            }
     2757
     2758            section_start = s;
     2759        }
     2760
     2761        if ((section_start >= 0) &&
     2762            (s > (section_start + 32)))
     2763        {
     2764            long long f = (long long)(section_start * fps);
     2765            bool found_end = false;
     2766
     2767            for(int i = 0; i < fps; i++, f++)
     2768            {
     2769                if (sceneMap.contains(f))
     2770                {
     2771                    frm_dir_map_t::iterator dit =  sceneCommBreakMap.find(f);
     2772                    if (dit != sceneCommBreakMap.end())
     2773                        sceneCommBreakMap.erase(dit);
     2774                    else
     2775                        sceneCommBreakMap[f] = MARK_COMM_END;
     2776                    i = (int)(fps) + 1;
     2777                    found_end = true;
     2778                }
     2779            }
     2780            section_start = -1;
     2781
     2782            if (!found_end)
     2783            {
     2784                f = (long long)(section_start * fps);
     2785                sceneCommBreakMap[f] = MARK_COMM_END;
     2786            }
     2787        }
     2788    }
     2789    delete[] sc_histogram;
     2790
     2791    if (section_start >= 0)
     2792        sceneCommBreakMap[framesProcessed] = MARK_COMM_END;
     2793
     2794    frm_dir_map_t deleteMap;
     2795    frm_dir_map_t::iterator it = sceneCommBreakMap.begin();
     2796    frm_dir_map_t::iterator prev = it;
     2797    if (it != sceneCommBreakMap.end())
     2798    {
     2799        ++it;
     2800        while (it != sceneCommBreakMap.end())
     2801        {
     2802            if ((*it == MARK_COMM_END) &&
     2803                (it.key() - prev.key()) < (30 * fps))
     2804            {
     2805                deleteMap[it.key()] = MARK_CUT_START;
     2806                deleteMap[prev.key()] = MARK_CUT_START;
     2807            }
     2808            ++prev;
     2809            if (it != sceneCommBreakMap.end())
     2810                ++it;
     2811        }
     2812
     2813        frm_dir_map_t::iterator dit;
     2814        for (dit = deleteMap.begin(); dit != deleteMap.end(); ++dit)
     2815            sceneCommBreakMap.remove(dit.key());
     2816    }
     2817
     2818    LOG(VB_COMMFLAG, LOG_INFO, "Scene-Change Commercial Break Map" );
     2819    for (it = sceneCommBreakMap.begin(); it != sceneCommBreakMap.end(); ++it)
     2820    {
     2821        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
     2822            .arg(it.key()).arg(*it));
     2823    }
     2824}
     2825
     2826
     2827void NextgenCommDetector::BuildLogoCommList()
     2828{
     2829    show_map_t showmap;
     2830    GetLogoCommBreakMap(showmap);
     2831    CondenseMarkMap(showmap, (int)(25 * fps), (int)(30 * fps));
     2832    ConvertShowMapToCommMap(logoCommBreakMap, showmap);
     2833
     2834    frm_dir_map_t::iterator it;
     2835    LOG(VB_COMMFLAG, LOG_INFO, "Logo Commercial Break Map" );
     2836    for(it = logoCommBreakMap.begin(); it != logoCommBreakMap.end(); ++it)
     2837        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
     2838                .arg(it.key()).arg(*it));
     2839}
     2840
     2841void NextgenCommDetector::MergeBlankCommList(void)
     2842{
     2843    frm_dir_map_t::iterator it;
     2844    frm_dir_map_t::iterator prev;
     2845    QMap<long long, long long> tmpMap;
     2846    QMap<long long, long long>::Iterator tmpMap_it;
     2847    QMap<long long, long long>::Iterator tmpMap_prev;
     2848
     2849    blankCommBreakMap.clear();
     2850
     2851    if (blankCommMap.isEmpty())
     2852        return;
     2853
     2854    for (it = blankCommMap.begin(); it != blankCommMap.end(); ++it)
     2855        blankCommBreakMap[it.key()] = *it;
     2856
     2857    if (blankCommBreakMap.isEmpty())
     2858        return;
     2859
     2860    it = blankCommMap.begin();
     2861    prev = it;
     2862    ++it;
     2863    for(; it != blankCommMap.end(); ++it, ++prev)
     2864    {
     2865        // if next commercial starts less than 15*fps frames away then merge
     2866        if ((((prev.key() + 1) == it.key()) ||
     2867            ((prev.key() + (15 * fps)) > it.key())) &&
     2868            (*prev == MARK_COMM_END) &&
     2869            (*it == MARK_COMM_START))
     2870        {
     2871            blankCommBreakMap.remove(prev.key());
     2872            blankCommBreakMap.remove(it.key());
     2873        }
     2874    }
     2875
     2876
     2877    // make temp copy of commercial break list
     2878    it = blankCommBreakMap.begin();
     2879    prev = it;
     2880    ++it;
     2881    tmpMap[prev.key()] = it.key();
     2882    for(; it != blankCommBreakMap.end(); ++it, ++prev)
     2883    {
     2884        if ((*prev == MARK_COMM_START) &&
     2885            (*it == MARK_COMM_END))
     2886            tmpMap[prev.key()] = it.key();
     2887    }
     2888
     2889    tmpMap_it = tmpMap.begin();
     2890    tmpMap_prev = tmpMap_it;
     2891    tmpMap_it++;
     2892    for(; tmpMap_it != tmpMap.end(); ++tmpMap_it, ++tmpMap_prev)
     2893    {
     2894        // if we find any segments less than 35 seconds between commercial
     2895        // breaks include those segments in the commercial break.
     2896        if (((*tmpMap_prev + (35 * fps)) > tmpMap_it.key()) &&
     2897            ((*tmpMap_prev - tmpMap_prev.key()) > (35 * fps)) &&
     2898            ((*tmpMap_it - tmpMap_it.key()) > (35 * fps)))
     2899        {
     2900            blankCommBreakMap.remove(*tmpMap_prev);
     2901            blankCommBreakMap.remove(tmpMap_it.key());
     2902        }
     2903    }
     2904}
     2905
     2906bool NextgenCommDetector::FrameIsInBreakMap(
     2907    uint64_t f, const frm_dir_map_t &breakMap) const
     2908{
     2909    for (uint64_t i = f; i < framesProcessed; i++)
     2910    {
     2911        if (breakMap.contains(i))
     2912        {
     2913            int type = breakMap[i];
     2914            if ((type == MARK_COMM_END) || (i == f))
     2915                return true;
     2916            if (type == MARK_COMM_START)
     2917                return false;
     2918        }
     2919    }
     2920
     2921    // We want from f down to 0, but without wrapping the counter to negative
     2922    // on an unsigned counter.
     2923    for (uint64_t i = (f + 1); i-- > 0; )
     2924    {
     2925        if (breakMap.contains(i))
     2926        {
     2927            int type = breakMap[i];
     2928            if ((type == MARK_COMM_START) || (i == f))
     2929                return true;
     2930            if (type == MARK_COMM_END)
     2931                return false;
     2932        }
     2933    }
     2934
     2935    return false;
     2936}
     2937
     2938void NextgenCommDetector::DumpMap(frm_dir_map_t &map)
     2939{
     2940    frm_dir_map_t::iterator it;
     2941    QString msg;
     2942
     2943    LOG(VB_COMMFLAG, LOG_INFO,
     2944        "---------------------------------------------------");
     2945    for (it = map.begin(); it != map.end(); ++it)
     2946    {
     2947        long long frame = it.key();
     2948        int flag = *it;
     2949        int my_fps = (int)ceil(fps);
     2950        int hour = (frame / my_fps) / 60 / 60;
     2951        int min = (frame / my_fps) / 60 - (hour * 60);
     2952        int sec = (frame / my_fps) - (min * 60) - (hour * 60 * 60);
     2953        int frm = frame - ((sec * my_fps) + (min * 60 * my_fps) +
     2954                           (hour * 60 * 60 * my_fps));
     2955        int my_sec = (int)(frame / my_fps);
     2956        msg.sprintf("%7ld : %d (%02d:%02d:%02d.%02d) (%d)",
     2957                    (long)frame, flag, hour, min, sec, frm, my_sec);
     2958        LOG(VB_COMMFLAG, LOG_INFO, msg);
     2959    }
     2960    LOG(VB_COMMFLAG, LOG_INFO,
     2961        "---------------------------------------------------");
     2962}
     2963
     2964void NextgenCommDetector::CondenseMarkMap(show_map_t &map, int spacing,
     2965                                          int length)
     2966{
     2967    show_map_t::iterator it;
     2968    show_map_t::iterator prev;
     2969    show_map_t tmpMap;
     2970
     2971    if (map.size() <= 2)
     2972        return;
     2973
     2974    // merge any segments less than 'spacing' frames apart from each other
     2975    LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map Before condense:" );
     2976    for (it = map.begin(); it != map.end(); ++it)
     2977    {
     2978        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
     2979                .arg(it.key()).arg(*it));
     2980        tmpMap[it.key()] = *it;
     2981    }
     2982
     2983    prev = tmpMap.begin();
     2984    it = prev;
     2985    ++it;
     2986    while (it != tmpMap.end())
     2987    {
     2988        if ((*it == MARK_START) &&
     2989            (*prev == MARK_END) &&
     2990            ((it.key() - prev.key()) < (uint64_t)spacing))
     2991        {
     2992            map.remove(prev.key());
     2993            map.remove(it.key());
     2994        }
     2995        ++prev;
     2996        ++it;
     2997    }
     2998
     2999    if (map.size() == 0)
     3000        return;
     3001
     3002    // delete any segments less than 'length' frames in length
     3003    tmpMap.clear();
     3004    for (it = map.begin(); it != map.end(); ++it)
     3005        tmpMap[it.key()] = *it;
     3006
     3007    prev = tmpMap.begin();
     3008    it = prev;
     3009    ++it;
     3010    while (it != tmpMap.end())
     3011    {
     3012        if ((*prev == MARK_START) &&
     3013            (*it == MARK_END) &&
     3014            ((it.key() - prev.key()) < (uint64_t)length))
     3015        {
     3016            map.remove(prev.key());
     3017            map.remove(it.key());
     3018        }
     3019        ++prev;
     3020        ++it;
     3021    }
     3022
     3023    LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map After condense:" );
     3024    for (it = map.begin(); it != map.end(); ++it)
     3025        LOG(VB_COMMFLAG, LOG_INFO, QString("    %1:%2")
     3026                .arg(it.key()).arg(*it));
     3027}
     3028
     3029void NextgenCommDetector::ConvertShowMapToCommMap(
     3030    frm_dir_map_t &out, const show_map_t &in)
     3031{
     3032    out.clear();
     3033    if (in.empty())
     3034        return;
     3035
     3036    show_map_t::const_iterator sit;
     3037    for (sit = in.begin(); sit != in.end(); ++sit)
     3038    {
     3039        if (*sit == MARK_START)
     3040            out[sit.key()] = MARK_COMM_END;
     3041        else
     3042            out[sit.key()] = MARK_COMM_START;
     3043    }
     3044
     3045    frm_dir_map_t::iterator it = out.begin();
     3046    if (it == out.end())
     3047        return;
     3048
     3049    switch (out[it.key()])
     3050    {
     3051        case MARK_COMM_END:
     3052            if (it.key() == 0)
     3053                out.remove(0);
     3054            else
     3055                out[0] = MARK_COMM_START;
     3056            break;
     3057        case MARK_COMM_START:
     3058            break;
     3059        default:
     3060            out.remove(0);
     3061            break;
     3062    }
     3063}
     3064
     3065
     3066/* ideas for this method ported back from comskip.c mods by Jere Jones
     3067 * which are partially mods based on Myth's original commercial skip
     3068 * code written by Chris Pinkham. */
     3069
     3070void NextgenCommDetector::CleanupFrameInfo(void)
     3071{
     3072    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::CleanupFrameInfo()");
     3073
     3074    int value;
     3075    int before, after;
     3076
     3077    // try to account for noisy signal causing blank frames to be undetected
     3078    if ((framesProcessed > (fps * 60)) &&
     3079        (blankFrameCount < (framesProcessed * 0.0004)))
     3080    {
     3081        int avgHistogram[256];
     3082        int minAvg = -1;
     3083        int newThreshold = -1;
     3084
     3085        LOG(VB_COMMFLAG, LOG_INFO,
     3086            QString("NextgenCommDetect: Only found %1 blank frames but "
     3087                    "wanted at least %2, rechecking data using higher "
     3088                    "threshold.")
     3089                .arg(blankFrameCount)
     3090                .arg((int)(framesProcessed * 0.0004)));
     3091        blankFrameMap.clear();
     3092        blankFrameCount = 0;
     3093
     3094        memset(avgHistogram, 0, sizeof(avgHistogram));
     3095
     3096        for (uint64_t i = 1; i <= framesProcessed; i++)
     3097            avgHistogram[clamp(frameInfo[i].avgBrightness, 0, 255)] += 1;
     3098
     3099        for (int i = 1; i <= 255 && minAvg == -1; i++)
     3100            if (avgHistogram[i] > (framesProcessed * 0.0004))
     3101                minAvg = i;
     3102
     3103        newThreshold = minAvg + 3;
     3104        LOG(VB_COMMFLAG, LOG_INFO,
     3105            QString("Minimum Average Brightness on a frame "
     3106                    "was %1, will use %2 as new threshold")
     3107                .arg(minAvg).arg(newThreshold));
     3108
     3109        for (uint64_t i = 1; i <= framesProcessed; i++)
     3110        {
     3111            value = frameInfo[i].flagMask;
     3112            frameInfo[i].flagMask = value & ~COMM_FRAME_BLANK;
     3113
     3114            if (( !(frameInfo[i].flagMask & COMM_FRAME_BLANK)) &&
     3115                (frameInfo[i].avgBrightness < newThreshold))
     3116            {
     3117                frameInfo[i].flagMask = value | COMM_FRAME_BLANK;
     3118                blankFrameMap[i] = MARK_BLANK_FRAME;
     3119                blankFrameCount++;
     3120            }
     3121        }
     3122
     3123        LOG(VB_COMMFLAG, LOG_INFO,
     3124            QString("Found %1 blank frames using new value")
     3125                .arg(blankFrameCount));
     3126    }
     3127
     3128    // try to account for fuzzy logo detection
     3129    for (uint64_t i = 1; i <= framesProcessed; i++)
     3130    {
     3131        if ((i < 10) || ((i+10) > framesProcessed))
     3132            continue;
     3133
     3134        before = 0;
     3135        for (int offset = 1; offset <= 10; offset++)
     3136            if (frameInfo[i - offset].flagMask & COMM_FRAME_LOGO_PRESENT)
     3137                before++;
     3138
     3139        after = 0;
     3140        for (int offset = 1; offset <= 10; offset++)
     3141            if (frameInfo[i + offset].flagMask & COMM_FRAME_LOGO_PRESENT)
     3142                after++;
     3143
     3144        value = frameInfo[i].flagMask;
     3145        if (value == -1)
     3146            frameInfo[i].flagMask = 0;
     3147
     3148        if (value & COMM_FRAME_LOGO_PRESENT)
     3149        {
     3150            if ((before < 4) && (after < 4))
     3151                frameInfo[i].flagMask = value & ~COMM_FRAME_LOGO_PRESENT;
     3152        }
     3153        else
     3154        {
     3155            if ((before > 6) && (after > 6))
     3156                frameInfo[i].flagMask = value | COMM_FRAME_LOGO_PRESENT;
     3157        }
     3158    }
     3159}
     3160
     3161void NextgenCommDetector::GetLogoCommBreakMap(show_map_t &map)
     3162{
     3163    LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetLogoCommBreakMap()");
     3164
     3165    map.clear();
     3166
     3167    bool PrevFrameLogo = false;
     3168
     3169    for (uint64_t curFrame = 1 ; curFrame <= framesProcessed; curFrame++)
     3170    {
     3171        bool CurrentFrameLogo =
     3172            (frameInfo[curFrame].flagMask & COMM_FRAME_LOGO_PRESENT);
     3173
     3174        if (!PrevFrameLogo && CurrentFrameLogo)
     3175            map[curFrame] = MARK_START;
     3176        else if (PrevFrameLogo && !CurrentFrameLogo)
     3177            map[curFrame] = MARK_END;
     3178
     3179        PrevFrameLogo = CurrentFrameLogo;
     3180    }
     3181}
     3182
     3183void NextgenCommDetector::logoDetectorBreathe()
     3184{
     3185    emit breathe();
     3186}
     3187
     3188void NextgenCommDetector::PrintFullMap(
     3189    ostream &out, const frm_dir_map_t *comm_breaks, bool verbose) const
     3190{
     3191    if (verbose)
     3192    {
     3193        QByteArray tmp = NGFrameInfoEntry::GetHeader().toAscii();
     3194        out << tmp.constData() << " mark" << endl;
     3195    }
     3196
     3197    for (long long i = 1; i < curFrameNumber; i++)
     3198    {
     3199        QMap<long long, NGFrameInfoEntry>::const_iterator it = frameInfo.find(i);
     3200        if (it == frameInfo.end())
     3201            continue;
     3202
     3203        QByteArray atmp = (*it).toString(i, verbose).toAscii();
     3204        out << atmp.constData() << " ";
     3205        if (comm_breaks)
     3206        {
     3207            frm_dir_map_t::const_iterator mit = comm_breaks->find(i);
     3208            if (mit != comm_breaks->end())
     3209            {
     3210                QString tmp = (verbose) ?
     3211                    toString((MarkTypes)*mit) : QString::number(*mit);
     3212                atmp = tmp.toAscii();
     3213
     3214                out << atmp.constData();
     3215            }
     3216        }
     3217        out << "\n";
     3218    }
     3219
     3220    out << flush;
     3221}
     3222
     3223/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/NextgenCommDetector.h

    diff --git a/mythtv/programs/mythcommflag/NextgenCommDetector.h b/mythtv/programs/mythcommflag/NextgenCommDetector.h
    new file mode 100644
    index 0000000..49b9ac7
    - +  
     1#ifndef _NEXTGEN_COMMDETECTOR_H_
     2#define _NEXTGEN_COMMDETECTOR_H_
     3
     4// POSIX headers
     5#include <stdint.h>
     6
     7// Qt headers
     8#include <QObject>
     9#include <QMap>
     10#include <QQueue>
     11#include <QDateTime>
     12
     13// MythTV headers
     14#include "programinfo.h"
     15#include "mythframe.h"
     16
     17// Commercial Flagging headers
     18#include "CommDetectorBase.h"
     19#include "ClassicCommDetector.h"
     20
     21class MythPlayer;
     22class LogoDetectorBase;
     23class SceneChangeDetectorBase;
     24
     25class AudioChangeDetectorBase;
     26class AudioBuffer;
     27class SubtitleChangeDetectorBase;
     28
     29// extends frameMaskValues
     30enum extraFrameMaskValues {
     31#if 0
     32    COMM_FRAME_SKIPPED       = 0x0001,
     33    COMM_FRAME_BLANK         = 0x0002,
     34    COMM_FRAME_SCENE_CHANGE  = 0x0004,
     35    COMM_FRAME_LOGO_PRESENT  = 0x0008,
     36    COMM_FRAME_ASPECT_CHANGE = 0x0010,
     37    COMM_FRAME_RATING_SYMBOL = 0x0020,
     38#endif
     39    COMM_FRAME_NO_AUDIO      = 0x0100,
     40    COMM_FRAME_AUDIO_CHANGE  = 0x0200,
     41    COMM_FRAME_INVALID_AUDIO = 0x0400,
     42    COMM_FRAME_SUBTITLE_PRESENT = 0x1000,
     43};
     44
     45class NGFrameInfoEntry
     46{
     47  public:
     48    int minBrightness;
     49    int maxBrightness;
     50    int avgBrightness;
     51    int sceneChangePercent;
     52    int aspect;
     53    int format;
     54    double audioPower;
     55    int audioMode;
     56    int subtitleMode;
     57    int flagMask;
     58    static QString GetHeader(void);
     59    QString toString(uint64_t frame, bool verbose) const;
     60};
     61
     62class NextgenCommDetector : public CommDetectorBase
     63{
     64    Q_OBJECT
     65
     66    public:
     67        NextgenCommDetector(SkipType commDetectMethod, bool showProgress,
     68                            bool fullSpeed, MythPlayer* player,
     69                            int chanid,
     70                            const QDateTime& startedAt_in,
     71                            const QDateTime& stopsAt_in,
     72                            const QDateTime& recordingStartedAt_in,
     73                            const QDateTime& recordingStopsAt_in);
     74        virtual void deleteLater(void);
     75
     76        bool go();
     77        void GetCommercialBreakList(frm_dir_map_t &comms);
     78        void recordingFinished(long long totalFileSize);
     79        void requestCommBreakMapUpdate(void);
     80
     81        void PrintFullMap(
     82            ostream &out, const frm_dir_map_t *comm_breaks,
     83            bool verbose) const;
     84
     85        void logoDetectorBreathe();
     86
     87        friend class NextgenLogoDetector;
     88
     89    protected:
     90        virtual ~NextgenCommDetector() {}
     91
     92    private:
     93        struct FrameBlock;
     94        typedef QExplicitlySharedDataPointer<FrameBlock> PFrameBlock;
     95        struct FrameBlock : QSharedData
     96        {
     97            long start;
     98            long end;
     99            long frames;
     100            double length;
     101            int bfCount;
     102            int saCount;
     103            int acCount;
     104            double aPowerAvg;
     105            int logoCount;
     106            int ratingCount;
     107            int scCount;
     108            int subCount;
     109            double scRate;
     110            int formatMatch;
     111            int aspectMatch;
     112            int segmentScore;
     113            int transitionScore;
     114
     115            vector<PFrameBlock> subBlock;
     116
     117            FrameBlock();
     118
     119            void Merge(FrameBlock& o);
     120        };
     121
     122        void ClearAllMaps(void);
     123        void GetBlankCommMap(frm_dir_map_t &comms);
     124        void GetBlankCommBreakMap(frm_dir_map_t &comms);
     125        void GetSilentCommMap(frm_dir_map_t &comms);
     126        void GetAudioCommBreakMap(frm_dir_map_t &comms);
     127        void GetSceneChangeMap(frm_dir_map_t &scenes,
     128                               int64_t start_frame);
     129        frm_dir_map_t Combine2Maps(
     130            const frm_dir_map_t &a, const frm_dir_map_t &b) const;
     131        void UpdateFrameBlock(PFrameBlock fbp, NGFrameInfoEntry& finfo,
     132                              int format, int aspect);
     133        void BuildAllMethodsCommList(void);
     134        void BuildBlankFrameCommList(void);
     135        void BuildAudioFrameCommList(void);
     136        void BuildSceneChangeCommList(void);
     137        void BuildLogoCommList();
     138        void MergeBlankCommList(void);
     139        bool FrameIsInBreakMap(uint64_t f, const frm_dir_map_t &breakMap) const;
     140        void DumpMap(frm_dir_map_t &map);
     141        void CondenseMarkMap(show_map_t &map, int spacing, int length);
     142        void ConvertShowMapToCommMap(
     143            frm_dir_map_t &out, const show_map_t &in);
     144        void CleanupFrameInfo(void);
     145        void GetLogoCommBreakMap(show_map_t &map);
     146
     147        const char * FrameBlockHeader1() const;
     148        const char * FrameBlockHeader2() const;
     149        const QString FrameBlockStr(int curBlock, PFrameBlock fbp, int *score = NULL) const;
     150
     151        enum SkipTypes commDetectMethod;
     152        frm_dir_map_t lastSentCommBreakMap;
     153        bool commBreakMapUpdateRequested;
     154        bool sendCommBreakMapUpdates;
     155
     156        int commDetectBorder;
     157        int commDetectBlankFrameMaxDiff;
     158        int commDetectDarkBrightness;
     159        int commDetectDimBrightness;
     160        int commDetectBoxBrightness;
     161        int commDetectDimAverage;
     162        int commDetectMaxCommBreakLength;
     163        int commDetectMinCommBreakLength;
     164        int commDetectMinShowLength;
     165        int commDetectMaxCommLength;
     166        bool commDetectBlankCanHaveLogo;
     167        int commDetectLargeSceneChangeThreshold;
     168        QList<float> commDetectCommLengths;
     169
     170        bool verboseDebugging;
     171
     172        long long lastFrameNumber;
     173        long long curFrameNumber;
     174
     175        int width;
     176        int height;
     177        int horizSpacing;
     178        int vertSpacing;
     179        double fpm;
     180        bool blankFramesOnly;
     181        int blankFrameCount;
     182        int currentAspect;
     183        int silentFrameCount;
     184
     185
     186        int totalMinBrightness;
     187
     188        bool detectBlankFrames;
     189        bool detectSceneChanges;
     190        bool detectStationLogo;
     191        bool detectSilentFrames;
     192
     193        bool subtitleInfoAvailable;
     194        int subtitleFrameCount;
     195
     196        bool logoInfoAvailable;
     197        LogoDetectorBase* logoDetector;
     198
     199        frm_dir_map_t blankFrameMap;
     200        frm_dir_map_t blankCommMap;
     201        frm_dir_map_t blankCommBreakMap;
     202        frm_dir_map_t silentFrameMap;
     203        frm_dir_map_t audioCommBreakMap;
     204        frm_dir_map_t sceneMap;
     205        frm_dir_map_t sceneCommBreakMap;
     206        frm_dir_map_t commBreakMap;
     207        frm_dir_map_t logoCommBreakMap;
     208
     209        bool frameIsBlank;
     210        bool sceneHasChanged;
     211        bool stationLogoPresent;
     212
     213        bool lastFrameWasBlank;
     214        bool lastFrameWasSceneChange;
     215        bool decoderFoundAspectChanges;
     216
     217        SceneChangeDetectorBase* sceneChangeDetector;
     218        AudioChangeDetectorBase* audioChangeDetector;
     219        SubtitleChangeDetectorBase* subtitleChangeDetector;
     220
     221protected:
     222        MythPlayer *player;
     223        QDateTime startedAt, stopsAt;
     224        QDateTime recordingStartedAt, recordingStopsAt;
     225        bool aggressiveDetection;
     226        bool stillRecording;
     227        bool fullSpeed;
     228        bool showProgress;
     229        double fps;
     230        uint64_t framesProcessed;
     231        long long preRoll;
     232        long long postRoll;
     233
     234        void Init();
     235        void SetVideoParams(float aspect);
     236        void ProcessFrame(VideoFrame *frame, long long frame_number);
     237        QMap<long long, NGFrameInfoEntry> frameInfo;
     238        AudioBuffer *audioBuffer;
     239        int64_t audioFrame;
     240
     241        QQueue<VideoFrame*> vfQueue;
     242
     243public slots:
     244        void sceneChangeDetectorHasNewInformation(unsigned int framenum, bool isSceneChange,float debugValue);
     245        void audioDetectorHasNewInformation(unsigned int framenum, bool hasChanged, float amplitude, float debugValue);
     246        void subtitleDetectorHasNewInformation(unsigned int framenum, bool hasChanged, float debugValue);
     247};
     248
     249#endif
     250
     251
     252/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/NextgenLogoDetector.cpp

    diff --git a/mythtv/programs/mythcommflag/NextgenLogoDetector.cpp b/mythtv/programs/mythcommflag/NextgenLogoDetector.cpp
    new file mode 100644
    index 0000000..534af55
    - +  
     1// POSIX headers
     2#include <unistd.h>
     3
     4// ANSI C headers
     5#include <cstdlib>
     6
     7// MythTV headers
     8#include "mythcorecontext.h"
     9#include "mythplayer.h"
     10
     11// Commercial Flagging headers
     12#include "NextgenLogoDetector.h"
     13#include "NextgenCommDetector.h"
     14
     15// for debugging
     16//#include "commercial_debug.h"
     17
     18typedef struct edgemaskentry
     19{
     20    int isedge;
     21    int horiz;
     22    int vert;
     23    int rdiag;
     24    int ldiag;
     25}
     26EdgeMaskEntry;
     27
     28#define IMG_DEBUG
     29#ifdef IMG_DEBUG
     30//#include <png.h>
     31//#include <zlib.h>
     32
     33
     34#endif
     35
     36NextgenLogoDetector::NextgenLogoDetector(NextgenCommDetector* commdetector,
     37                                         unsigned int w, unsigned int h,
     38                                         unsigned int commdetectborder_in,
     39                                         unsigned int xspacing_in,
     40                                         unsigned int yspacing_in)
     41    : LogoDetectorBase(w,h),
     42      commDetector(commdetector),
     43      previousFrameWasSceneChange(false),
     44      xspacing(xspacing_in),                            yspacing(yspacing_in),
     45      commDetectBorder(commdetectborder_in),            edgeMask(new EdgeMaskEntry[width * height]),
     46      logoMaxValues(new unsigned char[width * height]), logoMinValues(new unsigned char[width * height]),
     47      logoFrame(new unsigned char[width * height]),     logoMask(new unsigned char[width * height]),
     48      logoCheckMask(new unsigned char[width * height]), tmpBuf(new unsigned char[width * height]),
     49      logoEdgeDiff(0),                                  logoFrameCount(0),
     50      logoMinX(0),                                      logoMaxX(0),
     51      logoMinY(0),                                      logoMaxY(0),
     52      logoInfoAvailable(false)
     53{
     54    commDetectLogoSamplesNeeded =
     55        gCoreContext->GetNumSetting("CommDetectLogoSamplesNeeded", 240);
     56    commDetectLogoSampleSpacing =
     57        gCoreContext->GetNumSetting("CommDetectLogoSampleSpacing", 2);
     58    commDetectLogoSecondsNeeded = (int)(1.3 * commDetectLogoSamplesNeeded *
     59                                              commDetectLogoSampleSpacing);
     60    commDetectLogoGoodEdgeThreshold =
     61        gCoreContext->GetSetting("CommDetectLogoGoodEdgeThreshold", "0.75")
     62        .toDouble();
     63    commDetectLogoBadEdgeThreshold =
     64        gCoreContext->GetSetting("CommDetectLogoBadEdgeThreshold", "0.85")
     65        .toDouble();
     66
     67}
     68
     69unsigned int NextgenLogoDetector::getRequiredAvailableBufferForSearch()
     70{
     71    return commDetectLogoSecondsNeeded;
     72}
     73
     74void NextgenLogoDetector::deleteLater(void)
     75{
     76    commDetector = 0;
     77    if (edgeMask)
     78        delete [] edgeMask;
     79    if (logoFrame)
     80        delete [] logoFrame;
     81    if (logoMask)
     82        delete [] logoMask;
     83    if (logoCheckMask)
     84        delete [] logoCheckMask;
     85    if (logoMaxValues)
     86        delete [] logoMaxValues;
     87    if (logoMinValues)
     88        delete [] logoMinValues;
     89    if (tmpBuf)
     90        delete [] tmpBuf;
     91
     92    LogoDetectorBase::deleteLater();
     93}
     94
     95bool NextgenLogoDetector::searchForLogo(MythPlayer* player)
     96{
     97    int seekIncrement =
     98        (int)(commDetectLogoSampleSpacing * player->GetFrameRate());
     99    long long seekFrame;
     100    int loops;
     101    int maxLoops = commDetectLogoSamplesNeeded;
     102    EdgeMaskEntry *edgeCounts;
     103    unsigned int pos, i, x, y, dx, dy;
     104    int edgeDiffs[] = {5, 7, 10, 15, 20, 30, 40, 50, 60, 0 };
     105
     106
     107    LOG(VB_COMMFLAG, LOG_INFO, "Searching for Station Logo");
     108
     109    logoInfoAvailable = false;
     110
     111    edgeCounts = new EdgeMaskEntry[width * height];
     112
     113    // Back in 2005, a threshold of 50 minimum pixelsInMask was established.
     114    // I don't know whether that was tested against SD or HD resolutions.
     115    // I do know that in 2010, mythcommflag was changed to use ffmpeg's
     116    // lowres support, effectively dividing the video area by 16.
     117    // But the 50 pixel minimum was not adjusted accordingly.
     118    // I believe the minimum threshold should vary with the video's area.
     119    // I am using 1280x720 (for 720p) video as the baseline.
     120    // This should improve logo detection for SD video.
     121    int minPixelsInMask = 50 * (width*height) / (1280*720 / 16);
     122
     123    for (i = 0; edgeDiffs[i] != 0 && !logoInfoAvailable; i++)
     124    {
     125        int pixelsInMask = 0;
     126
     127        LOG(VB_COMMFLAG, LOG_INFO, QString("Trying with edgeDiff == %1, minPixelsInMask=%2")
     128                .arg(edgeDiffs[i]).arg(minPixelsInMask));
     129
     130        memset(edgeCounts, 0, sizeof(EdgeMaskEntry) * width * height);
     131        memset(edgeMask, 0, sizeof(EdgeMaskEntry) * width * height);
     132
     133        player->DiscardVideoFrame(player->GetRawVideoFrame(0));
     134
     135        loops = 0;
     136        seekFrame = commDetector->preRoll + seekIncrement;
     137        while (loops < maxLoops && player->GetEof() == kEofStateNone)
     138        {
     139            VideoFrame* vf = player->GetRawVideoFrame(seekFrame);
     140
     141            if ((loops % 50) == 0)
     142                commDetector->logoDetectorBreathe();
     143
     144            if (commDetector->m_bStop)
     145            {
     146                player->DiscardVideoFrame(vf);
     147                delete[] edgeCounts;
     148                return false;
     149            }
     150
     151            if (!commDetector->fullSpeed)
     152                usleep(10000);
     153
     154            DetectEdges(vf, edgeCounts, edgeDiffs[i]);
     155
     156            seekFrame += seekIncrement;
     157            loops++;
     158
     159            player->DiscardVideoFrame(vf);
     160        }
     161
     162        LOG(VB_COMMFLAG, LOG_INFO, "Analyzing edge data");
     163
     164#ifdef SHOW_DEBUG_WIN
     165        unsigned char *fakeFrame;
     166        fakeFrame = new unsigned char[width * height * 3 / 2];
     167        memset(fakeFrame, 0, width * height * 3 / 2);
     168#endif
     169
     170        for (y = 0; y < height; y++)
     171        {
     172            if ((y > (height/4)) && (y < (height * 3 / 4)))
     173                continue;
     174
     175            for (x = 0; x < width; x++)
     176            {
     177                if ((x > (width/4)) && (x < (width * 3 / 4)))
     178                    continue;
     179
     180                pos = y * width + x;
     181
     182                if (edgeCounts[pos].isedge > (maxLoops * 0.66))
     183                {
     184                    edgeMask[pos].isedge = 1;
     185                    pixelsInMask++;
     186#ifdef SHOW_DEBUG_WIN
     187                    fakeFrame[pos] = 0xff;
     188#endif
     189
     190                }
     191
     192                if (edgeCounts[pos].horiz > (maxLoops * 0.66))
     193                    edgeMask[pos].horiz = 1;
     194
     195                if (edgeCounts[pos].vert > (maxLoops * 0.66))
     196                    edgeMask[pos].vert = 1;
     197
     198                if (edgeCounts[pos].ldiag > (maxLoops * 0.66))
     199                    edgeMask[pos].ldiag = 1;
     200                if (edgeCounts[pos].rdiag > (maxLoops * 0.66))
     201                    edgeMask[pos].rdiag = 1;
     202            }
     203        }
     204
     205        SetLogoMaskArea();
     206
     207        for (y = logoMinY; y < logoMaxY; y++)
     208        {
     209            for (x = logoMinX; x < logoMaxX; x++)
     210            {
     211                int neighbors = 0;
     212
     213                if (!edgeMask[y * width + x].isedge)
     214                    continue;
     215
     216                for (dy = y - 2; dy <= (y + 2); dy++ )
     217                {
     218                    for (dx = x - 2; dx <= (x + 2); dx++ )
     219                    {
     220                        if (edgeMask[dy * width + dx].isedge)
     221                            neighbors++;
     222                    }
     223                }
     224
     225                if (neighbors < 5)
     226                    edgeMask[y * width + x].isedge = 0;
     227            }
     228        }
     229
     230        SetLogoMaskArea();
     231        LOG(VB_COMMFLAG, LOG_INFO,
     232            QString("Testing Logo area: topleft (%1,%2), bottomright (%3,%4)")
     233                .arg(logoMinX).arg(logoMinY)
     234                .arg(logoMaxX).arg(logoMaxY));
     235
     236#ifdef SHOW_DEBUG_WIN
     237        for (x = logoMinX; x < logoMaxX; x++)
     238        {
     239            pos = logoMinY * width + x;
     240            fakeFrame[pos] = 0x7f;
     241            pos = logoMaxY * width + x;
     242            fakeFrame[pos] = 0x7f;
     243        }
     244        for (y = logoMinY; y < logoMaxY; y++)
     245        {
     246            pos = y * width + logoMinX;
     247            fakeFrame[pos] = 0x7f;
     248            pos = y * width + logoMaxX;
     249            fakeFrame[pos] = 0x7f;
     250        }
     251
     252        comm_debug_show(fakeFrame);
     253        delete [] fakeFrame;
     254
     255        cerr << "Hit ENTER to continue" << endl;
     256        getchar();
     257#endif
     258        if (((logoMaxX - logoMinX) < (width / 4)) &&
     259            ((logoMaxY - logoMinY) < (height / 4)) &&
     260            (pixelsInMask > minPixelsInMask))
     261        {
     262            logoInfoAvailable = true;
     263            logoEdgeDiff = edgeDiffs[i];
     264
     265            LOG(VB_COMMFLAG, LOG_INFO,
     266                QString("Using Logo area: topleft (%1,%2), "
     267                        "bottomright (%3,%4), pixelsInMask (%5).")
     268                    .arg(logoMinX).arg(logoMinY)
     269                    .arg(logoMaxX).arg(logoMaxY)
     270                    .arg(pixelsInMask));
     271        }
     272        else
     273        {
     274            LOG(VB_COMMFLAG, LOG_INFO,
     275                QString("Rejecting Logo area: topleft (%1,%2), "
     276                        "bottomright (%3,%4), pixelsInMask (%5). "
     277                        "Not within specified limits.")
     278                    .arg(logoMinX).arg(logoMinY)
     279                    .arg(logoMaxX).arg(logoMaxY)
     280                    .arg(pixelsInMask));
     281        }
     282    }
     283
     284    delete [] edgeCounts;
     285
     286    if (!logoInfoAvailable)
     287        LOG(VB_COMMFLAG, LOG_NOTICE, "No suitable logo area found.");
     288
     289    player->DiscardVideoFrame(player->GetRawVideoFrame(0));
     290    return logoInfoAvailable;
     291}
     292
     293
     294void NextgenLogoDetector::SetLogoMaskArea()
     295{
     296    LOG(VB_COMMFLAG, LOG_INFO, "SetLogoMaskArea()");
     297
     298    logoMinX = width - 1;
     299    logoMaxX = 0;
     300    logoMinY = height - 1;
     301    logoMaxY = 0;
     302
     303    for (unsigned int y = 0; y < height; y++)
     304    {
     305        for (unsigned int x = 0; x < width; x++)
     306        {
     307            if (edgeMask[y * width + x].isedge)
     308            {
     309                if (x < logoMinX)
     310                    logoMinX = x;
     311                if (y < logoMinY)
     312                    logoMinY = y;
     313                if (x > logoMaxX)
     314                    logoMaxX = x;
     315                if (y > logoMaxY)
     316                    logoMaxY = y;
     317            }
     318        }
     319    }
     320
     321    logoMinX -= 5;
     322    logoMaxX += 5;
     323    logoMinY -= 5;
     324    logoMaxY += 5;
     325
     326    if (logoMinX < 4)
     327        logoMinX = 4;
     328    if (logoMaxX > (width-5))
     329        logoMaxX = (width-5);
     330    if (logoMinY < 4)
     331        logoMinY = 4;
     332    if (logoMaxY > (height-5))
     333        logoMaxY = (height-5);
     334}
     335
     336#if 0
     337void NextgenLogoDetector::SetLogoMask(unsigned char *mask)
     338{
     339    int pixels = 0;
     340
     341    memcpy(logoMask, mask, width * height);
     342
     343    SetLogoMaskArea();
     344
     345    for(unsigned int y = logoMinY; y <= logoMaxY; y++)
     346        for(unsigned int x = logoMinX; x <= logoMaxX; x++)
     347            if (!logoMask[y * width + x] == 1)
     348                pixels++;
     349
     350    if (pixels < 30)
     351        return;
     352
     353    // set the pixels around our logo
     354    for(unsigned int y = (logoMinY - 1); y <= (logoMaxY + 1); y++)
     355    {
     356        for(unsigned int x = (logoMinX - 1); x <= (logoMaxX + 1); x++)
     357        {
     358            if (!logoMask[y * width + x])
     359            {
     360                for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++)
     361                {
     362                    for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++)
     363                    {
     364                        if ((logoMask[y2 * width + x2] == 1) &&
     365                            (!logoMask[y * width + x]))
     366                        {
     367                            logoMask[y * width + x] = 2;
     368                            x2 = x + 2;
     369                            y2 = y + 2;
     370
     371                            logoCheckMask[y2 * width + x2] = 1;
     372                            logoCheckMask[y * width + x] = 1;
     373                        }
     374                    }
     375                }
     376            }
     377        }
     378    }
     379
     380    for(unsigned int y = (logoMinY - 2); y <= (logoMaxY + 2); y++)
     381    {
     382        for(unsigned int x = (logoMinX - 2); x <= (logoMaxX + 2); x++)
     383        {
     384            if (!logoMask[y * width + x])
     385            {
     386                for (unsigned int y2 = y - 1; y2 <= (y + 1); y2++)
     387                {
     388                    for (unsigned int x2 = x - 1; x2 <= (x + 1); x2++)
     389                    {
     390                        if ((logoMask[y2 * width + x2] == 2) &&
     391                            (!logoMask[y * width + x]))
     392                        {
     393                            logoMask[y * width + x] = 3;
     394                            x2 = x + 2;
     395                            y2 = y + 2;
     396
     397                            logoCheckMask[y * width + x] = 1;
     398                        }
     399                    }
     400                }
     401            }
     402        }
     403    }
     404
     405#ifdef SHOW_DEBUG_WIN
     406    DumpLogo(true,framePtr);
     407#endif
     408
     409    logoFrameCount = 0;
     410    logoInfoAvailable = true;
     411}
     412
     413void NextgenLogoDetector::DumpLogo(bool fromCurrentFrame,
     414    unsigned char* framePtr)
     415{
     416    char scrPixels[] = " .oxX";
     417
     418    if (!logoInfoAvailable)
     419        return;
     420
     421    cerr << "\nLogo Data ";
     422    if (fromCurrentFrame)
     423        cerr << "from current frame\n";
     424
     425    cerr << "\n     ";
     426
     427    for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++)
     428        cerr << (x % 10);
     429    cerr << "\n";
     430
     431    for(unsigned int y = logoMinY - 2; y <= (logoMaxY + 2); y++)
     432    {
     433        QString tmp = QString("%1: ").arg(y, 3);
     434        QString ba = tmp.toLatin1();
     435        cerr << ba.constData();
     436        for(unsigned int x = logoMinX - 2; x <= (logoMaxX + 2); x++)
     437        {
     438            if (fromCurrentFrame)
     439            {
     440                cerr << scrPixels[framePtr[y * width + x] / 50];
     441            }
     442            else
     443            {
     444                switch (logoMask[y * width + x])
     445                {
     446                        case 0:
     447                        case 2: cerr << " ";
     448                        break;
     449                        case 1: cerr << "*";
     450                        break;
     451                        case 3: cerr << ".";
     452                        break;
     453                }
     454            }
     455        }
     456        cerr << "\n";
     457    }
     458    cerr.flush();
     459}
     460#endif
     461
     462
     463/* ideas for this method ported back from comskip.c mods by Jere Jones
     464 * which are partially mods based on Myth's original commercial skip
     465 * code written by Chris Pinkham. */
     466bool NextgenLogoDetector::doesThisFrameContainTheFoundLogo(
     467    VideoFrame * frame)
     468{
     469    int radius = 2;
     470    unsigned int x, y;
     471    int pos1, pos2, pos3;
     472    int edgePos;
     473    int pixel;
     474    int goodEdges = 0;
     475    int badEdges = 0;
     476    int testEdges = 0;
     477    int testNotEdges = 0;
     478
     479    unsigned char* framePtr = frame->buf;
     480    int bytesPerLine = frame->pitches[0];
     481
     482    for (y = logoMinY; y <= logoMaxY; y++ )
     483    {
     484        for (x = logoMinX; x <= logoMaxX; x++ )
     485        {
     486            pos1 = y * bytesPerLine + x;
     487            edgePos = y * width + x;
     488            pos2 = (y - radius) * bytesPerLine + x;
     489            pos3 = (y + radius) * bytesPerLine + x;
     490
     491            pixel = framePtr[pos1];
     492
     493            if (edgeMask[edgePos].horiz)
     494            {
     495                if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) ||
     496                    (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff))
     497                    goodEdges++;
     498                testEdges++;
     499            }
     500            else
     501            {
     502                if ((abs(framePtr[pos1 - radius] - pixel) >= logoEdgeDiff) ||
     503                    (abs(framePtr[pos1 + radius] - pixel) >= logoEdgeDiff))
     504                    badEdges++;
     505                testNotEdges++;
     506            }
     507
     508            if (edgeMask[edgePos].vert)
     509            {
     510                if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) ||
     511                    (abs(framePtr[pos3] - pixel) >= logoEdgeDiff))
     512                    goodEdges++;
     513                testEdges++;
     514            }
     515            else
     516            {
     517                if ((abs(framePtr[pos2] - pixel) >= logoEdgeDiff) ||
     518                    (abs(framePtr[pos3] - pixel) >= logoEdgeDiff))
     519                    badEdges++;
     520                testNotEdges++;
     521            }
     522        }
     523    }
     524
     525    double goodEdgeRatio = (testEdges) ?
     526        (double)goodEdges / (double)testEdges : 0.0;
     527    double badEdgeRatio = (testNotEdges) ?
     528        (double)badEdges / (double)testNotEdges : 0.0;
     529    if ((goodEdgeRatio > commDetectLogoGoodEdgeThreshold) &&
     530        (badEdgeRatio < commDetectLogoBadEdgeThreshold))
     531        return true;
     532    else
     533        return false;
     534}
     535
     536bool NextgenLogoDetector::pixelInsideLogo(unsigned int x, unsigned int y)
     537{
     538    if (!logoInfoAvailable)
     539        return false;
     540
     541    return ((x > logoMinX) && (x < logoMaxX) &&
     542            (y > logoMinY) && (y < logoMaxY));
     543}
     544
     545void NextgenLogoDetector::DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges,
     546                                      int edgeDiff)
     547{
     548    int r = 2;
     549    unsigned char *buf = frame->buf;
     550    int bytesPerLine = frame->pitches[0];
     551    unsigned char p;
     552    unsigned int pos, x, y;
     553
     554    for (y = commDetectBorder + r; y < (height - commDetectBorder - r); y++)
     555    {
     556        if ((y > (height/4)) && (y < (height * 3 / 4)))
     557            continue;
     558
     559        for (x = commDetectBorder + r; x < (width - commDetectBorder - r); x++)
     560        {
     561            int edgeCount = 0;
     562
     563            if ((x > (width/4)) && (x < (width * 3 / 4)))
     564                continue;
     565
     566            pos = y * width + x;
     567            p = buf[y * bytesPerLine + x];
     568
     569            if (( abs(buf[y * bytesPerLine + (x - r)] - p) >= edgeDiff) ||
     570                ( abs(buf[y * bytesPerLine + (x + r)] - p) >= edgeDiff))
     571            {
     572                edges[pos].horiz++;
     573                edgeCount++;
     574            }
     575            if (( abs(buf[(y - r) * bytesPerLine + x] - p) >= edgeDiff) ||
     576                ( abs(buf[(y + r) * bytesPerLine + x] - p) >= edgeDiff))
     577            {
     578                edges[pos].vert++;
     579                edgeCount++;
     580            }
     581
     582            if (( abs(buf[(y - r) * bytesPerLine + (x - r)] - p) >= edgeDiff) ||
     583                ( abs(buf[(y + r) * bytesPerLine + (x + r)] - p) >= edgeDiff))
     584            {
     585                edges[pos].ldiag++;
     586                edgeCount++;
     587            }
     588
     589            if (( abs(buf[(y - r) * bytesPerLine + (x + r)] - p) >= edgeDiff) ||
     590                ( abs(buf[(y + r) * bytesPerLine + (x - r)] - p) >= edgeDiff))
     591            {
     592                edges[pos].rdiag++;
     593                edgeCount++;
     594            }
     595
     596            if (edgeCount >= 3)
     597                edges[pos].isedge++;
     598        }
     599    }
     600}
     601
     602/* vim: set expandtab tabstop=4 shiftwidth=4: */
     603
  • new file mythtv/programs/mythcommflag/NextgenLogoDetector.h

    diff --git a/mythtv/programs/mythcommflag/NextgenLogoDetector.h b/mythtv/programs/mythcommflag/NextgenLogoDetector.h
    new file mode 100644
    index 0000000..83ea121
    - +  
     1#ifndef _NEXTGENLOGOGEDETECTOR_H_
     2#define _NEXTGENLOGOGEDETECTOR_H_
     3
     4#include "LogoDetectorBase.h"
     5
     6typedef struct edgemaskentry EdgeMaskEntry;
     7typedef struct VideoFrame_ VideoFrame;
     8class NextgenCommDetector;
     9
     10class NextgenLogoDetector : public LogoDetectorBase
     11{
     12  public:
     13    NextgenLogoDetector(NextgenCommDetector* commDetector,unsigned int width,
     14        unsigned int height, unsigned int commdetectborder,
     15        unsigned int xspacing, unsigned int yspacing);
     16    virtual void deleteLater(void);
     17
     18    bool searchForLogo(MythPlayer* player);
     19    bool doesThisFrameContainTheFoundLogo(VideoFrame* frame);
     20    bool pixelInsideLogo(unsigned int x, unsigned int y);
     21
     22    unsigned int getRequiredAvailableBufferForSearch();
     23
     24  protected:
     25    virtual ~NextgenLogoDetector() {}
     26
     27  private:
     28    void SetLogoMaskArea();
     29    //void SetLogoMask(unsigned char *mask);
     30    //void DumpLogo(bool fromCurrentFrame,unsigned char* framePtr);
     31    void DetectEdges(VideoFrame *frame, EdgeMaskEntry *edges, int edgeDiff);
     32
     33    NextgenCommDetector* commDetector;
     34    bool previousFrameWasSceneChange;
     35    unsigned int xspacing, yspacing;
     36    unsigned int commDetectBorder;
     37
     38    int commDetectLogoSamplesNeeded;
     39    int commDetectLogoSampleSpacing;
     40    int commDetectLogoSecondsNeeded;
     41    double commDetectLogoGoodEdgeThreshold;
     42    double commDetectLogoBadEdgeThreshold;
     43
     44    EdgeMaskEntry *edgeMask;
     45
     46    unsigned char *logoMaxValues;
     47    unsigned char *logoMinValues;
     48    unsigned char *logoFrame;
     49    unsigned char *logoMask;
     50    unsigned char *logoCheckMask;
     51    unsigned char *tmpBuf;
     52
     53    int logoEdgeDiff;
     54    unsigned int logoFrameCount;
     55    unsigned int logoMinX;
     56    unsigned int logoMaxX;
     57    unsigned int logoMinY;
     58    unsigned int logoMaxY;
     59
     60    bool logoInfoAvailable;
     61};
     62
     63#endif
     64
     65/* vim: set expandtab tabstop=4 shiftwidth=4: */
     66
  • new file mythtv/programs/mythcommflag/NextgenLogoDetector2.cpp

    diff --git a/mythtv/programs/mythcommflag/NextgenLogoDetector2.cpp b/mythtv/programs/mythcommflag/NextgenLogoDetector2.cpp
    new file mode 100644
    index 0000000..e84134a
    - +  
     1/*
     2 * Searches:
     3 * transparent logo detection
     4 *
     5 * Refs:
     6 * http://www.busim.ee.boun.edu.tr/~sankur/SankurFolder/EUSIPCO2009_Automatic_TV_Logo.pdf
     7 * http://www.graphicon.ru/proceedings/2011/conference/gc2011erofeev.pdf
     8 * http://www.junjanes.com/files/research/a-fast-method-for-animated-tv-logo-detection.pdf
     9 * http://www.nlpr.ia.ac.cn/2007papers/gjhy/gh40.pdf
     10 * http://avss2012.org/2006papers/gjhy/gh40.pdf
     11 *
     12 */
     13
     14
     15// Qt headers
     16#include <QSharedPointer>
     17#include <QFile>
     18#include <QFileInfo>
     19#include <QTextStream>
     20
     21// MythTV headers
     22#include "mythplayer.h"
     23#include "mythcorecontext.h"    /* gContext */
     24#include "mythframe.h"          /* VideoFrame */
     25#include "mythmiscutil.h"
     26#include "mythsystemlegacy.h"
     27#include "exitcodes.h"
     28
     29// Commercial Flagging headers
     30#include "NextgenLogoDetector2.h"
     31#include "NextgenCommDetector.h"
     32
     33#include "pgm.h"
     34#include "PGMConverter.h"
     35#include "BorderDetector.h"
     36#include "EdgeDetector.h"
     37#include "CannyEdgeDetector.h"
     38
     39#define SAFE_DELETE(x) if (x) { delete x; x = NULL; }
     40
     41
     42class Histogram2;
     43class CAVPicture //: QSharedData
     44{
     45    public:
     46        CAVPicture() : needsFree(false), width(0), height(0)
     47        {
     48            memset(&avpic, 0, sizeof(avpic));
     49        }
     50        CAVPicture(int width, int height) : needsFree(false), width(0), height(0)
     51        {
     52            memset(&avpic, 0, sizeof(avpic));
     53            alloc(width, height);
     54            zero();
     55        }
     56        CAVPicture(uint8_t* img, int width, int height) : needsFree(false), width(0), height(0)
     57        {
     58            memset(&avpic, 0, sizeof(avpic));
     59            //alloc(width, height);
     60            avpicture_fill(&avpic, img, PIX_FMT_GRAY8, width, height);
     61            this->width = width;
     62            this->height = height;
     63        }
     64        CAVPicture(CAVPicture& pic) : needsFree(false), width(0), height(0)
     65        {
     66            memset(&avpic, 0, sizeof(avpic));
     67            copy(pic);
     68        }
     69        CAVPicture(CAVPicture& pic, int x, int y, int w, int h) : needsFree(false), width(0), height(0)
     70        {
     71            memset(&avpic, 0, sizeof(avpic));
     72            copy(pic, x, y, w, h);
     73        }
     74
     75
     76        ~CAVPicture()
     77        {
     78            free();
     79        }
     80
     81        bool resize(int width, int height)
     82        {
     83            return alloc(width, height);
     84        }
     85
     86        void copy(const AVPicture* img)
     87        {
     88            //avpicture_fill(&avpic, img->data[0], PIX_FMT_GRAY8, width, height);
     89            memcpy(avpic.data[0], img->data[0], width * height);
     90        }
     91
     92        void copy(const CAVPicture& pic)
     93        {
     94            alloc(pic.width, pic.height);
     95            memcpy(avpic.data[0], pic.avpic.data[0], width * height);
     96        }
     97        void copy(const CAVPicture& pic, int x, int y, int w, int h)
     98        {
     99            alloc(w, h);
     100            // note row (y) comes before col (x) in args. this is not wrong
     101            pgm_crop(&avpic, &pic.avpic, pic.width, y, x, w, h);
     102        }
     103
     104
     105        void zero()
     106        {
     107            if (avpic.data[0])
     108            {
     109                memset(avpic.data[0], 0, width*height);
     110            }
     111        }
     112
     113        AVPicture avpic;
     114
     115    private:
     116        bool needsFree;
     117        int width;
     118        int height;
     119        bool alloc(int newwidth, int newheight)
     120        {
     121            if (avpic.data[0])
     122            {
     123                if (newwidth == width && newheight == height)
     124                    return true;
     125                free();
     126            }
     127            if (avpicture_alloc(&avpic, PIX_FMT_GRAY8, newwidth, newheight) != 0)
     128            {
     129                return false;
     130            }
     131            needsFree = true;
     132            width = newwidth;
     133            height = newheight;
     134            return true;
     135        }
     136        void free()
     137        {
     138            if (avpic.data[0] && needsFree)
     139            {
     140                avpicture_free(&avpic);
     141                memset(&avpic, 0, sizeof(avpic));
     142                needsFree = false;
     143            }
     144        }
     145    public:
     146        int writeJPG(QString prefix, bool force = true)
     147        {
     148            const int imgwidth = avpic.linesize[0];
     149            const int imgheight = height;
     150            QFileInfo jpgfi(prefix + ".jpg");
     151            if (!jpgfi.exists() || force)
     152            {
     153                QFile pgmfile(prefix + ".pgm");
     154                if (!pgmfile.exists() || force)
     155                {
     156                    QByteArray pfname = pgmfile.fileName().toLocal8Bit();
     157                    if (pgm_write(avpic.data[0], imgwidth, imgheight,
     158                                pfname.constData()))
     159                    {
     160                        return -1;
     161                    }
     162                }
     163
     164                //QString cmd = QString("convert -quality 50 -resize 192x144 %1 %2")
     165                QString cmd = QString("convert -quality 50 %1 %2")
     166                    .arg(pgmfile.fileName()).arg(jpgfi.filePath());
     167                if (myth_system(cmd) != GENERIC_EXIT_OK)
     168                    return -1;
     169
     170#if 0
     171                if (!pgmfile.remove())
     172                {
     173                    LOG(VB_COMMFLAG, LOG_ERR,
     174                            QString("TemplateFinder.writeJPG error removing %1 (%2)")
     175                            .arg(pgmfile.fileName()).arg(strerror(errno)));
     176                    return -1;
     177                }
     178#endif
     179            }
     180            return 0;
     181        }
     182        void AverageImage(unsigned char* framePtr, int srcWidth, int xs, int xe, int ys, int ye, int alpha );
     183        void Average(const AVPicture* pic, int alpha );
     184        int  IntersectionRatio(const AVPicture* pic );
     185        int  SimilarityRatio(const AVPicture* mask, const AVPicture* pic);
     186        void Threshold(int32_t minThreshold);
     187
     188        void Dilate(int xsize, int ysize);
     189        void Erode(int xsize, int ysize);
     190        void Close(int xsize, int ysize);
     191        void Open(int xsize, int ysize);
     192        void Fill();
     193        uint32_t CalcArea(uint8_t threshold);
     194        void Invert();
     195        void Union(const CAVPicture& pic);
     196        void Intersection(const CAVPicture& pic);
     197        void GetComponents(ComponentList& components, CAVPicture* templateImage = NULL);
     198        void LabelConnectedRegion(PComponent&, uint8_t label, int x, int y);
     199        void SetTemplate(PComponent component);
     200
     201    protected:
     202        void Fill1(int x, int y);
     203
     204    friend class Histogram2;
     205};
     206//typedef QExplicitlySharedDataPointer< CAVPicture > PCAVPicture;
     207
     208class Histogram2
     209{
     210    public:
     211        Histogram2()
     212        {
     213            memset(data, 0, sizeof(data));
     214        }
     215        Histogram2(const CAVPicture& pic)
     216        {
     217            Process(pic);
     218        }
     219        ~Histogram2()
     220        {
     221        }
     222
     223        void Process(const CAVPicture& pic)
     224        {
     225            int i;
     226            memset(data, 0, sizeof(data));
     227            N = pic.width*pic.height;
     228            for(i = 0; i < N; i++)
     229            {
     230                data[pic.avpic.data[0][i]]++;
     231            }
     232        }
     233        // OTSU threshold calc
     234        int GetThreshold(int last = 256)
     235        {
     236            int i;
     237            double sum, sumB, wB, wF, varMax;
     238            int threshold = 0;
     239            sum = sumB = wB = wF = varMax = 0;
     240            for(i=0;i<last;i++)
     241            {
     242                sum += i * data[i];
     243            }
     244            for(i=0;i<last;i++)
     245            {
     246                wB += data[i];
     247                if (wB == 0) continue;
     248                wF = N - wB;
     249                if (wF == 0) break;
     250
     251                sumB += i * data[i];
     252                double mB = sumB/wB;
     253                double mF = (sum - sumB)/wF;
     254                double varBetween = wB * wF * (mB - mF) * (mB - mF);
     255                if (varBetween > varMax)
     256                {
     257                    varMax = varBetween;
     258                    threshold = i;
     259                }
     260            }
     261            return threshold;
     262        }
     263
     264        int N;
     265        int data[256];
     266};
     267
     268void CAVPicture::Dilate(int xsize, int ysize)
     269{
     270    int x,y,kx,ky;
     271    CAVPicture refpic(*this);
     272    for(ky=-ysize/2; ky <= ysize/2; ky++)
     273    {
     274        for(kx=-xsize/2; kx <= xsize/2; kx++)
     275        {
     276            for(y=0; y < height; y++)
     277            {
     278                int yofs = y * width;
     279                int kofs = (ky+y) * width + kx;
     280                for(x=0; x < width; x++)
     281                {
     282                    if (((ky+y) >= 0) && ((ky+y) < height) &&
     283                        ((kx+x) >= 0) && ((kx+x) < width))
     284                        avpic.data[0][yofs + x] |= refpic.avpic.data[0][kofs + x];
     285                }
     286            }
     287        }
     288    }
     289}
     290void CAVPicture::Erode(int xsize, int ysize)
     291{
     292    int x,y,kx,ky;
     293    CAVPicture refpic(*this);
     294    for(ky=-ysize/2; ky <= ysize/2; ky++)
     295    {
     296        for(kx=-xsize/2; kx <= xsize/2; kx++)
     297        {
     298            for(y=0; y < height; y++)
     299            {
     300                int yofs = y * width;
     301                int kofs = (ky+y) * width + kx;
     302                for(x=0; x < width; x++)
     303                {
     304                    if (((kofs + x) >= 0) && ((kofs+x) < (width*height)) &&
     305                        ((kx+x) >= 0) && ((kx+x) < width))
     306                        avpic.data[0][yofs + x] &= refpic.avpic.data[0][kofs + x];
     307                    else
     308                        avpic.data[0][yofs + x] = 0;
     309                }
     310            }
     311        }
     312    }
     313}
     314
     315void CAVPicture::Close(int xsize, int ysize)
     316{
     317    Dilate(xsize, ysize);
     318    Erode(xsize, ysize);
     319}
     320
     321void CAVPicture::Open(int xsize, int ysize)
     322{
     323    Erode(xsize, ysize);
     324    Dilate(xsize, ysize);
     325}
     326
     327void CAVPicture::Fill1(int x, int y)
     328{
     329    if (avpic.data[0][y*width+x])
     330        return;
     331    avpic.data[0][y*width+x] = 255;
     332    if (x < width-1) Fill1(x+1,y);
     333    if (y < height-1) Fill1(x,y+1);
     334    if (x > 0) Fill1(x-1,y);
     335    if (y > 0) Fill1(x,y-1);
     336}
     337
     338void CAVPicture::Fill()
     339{
     340    CAVPicture tmp(*this);
     341    // find a seed point
     342    tmp.Fill1(0,0);
     343    tmp.Invert();
     344    Union(tmp);
     345}
     346
     347void CAVPicture::Invert()
     348{
     349    uint8_t *p1 = avpic.data[0];
     350    uint8_t *pEnd = p1 + width * height;
     351    for(; p1 < pEnd; p1++)
     352    {
     353        *p1 = 255 - *p1;
     354    }
     355}
     356
     357void CAVPicture::Union(const CAVPicture& pic)
     358{
     359    uint8_t *p1 = avpic.data[0];
     360    uint8_t *p2 = pic.avpic.data[0];
     361    uint8_t *pEnd = p1 + width * height;
     362    for(; p1 < pEnd; p1++, p2++)
     363    {
     364        *p1 = *p1 | *p2;
     365    }
     366}
     367
     368void CAVPicture::Intersection(const CAVPicture& pic)
     369{
     370    uint8_t *p1 = avpic.data[0];
     371    uint8_t *p2 = pic.avpic.data[0];
     372    uint8_t *pEnd = p1 + width * height;
     373    for(; p1 < pEnd; p1++, p2++)
     374    {
     375        *p1 = *p1 & *p2;
     376    }
     377}
     378
     379uint32_t CAVPicture::CalcArea(uint8_t threshold)
     380{
     381    uint8_t *p1 = avpic.data[0];
     382    uint8_t *pEnd = p1 + width * height;
     383    uint32_t area = 0;
     384    for(; p1 < pEnd; p1++)
     385    {
     386        if (*p1 >= threshold)
     387        {
     388            area++;
     389        }
     390    }
     391    return area;
     392}
     393
     394void CAVPicture::AverageImage(unsigned char* data, int srcWidth, int xs, int ys, int w, int h, int alpha )
     395{
     396    int x, y;
     397    int ye = ys+h;
     398    uint8_t *p = avpic.data[0];
     399    int alpha1 = alpha-1;
     400    int alpha2 = alpha/2;
     401
     402    for(y=ys; y<ye; y++)
     403    {
     404        int yofs = y * srcWidth;
     405        int xe = xs+w+yofs;
     406        for(x=xs + yofs; x<xe; x++, p++)
     407        {
     408            //avpic->data[0][x] = (framePtr[x] + avpic->data[0][x] * (alpha-1))/alpha;
     409#if 1
     410            *p = (data[x] + (*p * alpha1) + alpha2)/alpha;
     411#else
     412            *p = data[x];
     413#endif
     414        }
     415    }
     416}
     417
     418void CAVPicture::Average(const AVPicture* pic, int alpha )
     419{
     420    uint8_t *p1 = avpic.data[0];
     421    uint8_t *pEnd = p1 + width * height;
     422    uint8_t *p2 = pic->data[0];
     423    int alpha1 = alpha-1;
     424    int alpha2 = alpha/2;
     425
     426    for(; p1 < pEnd; p1++, p2++)
     427    {
     428            *p1 = (*p2 + (*p1 * alpha1) + alpha2)/alpha;
     429    }
     430}
     431
     432int CAVPicture::IntersectionRatio(const AVPicture* pic )
     433{
     434    int pixCount = 0;
     435    int sameCount = 0;
     436    uint8_t *p1 = avpic.data[0];
     437    uint8_t *pEnd = p1 + width * height;
     438    uint8_t *p2 = pic->data[0];
     439
     440    for(; p1 < pEnd; p1++, p2++)
     441    {
     442        if (*p1 || *p2) pixCount++;
     443        if (*p1 && *p2) sameCount++;
     444    }
     445
     446    if (pixCount == 0) return 0;
     447    return (sameCount * 100)/pixCount;
     448}
     449
     450int CAVPicture::SimilarityRatio(const AVPicture* mask, const AVPicture* pic)
     451{
     452    int pixCount = 0;
     453    int sameCount = 0;
     454    uint8_t *p1 = avpic.data[0];
     455    uint8_t *pEnd = p1 + width * height;
     456    uint8_t *p2 = pic->data[0];
     457    uint8_t *pmask = mask->data[0];
     458
     459    for(; p1 < pEnd; p1++, p2++, pmask++)
     460    {
     461        if (*pmask)
     462        {
     463            pixCount++;
     464            if (*p1 == *p2) sameCount++;
     465        }
     466    }
     467
     468    if (pixCount == 0) return 0;
     469    return (sameCount * 100)/pixCount;
     470}
     471
     472void CAVPicture::Threshold(int32_t minThreshold)
     473{
     474    Histogram2 hist(*this);
     475    int thresh = hist.GetThreshold();
     476    uint8_t *p1 = avpic.data[0];
     477    uint8_t *pEnd = p1 + width * height;
     478    if (thresh < minThreshold)
     479    {
     480        thresh = minThreshold;
     481    }
     482    if (thresh == 0)
     483    {
     484        thresh = 1;
     485    }
     486    for(; p1 < pEnd; p1++)
     487    {
     488        if (*p1 >= thresh)
     489            *p1 = 255;
     490        else
     491            *p1 = 0;
     492    }
     493}
     494
     495void CAVPicture::LabelConnectedRegion(PComponent& component, uint8_t label, int x, int y)
     496{
     497    if ((avpic.data[0][y*width+x]) != 255)
     498        return;
     499    avpic.data[0][y*width+x] = label;
     500    component->area++;
     501    // grow bounding box
     502    if (x < component->boundingBox.tl.x) component->boundingBox.tl.x = x;
     503    if (y < component->boundingBox.tl.y) component->boundingBox.tl.y = y;
     504    if (x > component->boundingBox.br.x) component->boundingBox.br.x = x;
     505    if (y > component->boundingBox.br.y) component->boundingBox.br.y = y;
     506
     507    if (x < width-1) LabelConnectedRegion(component, label, x+1,y);
     508    if (y < height-1) LabelConnectedRegion(component, label, x,y+1);
     509    if (x > 0) LabelConnectedRegion(component, label, x-1,y);
     510    if (y > 0) LabelConnectedRegion(component, label, x,y-1);
     511}
     512
     513void CAVPicture::GetComponents(ComponentList& components, CAVPicture* templateImage)
     514{
     515    CAVPicture work(*this);
     516    uint8_t label = 1;
     517    uint8_t *p1 = work.avpic.data[0];
     518    uint8_t *pEnd = p1 + work.width * work.height;
     519
     520    for(; p1 < pEnd; p1++)
     521    {
     522        if (*p1 == 255)
     523        {
     524            int x = (p1-work.avpic.data[0])%work.width;
     525            int y = (p1-work.avpic.data[0])/work.width;
     526            PComponent component(new Component(x,y));
     527            work.LabelConnectedRegion(component, label, x, y);
     528            // copy it into cMask
     529            component->cMask = new CAVPicture(*this,
     530                    component->boundingBox.tl.x,
     531                    component->boundingBox.tl.y,
     532                    component->boundingBox.br.x - component->boundingBox.tl.x + 1,
     533                    component->boundingBox.br.y - component->boundingBox.tl.y + 1);
     534            if (templateImage)
     535            {
     536                component->cTemplate = new CAVPicture(*templateImage,
     537                        component->boundingBox.tl.x,
     538                        component->boundingBox.tl.y,
     539                        component->boundingBox.br.x - component->boundingBox.tl.x + 1,
     540                        component->boundingBox.br.y - component->boundingBox.tl.y + 1);
     541                component->cTemplate->Intersection(*(component->cMask));
     542            }
     543            components.push_back(component);
     544            label++;
     545        }
     546    }
     547}
     548
     549void CAVPicture::SetTemplate(PComponent component)
     550{
     551    SAFE_DELETE(component->cTemplate);
     552    component->cTemplate = new CAVPicture(*this,
     553            component->boundingBox.tl.x,
     554            component->boundingBox.tl.y,
     555            component->boundingBox.br.x - component->boundingBox.tl.x + 1,
     556            component->boundingBox.br.y - component->boundingBox.tl.y + 1);
     557}
     558
     559Component::~Component()
     560{
     561    SAFE_DELETE(cTemplate);
     562    SAFE_DELETE(cMask);
     563}
     564
     565NextgenLogoDetector2::NextgenLogoDetector2(NextgenCommDetector* commdetector,
     566                                         unsigned int w, unsigned int h,
     567                                         unsigned int commdetectborder_in,
     568                                         unsigned int xspacing_in,
     569                                         unsigned int yspacing_in)
     570    : LogoDetectorBase(w,h),
     571      commDetector(commdetector),
     572      pgmConverter(new PGMConverter()),
     573      borderDetector(new BorderDetector()),
     574      edgeDetector(new CannyEdgeDetector()),
     575      averageImage(new CAVPicture(w,h)),
     576      logoList()
     577{
     578    commDetectLogoSamplesNeeded =
     579        gCoreContext->GetNumSetting("CommDetectLogoSamplesNeeded", 240);
     580    commDetectLogoSampleSpacing =
     581        gCoreContext->GetNumSetting("CommDetectLogoSampleSpacing", 1);
     582    commDetectLogoSampleAveraging =
     583        gCoreContext->GetNumSetting("CommDetectLogoSampleAveraging", 20);
     584    commDetectLogoSecondsNeeded = (int)(1.3 * commDetectLogoSamplesNeeded *
     585                                              commDetectLogoSampleSpacing);
     586    commDetectLogoSampleSpacingInFrames = commDetectLogoSampleSpacing * 25;
     587
     588    // split image to 3:5:3
     589    subWidth = (width/11.0)*3;
     590    subHeight = (height/11.0)*3;
     591    for(int i = 0; i < 4; i++)
     592    {
     593        avgSubImage[i] = new CAVPicture(subWidth, subHeight);
     594        previousEdges[i] = new CAVPicture(subWidth, subHeight);
     595        previousCalcImg[i] = new CAVPicture(subWidth, subHeight);
     596        timeAveragedEdges[i] = new CAVPicture(subWidth, subHeight);
     597        timeAveragedThresholdedEdges[i] = new CAVPicture(subWidth, subHeight);
     598        persistenceCount[i] = 0;
     599    }
     600
     601}
     602
     603void NextgenLogoDetector2::deleteLater(void)
     604{
     605    commDetector = NULL;
     606
     607    for(int i = 0; i < 4; i++)
     608    {
     609        SAFE_DELETE(avgSubImage[i]);
     610        SAFE_DELETE(previousEdges[i]);
     611        SAFE_DELETE(previousCalcImg[i]);
     612        SAFE_DELETE(timeAveragedEdges[i]);
     613        SAFE_DELETE(timeAveragedThresholdedEdges[i]);
     614    }
     615    SAFE_DELETE(pgmConverter);
     616    SAFE_DELETE(borderDetector);
     617    SAFE_DELETE(edgeDetector);
     618
     619    LogoDetectorBase::deleteLater();
     620}
     621
     622unsigned int NextgenLogoDetector2::getRequiredAvailableBufferForSearch()
     623{
     624    return commDetectLogoSecondsNeeded;
     625}
     626
     627bool NextgenLogoDetector2::searchForLogo(MythPlayer* /*player*/)
     628{
     629    // allow rest of algo to work so always true
     630    return true;
     631}
     632
     633bool NextgenLogoDetector2::doesThisFrameContainTheFoundLogo(
     634    VideoFrame * frame)
     635{
     636    const int           FRAMESGMPCTILE = 95;
     637    int                 i;
     638    const AVPicture     *edges;
     639    unsigned char       *framePtr = frame->buf;
     640    uint32_t            rowWidth = frame->pitches[0];
     641
     642    //return false;
     643
     644#define AVG_MODE 0
     645#if AVG_MODE
     646#define AVERAGE_COEFF commDetectLogoSampleSpacingInFrames
     647//#define AVERAGE_COEFF 1
     648        avgSubImage[0]->AverageImage(framePtr, rowWidth, 0, 0, subWidth, subHeight, AVERAGE_COEFF);
     649        avgSubImage[1]->AverageImage(framePtr, rowWidth, width-subWidth, 0, subWidth, subHeight, AVERAGE_COEFF);
     650        avgSubImage[2]->AverageImage(framePtr, rowWidth, 0, height-subHeight, subWidth, subHeight, AVERAGE_COEFF);
     651        avgSubImage[3]->AverageImage(framePtr, rowWidth, width-subWidth, height-subHeight, subWidth, subHeight, AVERAGE_COEFF);
     652#endif
     653
     654    if ((frame->frameNumber % (commDetectLogoSampleSpacingInFrames)) == 0)
     655    {
     656#if !AVG_MODE
     657//#define AVERAGE_COEFF commDetectLogoSampleAveraging
     658#define AVERAGE_COEFF 1
     659        avgSubImage[0]->AverageImage(framePtr, rowWidth, 0, 0, subWidth, subHeight, AVERAGE_COEFF);
     660        avgSubImage[1]->AverageImage(framePtr, rowWidth, width-subWidth, 0, subWidth, subHeight, AVERAGE_COEFF);
     661        avgSubImage[2]->AverageImage(framePtr, rowWidth, 0, height-subHeight, subWidth, subHeight, AVERAGE_COEFF);
     662        avgSubImage[3]->AverageImage(framePtr, rowWidth, width-subWidth, height-subHeight, subWidth, subHeight, AVERAGE_COEFF);
     663#endif
     664
     665#if 1
     666        int equalEdges[4];
     667        int persistenceFactor[4];
     668        CAVPicture tamask[4];
     669        CAVPicture calcPic;
     670        ComponentList newmasklist[4];
     671#define USE_MASK_AREA 0
     672#if USE_MASK_AREA
     673        int32_t maskArea[4];
     674#endif
     675
     676        for(i=0; i<4; i++)
     677        {
     678#if USE_MASK_AREA
     679            maskArea[i] = 0;
     680#endif
     681            edges = edgeDetector->detectEdges(&avgSubImage[i]->avpic, subHeight, FRAMESGMPCTILE);
     682            if (edges)
     683            {
     684                //equalEdges[i] = memcmp(previousEdges[i]->avpic.data[0], edges->data[0], subWidth*subHeight)==0;
     685#if 0
     686                persistenceFactor[i] = previousEdges[i]->IntersectionRatio(edges);
     687#endif
     688                previousEdges[i]->copy(edges);
     689
     690                //previousEdges[i]->Close(5,5);
     691                //previousEdges[i]->Open(5,5);
     692#if 0
     693                tamask[i].copy(*previousEdges[i]);
     694                tamask[i].Close(5,5);
     695                tamask[i].Fill();
     696                tamask[i].Open(5,5);
     697#else
     698                timeAveragedEdges[i]->Average(&previousEdges[i]->avpic, commDetectLogoSampleAveraging);
     699                timeAveragedThresholdedEdges[i]->copy(*timeAveragedEdges[i]);
     700                timeAveragedThresholdedEdges[i]->Threshold(8);
     701                tamask[i].copy(*timeAveragedThresholdedEdges[i]);
     702                tamask[i].Close(5,5);
     703                tamask[i].Fill();
     704                tamask[i].Open(5,5);
     705                // componentize
     706                tamask[i].GetComponents(newmasklist[i], timeAveragedThresholdedEdges[i]);
     707#endif
     708                // check for stability in candidate mask list
     709
     710#if 1
     711                calcPic.copy(*previousEdges[i]);
     712                calcPic.Intersection(tamask[i]);
     713                persistenceFactor[i] = previousCalcImg[i]->IntersectionRatio(&calcPic.avpic);
     714                //persistenceFactor[i] = previousCalcImg[i]->SimilarityRatio(&tamask[i].avpic, &calcPic.avpic);
     715                previousCalcImg[i]->copy(calcPic);
     716                equalEdges[i] = calcPic.CalcArea(128);
     717#endif
     718
     719#if USE_MASK_AREA
     720                maskArea[i] = tamask[i].CalcArea(128);
     721#endif
     722#if 0
     723                tamask[i].Close(3,3);
     724                tamask[i].Fill();
     725                tamask[i].Open(3,3);
     726#endif
     727                if (persistenceFactor[i] >= 70)
     728                {
     729                    persistenceCount[i]++;
     730                }
     731                else
     732                {
     733                    persistenceCount[i] = 0;
     734                }
     735
     736                //
     737                // check if any in new mask list matches a registered mask
     738                //
     739            }
     740
     741        }
     742#if 0
     743        if (frame->frameNumber == 8*60*25)
     744        {
     745            CAVPicture pic1(framePtr, rowWidth, height);
     746            pic1.writeJPG("f12000");
     747            for(i=0; i<4;i++)
     748            {
     749                avgSubImage[i]->writeJPG(QString("f12000_sub%1").arg(i));
     750                previousEdges[i]->writeJPG(QString("f12000_edge%1").arg(i));
     751                previousCalcImg[i]->writeJPG(QString("f12000_pci%1").arg(i));
     752                timeAveragedEdges[i]->writeJPG(QString("f12000_taedge%1").arg(i));
     753                timeAveragedThresholdedEdges[i]->writeJPG(QString("f12000_tatedge%1").arg(i));
     754                tamask[i].writeJPG(QString("f12000_tamask%1").arg(i));
     755            }
     756        }
     757#endif
     758        LOG(VB_COMMFLAG, LOG_INFO, QString("LogoDet factors %1(%2,%9) %3(%4,%10) %5(%6,%11) %7(%8,%12)")
     759        //LOG(VB_COMMFLAG, LOG_INFO, QString("LogoDet factors %1(%2) %3(%4) %5(%6) %7(%8)")
     760                .arg(persistenceFactor[0])
     761                .arg(persistenceCount[0])
     762                .arg(persistenceFactor[1])
     763                .arg(persistenceCount[1])
     764                .arg(persistenceFactor[2])
     765                .arg(persistenceCount[2])
     766                .arg(persistenceFactor[3])
     767                .arg(persistenceCount[3])
     768#if 1
     769                .arg(equalEdges[0])
     770                .arg(equalEdges[1])
     771                .arg(equalEdges[2])
     772                .arg(equalEdges[3])
     773#endif
     774#if USE_MASK_AREA
     775                .arg(maskArea[0])
     776                .arg(maskArea[1])
     777                .arg(maskArea[2])
     778                .arg(maskArea[3])
     779#endif
     780                );
     781#endif
     782    }
     783    return false;
     784}
     785
     786bool NextgenLogoDetector2::pixelInsideLogo(unsigned int x, unsigned int y)
     787{
     788    return false;
     789}
     790
  • new file mythtv/programs/mythcommflag/NextgenLogoDetector2.h

    diff --git a/mythtv/programs/mythcommflag/NextgenLogoDetector2.h b/mythtv/programs/mythcommflag/NextgenLogoDetector2.h
    new file mode 100644
    index 0000000..d3e6f83
    - +  
     1#ifndef _NEXTGENLOGOGEDETECTOR2_H_
     2#define _NEXTGENLOGOGEDETECTOR2_H_
     3
     4#include "LogoDetectorBase.h"
     5
     6extern "C" {
     7#include "libavcodec/avcodec.h"    /* AVPicture */
     8}
     9#include "FrameAnalyzer.h"
     10
     11class PGMConverter;
     12class BorderDetector;
     13class EdgeDetector;
     14class CannyEdgeDetector;
     15class CAVPicture;
     16class NextgenCommDetector;
     17
     18struct Point
     19{
     20    Point() : x(0), y(0) { }
     21    int x;
     22    int y;
     23};
     24struct Rect
     25{
     26    Point   tl;
     27    Point   br;
     28};
     29
     30class Component : QSharedData
     31{
     32    public:
     33        Component() : area(0), maskStabilityCount(0), cTemplate(NULL), cMask(NULL), index(-1)
     34        {
     35        }
     36        Component(int x, int y) : area(0), maskStabilityCount(0), cTemplate(NULL), cMask(NULL), index(-1)
     37        {
     38            boundingBox.tl.x = boundingBox.br.x = x;
     39            boundingBox.tl.y = boundingBox.br.y = y;
     40        }
     41        ~Component();
     42
     43        int             area;
     44        int             maskStabilityCount;
     45        Rect            boundingBox;
     46        CAVPicture*     cTemplate;
     47        CAVPicture*     cMask;
     48        int             index;  // index of subframe
     49        friend class QExplicitlySharedDataPointer< Component >;
     50};
     51typedef QExplicitlySharedDataPointer< Component > PComponent;
     52typedef list<PComponent> ComponentList;
     53
     54class NextgenLogoDetector2 : public LogoDetectorBase
     55{
     56  public:
     57    /* Ctor/dtor. */
     58    NextgenLogoDetector2(NextgenCommDetector* commDetector,unsigned int width,
     59        unsigned int height, unsigned int commdetectborder,
     60        unsigned int xspacing, unsigned int yspacing);
     61    virtual void deleteLater(void);
     62    //NextgenLogoDetector2(PGMConverter *pgmc, BorderDetector *bd, EdgeDetector *ed,
     63            //MythPlayer *player, int proglen, QString debugdir);
     64
     65    bool searchForLogo(MythPlayer* player);
     66    bool doesThisFrameContainTheFoundLogo(VideoFrame* frame);
     67    bool pixelInsideLogo(unsigned int x, unsigned int y);
     68
     69    unsigned int getRequiredAvailableBufferForSearch();
     70
     71  protected:
     72    virtual ~NextgenLogoDetector2() {}
     73
     74  private:
     75    NextgenCommDetector* commDetector;
     76
     77    PGMConverter    *pgmConverter;
     78    BorderDetector  *borderDetector;
     79    EdgeDetector    *edgeDetector;
     80
     81    CAVPicture      *averageImage;
     82    CAVPicture      *avgSubImage[4];
     83    CAVPicture      *previousEdges[4];
     84    CAVPicture      *timeAveragedEdges[4];
     85    CAVPicture      *timeAveragedThresholdedEdges[4];
     86    CAVPicture      *previousCalcImg[4];
     87    ComponentList   logoList;
     88
     89    int             persistenceCount[4];
     90    int             subWidth;
     91    int             subHeight;
     92
     93    // algo parameters
     94    int commDetectLogoSamplesNeeded;
     95    int commDetectLogoSampleSpacing;
     96    int commDetectLogoSampleAveraging;
     97    int commDetectLogoSecondsNeeded;
     98
     99    int commDetectLogoSampleSpacingInFrames;
     100};
     101
     102#endif
     103
  • new file mythtv/programs/mythcommflag/NextgenSceneChangeDetector.cpp

    diff --git a/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.cpp b/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.cpp
    new file mode 100644
    index 0000000..6d8fc72
    - +  
     1#include <algorithm>
     2using namespace std;
     3
     4// MythTV headers
     5#include "mythcontext.h"
     6#include "mythframe.h"
     7
     8#include "NextgenSceneChangeDetector.h"
     9#include "Histogram.h"
     10
     11NextgenSceneChangeDetector::NextgenSceneChangeDetector(unsigned int width,
     12        unsigned int height, unsigned int commdetectborder_in,
     13        unsigned int xspacing_in, unsigned int yspacing_in):
     14    SceneChangeDetectorBase(width,height),
     15    frameNumber(0),
     16    previousFrameWasSceneChange(false),
     17    xspacing(xspacing_in),
     18    yspacing(yspacing_in),
     19    commdetectborder(commdetectborder_in)
     20{
     21    histogram = new Histogram;
     22    previousHistogram = new Histogram;
     23    commDetectSceneChangeThreshold =
     24        gCoreContext->GetNumSetting("CommDetectSceneChangeThreshold", 70)/100.0;
     25}
     26
     27void NextgenSceneChangeDetector::deleteLater(void)
     28{
     29    delete histogram;
     30    delete previousHistogram;
     31    SceneChangeDetectorBase::deleteLater();
     32}
     33
     34void NextgenSceneChangeDetector::processFrame(VideoFrame* frame)
     35{
     36    int width = frame->pitches[0];
     37    histogram->generateFromImage(frame, width, height, commdetectborder,
     38                                 width-commdetectborder, commdetectborder,
     39                                 height-commdetectborder, xspacing, yspacing);
     40    float similar = histogram->calculateSimilarityWith(*previousHistogram);
     41
     42    bool isSceneChange = (similar < commDetectSceneChangeThreshold && !previousFrameWasSceneChange);
     43
     44    emit(haveNewInformation(frameNumber,isSceneChange,similar));
     45    previousFrameWasSceneChange = isSceneChange;
     46
     47    std::swap(histogram,previousHistogram);
     48}
     49
     50/* vim: set expandtab tabstop=4 shiftwidth=4: */
     51
  • new file mythtv/programs/mythcommflag/NextgenSceneChangeDetector.h

    diff --git a/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.h b/mythtv/programs/mythcommflag/NextgenSceneChangeDetector.h
    new file mode 100644
    index 0000000..86f105e
    - +  
     1#ifndef _NEXTGENSCENECHANGEDETECTOR_H_
     2#define _NEXTGENSCENECHANGEDETECTOR_H_
     3
     4#include "SceneChangeDetectorBase.h"
     5
     6class Histogram;
     7
     8class NextgenSceneChangeDetector : public SceneChangeDetectorBase
     9{
     10  public:
     11    NextgenSceneChangeDetector(unsigned int width, unsigned int height,
     12        unsigned int commdetectborder, unsigned int xspacing,
     13        unsigned int yspacing);
     14    virtual void deleteLater(void);
     15
     16    void processFrame(VideoFrame* frame);
     17
     18  private:
     19    ~NextgenSceneChangeDetector() {}
     20
     21  private:
     22    Histogram* histogram;
     23    Histogram* previousHistogram;
     24    unsigned int frameNumber;
     25    bool previousFrameWasSceneChange;
     26    unsigned int xspacing, yspacing;
     27    unsigned int commdetectborder;
     28    double commDetectSceneChangeThreshold;
     29};
     30
     31#endif
     32
     33/* vim: set expandtab tabstop=4 shiftwidth=4: */
     34
  • new file mythtv/programs/mythcommflag/SubtitleChangeDetector.cpp

    diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetector.cpp b/mythtv/programs/mythcommflag/SubtitleChangeDetector.cpp
    new file mode 100644
    index 0000000..af46830
    - +  
     1using namespace std;
     2
     3// MythTV headers
     4#include "mythframe.h"
     5#include "mythplayer.h"
     6
     7extern "C" {
     8#include "libavcodec/avcodec.h"
     9}
     10
     11#include "SubtitleChangeDetector.h"
     12
     13const int    kTeletextColumns = 40;
     14const int    kTeletextRows    = 26;
     15
     16SubtitleChangeDetector::SubtitleChangeDetector(MythPlayer* player)
     17    :
     18        m_teletextIsBlank(true),
     19        m_cc608IsBlank(true),
     20        m_cc708IsBlank(true)
     21{
     22    TeletextReader * teletextReader = player->GetTeletextReader();
     23    if (teletextReader)
     24    {
     25        int page = player->GetDecoder()->GetTrackLanguageIndex(
     26                            kTrackTypeTeletextCaptions,
     27                            player->GetDecoder()->GetTrack(kTrackTypeTeletextCaptions));
     28        teletextReader->SetPage(page, -1);
     29    }   
     30}
     31
     32SubtitleChangeDetector::SubtitleTiming::SubtitleTiming(int start_, int end_)
     33    : start(start_), end(end_)
     34{
     35}
     36
     37void SubtitleChangeDetector::Add(int start, int end)
     38{
     39    LOG(VB_COMMFLAG, LOG_DEBUG,
     40            QString("subtitle start %1 end %2")
     41            .arg(start).arg(end));
     42
     43    if (!subtitleTimingList.empty())
     44    {
     45        MythDeque<SubtitleTiming>::iterator sit;
     46        for(sit = subtitleTimingList.begin(); sit != subtitleTimingList.end(); ++sit)
     47        {
     48            if (end < (*sit).start)
     49            {
     50                // wholy before current one so insert it
     51                subtitleTimingList.insert(sit, SubtitleTiming(start, end));
     52                break;
     53            }
     54            else if (start > (*sit).end)
     55            {
     56                // wholly after this one
     57            }
     58            else
     59            {
     60                if (start < (*sit).start)
     61                    (*sit).start = start;
     62                if (end > (*sit).end)
     63                    (*sit).end = end;
     64                break;
     65            }
     66        }
     67        if (sit == subtitleTimingList.end())
     68        {
     69            // wholly at the end
     70            subtitleTimingList.push_back(SubtitleTiming(start, end));
     71        }
     72    }
     73    else
     74    {
     75        subtitleTimingList.push_back(SubtitleTiming(start, end));
     76    }
     77}
     78
     79void SubtitleChangeDetector::processFrame(MythPlayer* player, VideoFrame * frame)
     80{
     81#if 1
     82    SubtitleReader * reader = player->GetSubReader();
     83    int frameNumber = frame->frameNumber;
     84    double fps = player->GetFrameRate();
     85    //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle frame %1").arg(frameNumber));
     86#if 0
     87    uint64_t duration = 0;
     88    const QStringList rawSubs = reader->GetRawTextSubtitles(duration);
     89
     90    if (!rawSubs.isEmpty())
     91    {
     92        LOG(VB_COMMFLAG, LOG_DEBUG,
     93                QString("There are also %1 raw text subtitles with duration %2")
     94                .arg(rawSubs.size()).arg(duration));
     95    }
     96
     97    reader->ClearRawTextSubtitles();
     98
     99#endif
     100    AVSubtitles *subtitles = reader->GetAVSubtitles();
     101    if (subtitles && !subtitles->buffers.empty())
     102    {
     103        QMutexLocker locker(&(subtitles->lock));
     104        while (!subtitles->buffers.empty())
     105        {
     106            const AVSubtitle subtitle = subtitles->buffers.front();
     107            subtitles->buffers.pop_front();
     108
     109            //subtitle.start_display_time;    /* relative to packet pts, in ms */
     110            //subtitle.end_display_time;  /* relative to packet pts, in ms */
     111            //subtitle.pts;               /* in AV_TIME_BASE, use frame number * fps */
     112
     113            int start = frameNumber + subtitle.start_display_time * fps / 1000;
     114            int end   = frameNumber + subtitle.end_display_time * fps / 1000;
     115            Add(start, end);
     116            reader->FreeAVSubtitle(subtitle);
     117
     118            reader->ClearRawTextSubtitles();
     119        }
     120    }
     121
     122    TeletextReader * teletextReader = player->GetTeletextReader();
     123    if (teletextReader)
     124    {
     125        if (teletextReader->PageChanged())
     126        {
     127            LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext changed 1"));
     128            TeletextSubPage *subpage = teletextReader->FindSubPage();
     129            if (subpage)
     130            {
     131                LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext subpage found %1 %2").arg((quintptr)subpage,0,16).arg(subpage->subpagenum));
     132                teletextReader->SetSubPage(subpage->subpagenum);
     133                int a = 0;
     134                if ((subpage->subtitle) ||
     135                        (subpage->flags & (TP_SUPPRESS_HEADER | TP_NEWSFLASH | TP_SUBTITLE)))
     136                {
     137                    LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext %1").arg(teletextReader->IsSubtitle()));
     138                    a = 1;
     139                    teletextReader->SetShowHeader(false);
     140                    teletextReader->SetIsSubtitle(true);
     141                    // check text is not blank
     142                    if (teletextReader->IsSubtitle() || teletextReader->IsTransparent())
     143                    {
     144                        QString caption;
     145                        bool isBlank = true;
     146                        unsigned char ch;
     147                        for (int y = kTeletextRows - a; y >= 2; y--)
     148                        {
     149                            for (uint i = (y == 1 ? 8 : 0); i < (uint) kTeletextColumns; i++)
     150                            {
     151                                ch = subpage->data[y-1][i] & 0x7F;
     152                                caption += ch;
     153                                if (ch != ' ')
     154                                {
     155                                    isBlank = false;
     156                                    //goto done_char_scan;
     157                                }
     158                            }
     159                        }
     160//done_char_scan:
     161                        m_teletextIsBlank = isBlank;
     162                        caption = caption.simplified();
     163                        LOG(VB_COMMFLAG, LOG_DEBUG, QString("new teletext subtitle string '%1' %2").arg(caption).arg(isBlank));
     164                    }
     165                    else
     166                    {
     167                        m_teletextIsBlank = true;
     168                    }
     169
     170                }
     171                else
     172                {
     173                    teletextReader->SetShowHeader(true);
     174                    teletextReader->SetIsSubtitle(false);
     175                    teletextReader->SetHeaderChanged(false);
     176                    m_teletextIsBlank = true;
     177                }
     178            }
     179            teletextReader->SetPageChanged(false);
     180        }
     181        if (!m_teletextIsBlank)
     182        {
     183            //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle teletext present"));
     184            haveNewInformation(frameNumber, true, 0);
     185        }
     186    }
     187    CC608Reader * cc608Reader = player->GetCC608Reader();
     188    if (cc608Reader)
     189    {
     190        bool changed = false;
     191        CC608Buffer* textlist = cc608Reader->GetOutputText(changed);
     192        if (changed && textlist)
     193        {
     194            textlist->lock.lock();
     195            bool isBlank = true;
     196            QString caption;
     197            if (!textlist->buffers.empty())
     198            {
     199                for(vector<CC608Text*>::const_iterator it = textlist->buffers.begin();
     200                        it != textlist->buffers.end();
     201                        ++it)
     202                {
     203                    if (!(*it)->text.isEmpty())
     204                    {
     205                        isBlank = false;
     206                        caption += " " + (*it)->text;
     207                    }
     208                }
     209            }
     210            textlist->lock.unlock();
     211            m_cc608IsBlank = isBlank;
     212            caption = caption.simplified();
     213            LOG(VB_COMMFLAG, LOG_DEBUG, QString("new cc608 subtitle string '%1' %2").arg(caption).arg(isBlank));
     214        }
     215        if (!m_cc608IsBlank)
     216        {
     217            //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle cc608 present"));
     218            haveNewInformation(frameNumber, true, 0);
     219        }
     220    }
     221    CC708Reader * cc708Reader = player->GetCC708Reader();
     222    if (cc708Reader)
     223    {
     224        int cc708ChangedMask = 0;
     225        bool isBlank = true;
     226        QString caption;
     227        CC708Service *cc708service = cc708Reader->GetCurrentService();
     228        for (uint i = 0; i < 8; i++)
     229        {
     230            CC708Window &win = cc708service->windows[i];
     231            if (win.GetExists() && win.GetVisible() && !win.GetChanged())
     232                continue;
     233            if (!win.GetExists() || !win.GetVisible())
     234                continue;
     235
     236            QMutexLocker locker(&win.lock);
     237            cc708ChangedMask |= 1<<i;
     238            vector<CC708String*> list = win.GetStrings();
     239            if (!list.empty())
     240            {
     241                for(vector<CC708String*>::const_iterator it = list.begin();
     242                        it != list.end();
     243                        ++it)
     244                {
     245                    if (!(*it)->str.isEmpty())
     246                    {
     247                        isBlank = false;
     248                        caption += " " + (*it)->str;
     249                    }
     250                }
     251            }
     252            for (uint j = 0; j < list.size(); j++)
     253                delete list[j];
     254            win.ResetChanged();
     255        }
     256        if (cc708ChangedMask != 0)
     257        {
     258            m_cc708IsBlank = isBlank;
     259            caption = caption.simplified();
     260            LOG(VB_COMMFLAG, LOG_DEBUG, QString("new cc708 subtitle string '%1' %2").arg(caption).arg(isBlank).arg(cc708ChangedMask,2,16,QChar('0')));
     261        }
     262        if (!m_cc708IsBlank)
     263        {
     264            //LOG(VB_COMMFLAG, LOG_DEBUG, QString("subtitle cc708 present"));
     265            haveNewInformation(frameNumber, true, 0);
     266        }
     267    }
     268
     269    if (!subtitleTimingList.empty())
     270    {
     271        SubtitleTiming& currentSub = subtitleTimingList.front();
     272        if (currentSub.start <= frameNumber && frameNumber <= currentSub.end)
     273        {
     274            currentSub.start = frameNumber + 1;
     275            haveNewInformation(frameNumber, true, 0);
     276        }
     277        if (frameNumber >= currentSub.end)
     278        {
     279            subtitleTimingList.pop_front();
     280        }
     281    }
     282#endif
     283}
     284
     285/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/SubtitleChangeDetector.h

    diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetector.h b/mythtv/programs/mythcommflag/SubtitleChangeDetector.h
    new file mode 100644
    index 0000000..89c69c3
    - +  
     1/*
     2 * SubtitleChangeDetector
     3 *
     4 * Detect subtitle changes
     5 */
     6
     7#ifndef __SUBTITLECHANGEDETECTOR_H__
     8#define __SUBTITLECHANGEDETECTOR_H__
     9
     10#include "SubtitleChangeDetectorBase.h"
     11#include "subtitlereader.h"
     12
     13class SubtitleChangeDetector : public SubtitleChangeDetectorBase
     14{
     15public:
     16    SubtitleChangeDetector(MythPlayer* player);
     17    virtual ~SubtitleChangeDetector() {}
     18
     19    void processFrame(MythPlayer* player, VideoFrame * frame);
     20
     21protected:
     22    void Add(int start, int end);
     23
     24    struct SubtitleTiming
     25    {
     26        int start;
     27        int end;
     28        SubtitleTiming(int start, int end);
     29    };
     30    MythDeque<SubtitleTiming> subtitleTimingList;
     31
     32    bool m_teletextIsBlank;
     33    bool m_cc608IsBlank;
     34    bool m_cc708IsBlank;
     35};
     36
     37#endif  /* !__SUBTITLECHANGEDETECTOR_H__ */
     38
     39/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.cpp

    diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.cpp b/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.cpp
    new file mode 100644
    index 0000000..f61e131
    - +  
     1
     2#include "SubtitleChangeDetectorBase.h"
     3
     4SubtitleChangeDetectorBase::SubtitleChangeDetectorBase()
     5{
     6}
     7
     8/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.h

    diff --git a/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.h b/mythtv/programs/mythcommflag/SubtitleChangeDetectorBase.h
    new file mode 100644
    index 0000000..1822db9
    - +  
     1#ifndef _SUBTITLECHANGEDETECTORBASE_H_
     2#define _SUBTITLECHANGEDETECTORBASE_H_
     3
     4#include <QObject>
     5
     6#include "mythframe.h"
     7class MythPlayer;
     8
     9class SubtitleChangeDetectorBase : public QObject
     10{
     11    Q_OBJECT
     12
     13  public:
     14    SubtitleChangeDetectorBase();
     15
     16    virtual void processFrame(MythPlayer* player, VideoFrame * frame) = 0;
     17
     18  signals:
     19    void haveNewInformation(unsigned int framenum, bool subtitleState,
     20                            float debugValue = 0.0);
     21
     22  protected:
     23    virtual ~SubtitleChangeDetectorBase() {}
     24
     25};
     26
     27#endif
     28
     29/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • mythtv/programs/mythcommflag/commandlineparser.cpp

    diff --git a/mythtv/programs/mythcommflag/commandlineparser.cpp b/mythtv/programs/mythcommflag/commandlineparser.cpp
    index caa6955..b1b74a7 100644
    a b void MythCommFlagCommandLineParser::LoadArguments(void) 
    6161        "off, blank, scene, blankscene, logo, all, "
    6262        "d2, d2_logo, d2_blank, d2_scene, d2_all", "")
    6363            ->SetGroup("Commflagging");
     64    add("--high-resolution", "highres", false,
     65        "perform commercial detection in actual resolution instead of 1/16th", "");
    6466    add("--outputmethod", "outputmethod", "",
    6567        "Format of output written to outputfile, essentials, full.", "")
    6668            ->SetGroup("Commflagging");
  • mythtv/programs/mythcommflag/main.cpp

    diff --git a/mythtv/programs/mythcommflag/main.cpp b/mythtv/programs/mythcommflag/main.cpp
    index c539f12..33c5468 100644
    a b using namespace std; 
    4646#include "CommDetectorFactory.h"
    4747#include "SlotRelayer.h"
    4848#include "CustomEventRelayer.h"
     49//#include "AudioBuffer.h"
    4950
    5051#define LOC      QString("MythCommFlag: ")
    5152#define LOC_WARN QString("MythCommFlag, Warning: ")
    static QMap<QString,SkipTypes> *init_skip_types(void) 
    109110    (*tmp)["blankscene"]  = COMM_DETECT_BLANK_SCENE;
    110111    (*tmp)["blank_scene"] = COMM_DETECT_BLANK_SCENE;
    111112    (*tmp)["logo"]        = COMM_DETECT_LOGO;
     113    (*tmp)["audio"]       = (SkipTypes)COMM_DETECT_AUDIO;
     114    (*tmp)["sub"]         = (SkipTypes)COMM_DETECT_SUBTITLES;
    112115    (*tmp)["all"]         = COMM_DETECT_ALL;
    113116    (*tmp)["d2"]          = COMM_DETECT_2;
    114117    (*tmp)["d2_logo"]     = COMM_DETECT_2_LOGO;
    115118    (*tmp)["d2_blank"]    = COMM_DETECT_2_BLANK;
    116119    (*tmp)["d2_scene"]    = COMM_DETECT_2_SCENE;
    117120    (*tmp)["d2_all"]      = COMM_DETECT_2_ALL;
     121    (*tmp)["ng"]          = (SkipTypes)(COMM_DETECT_NG);
     122    (*tmp)["ng_logo"]     = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_LOGO);
     123    (*tmp)["ng_blank"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_BLANK);
     124    (*tmp)["ng_scene"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_SCENE);
     125    (*tmp)["ng_audio"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_AUDIO);
     126    (*tmp)["ng_all"]      = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_ALL | COMM_DETECT_AUDIO | COMM_DETECT_SUBTITLES);
     127    (*tmp)["ng_allx"]     = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_ALL | COMM_DETECT_AUDIO | COMM_DETECT_SUBTITLES | COMM_DETECT_LOGO_EXPERIMENTAL);
     128    (*tmp)["ng_old"]      = (SkipTypes)(COMM_DETECT_NG_OLD);
    118129    return tmp;
    119130}
    120131
    static int FlagCommercials(ProgramInfo *program_info, int jobid, 
    739750    // configure commercial detection method
    740751    SkipTypes commDetectMethod = (SkipTypes)gCoreContext->GetNumSetting(
    741752                                    "CommercialSkipMethod", COMM_DETECT_ALL);
     753    bool commDetectHighResolution =
     754            gCoreContext->GetNumSetting(
     755                                    "CommercialSkipResolution", 0);
    742756
    743757    if (cmdline.toBool("commmethod"))
    744758    {
    static int FlagCommercials(ProgramInfo *program_info, int jobid, 
    837851    else if (commDetectMethod == COMM_DETECT_OFF)
    838852        return GENERIC_EXIT_OK;
    839853
     854    if (cmdline.toBool("highres"))
     855        // default to a cheaper method for debugging purposes
     856        commDetectHighResolution = true;
     857
    840858    frm_dir_map_t blanks;
    841859    recorder = NULL;
    842860
    static int FlagCommercials(ProgramInfo *program_info, int jobid, 
    886904
    887905    PlayerFlags flags = (PlayerFlags)(kAudioMuted   |
    888906                                      kVideoIsNull  |
    889                                       kDecodeLowRes |
     907                                      (commDetectHighResolution?0:kDecodeLowRes) |
    890908                                      kDecodeSingleThreaded |
    891909                                      kDecodeNoLoopFilter |
    892910                                      kNoITV);
  • mythtv/programs/mythcommflag/mythcommflag.pro

    diff --git a/mythtv/programs/mythcommflag/mythcommflag.pro b/mythtv/programs/mythcommflag/mythcommflag.pro
    index 997966e..d1858ba 100644
    a b HEADERS += LogoDetectorBase.h SceneChangeDetectorBase.h 
    3535HEADERS += SlotRelayer.h CustomEventRelayer.h
    3636HEADERS += commandlineparser.h
    3737
     38HEADERS += NextgenCommDetector.h
     39HEADERS += NextgenLogoDetector.h
     40HEADERS += NextgenLogoDetector2.h
     41HEADERS += NextgenSceneChangeDetector.h
     42HEADERS += AudioChangeDetectorBase.h AudioChangeDetector.h
     43HEADERS += AudioBuffer.h
     44HEADERS += SubtitleChangeDetectorBase.h SubtitleChangeDetector.h
     45
    3846SOURCES += CommDetectorFactory.cpp CommDetectorBase.cpp
    3947SOURCES += ClassicLogoDetector.cpp
    4048SOURCES += ClassicSceneChangeDetector.cpp
    SOURCES += HistogramAnalyzer.cpp 
    5159SOURCES += BlankFrameDetector.cpp
    5260SOURCES += SceneChangeDetector.cpp
    5361SOURCES += PrePostRollFlagger.cpp
     62SOURCES += AudioChangeDetectorBase.cpp AudioChangeDetector.cpp
     63SOURCES += AudioBuffer.cpp
     64SOURCES += SubtitleChangeDetectorBase.cpp SubtitleChangeDetector.cpp
     65SOURCES += NextgenLogoDetector.cpp
     66SOURCES += NextgenLogoDetector2.cpp
     67SOURCES += NextgenSceneChangeDetector.cpp
     68SOURCES += NextgenCommDetector.cpp
     69
     70SOURCES += ../../libs/libmythtv/subtitlereader.cpp
     71SOURCES += ../../libs/libmythtv/textsubtitleparser.cpp
     72SOURCES += ../../libs/libmythtv/xine_demux_sputext.cpp
     73SOURCES += ../../libs/libmythtv/teletextreader.cpp
     74SOURCES += ../../libs/libmythtv/vbilut.cpp
    5475
    5576SOURCES += main.cpp commandlineparser.cpp
    5677