[xiph-commits] r16338 - in trunk/ffmpeg2theora: frontend frontend/theoraenc src

j at svn.xiph.org j at svn.xiph.org
Sun Jul 26 07:17:55 PDT 2009


Author: j
Date: 2009-07-26 07:17:54 -0700 (Sun, 26 Jul 2009)
New Revision: 16338

Modified:
   trunk/ffmpeg2theora/frontend/Simple Theora Encoder.py
   trunk/ffmpeg2theora/frontend/theoraenc/addVideoDialog.py
   trunk/ffmpeg2theora/frontend/theoraenc/theoraenc.py
   trunk/ffmpeg2theora/src/theorautils.c
Log:
cleanup frontend a bit, update status less often

Modified: trunk/ffmpeg2theora/frontend/Simple Theora Encoder.py
===================================================================
--- trunk/ffmpeg2theora/frontend/Simple Theora Encoder.py	2009-07-26 10:52:02 UTC (rev 16337)
+++ trunk/ffmpeg2theora/frontend/Simple Theora Encoder.py	2009-07-26 14:17:54 UTC (rev 16338)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
+# vi:si:et:sw=2:sts=2:ts=2
 # -*- coding: utf-8 -*-
-# vi:si:et:sw=2:sts=2:ts=2
 # Written 2007 by j at v2v.cc
 #
 # see LICENSE.txt for license information
@@ -32,31 +32,39 @@
   _qd_key = {}
   encodingQueueInitialized = False
   inputFile = False
+  encoding = False
+  quit = False
 
+
   def initMainInterface(self):
     #TODO: addd menue
     
     self.encodingQueue = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
-    self.encodingQueue.SetPosition(wx.Point(15,50))
-    self.encodingQueue.SetSize(wx.Size(435, 165))
+    self.encodingQueue.SetPosition(wx.Point(10,50))
+    self.encodingQueue.SetSize(wx.Size(440, 165))
     self.encodingQueue.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
     
-    buttonSize = wx.Size(88,-1)
-    self.addItem = wx.Button(self, wx.ID_ANY, "Add...", wx.Point(462, 71), buttonSize)
+    buttonSize = wx.Size(80,-1)
+    self.addItem = wx.Button(self, wx.ID_ANY, "Add...", wx.Point(460, 70), buttonSize)
     self.Bind(wx.EVT_BUTTON, self.OnClickAdd, self.addItem)
     
-    self.removeItem = wx.Button(self, wx.ID_ANY, "Remove", wx.Point(462, 106), buttonSize)
+    self.removeItem = wx.Button(self, wx.ID_ANY, "Remove", wx.Point(460, 100), buttonSize)
     self.Bind(wx.EVT_BUTTON, self.OnClickRemove, self.removeItem)
     self.removeItem.Disable()
     
-    self.buttonQuit = wx.Button(self, wx.ID_ANY, "Quit", wx.Point(462, 187), buttonSize)
-    self.Bind(wx.EVT_BUTTON, self.OnExit, self.buttonQuit)
-    
+    self.buttonEncode = wx.Button(self, wx.ID_ANY, "Encode", wx.Point(460, 190), buttonSize)
+    self.Bind(wx.EVT_BUTTON, self.OnEncode, self.buttonEncode)
+    self.buttonEncode.Disable()
+
+    self.Bind(wx.EVT_CLOSE, self.OnClose)
+
     #Title
-    self.title = wx.StaticText(self, -1, "Simple Theora Encoder", wx.Point(15, 10))
+    titleFont = wx.Font(14, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, u'Sans')
+    self.title = wx.StaticText(self, -1, "Simple Theora Encoder", wx.Point(10, 10))
+    self.title.SetFont(titleFont)
     
   def __init__(self, parent, id, title, inputFile=None):
-    wx.Frame.__init__(self, parent, id, title, size=(559,260))
+    wx.Frame.__init__(self, parent, id, title, size=(550,230))
     self.inputFile = inputFile
     self.initMainInterface()
     self.Show(True)
