返回介绍

时间和媒体的表示

发布于 2024-10-04 21:59:41 字数 21164 浏览 0 评论 0 收藏 0

AV Foundation 框架中用 AVAsset 表示基于时间的视听数据, 比如电影文件或视频流. AVAsset 的结构决定了 AV Foundation 框架大部分的工作方式. AV Foundation 框架中使用的一些用来代表时间和媒体的底层数据结构来源于 Core Media 框架.

Asset的表示

AVAsset 是 AV Foundation 框架的核心关键类, 它提供了对视听数据的格式无关的抽象. 类之间的关系如下图所示. 大部分情况下, 使用的都是这些类的子类: 使用 composition 的子类创建新的 asset, 使用AVURLAsset根据一个指定的 URL 创建 asset.

一个 asset 包含一组 track, 每个 track 都有特定媒体类型, 包括但不限于 audio, video, text, closed captions 以及 subtitles. Asset 对象提供整个资源的信息, 比如时长和标题. Asset 对象也可能包含元数据 (metadata), metadata 由 AVMetadataItem 类表示.

如下图所示, 一个 track 由 AVAssetTrack 类表示. 简单场景下, 一个 track 代表 audio component, 另一个 track 代表 video component; 复杂场景下, 可能有多个 audio 和 video 重叠的 track.

一个 track 包含多个属性, 比如类型 (video or audio), 视觉或听觉特性, 元数据, 以及时间轴 (表现在其父 asset 中). 此外, track 还包含一个描述格式的数组. 这个数组中的元素为CMFormatDescription对象 (参见 CMFormatDescriptionRef), 用来描述 track 包含的媒体格式信息.

一个 track 可能被分为几段, 每一段由一个 AVAssetTrackSegment 对象表示. 一个AVAssetTrackSegment对象就是一个由资源数据到 track 时间轴的映射.

时间的表示

AV Foundation 框架中的时间由一个 Core Media 框架中的数据结构表示.

用 CMTime 表示一段时间

CMTime 是一个以有理数表示时间的 C 语言结构体, 用一个int64_t类型作为分子, 一个int32_t类型作为分母. 从概念上来看, 时间段 (timescale) 描述了一秒中包含多少个时间单元. 如果 timescale 等于 4, 则每个时间单元代表四分之一秒; 果 timescale 等于 10, 则每个时间单元代表十分之一秒, 以此类推.

除了用来表示时间, CMTime还可以用来表示非数值的值: 正无穷大, 负无穷大, 不确定.

使用 CMTime

使用方法 CMTimeMake 或者 CMTimeMakeWithSeconds 创建一个时间.

CMTime time1 = CMTimeMake(200, 2); // 200 half-seconds
CMTime time2 = CMTimeMake(400, 4); // 400 quarter-seconds

// time1 and time2 both represent 100 seconds, but using different timescales.
if (CMTimeCompare(time1, time2) == 0) {
    NSLog(@"time1 and time2 are the same");
}

Float64 float64Seconds = 200.0 / 3;
CMTime time3 = CMTimeMakeWithSeconds(float64Seconds , 3); // 66.66... third-seconds
time3 = CMTimeMultiply(time3, 3);
// time3 now represents 200 seconds; next subtract time1 (100 seconds).
time3 = CMTimeSubtract(time3, time1);
CMTimeShow(time3);

if (CMTIME_COMPARE_INLINE(time2, ==, time3)) {
    NSLog(@"time2 and time3 are the same");
}

更多详细信息参见 CMTime Reference.

CMTime 的特殊值

Core Media 框架提供了一些常量: kCMTimeZero, kCMTimeInvalid,kCMTimePositiveInfinitykCMTimeNegativeInfinity. CMTime结构体能够进行很多操作, 比如要判断一个时间是否有效, 可以使用一些定义好的宏, 例如 CMTIME_IS_INVALID, CMTIME_IS_POSITIVE_INFINITY 或者 CMTIME_IS_INDEFINITE.

CMTime myTime = <#Get a CMTime#>;
if (CMTIME_IS_INVALID(myTime)) {
    // Perhaps treat this as an error; display a suitable alert to the user.
}

不能将 CMTime 结构体与kCMTimeZero直接进行比较.

将 CMTime 转换为对象

如果要在注释或者Core Foundation容器中使用 CMTime, 使用方法 CMTimeCopyAsDictionaryCMTimeMakeFromDictionary 可以在 CMTime 结构体和CFDictionary类型 (参见 CFDictionaryRef) 之间进行相互转换. 使用方法 CMTimeCopyDescription 可以获取 CMTime 结构体的字符串描述.

循环次数 (Epochs)

CMTime结构体中的 epoch 通常被设置为 0. 但是你可以使用这个值来区分不同循环次数中的同一个时间点.

用 CMTimeRange 表示一个时间范围

CMTimeRange 是一个 C 语言结构体, 包含两个CMTime类型的属性: 起始时间start和时长duration. 一个时间范围并不包含start加上duration得到的时间.

