Ticket #10793: comm-detector-nextgen.2.patch

File comm-detector-nextgen.2.patch, 168.7 KB (added by Mark Spieth, 7 years ago)

improved comm detector patch #2

  • new file mythtv/programs/mythcommflag/AudioBuffer.cpp

    commit 13739fc1e7674a0be931e1f2aed7d30b7e3dad2c
    Author: Mark Spieth <mspieth@digivation.com.au>
    Date:   Wed Jan 5 10:49:43 2011 +1100
    
        nextgen comm detector with audio and subtitle data fusion
    
    diff --git a/mythtv/programs/mythcommflag/AudioBuffer.cpp b/mythtv/programs/mythcommflag/AudioBuffer.cpp
    new file mode 100644
    index 0000000..10d15cd
    - +  
     1
     2#include "AudioBuffer.h"
     3
     4AudioSample::AudioSample()
     5{
     6    time = 0;
     7    duration = 0;
     8    power = 0;
     9    channels = 0;
     10    changed = false;
     11}
     12
     13QString AudioSample::toString() const
     14{
     15    return QString("%1 %2 %3 %4 %5")
     16        .arg(time)
     17        .arg(duration)
     18        .arg(power)
     19        .arg(channels)
     20        .arg(changed);
     21}
     22
     23
     24AudioBuffer::AudioBuffer(const AudioSettings &settings) :
     25    configured_channels(CHANNELS_MAX),
     26    last_audiotime(0),
     27    output_settings(0),
     28    changed(false),
     29    info_valid(false),
     30    m_lock(),
     31    verboseDebugging(false),
     32    enabled(false)
     33{
     34    if (getenv("DEBUGCOMMFLAG"))
     35        verboseDebugging = true;
     36
     37    Reset();
     38    //AudioSettings orig_settings("", "", AudioFormat::,);
     39    //Reconfigure(audio_bits, audio_channels, 0);
     40    Reconfigure(settings);
     41}
     42
     43AudioBuffer::~AudioBuffer()
     44{
     45    //delete [] audiobuffer;
     46    if (output_settings)
     47    {
     48        delete output_settings;
     49        output_settings = NULL;
     50    }
     51}
     52
     53AudioOutputSettings* AudioBuffer::GetOutputSettings(bool /*digital*/)
     54{
     55    AudioOutputSettings *settings = new AudioOutputSettings();
     56
     57    for (uint channels = CHANNELS_MIN; channels <= CHANNELS_MAX; channels++)
     58    {
     59        settings->AddSupportedChannels(channels);
     60    }
     61
     62    LOG(VB_COMMFLAG, LOG_INFO,
     63        QString("GetOutputSettings"));
     64    return settings;
     65}
     66
     67AudioOutputSettings* AudioBuffer::GetOutputSettingsUsers(bool digital)
     68{
     69    if (!output_settings)
     70        output_settings = GetOutputSettings(digital);
     71    return output_settings;
     72}
     73
     74AudioOutputSettings* AudioBuffer::GetOutputSettingsCleaned(bool digital)
     75{
     76    if (!output_settings)
     77        output_settings = GetOutputSettings(digital);
     78    return output_settings;
     79}
     80
     81void AudioBuffer::Enable()
     82{
     83    enabled = true;
     84}
     85
     86// reconfigure sound out for new params
     87void AudioBuffer::Reconfigure(const AudioSettings &orig_settings)
     88{
     89    ClearError();
     90    changed = (orig_settings.format != settings.format)
     91        | (orig_settings.channels != settings.channels)
     92        | (orig_settings.samplerate != settings.samplerate);
     93    settings = orig_settings;
     94
     95    source_bytes_per_frame = source_channels * AudioOutputSettings::SampleSize(settings.format);
     96    LOG(VB_COMMFLAG, LOG_INFO,
     97        QString("Audio Reconfigure changed %1.").arg(changed));
     98
     99    QMutexLocker locker(&m_lock);
     100    audioSamples.clear();
     101    last_audiotime = 0;
     102}
     103
     104// dsprate is in 100 * samples/second
     105void AudioBuffer::SetEffDsp(int /* dsprate */)
     106{
     107    //eff_audiorate = (dsprate / 100);
     108}
     109
     110void AudioBuffer::SetBlocking(bool block)
     111{
     112    (void)block;
     113}
     114
     115void AudioBuffer::Reset(void)
     116{
     117    if (!enabled)
     118        return;
     119    if (verboseDebugging)
     120    {
     121        LOG(VB_COMMFLAG, LOG_DEBUG,
     122            QString("audio reset"));
     123    }
     124    QMutexLocker locker(&m_lock);
     125    audioSamples.clear();
     126    last_audiotime = 0;
     127}
     128
     129bool AudioBuffer::AddFrames(void *in_buffer, int in_frames,
     130                                        int64_t timecode)
     131{
     132    if (!enabled)
     133    {
     134        last_audiotime = timecode;
     135        return true;
     136    }
     137    return AddData(in_buffer, in_frames * source_bytes_per_frame, timecode,
     138                                   in_frames);
     139}
     140
     141bool AudioBuffer::AddData(void *in_buffer, int in_len,
     142                     int64_t timecode, int in_frames)
     143{
     144    if (!enabled)
     145    {
     146        last_audiotime = timecode;
     147        return true;
     148    }
     149    int i;
     150    int f;
     151    PAudioSample aSample(new AudioSample);
     152    aSample->power = 0;
     153    aSample->time = timecode;    // in ms
     154    aSample->duration = (in_frames * 1000) / settings.samplerate; // in ms
     155    aSample->channels = settings.channels;
     156    if (changed)
     157    {
     158        aSample->changed = changed;
     159        changed = false;
     160    }
     161    if (timecode < last_audiotime)
     162    {
     163        LOG(VB_COMMFLAG, LOG_DEBUG,
     164            QString("audio time reset %1").arg(timecode));
     165        QMutexLocker locker(&m_lock);
     166        audioSamples.clear();
     167        last_audiotime = 0;
     168    }
     169    if (last_audiotime != 0)
     170    {
     171        int64_t nextAudiotime = last_audiotime + aSample->duration;
     172        while (aSample->time < nextAudiotime)
     173        {
     174            PAudioSample nullSample(new AudioSample);
     175            nullSample->power = -1;
     176            nullSample->time = nextAudiotime;
     177            nullSample->duration = aSample->duration;
     178            {
     179                QMutexLocker locker(&m_lock);
     180                audioSamples.push_back(nullSample);
     181            }
     182#ifdef AUDIODEBUGGING
     183            LOG(VB_COMMFLAG, LOG_DEBUG,
     184                QString("Audio AddData invalid time %1").arg(nullSample->time));
     185#endif
     186        }
     187    }
     188    switch (settings.format)
     189    {
     190        case FORMAT_S16:
     191            {
     192                int64_t power = 0;
     193                int16_t *p = (int16_t*)in_buffer;
     194                for(f=in_frames;f>0;--f)
     195                {
     196                    // power as mono
     197                    int64_t sum = 0;
     198                    for(i=settings.channels; i>0; --i)
     199                    {
     200                        sum += *p++;
     201                        p++;
     202                    }
     203                    power += sum * sum;
     204                }
     205                aSample->power += ((double)power)/(INT16_MAX * INT16_MAX)/in_frames;
     206                info_valid = true;
     207                QMutexLocker locker(&m_lock);
     208                audioSamples.push_back(aSample);
     209            } break;
     210        case FORMAT_FLT:
     211            {
     212                double power = 0;
     213                float *p = (float*)in_buffer;
     214                for(f=in_frames;f>0;--f)
     215                {
     216                    // power as mono
     217                    int64_t sum = 0;
     218                    for(i=settings.channels; i>0; --i)
     219                    {
     220                        sum += *p++;
     221                        p++;
     222                    }
     223                    power += sum * sum;
     224                }
     225                aSample->power += power/in_frames;
     226                info_valid = true;
     227                QMutexLocker locker(&m_lock);
     228                audioSamples.push_back(aSample);
     229            } break;
     230        default:
     231            break;
     232    }
     233#ifdef AUDIODEBUGGING
     234    if (info_valid)
     235    {
     236        LOG(VB_COMMFLAG, LOG_DEBUG,
     237            QString("Audio AddData time %1 frames %2 power %3 changed %4").arg(timecode).arg(in_frames).arg(aSample->power).arg(aSample->changed));
     238    }
     239    else
     240    {
     241        LOG(VB_COMMFLAG, LOG_DEBUG,
     242            QString("Audio AddData time %1 frames %2").arg(timecode).arg(in_frames));
     243    }
     244#endif
     245    last_audiotime = timecode;
     246    return true;
     247}
     248
     249const PAudioSample AudioBuffer::GetSample(int64_t time)
     250{
     251    if (!audioSamples.empty())
     252    {
     253        QMutexLocker locker(&m_lock);
     254#ifdef USE_AUDIO_LIST
     255        PAudioSample s;
     256        do
     257        {
     258            if (audioSamples.empty())
     259                break;
     260            s = audioSamples.front();
     261            if (!s)
     262                break;
     263            int64_t dtime = s->time - time;
     264            if (dtime < 0)
     265            {
     266                // time already past
     267                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()));
     268                audioSamples.pop_front();
     269                continue;
     270            }
     271            if (dtime < s->duration)
     272            {
     273                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()));
     274                //audioSamples.pop_front();
     275                return s;
     276            }
     277            // not yet
     278            break;
     279        } while (true);
     280#else
     281        int64_t index = (time - audioSamples[0]->time)/audioSamples[0]->duration;
     282        //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()));
     283        if (index >= 0 && index < audioSamples.size())
     284            return audioSamples[index];
     285#endif
     286    }
     287    return PAudioSample();
     288}
     289
     290void AudioBuffer::SetTimecode(int64_t timecode)
     291{
     292    last_audiotime = timecode;
     293}
     294bool AudioBuffer::GetPause(void)
     295{
     296    return false;
     297}
     298void AudioBuffer::Pause(bool paused)
     299{
     300    (void)paused;
     301}
     302void AudioBuffer::Drain(void)
     303{
     304    // Do nothing
     305    return;
     306}
     307bool AudioBuffer::IsPaused() const { return false; }
     308void AudioBuffer::PauseUntilBuffered() { }
     309bool AudioBuffer::IsUpmixing() { return false; }
     310bool AudioBuffer::ToggleUpmix() { return false; }
     311bool AudioBuffer::CanUpmix() { return false; }
     312
     313
     314int64_t AudioBuffer::GetAudiotime(void)
     315{
     316    return last_audiotime;
     317}
     318
     319int AudioBuffer::GetVolumeChannel(int) const
     320{
     321    // Do nothing
     322    return 100;
     323}
     324void AudioBuffer::SetVolumeChannel(int, int)
     325{
     326    // Do nothing
     327}
     328void AudioBuffer::SetVolumeAll(int)
     329{
     330    // Do nothing
     331}
     332int AudioBuffer::GetSWVolume(void)
     333{
     334    return 100;
     335}
     336void AudioBuffer::SetSWVolume(int, bool) {}
     337
     338
     339int AudioBuffer::GetCurrentVolume(void)
     340{
     341    // Do nothing
     342    return 100;
     343}
     344void AudioBuffer::SetCurrentVolume(int)
     345{
     346    // Do nothing
     347}
     348void AudioBuffer::AdjustCurrentVolume(int)
     349{
     350    // Do nothing
     351}
     352void AudioBuffer::SetMute(bool)
     353{
     354    // Do nothing
     355}
     356void AudioBuffer::ToggleMute(void)
     357{
     358    // Do nothing
     359}
     360MuteState AudioBuffer::GetMute(void)
     361{
     362    // Do nothing
     363    return kMuteOff;
     364}
     365MuteState AudioBuffer::IterateMutedChannels(void)
     366{
     367    // Do nothing
     368    return kMuteOff;
     369}
     370
     371//  These are pure virtual in AudioOutput, but we don't need them here
     372void AudioBuffer::bufferOutputData(bool){ return; }
     373int AudioBuffer::readOutputData(unsigned char*, int ){ return 0; }
     374
     375/* 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..2689d40
    - +  
     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
     21class AudioSample : public QSharedData
     22{
     23public:
     24    int64_t time;
     25    int64_t duration;
     26    double  power;
     27    int     channels;
     28    bool    changed;
     29    AudioSample();
     30    QString toString() const;
     31};
     32
     33typedef QExplicitlySharedDataPointer< AudioSample > PAudioSample;
     34
     35
     36#define CHANNELS_MIN 1
     37#define CHANNELS_MAX 8
     38class AudioBuffer : public AudioOutput
     39{
     40 public:
     41    AudioBuffer(const AudioSettings &settings);
     42   ~AudioBuffer();
     43
     44    AudioOutputSettings* GetOutputSettings(bool /*digital*/);
     45    AudioOutputSettings* GetOutputSettingsUsers(bool digital);
     46    AudioOutputSettings* GetOutputSettingsCleaned(bool digital);
     47
     48    // reconfigure sound out for new params
     49    virtual void Reconfigure(const AudioSettings &orig_settings);
     50
     51    void Enable();
     52
     53    virtual void SetEffDsp(int /* dsprate */);
     54    virtual void SetBlocking(bool block);
     55    virtual void Reset(void);
     56    virtual bool AddFrames(void *in_buffer, int in_frames, int64_t timecode);
     57    virtual bool AddData(void *in_buffer, int in_len,
     58                         int64_t timecode, int in_frames);
     59    const PAudioSample GetSample(int64_t time);
     60    virtual void SetTimecode(int64_t timecode);
     61    virtual bool GetPause(void);
     62    virtual void Pause(bool paused);
     63    virtual void Drain(void);
     64    virtual bool IsPaused() const;
     65    virtual void PauseUntilBuffered();
     66    virtual bool IsUpmixing();
     67    virtual bool ToggleUpmix();
     68    virtual bool CanUpmix();
     69
     70    virtual int64_t GetAudiotime(void);
     71    virtual int GetVolumeChannel(int) const;
     72    virtual void SetVolumeChannel(int, int);
     73    virtual void SetVolumeAll(int);
     74    virtual int GetSWVolume(void);
     75    virtual void SetSWVolume(int, bool);
     76    virtual int GetCurrentVolume(void);
     77    virtual void SetCurrentVolume(int);
     78    virtual void AdjustCurrentVolume(int);
     79    virtual void SetMute(bool);
     80    virtual void ToggleMute(void);
     81    virtual MuteState GetMute(void);
     82    virtual MuteState IterateMutedChannels(void);
     83
     84    //  These are pure virtual in AudioOutput, but we don't need them here
     85    virtual void bufferOutputData(bool);
     86    virtual int readOutputData(unsigned char*, int );
     87
     88    // Basic details about the audio stream
     89    int channels;
     90    int codec;
     91    int bytes_per_frame;
     92    int output_bytes_per_frame;
     93    AudioFormat format;
     94    AudioFormat output_format;
     95    int samplerate;
     96    int source_channels;
     97    int source_samplerate;
     98    int source_bytes_per_frame;
     99
     100    int bufsize;
     101    //unsigned char *audiobuffer;
     102    //int audiobuffer_len;
     103    //int channels, bits, bytes_per_sample, eff_audiorate;
     104    int configured_channels;
     105    AudioSettings settings;
     106    int64_t last_audiotime;
     107
     108    AudioOutputSettings* output_settings;
     109
     110    bool changed;
     111    bool info_valid;
     112
     113    int64_t first_audiotime;
     114
     115#ifdef USE_AUDIO_LIST
     116    deque< PAudioSample > audioSamples;
     117#else
     118    vector< PAudioSample > audioSamples;
     119#endif
     120
     121 private:
     122    mutable QMutex m_lock;
     123    bool verboseDebugging;
     124    bool enabled;
     125};
     126
     127#endif
     128
     129/* 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/ClassicCommDetector.cpp

    diff --git a/mythtv/programs/mythcommflag/ClassicCommDetector.cpp b/mythtv/programs/mythcommflag/ClassicCommDetector.cpp
    index c5c40fc..28fd396 100644
    a b using namespace std; 
    2626enum frameAspects {
    2727    COMM_ASPECT_NORMAL = 0,
    2828    COMM_ASPECT_WIDE
    29 } FrameAspects;
     29};
    3030
    3131enum frameFormats {
    3232    COMM_FORMAT_NORMAL = 0,
    3333    COMM_FORMAT_LETTERBOX,
    3434    COMM_FORMAT_PILLARBOX,
    3535    COMM_FORMAT_MAX
    36 } FrameFormats;
     36};
    3737
    3838static QString toStringFrameMaskValues(int mask, bool verbose)
    3939{
    void ClassicCommDetector::ProcessFrame(VideoFrame *frame, 
    807807
    808808    if (commDetectMethod & COMM_DETECT_SCENE)
    809809    {
    810         sceneChangeDetector->processFrame(framePtr);
     810        sceneChangeDetector->processFrame(framePtr, curFrameNumber);
    811811    }
    812812
    813813    stationLogoPresent = false;
  • mythtv/programs/mythcommflag/ClassicSceneChangeDetector.cpp

    diff --git a/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.cpp b/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.cpp
    index c449353..b3f8c68 100644
    a b ClassicSceneChangeDetector::ClassicSceneChangeDetector(unsigned int width, 
    88        unsigned int height, unsigned int commdetectborder_in,
    99        unsigned int xspacing_in, unsigned int yspacing_in):
    1010    SceneChangeDetectorBase(width,height),
    11     frameNumber(0),
    1211    previousFrameWasSceneChange(false),
    1312    xspacing(xspacing_in),
    1413    yspacing(yspacing_in),
    void ClassicSceneChangeDetector::deleteLater(void) 
    2524    SceneChangeDetectorBase::deleteLater();
    2625}
    2726
    28 void ClassicSceneChangeDetector::processFrame(unsigned char* frame)
     27void ClassicSceneChangeDetector::processFrame(unsigned char* frame, long long frameNumber)
    2928{
    3029    histogram->generateFromImage(frame, width, height, commdetectborder,
    3130                                 width-commdetectborder, commdetectborder,
    void ClassicSceneChangeDetector::processFrame(unsigned char* frame) 
    3837    previousFrameWasSceneChange = isSceneChange;
    3938
    4039    std::swap(histogram,previousHistogram);
    41     frameNumber++;
    4240}
    4341
    4442/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • mythtv/programs/mythcommflag/ClassicSceneChangeDetector.h

    diff --git a/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.h b/mythtv/programs/mythcommflag/ClassicSceneChangeDetector.h
    index f4d2200..45330f7 100644
    a b class ClassicSceneChangeDetector : public SceneChangeDetectorBase 
    1313        unsigned int yspacing);
    1414    virtual void deleteLater(void);
    1515
    16     void processFrame(unsigned char* frame);
     16    void processFrame(unsigned char* frame, long long frameNumber);
    1717
    1818  private:
    1919    ~ClassicSceneChangeDetector() {}
    class ClassicSceneChangeDetector : public SceneChangeDetectorBase 
    2121  private:
    2222    Histogram* histogram;
    2323    Histogram* previousHistogram;
    24     unsigned int frameNumber;
    2524    bool previousFrameWasSceneChange;
    2625    unsigned int xspacing, yspacing;
    2726    unsigned int commdetectborder;
  • mythtv/programs/mythcommflag/CommDetectorBase.h

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

    diff --git a/mythtv/programs/mythcommflag/SceneChangeDetectorBase.h b/mythtv/programs/mythcommflag/SceneChangeDetectorBase.h
    index 67296d5..f901407 100644
    a b class SceneChangeDetectorBase : public QObject 
    1111    SceneChangeDetectorBase(unsigned int w, unsigned int h) :
    1212        width(w), height(h) {}
    1313
    14     virtual void processFrame(unsigned char *frame) = 0;
     14    virtual void processFrame(unsigned char *frame, long long frameNumber) = 0;
    1515
    1616  signals:
    1717    void haveNewInformation(unsigned int framenum, bool scenechange,
  • 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..b3cf10d
    - +  
     1using namespace std;
     2
     3// MythTV headers
     4#include "frame.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((uint)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.exists && win.visible && !win.changed)
     232                continue;
     233            if (!win.exists || !win.visible)
     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.changed = false;
     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..70861f3
    - +  
     1#ifndef _SUBTITLECHANGEDETECTORBASE_H_
     2#define _SUBTITLECHANGEDETECTORBASE_H_
     3
     4#include <QObject>
     5
     6#include "frame.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/main.cpp

    diff --git a/mythtv/programs/mythcommflag/main.cpp b/mythtv/programs/mythcommflag/main.cpp
    index bbba950..66a7f54 100644
    a b using namespace std; 
    4444#include "CommDetectorFactory.h"
    4545#include "SlotRelayer.h"
    4646#include "CustomEventRelayer.h"
     47//#include "AudioBuffer.h"
    4748
    4849#define LOC      QString("MythCommFlag: ")
    4950#define LOC_WARN QString("MythCommFlag, Warning: ")
    static QMap<QString,SkipTypes> *init_skip_types(void) 
    107108    (*tmp)["blankscene"]  = COMM_DETECT_BLANK_SCENE;
    108109    (*tmp)["blank_scene"] = COMM_DETECT_BLANK_SCENE;
    109110    (*tmp)["logo"]        = COMM_DETECT_LOGO;
     111    (*tmp)["audio"]       = (SkipTypes)COMM_DETECT_AUDIO;
     112    (*tmp)["sub"]         = (SkipTypes)COMM_DETECT_SUBTITLES;
    110113    (*tmp)["all"]         = COMM_DETECT_ALL;
    111114    (*tmp)["d2"]          = COMM_DETECT_2;
    112115    (*tmp)["d2_logo"]     = COMM_DETECT_2_LOGO;
    113116    (*tmp)["d2_blank"]    = COMM_DETECT_2_BLANK;
    114117    (*tmp)["d2_scene"]    = COMM_DETECT_2_SCENE;
    115118    (*tmp)["d2_all"]      = COMM_DETECT_2_ALL;
     119    (*tmp)["ng"]          = (SkipTypes)(COMM_DETECT_NG);
     120    (*tmp)["ng_logo"]     = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_LOGO);
     121    (*tmp)["ng_blank"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_BLANK);
     122    (*tmp)["ng_scene"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_SCENE);
     123    (*tmp)["ng_audio"]    = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_AUDIO);
     124    (*tmp)["ng_all"]      = (SkipTypes)(COMM_DETECT_NG | COMM_DETECT_ALL | COMM_DETECT_AUDIO | COMM_DETECT_SUBTITLES);
     125    (*tmp)["ng_old"]      = (SkipTypes)(COMM_DETECT_NG_OLD);
    116126    return tmp;
    117127}
    118128
  • mythtv/programs/mythcommflag/mythcommflag.pro

    diff --git a/mythtv/programs/mythcommflag/mythcommflag.pro b/mythtv/programs/mythcommflag/mythcommflag.pro
    index b7242cf..ec6abfb 100644
    a b HEADERS += LogoDetectorBase.h SceneChangeDetectorBase.h 
    3131HEADERS += SlotRelayer.h CustomEventRelayer.h
    3232HEADERS += commandlineparser.h
    3333
     34HEADERS += NextgenCommDetector.h
     35HEADERS += NextgenLogoDetector.h
     36HEADERS += NextgenSceneChangeDetector.h
     37HEADERS += AudioChangeDetectorBase.h AudioChangeDetector.h
     38HEADERS += AudioBuffer.h
     39HEADERS += SubtitleChangeDetectorBase.h SubtitleChangeDetector.h
     40
    3441SOURCES += CommDetectorFactory.cpp CommDetectorBase.cpp
    3542SOURCES += ClassicLogoDetector.cpp
    3643SOURCES += ClassicSceneChangeDetector.cpp
    SOURCES += HistogramAnalyzer.cpp 
    4754SOURCES += BlankFrameDetector.cpp
    4855SOURCES += SceneChangeDetector.cpp
    4956SOURCES += PrePostRollFlagger.cpp
     57SOURCES += AudioChangeDetectorBase.cpp AudioChangeDetector.cpp
     58SOURCES += AudioBuffer.cpp
     59SOURCES += SubtitleChangeDetectorBase.cpp SubtitleChangeDetector.cpp
     60SOURCES += NextgenLogoDetector.cpp
     61SOURCES += NextgenSceneChangeDetector.cpp
     62SOURCES += NextgenCommDetector.cpp
     63
     64SOURCES += ../../libs/libmythtv/subtitlereader.cpp
     65SOURCES += ../../libs/libmythtv/textsubtitleparser.cpp
     66SOURCES += ../../libs/libmythtv/xine_demux_sputext.cpp
     67SOURCES += ../../libs/libmythtv/teletextreader.cpp
     68SOURCES += ../../libs/libmythtv/vbilut.cpp
    5069
    5170SOURCES += main.cpp commandlineparser.cpp
    5271