@@ -69,7 +77,7 @@
       q.ClearAll()
       q.InsertColumn(0, "Name")
       q.InsertColumn(1, "Stats")
-      q.SetColumnWidth(0, 195)
+      q.SetColumnWidth(0, 200)
       q.SetColumnWidth(1, 240)
       
       q.itemDataMap = self.queuedata
@@ -106,8 +114,8 @@
     itemID = item['itemID']
     if item['status'] != status:
       item['status'] = status
+      #self.title.SetLabel(os.path.basename(item['path']) +': '+ status)
       self.encodingQueue.SetStringItem(itemID, 1, status)
-    now = time.mktime(time.localtime())
 
   def getSettings(self, options):
     settings = []
@@ -131,12 +139,36 @@
         settings.append('%s' % s['encoding'])
     return settings
 
-  def addItemThread(self, item):
-    if not item['enc'].encode():
-      print  "encoding failed"
-      return
-    return True
-  
+  def encodeItem(self, item):
+    item['encoding'] = True
+    if self.currentItem == item['itemID']:
+      self.removeItem.SetLabel('Cancel')
+    self.setItemStatus(item['itemID'], 'encoding')
+    result = item['enc'].encode()
+    if not result:
+      self.setItemStatus(item['itemID'], 'encoding failed.')
+    else:
+      self.setItemStatus(item['itemID'], 'encoding done.')
+
+    item['encoded'] = True
+    item['encoding'] = False
+    return result
+
+  def encodeQueue(self, foo):
+    def nextItem():
+      items = self.queuedata.items()
+      for x in range(len(items)):
+        key, item = items[x]
+        if not item['encoded']:
+          return item
+      return None
+
+    next = nextItem()
+    while next and not self.quit:
+      self.encodeItem(next)
+      next = nextItem()
+    self.encoding = False
+
   def addItemToQueue(self, videoFile, options):
     name = os.path.basename(videoFile)
     display_path = videoFile
@@ -146,10 +178,12 @@
       path = videoFile, 
       options = options,
       display_path = display_path, 
-      status = 'encoding...           ',
+      status = 'waiting...           ',
       listID = 0,
       name = name,
     )
+    item['encoding'] = False
+    item['encoded'] = False
     item['enc'] = theoraenc.TheoraEnc(videoFile, None, lambda x: self.updateItemStatus(name, x))
     item['enc'].settings = self.getSettings(options)
 
@@ -170,10 +204,15 @@
       self.queuedata[key] = item
       self.initializeUploadQueue()
     self._qd_key[name] = key
-    thread.start_new_thread(self.addItemThread, (item, ))
 
   def OnItemSelected(self, event):
     self.currentItem = event.m_itemIndex
+    key = self.encodingQueue.GetItemData(self.currentItem)
+    item = self.queuedata[key]
+    if item['encoding']:
+        self.removeItem.SetLabel('Cancel')
+    else:
+        self.removeItem.SetLabel('Remove')
     self.removeItem.Enable()
   
   def OnClickAdd(self, event):
@@ -181,24 +220,42 @@
     time.sleep(0.5)
     if result['ok']:
       self.addItemToQueue(result['videoFile'], result)
+      if not self.encoding:
+        self.buttonEncode.Enable()
   
   def OnClickRemove(self, event):
     key = self.encodingQueue.GetItemData(self.currentItem)
-    print key
     if 'enc' in self.queuedata[key]:
       self.queuedata[key]['enc'].cancel()
     del self.queuedata[key]
     self.initializeUploadQueue(self.currentItem)
 
-  def OnExit(self, event):
-    for key in self.queuedata:
-      if 'enc' in self.queuedata[key]:
-        try:
-          self.queuedata[key]['enc'].cancel()
-        except:
-          pass
-    sys.exit()
+  def OnEncode(self, event):
+    if not self.encoding:
+      self.encoding = True
+      thread.start_new_thread(self.encodeQueue, ("foo", ))
+      self.buttonEncode.Disable()
 
