1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/base/mac/coremedia_glue.h"
6
7#include <dlfcn.h>
8#import <Foundation/Foundation.h>
9
10#include "base/logging.h"
11#include "base/lazy_instance.h"
12
13namespace {
14
15// This class is used to retrieve some CoreMedia library functions. It must be
16// used as a LazyInstance so that it is initialised once and in a thread-safe
17// way. Normally no work is done in constructors: LazyInstance is an exception.
18class CoreMediaLibraryInternal {
19 public:
20  typedef CoreMediaGlue::CMTime (*CMTimeMakeMethod)(int64_t, int32_t);
21
22  typedef OSStatus (*CMBlockBufferCreateContiguousMethod)(
23      CFAllocatorRef,
24      CoreMediaGlue::CMBlockBufferRef,
25      CFAllocatorRef,
26      const CoreMediaGlue::CMBlockBufferCustomBlockSource*,
27      size_t,
28      size_t,
29      CoreMediaGlue::CMBlockBufferFlags,
30      CoreMediaGlue::CMBlockBufferRef*);
31  typedef size_t (*CMBlockBufferGetDataLengthMethod)(
32      CoreMediaGlue::CMBlockBufferRef);
33  typedef OSStatus (*CMBlockBufferGetDataPointerMethod)(
34      CoreMediaGlue::CMBlockBufferRef,
35      size_t,
36      size_t*,
37      size_t*,
38      char**);
39  typedef Boolean (*CMBlockBufferIsRangeContiguousMethod)(
40      CoreMediaGlue::CMBlockBufferRef,
41      size_t,
42      size_t);
43
44  typedef CoreMediaGlue::CMBlockBufferRef (*CMSampleBufferGetDataBufferMethod)(
45      CoreMediaGlue::CMSampleBufferRef);
46  typedef CoreMediaGlue::CMFormatDescriptionRef (
47      *CMSampleBufferGetFormatDescriptionMethod)(
48      CoreMediaGlue::CMSampleBufferRef);
49  typedef CVImageBufferRef (*CMSampleBufferGetImageBufferMethod)(
50      CoreMediaGlue::CMSampleBufferRef);
51  typedef CFArrayRef (*CMSampleBufferGetSampleAttachmentsArrayMethod)(
52      CoreMediaGlue::CMSampleBufferRef,
53      Boolean);
54
55  typedef FourCharCode (*CMFormatDescriptionGetMediaSubTypeMethod)(
56      CoreMediaGlue::CMFormatDescriptionRef desc);
57  typedef CoreMediaGlue::CMVideoDimensions
58      (*CMVideoFormatDescriptionGetDimensionsMethod)(
59          CoreMediaGlue::CMVideoFormatDescriptionRef videoDesc);
60  typedef OSStatus (*CMVideoFormatDescriptionGetH264ParameterSetAtIndexMethod)(
61      CoreMediaGlue::CMFormatDescriptionRef,
62      size_t,
63      const uint8_t**,
64      size_t*,
65      size_t*,
66      int*);
67
68  CoreMediaLibraryInternal() {
69    NSBundle* bundle = [NSBundle
70        bundleWithPath:@"/System/Library/Frameworks/CoreMedia.framework"];
71
72    const char* path = [[bundle executablePath] fileSystemRepresentation];
73    CHECK(path);
74    void* library_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
75    CHECK(library_handle) << dlerror();
76
77    // Now extract the methods.
78    cm_time_make_ = reinterpret_cast<CMTimeMakeMethod>(
79        dlsym(library_handle, "CMTimeMake"));
80    CHECK(cm_time_make_) << dlerror();
81
82    cm_block_buffer_create_contiguous_method_ =
83        reinterpret_cast<CMBlockBufferCreateContiguousMethod>(
84            dlsym(library_handle, "CMBlockBufferCreateContiguous"));
85    CHECK(cm_block_buffer_create_contiguous_method_) << dlerror();
86    cm_block_buffer_get_data_length_method_ =
87        reinterpret_cast<CMBlockBufferGetDataLengthMethod>(
88            dlsym(library_handle, "CMBlockBufferGetDataLength"));
89    CHECK(cm_block_buffer_get_data_length_method_) << dlerror();
90    cm_block_buffer_get_data_pointer_method_ =
91        reinterpret_cast<CMBlockBufferGetDataPointerMethod>(
92            dlsym(library_handle, "CMBlockBufferGetDataPointer"));
93    CHECK(cm_block_buffer_get_data_pointer_method_) << dlerror();
94    cm_block_buffer_is_range_contiguous_method_ =
95        reinterpret_cast<CMBlockBufferIsRangeContiguousMethod>(
96            dlsym(library_handle, "CMBlockBufferIsRangeContiguous"));
97    CHECK(cm_block_buffer_is_range_contiguous_method_) << dlerror();
98
99    cm_sample_buffer_get_data_buffer_method_ =
100        reinterpret_cast<CMSampleBufferGetDataBufferMethod>(
101            dlsym(library_handle, "CMSampleBufferGetDataBuffer"));
102    CHECK(cm_sample_buffer_get_data_buffer_method_) << dlerror();
103    cm_sample_buffer_get_format_description_method_ =
104        reinterpret_cast<CMSampleBufferGetFormatDescriptionMethod>(
105            dlsym(library_handle, "CMSampleBufferGetFormatDescription"));
106    CHECK(cm_sample_buffer_get_format_description_method_) << dlerror();
107    cm_sample_buffer_get_image_buffer_method_ =
108        reinterpret_cast<CMSampleBufferGetImageBufferMethod>(
109            dlsym(library_handle, "CMSampleBufferGetImageBuffer"));
110    CHECK(cm_sample_buffer_get_image_buffer_method_) << dlerror();
111    cm_sample_buffer_get_sample_attachments_array_method_ =
112        reinterpret_cast<CMSampleBufferGetSampleAttachmentsArrayMethod>(
113            dlsym(library_handle, "CMSampleBufferGetSampleAttachmentsArray"));
114    CHECK(cm_sample_buffer_get_sample_attachments_array_method_) << dlerror();
115    k_cm_sample_attachment_key_not_sync_ = reinterpret_cast<CFStringRef*>(
116        dlsym(library_handle, "kCMSampleAttachmentKey_NotSync"));
117    CHECK(k_cm_sample_attachment_key_not_sync_) << dlerror();
118
119    cm_format_description_get_media_sub_type_method_ =
120        reinterpret_cast<CMFormatDescriptionGetMediaSubTypeMethod>(
121            dlsym(library_handle, "CMFormatDescriptionGetMediaSubType"));
122    CHECK(cm_format_description_get_media_sub_type_method_) << dlerror();
123    cm_video_format_description_get_dimensions_method_ =
124        reinterpret_cast<CMVideoFormatDescriptionGetDimensionsMethod>(
125            dlsym(library_handle, "CMVideoFormatDescriptionGetDimensions"));
126    CHECK(cm_video_format_description_get_dimensions_method_) << dlerror();
127
128    // Available starting (OS X 10.9, iOS 7), allow to be null.
129    cm_video_format_description_get_h264_parameter_set_at_index_method_ =
130        reinterpret_cast<
131            CMVideoFormatDescriptionGetH264ParameterSetAtIndexMethod>(
132            dlsym(library_handle,
133                  "CMVideoFormatDescriptionGetH264ParameterSetAtIndex"));
134  }
135
136  const CMTimeMakeMethod& cm_time_make() const { return cm_time_make_; }
137
138  const CMBlockBufferCreateContiguousMethod&
139  cm_block_buffer_create_contiguous_method() const {
140    return cm_block_buffer_create_contiguous_method_;
141  }
142  const CMBlockBufferGetDataLengthMethod&
143  cm_block_buffer_get_data_length_method() const {
144    return cm_block_buffer_get_data_length_method_;
145  }
146  const CMBlockBufferGetDataPointerMethod&
147  cm_block_buffer_get_data_pointer_method() const {
148    return cm_block_buffer_get_data_pointer_method_;
149  }
150  const CMBlockBufferIsRangeContiguousMethod&
151  cm_block_buffer_is_range_contiguous_method() const {
152    return cm_block_buffer_is_range_contiguous_method_;
153  }
154
155  const CMSampleBufferGetDataBufferMethod&
156  cm_sample_buffer_get_data_buffer_method() const {
157    return cm_sample_buffer_get_data_buffer_method_;
158  }
159  const CMSampleBufferGetFormatDescriptionMethod&
160  cm_sample_buffer_get_format_description_method() const {
161    return cm_sample_buffer_get_format_description_method_;
162  }
163  const CMSampleBufferGetImageBufferMethod&
164      cm_sample_buffer_get_image_buffer_method() const {
165    return cm_sample_buffer_get_image_buffer_method_;
166  }
167  const CMSampleBufferGetSampleAttachmentsArrayMethod&
168  cm_sample_buffer_get_sample_attachments_array_method() const {
169    return cm_sample_buffer_get_sample_attachments_array_method_;
170  }
171  CFStringRef* const& k_cm_sample_attachment_key_not_sync() const {
172    return k_cm_sample_attachment_key_not_sync_;
173  }
174
175  const CMFormatDescriptionGetMediaSubTypeMethod&
176      cm_format_description_get_media_sub_type_method() const {
177    return cm_format_description_get_media_sub_type_method_;
178  }
179  const CMVideoFormatDescriptionGetDimensionsMethod&
180      cm_video_format_description_get_dimensions_method() const {
181    return cm_video_format_description_get_dimensions_method_;
182  }
183  const CMVideoFormatDescriptionGetH264ParameterSetAtIndexMethod&
184  cm_video_format_description_get_h264_parameter_set_at_index_method() const {
185    return cm_video_format_description_get_h264_parameter_set_at_index_method_;
186  }
187
188 private:
189  CMTimeMakeMethod cm_time_make_;
190
191  CMBlockBufferCreateContiguousMethod cm_block_buffer_create_contiguous_method_;
192  CMBlockBufferGetDataLengthMethod cm_block_buffer_get_data_length_method_;
193  CMBlockBufferGetDataPointerMethod cm_block_buffer_get_data_pointer_method_;
194  CMBlockBufferIsRangeContiguousMethod
195      cm_block_buffer_is_range_contiguous_method_;
196
197  CMSampleBufferGetDataBufferMethod cm_sample_buffer_get_data_buffer_method_;
198  CMSampleBufferGetFormatDescriptionMethod
199      cm_sample_buffer_get_format_description_method_;
200  CMSampleBufferGetImageBufferMethod cm_sample_buffer_get_image_buffer_method_;
201  CMSampleBufferGetSampleAttachmentsArrayMethod
202      cm_sample_buffer_get_sample_attachments_array_method_;
203  CFStringRef* k_cm_sample_attachment_key_not_sync_;
204
205  CMFormatDescriptionGetMediaSubTypeMethod
206      cm_format_description_get_media_sub_type_method_;
207  CMVideoFormatDescriptionGetDimensionsMethod
208      cm_video_format_description_get_dimensions_method_;
209  CMVideoFormatDescriptionGetH264ParameterSetAtIndexMethod
210      cm_video_format_description_get_h264_parameter_set_at_index_method_;
211
212  DISALLOW_COPY_AND_ASSIGN(CoreMediaLibraryInternal);
213};
214
215}  // namespace
216
217static base::LazyInstance<CoreMediaLibraryInternal> g_coremedia_handle =
218    LAZY_INSTANCE_INITIALIZER;
219
220// static
221CoreMediaGlue::CMTime CoreMediaGlue::CMTimeMake(int64_t value,
222                                                int32_t timescale) {
223  return g_coremedia_handle.Get().cm_time_make()(value, timescale);
224}
225
226// static
227OSStatus CoreMediaGlue::CMBlockBufferCreateContiguous(
228    CFAllocatorRef structureAllocator,
229    CMBlockBufferRef sourceBuffer,
230    CFAllocatorRef blockAllocator,
231    const CMBlockBufferCustomBlockSource* customBlockSource,
232    size_t offsetToData,
233    size_t dataLength,
234    CMBlockBufferFlags flags,
235    CMBlockBufferRef* newBBufOut) {
236  return g_coremedia_handle.Get().cm_block_buffer_create_contiguous_method()(
237      structureAllocator,
238      sourceBuffer,
239      blockAllocator,
240      customBlockSource,
241      offsetToData,
242      dataLength,
243      flags,
244      newBBufOut);
245}
246
247// static
248size_t CoreMediaGlue::CMBlockBufferGetDataLength(CMBlockBufferRef theBuffer) {
249  return g_coremedia_handle.Get().cm_block_buffer_get_data_length_method()(
250      theBuffer);
251}
252
253// static
254OSStatus CoreMediaGlue::CMBlockBufferGetDataPointer(CMBlockBufferRef theBuffer,
255                                                    size_t offset,
256                                                    size_t* lengthAtOffset,
257                                                    size_t* totalLength,
258                                                    char** dataPointer) {
259  return g_coremedia_handle.Get().cm_block_buffer_get_data_pointer_method()(
260      theBuffer, offset, lengthAtOffset, totalLength, dataPointer);
261}
262
263// static
264Boolean CoreMediaGlue::CMBlockBufferIsRangeContiguous(
265    CMBlockBufferRef theBuffer,
266    size_t offset,
267    size_t length) {
268  return g_coremedia_handle.Get().cm_block_buffer_is_range_contiguous_method()(
269      theBuffer, offset, length);
270}
271
272// static
273CoreMediaGlue::CMBlockBufferRef CoreMediaGlue::CMSampleBufferGetDataBuffer(
274    CMSampleBufferRef sbuf) {
275  return g_coremedia_handle.Get().cm_sample_buffer_get_data_buffer_method()(
276      sbuf);
277}
278
279// static
280CoreMediaGlue::CMFormatDescriptionRef
281CoreMediaGlue::CMSampleBufferGetFormatDescription(
282    CoreMediaGlue::CMSampleBufferRef sbuf) {
283  return g_coremedia_handle.Get()
284      .cm_sample_buffer_get_format_description_method()(sbuf);
285}
286
287// static
288CVImageBufferRef CoreMediaGlue::CMSampleBufferGetImageBuffer(
289    CMSampleBufferRef buffer) {
290  return g_coremedia_handle.Get().cm_sample_buffer_get_image_buffer_method()(
291      buffer);
292}
293
294// static
295CFArrayRef CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray(
296    CMSampleBufferRef sbuf,
297    Boolean createIfNecessary) {
298  return g_coremedia_handle.Get()
299      .cm_sample_buffer_get_sample_attachments_array_method()(
300          sbuf, createIfNecessary);
301}
302
303// static
304CFStringRef CoreMediaGlue::kCMSampleAttachmentKey_NotSync() {
305  return *g_coremedia_handle.Get().k_cm_sample_attachment_key_not_sync();
306}
307
308// static
309FourCharCode CoreMediaGlue::CMFormatDescriptionGetMediaSubType(
310      CMFormatDescriptionRef desc) {
311  return g_coremedia_handle.Get()
312      .cm_format_description_get_media_sub_type_method()(desc);
313}
314
315// static
316CoreMediaGlue::CMVideoDimensions
317    CoreMediaGlue::CMVideoFormatDescriptionGetDimensions(
318        CMVideoFormatDescriptionRef videoDesc) {
319  return g_coremedia_handle.Get()
320      .cm_video_format_description_get_dimensions_method()(videoDesc);
321}
322
323// static
324OSStatus CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
325    CMFormatDescriptionRef videoDesc,
326    size_t parameterSetIndex,
327    const uint8_t** parameterSetPointerOut,
328    size_t* parameterSetSizeOut,
329    size_t* parameterSetCountOut,
330    int* NALUnitHeaderLengthOut) {
331  return g_coremedia_handle.Get()
332      .cm_video_format_description_get_h264_parameter_set_at_index_method()(
333          videoDesc,
334          parameterSetIndex,
335          parameterSetPointerOut,
336          parameterSetSizeOut,
337          parameterSetCountOut,
338          NALUnitHeaderLengthOut);
339}
340