I thought maybe I should post my final conclusions for people who may
run into this issue in the future.  Commercial detection as
implemented by Freevo is unreliable.  Period.  It searches for 'black
frames' it assumes are the beginning and end of commercials.  It
cannot account for the fact that there are black frames in scene
transitions, as well as in the gaps between some commercials.

There are other methods for detecting commercials which Freevo does
not support that must also be employed.

On Tue, Mar 17, 2009 at 9:59 AM, Bill Bierman <b...@thebiermans.org> wrote:
> I'm using tvtime, though I don't remember why =)
>
> I've inserted a few debug statements in the commdetectcore.py code
> which parses the mencoder output.  Hopefully this will shed some light
> on the situation.
>
> Incidentally, the mencoder vf_blackframes output is hideous.  It's
> neither human-friendly nor tokenizer friendly.
>
> On Tue, Mar 17, 2009 at 6:44 AM, Evan Hisey <ehi...@gmail.com> wrote:
>> On Tue, Mar 17, 2009 at 2:39 AM, Bill Bierman <b...@thebiermans.org> wrote:
>>> I'm actually using tvtime, but I have manually ran mplayer using the
>>> EDL files generated by Freevo, and it still doesn't work.
>>>
>>> I assume the problem is with the mencoder execution, or possibly the
>>> parsing of its output, as the EDL file generated does not even seem
>>> sane.
>>>
>>> "3544 2603 0" is the single line in the example I gave in my original
>>> post.  Multiple attempts at recording hour-long programs resulted in
>>> similar outputs.. a single line with the first parameter being greater
>>> than the second.  The EDL format as I understand it is a start time
>>> followed by a stop time, and is therefore semantically invalid for the
>>> second parameter to be less than the first.
>>>
>>> As for whether the culprit is the mencoder parameters/execution or the
>>> parsing of its output, I haven't got a clue at this point.  I'm still
>>> trying to figure out what the mencoder output means exactly, and
>>> trying to understand how commdetectcore.py handles it.
>>>
>> This I can help you with alittle. The process works like this. The
>> recordeserver uses mencoder to record the whole show first, so
>> mencoder output is not actually seen by the commdetect server. The
>> commdect server then runs mplayer over the whole the whole thing
>> looking for 98% black frames and assumes the first one is the start of
>> the commercial and the second one is the end of one. I would agree it
>> looks like you are getting strange index numbers. Are you using tvtime
>> to record the shows or mencoder? I have not had this particular
>> problem yet with commdetect
>>
>> Evan
>>
>> ------------------------------------------------------------------------------
>> Apps built with the Adobe(R) Flex(R) framework and Flex Builder(TM) are
>> powering Web 2.0 with engaging, cross-platform capabilities. Quickly and
>> easily build your RIAs with Flex Builder, the Eclipse(TM)based development
>> software that enables intelligent coding and step-through debugging.
>> Download the free 60 day trial. http://p.sf.net/sfu/www-adobe-com
>> _______________________________________________
>> Freevo-users mailing list
>> Freevo-users@lists.sourceforge.net
>> https://lists.sourceforge.net/lists/listinfo/freevo-users
>>
>
--- commdetectcore.py.ORIG	2008-05-31 06:14:43.000000000 -1000
+++ commdetectcore.py	2009-03-17 15:54:11.000000000 -1000
@@ -57,14 +57,12 @@
         nosound='-nosound'
         videoFilter='-vf blackframe'
         output='-o /dev/null'
-        grep='| grep vf_blackframe'
         outfile='> /tmp/blackframes.txt'
         string=" "+videoCodec+\
                " "+nosound+\
                " "+videoFilter+\
                " "+self.source+\
                " "+output+\
-               " "+grep+\
                " "+outfile
         self.cls = [string]
 
@@ -85,76 +83,74 @@
         endSkipTime=0.0
         action=0.0
 
-    def grabBlackFrames(self):
+    def genEdl(self):
         #Grab all possible blackframes
         fileHandle = open('/tmp/blackframes.txt','r')
+
+        frames = 0
+        maxFrames = 1000
+        FrameQueue = []
+        BlackFrames = []
+
         for line in fileHandle.readlines():
             splitln = line.split("\r")