+  def OnClose(self, event):
+    close = True
+    if self.encoding:
+      dlg = wx.MessageDialog(self, 
+          "Videos are still encoded.\nDo you really want to close Simple Theora Encoder?",
+          "Confirm Exit", wx.OK|wx.CANCEL|wx.ICON_QUESTION)
+      result = dlg.ShowModal()
+      dlg.Destroy()
+      if result != wx.ID_OK:
+        close = False
+    if close:
+      self.quit = True
+      for key in self.queuedata:
+        if 'enc' in self.queuedata[key]:
+          try:
+            self.queuedata[key]['enc'].cancel()
+          except:
+            pass
+      self.Destroy()
+
 def gui(inputFile = None):
   app = wx.PySimpleApp()
   frame=SimpleTheoraEncoder(None, wx.ID_ANY, 'Simple Theora Encoder', inputFile = inputFile)

Modified: trunk/ffmpeg2theora/frontend/theoraenc/addVideoDialog.py
===================================================================
--- trunk/ffmpeg2theora/frontend/theoraenc/addVideoDialog.py	2009-07-26 10:52:02 UTC (rev 16337)
+++ trunk/ffmpeg2theora/frontend/theoraenc/addVideoDialog.py	2009-07-26 14:17:54 UTC (rev 16338)
@@ -7,6 +7,8 @@
 from addSubtitlesDialog import addSubtitlesPropertiesDialog, SubtitlesList
 
 import wx
+
+import theoraenc
 
 class AddVideoDialog(wx.Dialog):
   def __init__(
@@ -273,7 +275,7 @@
     self.btnOK = wx.Button(self, wx.ID_OK)
     self.btnOK.SetDefault()
     self.btnOK.Disable()
-    self.btnOK.SetLabel('Encode')
+    self.btnOK.SetLabel('Add to queue')
     hbox.Add(self.btnOK, 0, wx.EXPAND|wx.ALL, padding)
 
     hbox = wx.BoxSizer(wx.HORIZONTAL)
@@ -304,14 +306,25 @@
     return filename
   
   def selectVideoFile(self, videoFile):
-        self.videoFile = videoFile
-        lValue = videoFile
-        lLength = 45
-        if len(lValue) > lLength:
-          lValue = "..." + lValue[-lLength:]
-        self.btnVideoFile.SetLabel(lValue)
-        self.btnOK.Enable()
+        self.info = theoraenc.fileInfo(videoFile)
+        if self.info:
+          #FIXME: enable/disable options based on input
+          """
+          if "video" in self.info: #source has video
+            #enable video options
+          if "audio" in self.info: #source has audio
+            #enable audio options
+          if "audio" in self.info: #source has audio
 
+          """
+          self.videoFile = videoFile
+          lValue = videoFile
+          lLength = 45
+          if len(lValue) > lLength:
+            lValue = "..." + lValue[-lLength:]
+          self.btnVideoFile.SetLabel(lValue)
+          self.btnOK.Enable()
+
   def CheckSubtitlesSelection(self, event):
     idx=self.subtitles.GetFirstSelected()
     if idx<0:
@@ -380,8 +393,22 @@
         encoding = dlg.subtitles.GetItem(idx, 2).GetText()
         file = dlg.subtitles.GetItem(idx, 3).GetText()
         result['subtitles'].append({'encoding':encoding, 'language':language, 'category':category, 'file':file})
-    print result
   else:
     result['ok'] = False
   dlg.Destroy()
   return result
+if __name__ == "__main__":
+  import sys
+  class Frame(wx.Frame):
+    inputFile = None
+    def __init__(self):
+      wx.Frame.__init__(self, None, wx.ID_ANY, "add video test", size=(559,260))
+      self.Show(True)
+
+  app = wx.PySimpleApp()
+  frame=Frame()
+  if len(sys.argv) > 1:
+    frame.inputFile = sys.argv[1]
+  result = addVideoDialog(frame, True)
+  print result
+

Modified: trunk/ffmpeg2theora/frontend/theoraenc/theoraenc.py
===================================================================
--- trunk/ffmpeg2theora/frontend/theoraenc/theoraenc.py	2009-07-26 10:52:02 UTC (rev 16337)
+++ trunk/ffmpeg2theora/frontend/theoraenc/theoraenc.py	2009-07-26 14:17:54 UTC (rev 16338)
@@ -7,8 +7,10 @@
 import sys
 import signal
 import subprocess
+import threading
 
 import simplejson
+import wx
 
 
 resourcePath = abspath(dirname(__file__))
@@ -45,6 +47,22 @@
   seconds = (seconds-((hours*3600)+(minutes*60)))
   return '%02d:%02d:%02d' % (hours, minutes, seconds)
 
+class ThreadWorker(threading.Thread):
+    def __init__(self, callable, *args, **kwargs):
+        super(ThreadWorker, self).__init__()
+        self.callable = callable
+        self.args = args
+        self.kwargs = kwargs
+        self.setDaemon(True)
+
+    def run(self):
+        try:
+            self.callable(*self.args, **self.kwargs)
+        except wx.PyDeadObjectError:
+            pass
+        except Exception, e:
+            print e
+
 class TheoraEnc:
   settings = []
   p = None
@@ -88,44 +106,52 @@
  
   def encode(self):
     cmd = self.commandline()
-    print cmd
     p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, close_fds=True)
     self.p = p
