1 #include <QtCore/QCoreApplication>
2 #include <QtCore/QFile>
3 #include <QtCore/QMap>
4 #include <QtCore/QMetaEnum>
5 #include <QtCore/QProcess>
6 #include <QtCore/QTextStream>
7 
8 #ifdef QT_SCRIPTTOOLS
9 #include <QtGui/QAction>
10 #include <QtGui/QMainWindow>
11 #include <QtScriptTools/QScriptEngineDebugger>
12 #endif
13 
14 #include "MyQScriptEngine.h"
15 #include "QtScriptWriter.h"
16 
17 #include "AudioEncoder.h"
18 #include "AudioOutput.h"
19 #include "CheckBoxControl.h"
20 #include "ComboBoxControl.h"
21 #include "ComboBoxItem.h"
22 #include "Dialog.h"
23 #include "Directory.h"
24 #include "DoubleSpinBoxControl.h"
25 #include "Editor.h"
26 #include "File.h"
27 #include "FileInformation.h"
28 #include "FrameProperties.h"
29 #include "LineEditControl.h"
30 #include "Muxer.h"
31 #include "SliderControl.h"
32 #include "SpinBoxControl.h"
33 #include "VideoEncoder.h"
34 #include "VideoFilter.h"
35 
36 #include "BVector.h"
37 #include "ADM_muxerProto.h"
38 #include "ADM_coreVideoFilterInternal.h"
39 
40 using namespace std;
41 
42 extern "C"
43 {
createEngine()44 	IScriptEngine* createEngine()
45 	{
46 		return new ADM_qtScript::QtScriptEngine();
47 	}
48 }
49 
50 namespace ADM_qtScript
51 {
MyQScriptEngine(QtScriptEngine * wrapperEngine)52     MyQScriptEngine::MyQScriptEngine(QtScriptEngine *wrapperEngine) : QScriptEngine()
53     {
54         this->wrapperEngine = wrapperEngine;
55     }
56 
QtScriptEngine()57     QtScriptEngine::QtScriptEngine()
58     {
59         this->_mapper = new AdmScriptMapper();
60     }
61 
~QtScriptEngine()62     QtScriptEngine::~QtScriptEngine()
63     {
64         this->callEventHandlers(IScriptEngine::Information, NULL, -1, "Closing QtScript");
65 
66         delete _mapper;
67     }
68 
capabilities()69     IScriptEngine::Capabilities QtScriptEngine::capabilities()
70     {
71 #ifdef QT_SCRIPTTOOLS
72         return (IScriptEngine::Capabilities)(IScriptEngine::Debugger | IScriptEngine::DebuggerShell);
73 #else
74         return IScriptEngine::None;
75 #endif
76     }
77 
createScriptWriter()78     IScriptWriter* QtScriptEngine::createScriptWriter()
79     {
80         return new QtScriptWriter();
81     }
82 
defaultFileExtension()83 	string QtScriptEngine::defaultFileExtension()
84 	{
85 		return "admjs";
86 	}
87 
editor()88     IEditor* QtScriptEngine::editor()
89     {
90         return _editor;
91     }
92 
initialise(IEditor * editor)93     void QtScriptEngine::initialise(IEditor *editor)
94     {
95         ADM_assert(editor);
96 
97         this->_editor = editor;
98         this->callEventHandlers(IScriptEngine::Information, NULL, -1, "Initialised");
99     }
100 
name()101     string QtScriptEngine::name()
102     {
103         return "QtScript";
104     }
105 
maturityRanking()106     int QtScriptEngine::maturityRanking()
107     {
108         return 2;
109     }
110 
openDebuggerShell()111 	void QtScriptEngine::openDebuggerShell()
112 	{
113 #ifdef QT_SCRIPTTOOLS
114 		this->runScript("debugger;", "", IScriptEngine::DebugOnError);
115 #endif
116 	}
117 
referenceUrl()118 	string QtScriptEngine::referenceUrl()
119 	{
120         return "index.html";
121 	}
122 
registerEventHandler(eventHandlerFunc * func)123     void QtScriptEngine::registerEventHandler(eventHandlerFunc *func)
124     {
125         _eventHandlerSet.insert(func);
126     }
127 
unregisterEventHandler(eventHandlerFunc * func)128     void QtScriptEngine::unregisterEventHandler(eventHandlerFunc *func)
129     {
130         _eventHandlerSet.erase(func);
131     }
132 
callEventHandlers(EventType eventType,const char * fileName,int lineNo,const char * message)133     void QtScriptEngine::callEventHandlers(EventType eventType, const char *fileName, int lineNo, const char *message)
134     {
135         EngineEvent event = { this, eventType, fileName, lineNo, message };
136         set<eventHandlerFunc*>::iterator it;
137 
138         for (it = _eventHandlerSet.begin(); it != _eventHandlerSet.end(); ++it)
139         {
140             (*it)(&event);
141         }
142     }
143 
runScript(const QString & script,const QString & name,RunMode mode)144     bool QtScriptEngine::runScript(const QString& script, const QString& name, RunMode mode)
145     {
146 		QCoreApplication *coreApplication = NULL;
147 		char **argv = NULL;
148 		int argc;
149 
150 		if (QCoreApplication::instance() == NULL)
151 		{
152 			argc = 1;
153 			argv = new char*[1];
154 			argv[0] = new char[1];
155 			argv[0][0] = '\0';
156 
157 			coreApplication = new QCoreApplication(argc, argv);
158 		}
159 
160         MyQScriptEngine engine(this);
161 
162         map<ADM_dynMuxer*, Muxer*> muxers;
163         map<ADM_videoEncoder6*, VideoEncoder*> videoEncoders;
164 
165 #ifdef QT_SCRIPTTOOLS
166         QScriptEngineDebugger debugger;
167 
168         if (mode == IScriptEngine::Debug || mode == IScriptEngine::DebugOnError)
169         {
170             debugger.attachTo(&engine);
171             debugger.standardWindow()->setWindowTitle(QT_TR_NOOP("Avidemux Script Debugger"));
172             debugger.standardWindow()->setWindowModality(Qt::ApplicationModal);
173 
174             if (mode == IScriptEngine::Debug)
175             {
176                 debugger.action(QScriptEngineDebugger::InterruptAction)->trigger();
177             }
178         }
179 
180 #endif
181 
182         this->registerAudioEncoderPlugins(&engine);
183         this->registerMuxerPlugins(&engine, &muxers);
184         this->registerVideoEncoderPlugins(&engine, &videoEncoders);
185         this->registerVideoFilterPlugins(&engine);
186 		this->registerDialogClasses(&engine);
187         this->registerScriptClasses(&engine, &muxers, &videoEncoders);
188 
189         QScriptValue result = engine.evaluate(script, name);
190         bool success = false;
191 
192         if (engine.hasUncaughtException())
193         {
194             QString errorDetails = (QString("Unable to process script.\n\nLine number: %1\n").arg(
195                                         engine.uncaughtExceptionLineNumber()) + result.toString());
196 
197             this->callEventHandlers(IScriptEngine::Error, NULL, -1, (QString("Script error ") + errorDetails).toUtf8().constData());
198             success = false;
199         }
200         else
201         {
202             this->callEventHandlers(IScriptEngine::Information, NULL, -1, (QString("Result: ") + result.toString()).toUtf8().constData());
203             success = true;
204         }
205 
206 		if (coreApplication && argv)
207 		{
208 			delete [] argv[0];
209 			delete [] argv;
210 			delete coreApplication;
211 		}
212 
213         return success;
214     }
215 
runScript(string script,RunMode mode)216     bool QtScriptEngine::runScript(string script, RunMode mode)
217     {
218         return this->runScript(QString(script.c_str()), "", mode);
219     }
220 
runScriptFile(string name,RunMode mode)221     bool QtScriptEngine::runScriptFile(string name, RunMode mode)
222     {
223         QFile scriptFile(name.c_str());
224 
225         if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text))
226         {
227             this->callEventHandlers(IScriptEngine::Error, NULL, -1, "Unable to open script file.");
228 
229             return false;
230         }
231 
232         QTextStream stream(&scriptFile);
233         QString contents = stream.readAll();
234         scriptFile.close();
235 
236         return this->runScript(contents, QString(name.c_str()), mode);
237     }
238 
copyEnumsToScriptObject(QScriptEngine * engine,const QMetaObject * metaObject,QScriptValue * object)239     void QtScriptEngine::copyEnumsToScriptObject(
240         QScriptEngine *engine, const QMetaObject *metaObject, QScriptValue *object)
241     {
242         for (int enumIndex = 0; enumIndex < metaObject->enumeratorCount(); enumIndex++)
243         {
244             QMetaEnum metaEnum = metaObject->enumerator(enumIndex);
245             QScriptValue enumClass = engine->newObject();
246 
247             for (int keyIndex = 0; keyIndex < metaEnum.keyCount(); keyIndex++)
248             {
249                 enumClass.setProperty(metaEnum.key(keyIndex), metaEnum.value(keyIndex));
250             }
251 
252             object->setProperty(metaEnum.name(), enumClass);
253         }
254     }
255 
registerDialogClasses(QScriptEngine * engine)256 	void QtScriptEngine::registerDialogClasses(QScriptEngine *engine)
257 	{
258 		// Register Dialog class
259         QScriptValue dialogObject = engine->newFunction(Dialog::constructor);
260 
261         engine->globalObject().setProperty("Dialog", dialogObject);
262 
263 		// Register CheckBoxControl class
264         QScriptValue checkBoxObject = engine->newFunction(CheckBoxControl::constructor);
265 
266         engine->globalObject().setProperty("CheckBoxControl", checkBoxObject);
267 
268 		// Register ComboBoxControl class
269         QScriptValue comboBoxObject = engine->newFunction(ComboBoxControl::constructor);
270 
271         engine->globalObject().setProperty("ComboBoxControl", comboBoxObject);
272 
273 		// Register ComboBoxItem class
274         QScriptValue comboBoxItemObject = engine->newFunction(ComboBoxItem::constructor);
275 
276         engine->globalObject().setProperty("ComboBoxItem", comboBoxItemObject);
277 
278 		// Register DoubleSpinBoxControl class
279         QScriptValue doubleSpinBoxObject = engine->newFunction(DoubleSpinBoxControl::constructor);
280 
281         engine->globalObject().setProperty("DoubleSpinBoxControl", doubleSpinBoxObject);
282 
283 		// Register LineEditControl class
284         QScriptValue lineEditObject = engine->newFunction(LineEditControl::constructor);
285 
286         engine->globalObject().setProperty("LineEditControl", lineEditObject);
287 
288 		// Register SliderControl class
289         QScriptValue sliderObject = engine->newFunction(SliderControl::constructor);
290 
291         engine->globalObject().setProperty("SliderControl", sliderObject);
292 
293 		// Register SpinBoxControl class
294         QScriptValue spinBoxObject = engine->newFunction(SpinBoxControl::constructor);
295 
296         engine->globalObject().setProperty("SpinBoxControl", spinBoxObject);
297 	}
298 
registerScriptEnums(QScriptEngine * engine,const QString & parentPropertyName,const QMetaObject * metaObject)299     void QtScriptEngine::registerScriptEnums(
300         QScriptEngine *engine, const QString& parentPropertyName, const QMetaObject* metaObject)
301     {
302         QScriptValue scriptObject = engine->newObject();
303 
304         this->copyEnumsToScriptObject(engine, metaObject, &scriptObject);
305         engine->globalObject().setProperty(parentPropertyName, scriptObject);
306     }
307 
registerScriptClasses(QScriptEngine * engine,map<ADM_dynMuxer *,Muxer * > * muxers,map<ADM_videoEncoder6 *,VideoEncoder * > * videoEncoders)308     void QtScriptEngine::registerScriptClasses(
309         QScriptEngine *engine, map<ADM_dynMuxer*, Muxer*>* muxers, map<ADM_videoEncoder6*, VideoEncoder*>* videoEncoders)
310     {
311         // Register various enums
312         this->registerScriptEnums(engine, "AudioOutput", &AudioOutput::staticMetaObject);
313         this->registerScriptEnums(engine, "FrameProperties", &FrameProperties::staticMetaObject);
314 
315         // Register Directory class
316         QScriptValue dirObject = engine->newFunction(Directory::constructor);
317 
318         this->copyEnumsToScriptObject(engine, &Directory::staticMetaObject, &dirObject);
319         engine->globalObject().setProperty("Directory", dirObject);
320 
321         // Register static Editor object
322         Editor* editor = new Editor(engine, this->_editor, muxers, videoEncoders);
323         QScriptValue mainObject = engine->newQObject(
324                                       editor, QScriptEngine::ScriptOwnership, QScriptEngine::ExcludeSlots);
325 
326         this->copyEnumsToScriptObject(engine, &Editor::staticMetaObject, &mainObject);
327         engine->globalObject().setProperty("Editor", mainObject);
328 
329         // Register File class
330         QScriptValue fileObject = engine->newFunction(File::constructor);
331 
332         this->copyEnumsToScriptObject(engine, &File::staticMetaObject, &fileObject);
333         engine->globalObject().setProperty("File", fileObject);
334 
335         // Register FileInfo class
336         QScriptValue fileInformationObject = engine->newFunction(FileInformation::constructor);
337 
338         this->copyEnumsToScriptObject(engine, &FileInformation::staticMetaObject, &fileInformationObject);
339         engine->globalObject().setProperty("FileInformation", fileInformationObject);
340 
341         // Register custom functions
342         QScriptValue executeFunc = engine->newFunction(executeFunction);
343         engine->globalObject().setProperty("execute", executeFunc);
344 
345         QScriptValue includeFunc = engine->newFunction(includeFunction);
346         engine->globalObject().setProperty("include", includeFunc);
347 
348 		QScriptValue debugPrintFunc = engine->globalObject().property("print");
349 
350 		if (debugPrintFunc.isValid())
351 		{
352 			engine->globalObject().setProperty("printDebug", debugPrintFunc);
353 		}
354 
355         QScriptValue printFunc = engine->newFunction(printFunction);
356         engine->globalObject().setProperty("print", printFunc);
357     }
358 
registerAudioEncoderPlugins(QScriptEngine * engine)359     void QtScriptEngine::registerAudioEncoderPlugins(QScriptEngine *engine)
360     {
361         for (unsigned int encoderIndex = 0; encoderIndex < ListOfAudioEncoder.size(); encoderIndex++)
362         {
363             ADM_audioEncoder* encoderPlugin = ListOfAudioEncoder[encoderIndex];
364             AudioEncoder *encoder = new AudioEncoder(engine, this->_editor, encoderPlugin, encoderIndex);
365 
366             engine->globalObject().setProperty(
367                 _mapper->getAudioEncoderClassName(encoderPlugin->codecName), engine->newFunction(
368                     AudioEncoder::constructor, engine->newQObject(encoder, QScriptEngine::ScriptOwnership)));
369         }
370     }
371 
registerMuxerPlugins(QScriptEngine * engine,map<ADM_dynMuxer *,Muxer * > * muxers)372     void QtScriptEngine::registerMuxerPlugins(QScriptEngine *engine, map<ADM_dynMuxer*, Muxer*>* muxers)
373     {
374         muxers->clear();
375 
376         for (unsigned int muxerIndex = 0; muxerIndex < ListOfMuxers.size(); muxerIndex++)
377         {
378             ADM_dynMuxer* muxerPlugin = ListOfMuxers[muxerIndex];
379             Muxer *muxer = new Muxer(engine, this->_editor, muxerPlugin);
380 
381             engine->globalObject().setProperty(
382                 _mapper->getMuxerClassName(muxerPlugin->name), engine->newQObject(muxer, QScriptEngine::ScriptOwnership));
383             muxers->insert(pair<ADM_dynMuxer*, Muxer*>(muxerPlugin, muxer));
384         }
385     }
386 
registerVideoEncoderPlugins(QScriptEngine * engine,map<ADM_videoEncoder6 *,VideoEncoder * > * encoders)387     void QtScriptEngine::registerVideoEncoderPlugins(
388         QScriptEngine *engine, map<ADM_videoEncoder6*, VideoEncoder*>* encoders)
389     {
390         encoders->clear();
391 
392         for (unsigned int encoderIndex = 0; encoderIndex < ListOfEncoders.size(); encoderIndex++)
393         {
394             ADM_videoEncoder6* encoderPlugin = ListOfEncoders[encoderIndex];
395             VideoEncoder *encoder = new VideoEncoder(engine, this->_editor, encoderPlugin);
396 
397             engine->globalObject().setProperty(
398                 _mapper->getVideoEncoderClassName(encoderPlugin->desc->encoderName), engine->newQObject(encoder, QScriptEngine::ScriptOwnership));
399             encoders->insert(pair<ADM_videoEncoder6*, VideoEncoder*>(encoderPlugin, encoder));
400         }
401     }
402 
registerVideoFilterPlugins(QScriptEngine * engine)403     void QtScriptEngine::registerVideoFilterPlugins(QScriptEngine *engine)
404     {
405         for (int filterGroupIndex = 0; filterGroupIndex < VF_MAX; filterGroupIndex++)
406         {
407             for (unsigned int filterIndex = 0; filterIndex < ADM_videoFilterPluginsList[filterGroupIndex].size(); filterIndex++)
408             {
409                 ADM_vf_plugin* filterPlugin = ADM_videoFilterPluginsList[filterGroupIndex][filterIndex];
410                 VideoFilter *filter = new VideoFilter(engine, this->_editor, filterPlugin);
411 
412                 engine->globalObject().setProperty(
413                     _mapper->getVideoFilterClassName(filterPlugin->getInternalName()), engine->newFunction(
414                         VideoFilter::constructor, engine->newQObject(filter, QScriptEngine::ScriptOwnership)));
415             }
416         }
417     }
418 
printFunction(QScriptContext * context,QScriptEngine * engine)419     QScriptValue QtScriptEngine::printFunction(QScriptContext *context, QScriptEngine *engine)
420     {
421 		QScriptValue debugPrintFunc = engine->globalObject().property("printDebug");
422 
423 		if (debugPrintFunc.isValid())
424 		{
425 			debugPrintFunc.call(context->thisObject(), context->argumentsObject());
426 		}
427 
428         QString output;
429 
430         for (int i = 0; i < context->argumentCount(); i++)
431         {
432             if (i > 0)
433             {
434                 output += " ";
435             }
436 
437             output += context->argument(i).toString();
438         }
439 
440         static_cast<MyQScriptEngine*>(engine)->wrapperEngine->callEventHandlers(
441             IScriptEngine::Information, NULL, -1, output.toUtf8().constData());
442 
443         return engine->undefinedValue();
444     }
445 
includeFunction(QScriptContext * context,QScriptEngine * engine)446     QScriptValue QtScriptEngine::includeFunction(QScriptContext *context, QScriptEngine *engine)
447     {
448         while (context->argumentCount())
449         {
450             QString filename = context->argument(0).toString();
451             QFile scriptFile(filename);
452 
453             if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text))
454             {
455                 context->throwError(QString(QT_TR_NOOP("Unable to open script file %1")).arg(filename));
456                 break;
457             }
458 
459             QTextStream stream(&scriptFile);
460             QString contents = stream.readAll();
461             scriptFile.close();
462 
463             context->setActivationObject(context->parentContext()->activationObject());
464             engine->evaluate(contents, filename);
465 
466             break;
467         }
468 
469         return engine->undefinedValue();
470     }
471 
executeFunction(QScriptContext * context,QScriptEngine * engine)472     QScriptValue QtScriptEngine::executeFunction(QScriptContext *context, QScriptEngine *engine)
473     {
474         if (context->argumentCount() < 1)
475         {
476             return engine->undefinedValue();
477         }
478 
479         QString command = context->argument(0).toString();
480         QStringList arguments = QStringList();
481 
482         for (int i = 1; i < context->argumentCount(); i++)
483         {
484             arguments.push_back(context->argument(i).toString());
485         }
486 
487         return QProcess::execute(command, arguments);
488     }
489 }
490