-            line=splitln[-2]+splitln[-1]
-            #output with blackframe
-            newBlackFrame = self.blackframe()
-            splitframe = line.split("vf_blackframe:")
-            matchSeconds = re.compile('\d+\.\d+s')
-            match = matchSeconds.search(splitframe[0])
-            if match:
-                seconds=re.sub('s','',match.group())
-                newBlackFrame.seconds=float(seconds)
-            matchFrame = re.compile('\d+f')
-            match = matchFrame.search(splitframe[0])
-            if match:
-                frame=re.sub('f','',match.group())
-                newBlackFrame.frame=float(frame)
-            matchTime = re.compile('\d+min')
-            match = matchTime.search(splitframe[0])
-            if match:
-                time=re.sub('min','',match.group())
-                newBlackFrame.time=int(time)
-            self.blackframes.append(newBlackFrame)
+
+            for ln in splitln:
+                if ln[0:4] == "Pos:":
+                    frameSeconds = float(ln[4:10])
+                    frameNumber = int(ln[11:18])
+                    FrameQueue.append([frameNumber, frameSeconds])
+                elif ln[0:14] == "vf_blackframe:":
+                    frameNumber = int(ln[14:20])
+
+                    for i in reversed(FrameQueue):
+                        if i[0] != frameNumber:
+                            continue
+                        else:
+                            frameSeconds = i[1]
+                            break
+
+                    BlackFrames.append([frameNumber, frameSeconds])
+
         fileHandle.close()
 
-    def findCommercials(self):
-        startFrame=None
-        startFrameTime=0
-        endFrame=None
-        for bframe in self.blackframes:
-            if (startFrameTime==0):
-                #Commerical break
-                startFrame=bframe
-                startFrameTime=bframe.time
-            else:
-                if ((bframe.time==startFrameTime)or(bframe.time==(startFrameTime-1))):
-                    #Same commercial break
-                    startFrameTime=bframe.time
-                    endFrame=bframe
-                else:
-                    #New commercial break
-                    if not endFrame:
-                        #Sometimes a blackframe is thrown in the beginning
-                        endFrame=startFrame
-                    if ((endFrame.seconds-startFrame.seconds)>0):
-                        newEdl = self.edl()
-                        newEdl.startSkipTime=startFrame.seconds
-                        newEdl.endSkipTime=endFrame.seconds
-                        self.edlList.append(newEdl)
-                    startFrame=None
-                    startFrameTime=0
-                    endFrame=None
-        if ((len(self.edlList)==0)and(startFrame and endFrame)):
-            #Only one commercial
-            newEdl = self.edl()
-            newEdl.startSkipTime=startFrame.seconds
-            newEdl.endSkipTime=endFrame.seconds
-            self.edlList.append(newEdl)
-
-    def writeEdl(self):
-        if (len(self.edlList)>0):
-            outputFile=self.source.split(".")
-            output=outputFile[0]+".edl"
-            fileHandle = open(output,'w')
-            for skipSegment in self.edlList:
-                fileHandle.write("%d %d %d\n" % (skipSegment.startSkipTime, \
-                                                skipSegment.endSkipTime, \
-                                                skipSegment.action))
+        fadeStartNum = -1
+        fadeStartTime = 0
+        lastFrameNum = 0
+        lastFrameTime = 0
+        startBlock = 1
+        startBlockTime = -1
+
+        if BlackFrames.length():
+            outputFile = self.source.split(".")
+            output = outputFile[0] + ".edl"
+            fileHandle = open(output, 'w')
+
+            for i in BlackFrames:
+                if fadeStartNum == -1:
+                    fadeStartNum = i[0]
+                    fadeStartTime = i[1]
+                    lastFrameNum = i[0]
+                    lastFrameTime = i[1]
+                    continue
+
+                if i[0] - lastFrameNum > 5:
+                    if startBlock:
+                        startBlock = 0
+                        startBlockTime = fadeStartTime
+                    else:
+                        fileHandle.write("%.1f %.1f 0\n" % (startBlockTime, lastFrameTime))
+                        startBlock = 1
+
+                    fadeStartNum = i[0]
+                    fadeStartTime = i[1]
+                    lastFrameNum = i[0]
+                    lastFrameTime = i[1]
+                    continue
+
+                lastFrameNum = i[0]
+                lastFrameTime = i[1]
+
             fileHandle.close()
 
 class CommandThread(threading.Thread):
