arikrupnik / ltcsync

Syncs media files using embedded timecode (LTC)
GNU General Public License v3.0
45 stars 8 forks source link

Generate XML timelines for Final Cut and Premiere #26

Open arikrupnik opened 4 years ago

arikrupnik commented 4 years ago

It can simplify post workflows for Final Cut and Premiere users if we could generate XML timelines which they could import directly into the NLE instead of using unfamiliar padding files.

DaVinci Resolve offers valuable insight into timeline exports, and ready examples of such files. I include here two such files, FCP XML (ironically for Premiere) and FCPXML for Final Cut Pro X. Both files contain the exact same multicam timeline, with two .mov files in it.

file.fpcxml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fcpxml>
<fcpxml version="1.8">
    <resources>
        <format width="1920" frameDuration="1001/24000s" id="r0" height="1080" name="FFVideoFormat1080p2398"/>
        <format width="1920" frameDuration="1001/30000s" id="r1" height="1080" name="FFVideoFormat1080p2997"/>
        <asset start="0/1s" duration="2221219/30000s" audioSources="1" id="r2" audioChannels="2" hasVideo="1" src="file:///media/ari/heir/dish-testimonials-ks1/EricJewell.2.prores.mov" hasAudio="1" format="r1" name="EricJewell.2.prores.mov"/>
        <asset start="0/1s" duration="339339/2000s" audioSources="1" id="r3" audioChannels="2" hasVideo="1" src="file:///media/ari/heir/dish-testimonials-ks1/AdamMoffat.prores.mov" hasAudio="1" format="r0" name="AdamMoffat.prores.mov"/>
    </resources>
    <library>
        <event name="Timeline 2 (Resolve)">
            <project name="Timeline 2 (Resolve)">
                <sequence duration="339339/2000s" tcStart="18018/5s" format="r0" tcFormat="NDF">
                    <spine>
                        <gap start="3600/1s" duration="73073/1500s" offset="18018/5s" name="Gap">
                            <asset-clip start="0/1s" duration="339339/2000s" ref="r3" enabled="1" offset="3600/1s" format="r0" tcFormat="NDF" lane="1" name="AdamMoffat.prores.mov">
                                <adjust-transform anchor="0 0" scale="1 1" position="0 0"/>
                            </asset-clip>
                        </gap>
                        <asset-clip start="0/1s" duration="71071/960s" ref="r2" enabled="1" offset="5478473/1500s" format="r1" tcFormat="NDF" name="EricJewell.2.prores.mov">
                            <adjust-transform anchor="0 0" scale="1 1" position="0 0"/>
                        </asset-clip>
                        <gap start="3600/1s" duration="3003/64s" offset="29810781/8000s" name="Gap"/>
                    </spine>
                </sequence>
            </project>
        </event>
    </library>
</fcpxml>