使用方法 CMTimeRangeMakeCMTimeRangeFromTimeToTime 创建一个时间范围, 但是存在一些限制:

  • CMTimeRange不能跨过epoch
  • 只能对startepoch值相同的 CMTimeRange 进行相互操作
  • durationepoch值应该一直为 0, startepoch值为非负

处理 CMTimeRange

Core Media 框架提供了一个时间范围是否包含某个时间点或者其他时间范围的方法, 以及判断两个时间范围是否相同, 对两个时间范围进行交集和并集运算的方法. 例如, CMTimeRangeContainsTime, CMTimeRangeEqual, CMTimeRangeContainsTimeRangeCMTimeRangeGetUnion.

注意下面的表达式永远返回 false:

CMTimeRangeContainsTime(range, CMTimeRangeGetEnd(range))

更多相关的详细信息, 参见 CMTimeRange Reference.

CMTimeRange 的特殊值

Core Media 提供了一个表示空范围的常量和一个表示无效范围的常量: kCMTimeRangeZerokCMTimeRangeInvalid. 可以使用以下这些宏对 CMTimeRange 的特殊值进行判断: CMTIMERANGE_IS_VALID, CMTIMERANGE_IS_INVALID, CMTIMERANGE_IS_EMPTY, CMTIMERANGE_IS_EMPTY.

CMTimeRange myTimeRange = <#Get a CMTimeRange#>;
if (CMTIMERANGE_IS_EMPTY(myTimeRange)) {
    // The time range is zero.
}

不能将 CMTimeRange 结构体与kCMTimeRangeInvalid直接进行比较.

将 CMTimeRange 转换为对象

如果要在注释或者Core Foundation容器中使用 CMTimeRange, 使用方法 CMTimeRangeCopyAsDictionaryCMTimeRangeMakeFromDictionary 可以在 CMTimeRange 结构体和CFDictionary类型 (参见 CFDictionaryRef) 之间进行相互转换. 使用方法 CMTimeRangeCopyDescription 可以获取 CMTimeRange 结构体的字符串描述.

媒体的表示

视频数据和与其相关联的元数据都使用 Core Media 框架中的对象类型来表示. Core Media 使用CMSampleBuffer(参见 CMSampleBufferRef) 类型表示视频数据. 一个CMSampleBuffer对象是一个包含了视频数据帧的 sample buffer, 用来作为 Core Video pixel buffer(参见 CVPixelBufferRef). 可以使用 CMSampleBufferGetImageBuffer 方法访问 sample buffer 中的 pixel buffer.

CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(<#A CMSampleBuffer#>);

可以在 pixel buffer 访问到实际的视频数据, 参见下文.

此外, 对于视频数据而言, 还可以视频帧其他方面的信息:

  • 时间信息: 使用 CMSampleBufferGetPresentationTimeStampCMSampleBufferGetDecodeTimeStamp 可以分别获取视频帧的初始时间和解码时间
  • 格式信息: 包含在一个CMFormatDescription对象中 (参见 CMFormatDescriptionRef). 通过格式描述, 可以使用CMVideoFormatDescriptionGetCodecType获取视频的编码信息, 使用CMVideoFormatDescriptionGetDimensions获取视频尺寸
  • 元数据: 以附件形式存储在一个字典中, 通过 CMGetAttachment 获取:

    CMSampleBufferRef sampleBuffer = <#Get a sample buffer#>; CFDictionaryRef metadataDictionary =

    ``` CMGetAttachment(sampleBuffer, CFSTR("MetadataDictionary", NULL);

    if (metadataDictionary) {
  // Do something with the metadata.

}


## 将CMSampleBuffer转换为UIImage <a id="&#x5C06;cmsamplebuffer&#x8F6C;&#x6362;&#x4E3A;uiimage"></a>

下面的代码示例了如何将`CMSampleBuffer`转换为`UIImage`. 这个转换相当消耗性能, 使用时必须进行谨慎考虑.

​```text
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
    // Get a CMSampleBuffer's Core Video image buffer for the media data
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    // Lock the base address of the pixel buffer
    CVPixelBufferLockBaseAddress(imageBuffer, 0);

    // Get the number of bytes per row for the pixel buffer
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);

    // Get the number of bytes per row for the pixel buffer
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
    // Get the pixel buffer width and height
    size_t width = CVPixelBufferGetWidth(imageBuffer);
    size_t height = CVPixelBufferGetHeight(imageBuffer);

    // Create a device-dependent RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    // Create a bitmap graphics context with the sample buffer data
    CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,
      bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    // Create a Quartz image from the pixel data in the bitmap graphics context
    CGImageRef quartzImage = CGBitmapContextCreateImage(context);
    // Unlock the pixel buffer
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);

    // Free up the context and color space
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    // Create an image object from the Quartz image
    UIImage *image = [UIImage imageWithCGImage:quartzImage];

    // Release the Quartz image
    CGImageRelease(quartzImage);

    return (image);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文