@@ -192,12 +188,8 @@
         except:
             pass
         self.kill_pipe()
-        _debug_("Grabbing Blackframes from file")
-        self.parent.grabBlackFrames()
-        _debug_("Finding Commercials")
-        self.parent.findCommercials()
-        _debug_("Writing edl file")
-        self.parent.writeEdl()
+        _debug_("Parsing mencoder output and generating edl file")
+        self.parent.genEdl()
         self.parent.busy = 2
         self.finalfunc()
         sys.exit(2)
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------
# Interface to CommDetectServer
# -----------------------------------------------------------------------
#
# Author: Justin Wetherell
# some parts taken or inspired by Freevo's encodingserver
#
# -----------------------------------------------------------------------
# Copyright (C) 2004 den_RDC (RVDM)
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation
#
# -----------------------------------------------------------------------

"""
Interface to Commercial Detection Server
"""

#Import statements
import threading
from time import sleep
import sys, os, re, popen2 #, ConfigParser, copy
import config
import kaa.metadata as mmpython
from copy import copy
from string import split, join


class CommDetectJob:
    """Class for creation & configuration of CommDetectJobs. This generates the mencoder commands"""

    def __init__(self, source, idnr):
        """Initialize class instance"""
        _debug_('commdetectcore.CommDetectJob.__init__(%s)' % (source))
        self.source = source
        self.name = source
        self.idnr = idnr
        self.pid = 0

        self.busy = 0
        """0==NEW, 1==BUSY 2==DONE"""

        self.blackframes=[]
        self.edlList=[]
        videoCodec='-ovc lavc'
        nosound='-nosound'
        videoFilter='-vf blackframe'
        output='-o /dev/null'
        outfile='> /tmp/blackframes.txt'
        string=" "+videoCodec+\
               " "+nosound+\
               " "+videoFilter+\
               " "+self.source+\
               " "+output+\
               " "+outfile
        self.cls = [string]

    def _run(self, program, arguments, source, flushbuffer, finalfunc):
        """Runs a program; supply program name (string) and arguments (list)"""
        command = program
        command += arguments
        self.thread = CommandThread(self, command, flushbuffer, finalfunc)
        self.thread.start()

    class blackframe:
        frame=0.0
        seconds=0.0
        time=0.0

    class edl:
        startSkipTime=0.0
        endSkipTime=0.0
        action=0.0

    def genEdl(self):
        #Grab all possible blackframes
        fileHandle = open('/tmp/blackframes.txt','r')

        frames = 0
        maxFrames = 1000
        FrameQueue = []
        BlackFrames = []

        for line in fileHandle.readlines():
            splitln = line.split("\r")

            for ln in splitln:
                if ln[0:4] == "Pos:":
                    frameSeconds = float(ln[4:10])
                    frameNumber = int(ln[11:18])
                    FrameQueue.append([frameNumber, frameSeconds])
                elif ln[0:14] == "vf_blackframe:":
                    frameNumber = int(ln[14:20])

                    for i in reversed(FrameQueue):
                        if i[0] != frameNumber:
                            continue
                        else:
                            frameSeconds = i[1]
                            break

                    BlackFrames.append([frameNumber, frameSeconds])

        fileHandle.close()

        fadeStartNum = -1
        fadeStartTime = 0
        lastFrameNum = 0
        lastFrameTime = 0
        startBlock = 1
        startBlockTime = -1

        if BlackFrames.length():
            outputFile = self.source.split(".")
            output = outputFile[0] + ".edl"
            fileHandle = open(output, 'w')

            for i in BlackFrames:
                if fadeStartNum == -1:
                    fadeStartNum = i[0]
                    fadeStartTime = i[1]
                    lastFrameNum = i[0]
                    lastFrameTime = i[1]
                    continue

                if i[0] - lastFrameNum > 5:
                    if startBlock:
                        startBlock = 0
                        startBlockTime = fadeStartTime
                    else:
                        fileHandle.write("%.1f %.1f 0\n" % (startBlockTime, lastFrameTime))
                        startBlock = 1

                    fadeStartNum = i[0]
                    fadeStartTime = i[1]
                    lastFrameNum = i[0]
                    lastFrameTime = i[1]
                    continue

                lastFrameNum = i[0]
                lastFrameTime = i[1]

            fileHandle.close()