file.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xmeml>
<xmeml version="5">
    <sequence>
        <name>Timeline 2 (Resolve)</name>
        <duration>4067</duration>
        <rate>
            <timebase>24</timebase>
            <ntsc>TRUE</ntsc>
        </rate>
        <in>-1</in>
        <out>-1</out>
        <timecode>
            <string>01:00:00:00</string>
            <frame>86400</frame>
            <displayformat>NDF</displayformat>
            <rate>
                <timebase>24</timebase>
                <ntsc>TRUE</ntsc>
            </rate>
        </timecode>
        <media>
            <video>
                <track>
                    <clipitem id="EricJewell.2.prores.mov 0">
                        <name>EricJewell.2.prores.mov</name>
                        <duration>1775</duration>
                        <rate>
                            <timebase>24</timebase>
                            <ntsc>TRUE</ntsc>
                        </rate>
                        <start>1168</start>
                        <end>2943</end>
                        <enabled>TRUE</enabled>
                        <in>0</in>
                        <out>1775</out>
                        <file id="EricJewell.2.prores.mov 2">
                            <duration>2219</duration>
                            <rate>
                                <timebase>30</timebase>
                                <ntsc>TRUE</ntsc>
                            </rate>
                            <name>EricJewell.2.prores.mov</name>
                            <pathurl>file:///media/ari/heir/dish-testimonials-ks1/EricJewell.2.prores.mov</pathurl>
                            <timecode>
                                <string>00:00:00:00</string>
                                <displayformat>NDF</displayformat>
                                <rate>
                                    <timebase>30</timebase>
                                    <ntsc>TRUE</ntsc>
                                </rate>
                            </timecode>
                            <media>
                                <video>
                                    <duration>2219</duration>
                                    <samplecharacteristics>
                                        <width>1920</width>
                                        <height>1080</height>
                                    </samplecharacteristics>
                                </video>
                                <audio>
                                    <channelcount>2</channelcount>
                                </audio>
                            </media>
                        </file>
                        <compositemode>normal</compositemode>
                        <filter>
                            <enabled>TRUE</enabled>
                            <start>0</start>
                            <end>1775</end>
                            <effect>
                                <name>Opacity</name>
                                <effectid>opacity</effectid>
                                <effecttype>motion</effecttype>
                                <mediatype>video</mediatype>
                                <effectcategory>motion</effectcategory>
                                <parameter>
                                    <name>opacity</name>
                                    <parameterid>opacity</parameterid>
                                    <value>100</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                            </effect>
                        </filter>
                        <filter>
                            <enabled>TRUE</enabled>
                            <start>0</start>
                            <end>1775</end>
                            <effect>
                                <name>Basic Motion</name>
                                <effectid>basic</effectid>
                                <effecttype>motion</effecttype>
                                <mediatype>video</mediatype>
                                <effectcategory>motion</effectcategory>
                                <parameter>
                                    <name>Scale</name>
                                    <parameterid>scale</parameterid>
                                    <value>100</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>10000</valuemax>
                                </parameter>
                                <parameter>
                                    <name>Center</name>
                                    <parameterid>center</parameterid>
                                    <value>
                                        <horiz>0</horiz>
                                        <vert>0</vert>
                                    </value>
                                </parameter>
                                <parameter>
                                    <name>Rotation</name>
                                    <parameterid>rotation</parameterid>
                                    <value>0</value>
                                    <valuemin>-100000</valuemin>
                                    <valuemax>100000</valuemax>
                                </parameter>
                                <parameter>
                                    <name>Anchor Point</name>
                                    <parameterid>centerOffset</parameterid>
                                    <value>
                                        <horiz>0</horiz>
                                        <vert>0</vert>
                                    </value>
                                </parameter>
                            </effect>
                        </filter>
                        <filter>
                            <enabled>TRUE</enabled>
                            <start>0</start>
                            <end>1775</end>
                            <effect>
                                <name>Crop</name>
                                <effectid>crop</effectid>
                                <effecttype>motion</effecttype>
                                <mediatype>video</mediatype>
                                <effectcategory>motion</effectcategory>
                                <parameter>
                                    <name>left</name>
                                    <parameterid>left</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                                <parameter>
                                    <name>right</name>
                                    <parameterid>right</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                                <parameter>
                                    <name>top</name>
                                    <parameterid>top</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                                <parameter>
                                    <name>bottom</name>
                                    <parameterid>bottom</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                            </effect>
                        </filter>
                        <link>
                            <linkclipref>EricJewell.2.prores.mov 0</linkclipref>
                        </link>
                        <link>
                            <linkclipref>EricJewell.2.prores.mov 3</linkclipref>
                        </link>
                    </clipitem>
                    <enabled>TRUE</enabled>
                    <locked>FALSE</locked>
                </track>
                <track>
                    <clipitem id="AdamMoffat.prores.mov 0">
                        <name>AdamMoffat.prores.mov</name>
                        <duration>4068</duration>
                        <rate>
                            <timebase>24</timebase>
                            <ntsc>TRUE</ntsc>
                        </rate>
                        <start>0</start>
                        <end>4068</end>
                        <enabled>TRUE</enabled>
                        <in>0</in>
                        <out>4068</out>
                        <file id="AdamMoffat.prores.mov 2">
                            <duration>4068</duration>
                            <rate>
                                <timebase>24</timebase>
                                <ntsc>TRUE</ntsc>
                            </rate>
                            <name>AdamMoffat.prores.mov</name>
                            <pathurl>file:///media/ari/heir/dish-testimonials-ks1/AdamMoffat.prores.mov</pathurl>
                            <timecode>
                                <string>00:00:00:00</string>
                                <displayformat>NDF</displayformat>
                                <rate>
                                    <timebase>24</timebase>
                                    <ntsc>TRUE</ntsc>
                                </rate>
                            </timecode>
                            <media>
                                <video>
                                    <duration>4068</duration>
                                    <samplecharacteristics>
                                        <width>1920</width>
                                        <height>1080</height>
                                    </samplecharacteristics>
                                </video>
                                <audio>
                                    <channelcount>2</channelcount>
                                </audio>
                            </media>
                        </file>
                        <compositemode>normal</compositemode>
                        <filter>
                            <enabled>TRUE</enabled>
                            <start>0</start>
                            <end>4068</end>
                            <effect>
                                <name>Opacity</name>
                                <effectid>opacity</effectid>
                                <effecttype>motion</effecttype>
                                <mediatype>video</mediatype>
                                <effectcategory>motion</effectcategory>
                                <parameter>
                                    <name>opacity</name>
                                    <parameterid>opacity</parameterid>
                                    <value>100</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                            </effect>
                        </filter>
                        <filter>
                            <enabled>TRUE</enabled>
                            <start>0</start>
                            <end>4068</end>
                            <effect>
                                <name>Basic Motion</name>
                                <effectid>basic</effectid>
                                <effecttype>motion</effecttype>
                                <mediatype>video</mediatype>
                                <effectcategory>motion</effectcategory>
                                <parameter>
                                    <name>Scale</name>
                                    <parameterid>scale</parameterid>
                                    <value>100</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>10000</valuemax>
                                </parameter>
                                <parameter>
                                    <name>Center</name>
                                    <parameterid>center</parameterid>
                                    <value>
                                        <horiz>0</horiz>
                                        <vert>0</vert>
                                    </value>
                                </parameter>
                                <parameter>
                                    <name>Rotation</name>
                                    <parameterid>rotation</parameterid>
                                    <value>0</value>
                                    <valuemin>-100000</valuemin>
                                    <valuemax>100000</valuemax>
                                </parameter>
                                <parameter>
                                    <name>Anchor Point</name>
                                    <parameterid>centerOffset</parameterid>
                                    <value>
                                        <horiz>0</horiz>
                                        <vert>0</vert>
                                    </value>
                                </parameter>
                            </effect>
                        </filter>
                        <filter>
                            <enabled>TRUE</enabled>
                            <start>0</start>
                            <end>4068</end>
                            <effect>
                                <name>Crop</name>
                                <effectid>crop</effectid>
                                <effecttype>motion</effecttype>
                                <mediatype>video</mediatype>
                                <effectcategory>motion</effectcategory>
                                <parameter>
                                    <name>left</name>
                                    <parameterid>left</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                                <parameter>
                                    <name>right</name>
                                    <parameterid>right</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                                <parameter>
                                    <name>top</name>
                                    <parameterid>top</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                                <parameter>
                                    <name>bottom</name>
                                    <parameterid>bottom</parameterid>
                                    <value>0</value>
                                    <valuemin>0</valuemin>
                                    <valuemax>100</valuemax>
                                </parameter>
                            </effect>
                        </filter>
                        <link>
                            <linkclipref>AdamMoffat.prores.mov 0</linkclipref>
                        </link>
                        <link>
                            <linkclipref>AdamMoffat.prores.mov 3</linkclipref>
                        </link>
                    </clipitem>
                    <enabled>TRUE</enabled>
                    <locked>FALSE</locked>
                </track>
                <format>
                    <samplecharacteristics>
                        <width>1920</width>
                        <height>1080</height>
                        <pixelaspectratio>square</pixelaspectratio>
                        <rate>
                            <timebase>24</timebase>
                            <ntsc>TRUE</ntsc>
                        </rate>
                        <codec>
                            <appspecificdata>
                                <appname>Final Cut Pro</appname>
                                <appmanufacturer>Apple Inc.</appmanufacturer>
                                <data>
                                    <qtcodec/>
                                </data>
                            </appspecificdata>
                        </codec>
                    </samplecharacteristics>
                </format>
            </video>
            <audio>
                <track>
                    <clipitem id="EricJewell.2.prores.mov 3">
                        <name>EricJewell.2.prores.mov</name>
                        <duration>1775</duration>
                        <rate>
                            <timebase>24</timebase>
                            <ntsc>TRUE</ntsc>
                        </rate>
                        <start>1168</start>
                        <end>2943</end>
                        <enabled>TRUE</enabled>
                        <in>0</in>
                        <out>1775</out>
                        <file id="EricJewell.2.prores.mov 2"/>
                        <sourcetrack>
                            <mediatype>audio</mediatype>
                            <trackindex>1</trackindex>
                        </sourcetrack>
                        <link>
                            <linkclipref>EricJewell.2.prores.mov 0</linkclipref>
                        </link>
                        <link>
                            <linkclipref>EricJewell.2.prores.mov 3</linkclipref>
                        </link>
                    </clipitem>
                    <enabled>TRUE</enabled>
                    <locked>FALSE</locked>
                </track>
                <track>
                    <clipitem id="AdamMoffat.prores.mov 3">
                        <name>AdamMoffat.prores.mov</name>
                        <duration>4068</duration>
                        <rate>
                            <timebase>24</timebase>
                            <ntsc>TRUE</ntsc>
                        </rate>
                        <start>0</start>
                        <end>4068</end>
                        <enabled>TRUE</enabled>
                        <in>0</in>
                        <out>4068</out>
                        <file id="AdamMoffat.prores.mov 2"/>
                        <sourcetrack>
                            <mediatype>audio</mediatype>
                            <trackindex>1</trackindex>
                        </sourcetrack>
                        <link>
                            <linkclipref>AdamMoffat.prores.mov 0</linkclipref>
                        </link>
                        <link>
                            <linkclipref>AdamMoffat.prores.mov 3</linkclipref>
                        </link>
                    </clipitem>
                    <enabled>TRUE</enabled>
                    <locked>FALSE</locked>
                </track>
            </audio>
        </media>
    </sequence>
