Script to export Womble trim list file

NickHope wrote on 12/5/2006, 8:49 AM
Hi all,

This has been prompted by this discussion in the video forum.

I am trying to write a script to export a Womble MPEG Video Wizard Trim List (.tls) file from a Vegas timeline that contains a single track of HDV .m2t files. The files have been trimmed but any effects have been removed. The purpose is to archive the best part of the trimmed events as .m2t files using Womble's smart rendering capability (no re-rendering of the bulk of the file), without making the cuts a 2nd time manually.

A typical Womble MPEG Video Wizard Trim List (.tls) file is pretty simple and looks like this:

[TRIM LIST HEADER INFO]
List Count: 4
[LIST ITEM 0]
Checked: 1
Selected: 0
InputFile: G:\capture\'20061101 15.54.19.m2t
IsFrameFormat: 0
FrameRate: 25.000000
Start: 1.520000
End: 2.640000
OutputFile: E:\trimmed\'20061101 15.54.19trimmed.m2t
[LIST ITEM 1]
Checked: 1
Selected: 0
InputFile: G:\capture\'20061101 15.55.20.m2t
IsFrameFormat: 0
FrameRate: 25.000000
Start: 1.200000
End: 3.840000
OutputFile: E:\trimmed\'20061101 15.55.20trimmed.m2t
[LIST ITEM 2]
Checked: 1
Selected: 0
InputFile: G:\capture\'20061101 15.55.57.m2t
IsFrameFormat: 0
FrameRate: 25.000000
Start: 1.720000
End: 4.520000
OutputFile: E:\trimmed\'20061101 15.55.57trimmed.m2t
[LIST ITEM 3]
Checked: 1
Selected: 0
InputFile: G:\capture\'20061104 11.37.27.m2t
IsFrameFormat: 0
FrameRate: 25.000000
Start: 22.120000
End: 54.680000
OutputFile: E:\trimmed\'20061104 11.37.27trimmed.m2t


Note that my slightly strange input filenames are like they are so that they play nice with my Sclive / Gearshift method.

I have made a half-baked start at this by modifying the "Export EDL.js" script that is bundled with Vegas in the scripts folder:

/**
* Sample script that exports the top-most video track as a
* Womble MPEG Video Wizard trim list file (.tls).
*
*
* Limitations:
* - supports only 1 video track.
* - supports cuts and cross fades (dissolves) only.
*
* By Nick Hope, based on the Export EDL.js script included with Vegas 7
*
* Revision Date: Dec. 05, 2006
**/

import System;
import System.Text;
import System.IO;
import System.Windows.Forms;
import Sony.Vegas;

var writer : StreamWriter = null;

try {
var noTime = new Timecode();

// find selected tracks
var videoTrack = FindTopTrackOfType(MediaType.Video);
if (null == videoTrack) {
throw "no tracks to export";
}

var projPath = Vegas.Project.FilePath;
var outputFilename = Path.ChangeExtension(projPath, ".tls");
var outputFilename = ShowSaveFileDialog("tls Files (*.tls)|*.tls", "Save tls", title);
if (null != outputFilename) {
writer = new StreamWriter(outputFilename, false, System.Text.Encoding.UTF8, 512);
// write the header stuff
writer.WriteLine("[TRIM LIST HEADER INFO]");
// don't know how to get the total number of events in the next line
writer.Writeline(" List Count:" + ???);

var editCount : int = 1;

// do video track
if (null != videoTrack) {
editCount = ExportTrack(new Enumerator(videoTrack.Events), editCount, null);
}

writer.Close();
}
} catch (e) {
if (null != writer)
writer.Close();
MessageBox.Show(e);
}