class CommandThread(threading.Thread):
    """Handle threading of external commands"""
    def __init__(self, parent, command, flushbuffer, finalfunc):
        threading.Thread.__init__(self)
        self.parent = parent
        self.command = command
        self.flushbuffer = 0
        self.finalfunc = finalfunc
        _debug_('command=\"%s\"' % command)

    def run(self):
        self.pipe = popen2.Popen4(self.command)
        pid = self.pipe.pid
        self.parent.pid = copy(pid)
        totallines = []
        _debug_("Mencoder running at PID %s" % self.pipe.pid)
        self.parent.busy = 1
        while 1:
            if self.flushbuffer:
                line = self.pipe.fromchild.read(1000)
            else:
                line = self.pipe.fromchild.readline()
            _debug_(line)
            if not line:
                break
            else:
                #don't save up the output if flushbuffer is enabled
                if self.flushbuffer != 1:
                    totallines.append(line)
        sleep(0.5)
        try:
            os.waitpid(pid, os.WNOHANG)
        except:
            pass
        self.kill_pipe()
        _debug_("Parsing mencoder output and generating edl file")
        self.parent.genEdl()
        self.parent.busy = 2
        self.finalfunc()
        sys.exit(2)

    def kill_pipe(self):
        """Kills current process (pipe)"""
        try:
            os.kill(self.pipe.pid, 9)
            os.waitpid(self.pipe.pid, os.WNOHANG)
        except:
            pass

class CommDetectQueue:
    """Class for generating an commdetect queue"""
    def __init__(self):
        #we keep a list and a dict because a dict doesn't store an order
        self.qlist = []
        self.qdict = {}
        self.running = False

    def addCommDetectJob(self, encjob):
        """Adds an commdetectjob to the queue"""
        self.qlist += [encjob]
        self.qdict[encjob.idnr] = encjob

    def startQueue(self):
        """Start the queue"""
        if not self.running:
            self.running = True
            _debug_("queue started", DINFO)
            self._runQueue()

    def listJobs(self):
        """Returns a list of queue'ed jobs"""
        if self.qdict == {}:
            return []
        else:
            jlist = []
            for idnr, job in self.qdict.items():
                jlist += [ (idnr, job.name) ]
            return jlist

    def _runQueue(self, line="", data=""):
        """Executes the jobs in the queue, and gets called after every mencoder run is completed"""
        if self.qlist == []:
            #empty queue, do nothing
            self.running = False
            if hasattr(self,"currentjob"):
                del self.currentjob
            _debug_("queue empty, stopping processing...", DINFO)
            return
        _debug_("runQueue callback data : %s" % line)

        #get the first queued object
        self.currentjob = self.qlist[0]
        _debug_("PID %s" % self.currentjob.pid)

        if self.currentjob.busy == 0:
            #NEW
            _debug_("Running Mencoder, to write the blackframes to a file")
            self.currentjob.busy = True
            self.currentjob._run(config.CONF.mencoder, \
                                self.currentjob.cls[0], \
                                self.currentjob.source, \
                                0, \
                                self._runQueue)
            _debug_("Started job %s, PID %s" % (self.currentjob.idnr, self.currentjob.pid))

        if self.currentjob.busy == 2:
            #DONE
            del self.qlist[0]
            del self.qdict[self.currentjob.idnr]
            self.currentjob.busy = 0
            self.running = False
            self._runQueue()
------------------------------------------------------------------------------
This SF.net email is sponsored by:
High Quality Requirements in a Collaborative Environment.
Download a free trial of Rational Requirements Composer Now!
http://p.sf.net/sfu/www-ibm-com
_______________________________________________
Freevo-users mailing list
Freevo-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/freevo-users

Reply via email to