-    f = p.stdout
-    line = f.readline()
     info = dict()
     status = ''
     self.warning_timeout = 0
-    while line:
-      now = time.time()
-      try:
-        data = simplejson.loads(line)
-        for key in data:
-          info[key] = data[key]
-        if 'WARNING' in info:
-          status = info['WARNING']
-          self.warning_timeout = now + 3
-          del info['WARNING']
+
+    def worker(pipe):
+      while True:
+        line = pipe.readline()
+        if line == '':
+          break
         else:
-          status=None
-          if now >= self.warning_timeout:
-            if 'position' in info:
-              if 'duration' in info and float(info['duration']):
-                encoded =  "encoding % 3d %% done " % ((float(info['position']) / float(info['duration'])) * 100)
-              else:
-                encoded = "encoded %s/" % timestr(float(info['position']))
-              if float(info['remaining'])>0:
-                status = encoded + '/ '+ timestr(float(info['remaining']))
-              else:
-                status = encoded
+          now = time.time()
+          try:
+            data = simplejson.loads(line)
+            for key in data:
+              info[key] = data[key]
+            if 'WARNING' in info:
+              status = info['WARNING']
+              self.warning_timeout = now + 3
+              del info['WARNING']
             else:
-              status = "encoding.."
-        if status != None:
-          self.updateGUI(status)
-      except:
-        pass
-      line = f.readline()
-    f.close()
+              status=None
+              if now >= self.warning_timeout:
+                if 'position' in info:
+                  if 'duration' in info and float(info['duration']):
+                    encoded =  "encoding % 3d %% done " % ((float(info['position']) / float(info['duration'])) * 100)
+                  else:
+                    encoded = "encoded %s/" % timestr(float(info['position']))
+                  if float(info['remaining'])>0:
+                    status = encoded + '/ '+ timestr(float(info['remaining']))
+                  else:
+                    status = encoded
+                  status =  "encoding % 3d %% done " % ((float(info['position']) / float(info['duration'])) * 100)
+                else:
+                  status = "encoding.."
+            if status != None:
+              self.updateGUI(status)
+          except:
+            pass
+
+    stdout_worker = ThreadWorker(worker, p.stdout)
+    stdout_worker.start()
+
+    p.wait()
+
     if info.get('result', 'no') == 'ok':
       self.updateGUI('Encoding done.')
       return True
@@ -133,6 +159,20 @@
       self.updateGUI(info.get('result', 'Encoding failed.'))
       return False
 