</xmeml>
RebelPhoton commented 4 years ago

I've gained some insight into FCP X's fcpxml format by reading the official Apple documentation for the format and trial and error. https://developer.apple.com/library/archive/documentation/FinalCutProX/Reference/FinalCutProXXMLFormat/Introduction/Introduction.html

Similarly the FCP xmeml format is openly documented although I believe that Adobe introduced some variations in its use.

https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/FinalCutPro_XML/AboutThisDoc/AboutThisDoc.html#//apple_ref/doc/uid/TP30001152-BAJHJEAA

Despite all this, I believe you might avoid some frustration by first taking a look at OpenTimelineIO, which is tackling this interoperability problem with more resources, and growing industry support.

arikrupnik commented 4 years ago

Thank you for adding the links, @RebelPhoton . fcpxml documentation is massive, and I have no access to a FCPX installation for testing. Having a known-good sample to work from is a great aide to official documentation.

OpenTimelineIO looks very promising. Do you have any direct experience with it?

RebelPhoton commented 4 years ago

I haven't used it yet, and I regret it, because all the work i did last year on FCPX pipelines is not of much use now that I'm working with Premiere. I'll dive into it sooner or later.

arikrupnik commented 4 years ago

Would be curious to hear about the work you did with FCPX pipelines.

Everillangel commented 4 years ago

