Timed metadata can be written to a track using AVAssetWriter. However if you append only one AVTimedMetadataGroup the results will be unexpected due to buggy behaviour.
Mathijs Kadijk, Tom Lokhorst
tldr; Writing a single AVTimedMetadataGroup
with a open time range gives unexpected results. Write another dummy AVTimedMetadataGroup
after the source time you pass to AVAssetWriter.endSession
as a workaround.
When writing AVTimedMetadataGroup
with a timerange that has its end time set to .invalid
the timerange will be automatically adjusted by AVAssetWriter
. This is very useful as at the time you append the metadata to the writer it's often unknown when the next bit of metadata will become available. This behaviour is explained in this WWDC talk from 2014: "Harnessing metadata in audiovisual media".
Perfect for app demos & presentations; Simply plug in an iPhone and it automatically shows up on your Mac.
We use this in our app Bezel that can record your iPhone, it can however occur that we have a metadata group created before the recording started. We append this metadata group once the recording starts to have the initial state.
When the user doesn't do anything special with the device, no new metadata sample will be generated. So the single appended sample during the recording is expected to cover the whole timespan of the recording.
If only a single AVTimedMetadataGroup
is appended to the AVAssetWriterInputMetadataAdaptor
the timerange isn't correctly adjusted. There are basically two different variants depending on the start of the timerange.
When the timerange start lays before the session start time given to the AVAssetWriter
, the group won't be added to the file at all. The metadata track will end up empty!
assetWriter.startWriting()
assetWriter.startSession(atSourceTime: CMTime(seconds: 5, preferredTimescale: 1000))
metadataInputAdaptor.append(timedMetadataGroupAtOneSecond)
assetWriter.endSession(atSourceTime: CMTime(seconds: 15, preferredTimescale: 1000))
await assetWriter.finishWriting()
When the timerange start is between the session start and end time given to the AVAssetWriter
the group is added to the file as expected. However the groups end time won't adjusted to the session end time, but is very close to the start time.
assetWriter.startWriting()
assetWriter.startSession(atSourceTime: CMTime(seconds: 5, preferredTimescale: 1000))
metadataInputAdaptor.append(timedMetadataGroupAtTenSeconds)
assetWriter.endSession(atSourceTime: CMTime(seconds: 15, preferredTimescale: 1000))
await assetWriter.finishWriting()
Once a second AVTimedMetadataGroup
is appended to the AVAssetWriterInputMetadataAdaptor
all end times are calculated correctly. That second groups start time may even be after the session end time that is passed to AVAssetWriter
. That group won't be written to the file as it is out of range of the session, but it does trigger correct calculation of the other sample.
So a workaround is to, right before ending the AVAssetWriter
session, append another AVTimedMetadataGroup
containing dummy with a timerange that is after the given session end time. This will make the described issue disappear.
assetWriter.startWriting()
assetWriter.startSession(atSourceTime: CMTime(seconds: 5, preferredTimescale: 1000))
metadataInputAdaptor.append(timedMetadataGroupAtOneSecond)
metadataInputAdaptor.append(timedMetadataGroupAtOneMinute)
assetWriter.endSession(atSourceTime: CMTime(seconds: 15, preferredTimescale: 1000))
await assetWriter.finishWriting()
This issue was submitted to Apple as FB13420918. We also created a demonstration project for this issue on GitHub.
Perfect for app demos & presentations; Simply plug in an iPhone and it automatically shows up on your Mac.