+
+def fileInfo(filename):
+  cmd = []
+  cmd.append(ffmpeg2theora)
+  cmd.append('--info')
+  cmd.append(filename)
+  p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, close_fds=True)
+  data, err = p.communicate()
+  try:
+    info = simplejson.loads(data)
+  except:
+    info = None
+  return info
+
 ffmpeg2theora = probe_ffmpeg2theora()
 hasKate = probe_kate(ffmpeg2theora)
 

Modified: trunk/ffmpeg2theora/src/theorautils.c
===================================================================
--- trunk/ffmpeg2theora/src/theorautils.c	2009-07-26 10:52:02 UTC (rev 16337)
+++ trunk/ffmpeg2theora/src/theorautils.c	2009-07-26 14:17:54 UTC (rev 16338)
@@ -631,6 +631,7 @@
 }
 
 static void print_stats(oggmux_info *info, double timebase) {
+    static double last = -2;
     int hundredths = timebase * 100 - (long) timebase * 100;
     int seconds = (long) timebase % 60;
     int minutes = ((long) timebase / 60) % 60;
@@ -639,34 +640,37 @@
     int remaining_seconds = (long) remaining % 60;
     int remaining_minutes = ((long) remaining / 60) % 60;
     int remaining_hours = (long) remaining / 3600;
-    if (info->frontend) {
-        fprintf(info->frontend, "{\"duration\": %lf, \"position\": %.02lf, \"audio_kbps\":  %d, \"video_kbps\": %d, \"remaining\": %.02lf}\n",
-            (double)info->duration,
-            timebase,
-            info->akbps, info->vkbps,
-            remaining
-        );
-        fflush (info->frontend);
-    }
-    else if (timebase > 0 ) {
-        if (!remaining) {
-            remaining = time(NULL) - info->start_time;
-            remaining_seconds = (long) remaining % 60;
-            remaining_minutes = ((long) remaining / 60) % 60;
-            remaining_hours = (long) remaining / 3600;
-            fprintf (stderr,"\r      %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, time elapsed: %02d:%02d:%02d ",
-                hours, minutes, seconds, hundredths,
+    if (timebase - last > 0.5) {
+        last = timebase;
+        if (info->frontend) {
+            fprintf(info->frontend, "{\"duration\": %lf, \"position\": %.02lf, \"audio_kbps\":  %d, \"video_kbps\": %d, \"remaining\": %.02lf}\n",
+                (double)info->duration,
+                timebase,
                 info->akbps, info->vkbps,
-                remaining_hours, remaining_minutes, remaining_seconds
+                remaining
             );
+            fflush (info->frontend);
         }
-        else {
-            fprintf (stderr,"\r  %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, ET: %02d:%02d:%02d, est. size: %.01lf MB ",
-                hours, minutes, seconds, hundredths,
-                info->akbps, info->vkbps,
-                remaining_hours, remaining_minutes, remaining_seconds,
-                estimated_size(info, timebase)
-            );
+        else if (timebase > 0 ) {
+            if (!remaining) {
+                remaining = time(NULL) - info->start_time;
+                remaining_seconds = (long) remaining % 60;
+                remaining_minutes = ((long) remaining / 60) % 60;
+                remaining_hours = (long) remaining / 3600;
+                fprintf (stderr,"\r      %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, time elapsed: %02d:%02d:%02d ",
+                    hours, minutes, seconds, hundredths,
+                    info->akbps, info->vkbps,
+                    remaining_hours, remaining_minutes, remaining_seconds
+                );
+            }
+            else {
+                fprintf (stderr,"\r  %d:%02d:%02d.%02d audio: %dkbps video: %dkbps, ET: %02d:%02d:%02d, est. size: %.01lf MB ",
+                    hours, minutes, seconds, hundredths,
+                    info->akbps, info->vkbps,
+                    remaining_hours, remaining_minutes, remaining_seconds,
+                    estimated_size(info, timebase)
+                );
+            }
         }
     }
 }



More information about the commits mailing list