function ExportTrack(events, editIndex, prevMediaPath) {
if (events.atEnd()) return editIndex;
var currentEvent = events.item();
// look ahead to see if we need to do a dissolve.
var nextEvent = null;
events.moveNext();
if (!events.atEnd()) {
nextEvent = events.item();
}

// compute parameters for current event
var activeTake = currentEvent.ActiveTake;
var mediaPath = activeTake.MediaPath;
var extFileName = Path.GetFileName(mediaPath);
var baseFileName = Path.GetFileNameWithoutExtension(extFileName);
var inputFileName = ((baseFilename) + ".m2t");
var sourceOut = activeTake.Offset + currentEvent.Length;
var outputFolder = (E:\trimmed\); // would be great to make this inputtable when script is run
var outputFile = ((outputFolder) + (baseFileName) + "trimmed.m2t");

// write information for each edited event
writer.WriteLine("[LIST ITEM ", editIndex, "]");
writer.WriteLine(" Checked: 1");
writer.WriteLine(" Selected: 0");
writer.WriteLine(" InputFile: " (inputFileName);
writer.WriteLine(" IsFrameFormat: 0");
writer.WriteLine(" FrameRate: 25.000000");
writer.WriteLine(" Start: " + (activeTake.Offset);
writer.WriteLine(" End: " + (sourceOut);
writer.WriteLine(" OutputFile: " + (outputFile);
return ExportTrack(events, editIndex+1, null);
}

function FindTopTrackOfType(mediaType) : Track {
var trackEnum = new Enumerator(Vegas.Project.Tracks);
while (!trackEnum.atEnd()) {
var track : Track = Track(trackEnum.item());
if (track.MediaType == mediaType) {
return track;
}
trackEnum.moveNext();
}
return null;
}

// an example filter: "PNG File (*.png)|*.png|JPEG File (*.jpg)|*.jpg"
function ShowSaveFileDialog(filter, title, defaultFilename) {
var saveFileDialog = new SaveFileDialog();
if (null == filter) {
filter = "All Files (*.*)|*.*";
}
saveFileDialog.Filter = filter;
if (null != title)
saveFileDialog.Title = title;
saveFileDialog.CheckPathExists = true;
saveFileDialog.AddExtension = true;
if (null != defaultFilename) {
var initialDir = Path.GetDirectoryName(defaultFilename);
if (Directory.Exists(initialDir)) {
saveFileDialog.InitialDirectory = initialDir;
}
saveFileDialog.DefaultExt = Path.GetExtension(defaultFilename);
saveFileDialog.FileName = Path.GetFileName(defaultFilename);
}
if (System.Windows.Forms.DialogResult.OK == saveFileDialog.ShowDialog()) {
return Path.GetFullPath(saveFileDialog.FileName);
} else {
return null;
}
}

To anyone who knows what they're doing, it will be obvious that I don't know what I'm doing. Needless to say the script doesn't run yet. But I'm hoping it might be fairly straightforward for someone to knock into shape or at least give me some pointers???

Any help anyone can give would be hugely appreciated.

A 30-day trial of Womble MPEG Video Wizard is available at www.womble.com.

Thanks! Nick

Comments

JohnnyRoy wrote on 12/6/2006, 8:36 AM
> To anyone who knows what they're doing, it will be obvious that I don't know what I'm doing. Needless to say the script doesn't run yet. But I'm hoping it might be fairly straightforward for someone to knock into shape or at least give me some pointers???

Actually it shows that you have a good understanding of what the script should do. You just need more details on how to implement it.

I've written the script below that will do this. It was just easier for me to start from scratch. This script assumes that the project frame rate matches the media frame rate. If this is not true change your project frame rate to match the media. I just didn't want to hard-code it and there is no way to get it from the media.

This is a C# file so make sure you give it a .CS extension when you save it:
/**
* Program: WombleExport.cs
* Author: John Rofrano
* Purpose: This script will export the first video track as a Womble trim list.
* It places the files in the same folder with the word "-trim(0)" appended
* to the filename with increasing numbers to make the name unique.
*
* Revision Date: Dec. 6, 2006
**/

using System;
using System.IO;
using System.Windows.Forms;
using Sony.Vegas;

class EntryPoint
{
public void FromVegas(Vegas vegas)
{
// get the first video track
VideoTrack videoTrack = null;
foreach (Track track in vegas.Project.Tracks)
{
if (track.IsVideo())
{
videoTrack = (VideoTrack)track;
break;
}
}

// if we didn't find a track then tell the user we can't continue
if (videoTrack == null)
{
MessageBox.Show("You must have at least one video track in your project",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

// make sure the project has a name
if (vegas.Project.FilePath == null)
{
MessageBox.Show("You must give your project a name by saving it before running this script",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

// create the womble trim list name from the project name
string wombleFilename = Path.ChangeExtension(vegas.Project.FilePath, ".tls");

int eventsProcessed = 0;
StreamWriter writer = null;
try
{
writer = new StreamWriter(wombleFilename, false, System.Text.Encoding.UTF8, 512);

// write the header stuff
writer.WriteLine("[TRIM LIST HEADER INFO]");
writer.WriteLine("List Count: " + videoTrack.Events.Count);

for (int i = 0; i < videoTrack.Events.Count; i++)
{
VideoEvent videoEvent = (VideoEvent)videoTrack.Events[i];

// create the file names
string infile = videoEvent.ActiveTake.MediaPath;
string baseName = Path.Combine(Path.GetDirectoryName(infile), Path.GetFileNameWithoutExtension(infile));
// make a unique output file in case the same input file is used in multiple events
string outfile = String.Format("{0}-trim({1}){2}", baseName, i, Path.GetExtension(infile));

// assume the start and end are expressed in seconds
double start = videoEvent.ActiveTake.Offset.ToMilliseconds() / 1000;
double end = (videoEvent.Length.ToMilliseconds() + videoEvent.ActiveTake.Offset.ToMilliseconds()) / 1000;

// write the lines out to the file
writer.WriteLine(String.Format("[LIST ITEM {0}]", i));
writer.WriteLine("Checked: 1");
writer.WriteLine("Selected: 0");
writer.WriteLine("InputFile: " + infile);
writer.WriteLine("IsFrameFormat: 0");
writer.WriteLine(String.Format("FrameRate: {0:F6}", vegas.Project.Video.FrameRate));
writer.WriteLine(String.Format("Start: {0:F6}", start));
writer.WriteLine(String.Format("End: {0:F6}", end));
writer.WriteLine("OutputFile: " + outfile);

eventsProcessed++;
}

writer.Close();
}
catch (Exception e)
{
if (null != writer)
{
writer.Close();
}
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

MessageBox.Show("Processed: " + eventsProcessed + " events",
"Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);

}
}
~jr
NickHope wrote on 12/7/2006, 1:08 AM
Thanks very much indeed for that JR. I'm off on a dive trip for 4 or 5 days but will test this out when I get back and give you some feedback.

Nick
JohnnyRoy wrote on 12/7/2006, 4:06 PM
Nick, I tested it when I got home and it seems to work fine. I generated some HDV with burned in timecode and the Womble trimmed files had the exact same trim points as the events so I assume it works fine. Let me know what you think when you test it.

Be careful diving.

~jr
NickHope wrote on 12/15/2006, 12:06 AM
Hi JR,

It works well here too. Nice and fast. With the list created by your script, Womble churned out trimmed copies of 73 files (14:30 of footage) in just 7mins 40 seconds on my P4 3.0GHz 1Gb RAM.

I do have one issue however. Womble starts the trimmed clips a number of frames ahead, typically 12 frames ahead (I'm shooting 50i so my GOPs are 12 frames). That's actually great because it gives my files a short "head" which is useful for archived stock.

However Womble also ends the trimmed clip up to 12 frames earlier than the event in Vegas, which is not good because critical footage at the end of the event can be lost.

Turning "GOP trim" on or off in the Womble options seems to make no difference whatsoever to the output files (compared a few with Winmerge to check this. So not quite sure what that switch actually does do).

So would it be possible to modify the script to add say 1 second to the "End" values in the tls file. I guess that shouldn't be too problematic but would it behave nicely if the extra second simply didn't exist at the end of the original event?

Even better would be user input of a "head" and "tail" in seconds. Or alternatively a separate script that adds a head and tail to all the events on the Vegas timeline before archiving by whatever means. Perhaps someone has already written such a script?

As background to the issue note I'm using HDVsplit for capturing, not Vegas Vidcap.

Also note I am running my Z1 with "quick rec" on which I know might lead to breaks in time code (and open GOPs?). I know everything in post might run easier with this mode off but I would rather keep it on but when a shark darts past my camera needs to react quickly. If quick rec was off then perhaps the issue I'm seeing would not be happening.

Another enhancement that would be great is to allow user input of the destination folder for the trimmed files. In my case my source files are on H: drive but I archive to E: drive. Perhaps I could hard code somehow this as a quick fix.

Anyway I really appreciate your effort on this as it's going to be a great timesaver for my workflow.

Nick
NickHope wrote on 12/15/2006, 1:42 AM
Well, I've managed to hard-code those couple of changes I was after. It's not elegant but it gets the job done:

/** * Program: WombleExport.cs
* Author: John Rofrano
* Purpose: This script will export the first video track as a Womble trim list.
* It places the files in the same folder with the word "-trim(0)" appended
* to the filename with increasing numbers to make the name unique.
*
* Revision Date: Dec. 6, 2006
**/
using System;
using System.IO;
using System.Windows.Forms;
using Sony.Vegas;
class EntryPoint
{
public void FromVegas(Vegas vegas)
{
// get the first video track
VideoTrack videoTrack = null;
foreach (Track track in vegas.Project.Tracks)
{
if (track.IsVideo())
{
videoTrack = (VideoTrack)track;
break;
}
}
// if we didn't find a track then tell the user we can't continue
if (videoTrack == null)
{
MessageBox.Show("You must have at least one video track in your project",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// make sure the project has a name
if (vegas.Project.FilePath == null)
{
MessageBox.Show("You must give your project a name by saving it before running this script",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// create the womble trim list name from the project name
string wombleFilename = Path.ChangeExtension(vegas.Project.FilePath, ".tls");
int eventsProcessed = 0;
StreamWriter writer = null;
try
{
writer = new StreamWriter(wombleFilename, false, System.Text.Encoding.UTF8, 512);

// write the header stuff
writer.WriteLine("[TRIM LIST HEADER INFO]");
writer.WriteLine("List Count: " + videoTrack.Events.Count);
for (int i = 0; i < videoTrack.Events.Count; i++)
{
VideoEvent videoEvent = (VideoEvent)videoTrack.Events[i];

// create the file names
string infile = videoEvent.ActiveTake.MediaPath;
// hard code the destination folder for trimmed files - Nick Hope
string baseName = Path.Combine("E:\\trimmed\\", Path.GetFileNameWithoutExtension(infile));
// make a unique output file in case the same input file is used in multiple events
string outfile = String.Format("{0}-trim({1}){2}", baseName, i, Path.GetExtension(infile));
// assume the start and end are expressed in seconds
double start = videoEvent.ActiveTake.Offset.ToMilliseconds() / 1000;
// one second added to prevent the end of the trimmed file being truncated - Nick Hope
double end = (videoEvent.Length.ToMilliseconds() + videoEvent.ActiveTake.Offset.ToMilliseconds()) / 1000 + 1;
// write the lines out to the file
writer.WriteLine(String.Format("[LIST ITEM {0}]", i));
writer.WriteLine("Checked: 1");
writer.WriteLine("Selected: 0");
writer.WriteLine("InputFile: " + infile);
writer.WriteLine("IsFrameFormat: 0");
writer.WriteLine(String.Format("FrameRate: {0:F6}", vegas.Project.Video.FrameRate));
writer.WriteLine(String.Format("Start: {0:F6}", start));
writer.WriteLine(String.Format("End: {0:F6}", end));
writer.WriteLine("OutputFile: " + outfile);
eventsProcessed++;
}
writer.Close();
}
catch (Exception e)
{
if (null != writer)
{
writer.Close();
}
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
MessageBox.Show("Processed: " + eventsProcessed + " events",
"Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
NickHope wrote on 12/16/2006, 1:35 AM
I've noticed that if the extra second of footage asked for doesn't actually exist at the end of the original event, Womble just ends the new trimmed m2t file. No crashes or repeated frames or anything. Which is great.

However I am noticing in general a few "funnies" with HDV files trimmed in Womble. In particular some of the trimmed files have a single repeated frame at the end, which of course would give very briefly frozen motion. Another file was created with a single frame of reversed video at the start.

Thankfully these glitches are at the beginning and ends of the files and therefore in the part sof the file that will be cut.
JohnnyRoy wrote on 12/16/2006, 5:53 AM
I see that you added 1 second to the ending by using +1 which is great. If you ever want to control the number of frames, instead of using 1 you can use
Timecode.FromFrames(1).ToMilliseconds()
to get the number of milliseconds in a frame and then divide by 1000 for seconds. This will allow you to trim or add however many frames you want. I'm not sure if it will be helpful here but I'm just passing this alone as a script tip for next time.

~jr
NickHope wrote on 1/31/2007, 8:45 AM
Kathy at Womble alerted me to this utility they've written which will convert a Vegas EDL (Edit Decision List) into a Womble project.

The trimmed files open up in the Womble timeline. I'm just working out how to export all the trimmed clips from there in one operation (if it can be done at all).

If this looks useful I'll post it back on the Vegas forum as well.
johnmeyer wrote on 1/31/2007, 3:33 PM
I'm in hog heaven. That utility is fantastic. I can use Vegas to edit but Womble to do lossless cuts. Vegas still needs to do this, but for some projects this will be sufficient. Great find, Nick!
NickHope wrote on 2/1/2007, 1:53 AM
Also I've noticed (on an extremely brief test) that a timeline of HDV m2t's play back more smoothly and is more responsive in Womble MVW than Vegas on my old P4 3.0GHz machine, so I may investigate using this program for simpler projects or just cutting footage up. I'm having to use DV proxies via Gearshift at the moment.

What I still don't know is if it's possible to batch export a whole timeline of clips from Womble for archive purposes. If not then JR's script is the better method still for my workflow.
NickHope wrote on 6/1/2007, 1:27 AM
A warning for anyone trimming m2t files using Womble...

Vegas 7 handles "Sony flavour" m2t files much better than "Main Concept" flavour m2t files. If you trim a file with Womble it will subsequently be treated in Vegas 7 as a "Main Concept" flavour m2t and the enhancements to m2t handling made in Vegas 7 will not apply to it.

More here and here.