How hard would this be to implement into LTCSync, I have been trying to find a way to either put the wav files into a mov container with videos or have an XML that then syncs tracks on import. I use resolve majority but want to be able to have a system where anyone could use linear timecode for syncing.

arikrupnik commented 4 years ago

@Everillangel , what operating system are you on? How comfortable are you with command line?

arikrupnik commented 4 years ago

If you can run ffmpeg on the command line, try this command and tell me if it gives you want you need (adjust video frame rate etc., as necessary):

ffmpeg -f lavfi -i color=c=black:s=640x480:r=24 -i fille.wav -c:a copy -shortest file.mov
Everillangel commented 3 years ago

If you can run ffmpeg on the command line, try this command and tell me if it gives you want you need (adjust video frame rate etc., as necessary):

ffmpeg -f lavfi -i color=c=black:s=640x480:r=24 -i fille.wav -c:a copy -shortest file.mov

Hi, I am comfortable with ffmpeg. this is not quite what I meant. I am looking to either sync in a container like mov which supports multitrack wav or produce an xml that will sync everything. Your current method is kinda hard to use surely there is a more efficent way. I know this is a free piece of software to go along side a piece of hardware. once you decode the timecode track you could easily sync it and put it in a container for less hassle.

arikrupnik commented 3 years ago

MOV supports multitrack audio, but not multiple video streams. This is an easy implementation, but hard to communicate to users who have multi-camera shoots.