00001
00002 #include <cstdlib>
00003 #include <cmath>
00004
00005
00006 #include "mythcorecontext.h"
00007 #include "mythplayer.h"
00008 #include "mythlogging.h"
00009
00010
00011 #include "CommDetector2.h"
00012 #include "FrameAnalyzer.h"
00013 #include "quickselect.h"
00014 #include "HistogramAnalyzer.h"
00015 #include "SceneChangeDetector.h"
00016
00017 using namespace commDetector2;
00018 using namespace frameAnalyzer;
00019
00020 namespace {
00021
00022 int
00023 scenechange_data_sort_desc_frequency(const void *aa, const void *bb)
00024 {
00025
00026 const struct SceneChangeDetector::scenechange_data *sc1 =
00027 (const struct SceneChangeDetector::scenechange_data*)aa;
00028 const struct SceneChangeDetector::scenechange_data *sc2 =
00029 (const struct SceneChangeDetector::scenechange_data*)bb;
00030 int freqdiff = sc2->frequency - sc1->frequency;
00031 return freqdiff ? freqdiff : sc1->color - sc2->color;
00032 }
00033
00034 void
00035 scenechange_data_init(SceneChangeDetector::SceneChangeData *scdata,
00036 const HistogramAnalyzer::Histogram *hh)
00037 {
00038 unsigned int ncolors = sizeof(*hh)/sizeof((*hh)[0]);
00039
00040 for (unsigned int ii = 0; ii < ncolors; ii++)
00041 {
00042 (*scdata)[ii].color = ii;
00043 (*scdata)[ii].frequency = (*hh)[ii];
00044 }
00045 qsort(*scdata, sizeof(*scdata)/sizeof((*scdata)[0]), sizeof((*scdata)[0]),
00046 scenechange_data_sort_desc_frequency);
00047 }
00048
00049 unsigned short
00050 scenechange_data_diff(const SceneChangeDetector::SceneChangeData *sc1,
00051 const SceneChangeDetector::SceneChangeData *sc2)
00052 {
00053
00054
00055
00056
00057 unsigned short diff = 0;
00058 for (unsigned int ii = 0; ii < sizeof(*sc1)/sizeof((*sc1)[0]); ii++)
00059 diff += abs((*sc1)[ii].frequency - (*sc2)[ii].frequency) +
00060 abs((*sc1)[ii].color - (*sc2)[ii].color);
00061 return diff;
00062 }
00063
00064 bool
00065 writeData(QString filename, const unsigned short *scdiff, long long nframes)
00066 {
00067 FILE *fp;
00068 long long frameno;
00069
00070 QByteArray fname = filename.toLocal8Bit();
00071 if (!(fp = fopen(fname.constData(), "w")))
00072 return false;
00073 for (frameno = 0; frameno < nframes; frameno++)
00074 (void)fprintf(fp, "%5u\n", scdiff[frameno]);
00075 if (fclose(fp))
00076 LOG(VB_COMMFLAG, LOG_ERR, QString("Error closing %1: %2")
00077 .arg(filename).arg(strerror(errno)));
00078 return true;
00079 }
00080
00081 void
00082 computeChangeMap(FrameAnalyzer::FrameMap *changeMap, long long nframes,
00083 const unsigned short *scdiff, unsigned short mindiff)
00084 {
00085
00086
00087
00088 long long frameno;
00089
00090 changeMap->clear();
00091 for (frameno = 0; frameno < nframes; frameno++)
00092 {
00093 if (scdiff[frameno] > mindiff)
00094 changeMap->insert(frameno, 0);
00095 }
00096 }
00097
00098 };
00099
00100 SceneChangeDetector::SceneChangeDetector(HistogramAnalyzer *ha,
00101 QString debugdir)
00102 : FrameAnalyzer()
00103 , histogramAnalyzer(ha)
00104 , fps(0.0f)
00105 , scdata(NULL)
00106 , scdiff(NULL)
00107 , debugLevel(0)
00108 , debugdata(debugdir + "/SceneChangeDetector.txt")
00109 , debug_scenechange(false)
00110 , scenechange_done(false)
00111 {
00112 LOG(VB_COMMFLAG, LOG_INFO, "SceneChangeDetector");
00113
00114
00115
00116
00117
00118
00119 debugLevel = gCoreContext->GetNumSetting("SceneChangeDetectorDebugLevel", 0);
00120
00121 if (debugLevel >= 1)
00122 {
00123 createDebugDirectory(debugdir,
00124 QString("SceneChangeDetector debugLevel %1").arg(debugLevel));
00125 debug_scenechange = true;
00126 }
00127 }
00128
00129 void SceneChangeDetector::deleteLater(void)
00130 {
00131 if (scdata)
00132 delete []scdata;
00133 if (scdiff)
00134 delete []scdiff;
00135 }
00136
00137 enum FrameAnalyzer::analyzeFrameResult
00138 SceneChangeDetector::MythPlayerInited(MythPlayer *player,
00139 long long nframes)
00140 {
00141 FrameAnalyzer::analyzeFrameResult ares =
00142 histogramAnalyzer->MythPlayerInited(player, nframes);
00143
00144 fps = player->GetFrameRate();
00145
00146 scdata = new SceneChangeData[nframes];
00147 memset(scdata, 0, nframes * sizeof(*scdata));
00148
00149 scdiff = new unsigned short[nframes];
00150 memset(scdiff, 0, nframes * sizeof(*scdiff));
00151
00152 QSize video_disp_dim = player->GetVideoSize();
00153
00154 LOG(VB_COMMFLAG, LOG_INFO,
00155 QString("SceneChangeDetector::MythPlayerInited %1x%2")
00156 .arg(video_disp_dim.width())
00157 .arg(video_disp_dim.height()));
00158
00159 return ares;
00160 }
00161
00162 enum FrameAnalyzer::analyzeFrameResult
00163 SceneChangeDetector::analyzeFrame(const VideoFrame *frame, long long frameno,
00164 long long *pNextFrame)
00165 {
00166 *pNextFrame = NEXTFRAME;
00167
00168 if (histogramAnalyzer->analyzeFrame(frame, frameno) ==
00169 FrameAnalyzer::ANALYZE_OK)
00170 return ANALYZE_OK;
00171
00172 LOG(VB_COMMFLAG, LOG_ERR,
00173 QString("SceneChangeDetector::analyzeFrame error at frame %1")
00174 .arg(frameno));
00175 return ANALYZE_ERROR;
00176 }
00177
00178 int
00179 SceneChangeDetector::finished(long long nframes, bool final)
00180 {
00181 if (histogramAnalyzer->finished(nframes, final))
00182 return -1;
00183
00184 LOG(VB_COMMFLAG, LOG_INFO, QString("SceneChangeDetector::finished(%1)")
00185 .arg(nframes));
00186
00187 const HistogramAnalyzer::Histogram *histogram =
00188 histogramAnalyzer->getHistograms();
00189 for (unsigned int frameno = 0; frameno < nframes; frameno++)
00190 (void)scenechange_data_init(&scdata[frameno], &histogram[frameno]);
00191 scdiff[0] = 0;
00192 for (unsigned int frameno = 1; frameno < nframes; frameno++)
00193 scdiff[frameno] = scenechange_data_diff(&scdata[frameno - 1],
00194 &scdata[frameno]);
00195
00196 if (!scenechange_done && debug_scenechange)
00197 {
00198 if (final && writeData(debugdata, scdiff, nframes))
00199 {
00200 LOG(VB_COMMFLAG, LOG_INFO,
00201 QString("SceneChangeDetector::finished wrote %1")
00202 .arg(debugdata));
00203 scenechange_done = true;
00204 }
00205 }
00206
00207
00208 unsigned short *scdiffsort = new unsigned short[nframes];
00209 memcpy(scdiffsort, scdiff, nframes * sizeof(*scdiff));
00210 unsigned short mindiff = quick_select_ushort(scdiffsort, nframes,
00211 (int)(0.979472 * nframes));
00212 LOG(VB_COMMFLAG, LOG_INFO,
00213 QString("SceneChangeDetector::finished applying threshold value %1")
00214 .arg(mindiff));
00215 computeChangeMap(&changeMap, nframes, scdiff, mindiff);
00216 delete []scdiffsort;
00217 if (debugLevel >= 2)
00218 frameAnalyzerReportMapms(&changeMap, fps, "SC frame");
00219
00220 return 0;
00221 }
00222
00223 int
00224 SceneChangeDetector::reportTime(void) const
00225 {
00226 return histogramAnalyzer->reportTime();
00227 }
00228
00229