我正在从一堆静态图像创建视频(QuickTime .mov 格式,H.264 编码),并且我想在此过程中添加章节轨道。 视频创建得很好,我没有检测到任何错误,但 QuickTime Player 不显示任何章节。 我知道这个问题,但它并不能解决我的问题。
旧版 QuickTime Player 7 与最新版本不同,可以显示有关电影曲目的信息。 当我打开带有工作章节的电影(使用旧的 QuickTime 代码创建)时,我会看到一个视频轨道和一个文本轨道,并且视频轨道知道文本轨道正在为视频提供章节。 然而,如果我检查由我的新代码创建的电影,会发现视频轨道和元数据轨道一起存在,但 QuickTime 不知道元数据轨道应该提供章节。 我读过的东西让我相信应该在章节中使用元数据,但有人真正做到这一点吗? 文本轨道有用吗?
这是我为元数据创建
AVAssetWriterInput
的方法。
// Make dummy AVMetadataItem to get its format
AVMutableMetadataItem* dummyMetaItem = [AVMutableMetadataItem metadataItem];
dummyMetaItem.identifier = AVMetadataIdentifierQuickTimeUserDataChapter;
dummyMetaItem.dataType = (NSString*) kCMMetadataBaseDataType_UTF8;
dummyMetaItem.value = @"foo";
AVTimedMetadataGroup* dummyGroup = [[[AVTimedMetadataGroup alloc]
initWithItems: @[dummyMetaItem]
timeRange: CMTimeRangeMake( kCMTimeZero, kCMTimeInvalid )] autorelease];
CMMetadataFormatDescriptionRef metaFmt = [dummyGroup copyFormatDescription];
// Make the input
AVAssetWriterInput* metaWriterInput = [AVAssetWriterInput
assetWriterInputWithMediaType: AVMediaTypeMetadata
outputSettings: nil
sourceFormatHint: metaFmt];
CFRelease( metaFmt );
// Associate metadata input with video input
[videoInput addTrackAssociationWithTrackOfInput: metaWriterInput
type: AVTrackAssociationTypeChapterList];
// Associate metadata input with AVAssetWriter
[writer addInput: metaWriterInput];
// Create a metadata adaptor
AVAssetWriterInputMetadataAdaptor* metaAdaptor = [AVAssetWriterInputMetadataAdaptor
assetWriterInputMetadataAdaptorWithAssetWriterInput: metaWriterInput];
附注我尝试使用文本轨道(
AVAssetWriterInput
,类型为 AVMediaTypeText
),QuickTime Player 表示结果“不是电影”。不知道我做错了什么。
我设法使用文本轨道来提供章节。 我经历了一次苹果开发者技术支持事件,并被告知这是正确的做法。
我假设
AVAssetWriter
已创建,并且已为其分配了视频轨道的 AVAssetWriterInput
。
这里最棘手的部分是创建文本格式描述。 文档说
CMTextFormatDescriptionCreateFromBigEndianTextDescriptionData
将 TextDescription
结构作为输入,但忽略了该结构的定义位置。 它位于 Movies.h 中,而 Movies.h 位于 QuickTime.framework 中,而 QuickTime.framework 不再是 Mac OS SDK 的一部分。 谢谢,苹果。
// Create AVAssetWriterInput
AVAssetWriterInput* textWriterInput = [AVAssetWriterInput
assetWriterInputWithMediaType: AVMediaTypeText
outputSettings: nil ];
textWriterInput.marksOutputTrackAsEnabled = NO;
// Connect input to writer
[writer addInput: textWriterInput];
// Mark the text track as providing chapter for the video
[videoWriterInput addTrackAssociationWithTrackOfInput: textWriterInput
type: AVTrackAssociationTypeChapterList];
// Create the text format description, which we will need
// when creating each sample.
CMFormatDescriptionRef textFmt = NULL;
TextDescription textDesc;
memset( &textDesc, 0, sizeof(textDesc) );
textDesc.descSize = OSSwapHostToBigInt32( sizeof(textDesc) );
textDesc.dataFormat = OSSwapHostToBigInt32( 'text' );
CMTextFormatDescriptionCreateFromBigEndianTextDescriptionData( NULL,
(const uint8_t*)&textDesc, sizeof(textDesc), NULL, kCMMediaType_Text,
&textFmt );
CMSampleTimingInfo timing =
{
CMTimeMakeWithSeconds( endTime - startTime, timeScale ), // duration
CMTimeMakeWithSeconds( startTime, timeScale ),
kCMTimeInvalid
};
CMSampleBufferRef textSample = NULL;
CMPSampleBufferCreateWithText( NULL, (CFStringRef)theTitle, true, NULL, NULL,
textFmt, &timing, &textSample );
[textWriterInput appendSampleBuffer: textSample];
函数
CMPSampleBufferCreateWithText
取自开源CoreMediaPlus。
如果您不想使用 CMPSampleBufferCreateWithText,您可以自行推出类似于 https://yhf8377.medium.com/quicktime-movie-editing-using-avfoundation-and-swift-5-33965c522abc (代码流程为任何语言都基本相同)
但我写在这里的真正原因是参考这个 How to create a CMBlockBufferRef from NSData
数据所有权并不十分明显,因为稍后可能会在另一个作用域中写入相同的 buf。因此,如果您构建了一个 NSData,它可能会在写入示例缓冲区之前被释放,这最终会导致一个没有标题的章节。在这方面花了很长时间,但您需要传递所有权,因为数据是引用的而不是复制的。