Ocean
Loading...
Searching...
No Matches
avfoundation/VideoEncoder.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef META_OCEAN_MEDIA_AVF_VIDEO_ENCODER_H
9#define META_OCEAN_MEDIA_AVF_VIDEO_ENCODER_H
10
12
13#include "ocean/base/Frame.h"
14#include "ocean/base/Lock.h"
15
16#include "ocean/math/Numeric.h"
17
18#include <VideoToolbox/VideoToolbox.h>
19
20#include <deque>
21
22namespace Ocean
23{
24
25namespace Media
26{
27
28namespace AVFoundation
29{
30
31/**
32 * This class implements a simple video encoder for iOS/macOS using Ocean::Frame objects as input.
33 * The encoder uses Apple's VideoToolbox framework (VTCompressionSession) for hardware-accelerated encoding.
34 *
35 * Usage:
36 * @code
37 * // a function which is e.g., running in a separate thread
38 * void threadRun()
39 * {
40 * VideoEncoder videoEncoder;
41 *
42 * if (!videoEncoder.initialize(1920u, 1080u))
43 * {
44 * // handle error
45 * }
46 *
47 * if (!videoEncoder.start())
48 * {
49 * // handle error
50 * }
51 *
52 * unsigned int frameIndex = 0u;
53 * double frameRate = 30.0;
54 *
55 * while (true)
56 * {
57 * Frame frame;
58 *
59 * // external function: function needs to provide frames from an external source - e.g., from a camera, a video stream, etc.
60 * if (doesNewFrameExist(frame))
61 * {
62 * // presentation time in microseconds
63 * uint64_t presentationTime = uint64_t(1.0e6 * double(frameIndex) / frameRate);
64 *
65 * // we forward the frame to the encoder, eventually it will be encoded and will be available through popSample()
66 * if (!videoEncoder.pushFrame(frame, presentationTime))
67 * {
68 * // handle error
69 * }
70 *
71 * ++frameIndex;
72 * }
73 *
74 * // we simply check whether another sample has been encoded
75 * VideoEncoder::Sample encodedSample = videoEncoder.popSample();
76 *
77 * if (encodedSample.isValid())
78 * {
79 * // external function: receiving encoded samples and processes them
80 * sendSampleToReceiver(std::move(encodedSample));
81 * }
82 * }
83 * }
84 * @endcode
85 * @ingroup mediaavf
86 */
88{
89 public:
90
91 /// Definition of a 1 Mbps bit rate
92 static constexpr int bitrateMbps1_ = 1000 * 1000;
93
94 /// Definition of a 2 Mbps bit rate
95 static constexpr int bitrateMbps2_ = bitrateMbps1_ * 2;
96
97 /// Definition of a 5 Mbps bit rate
98 static constexpr int bitrateMbps5_ = bitrateMbps1_ * 5;
99
100 /// Definition of a 10 Mbps bit rate
101 static constexpr int bitrateMbps10_ = bitrateMbps1_ * 10;
102
103 /**
104 * Definition of individual buffer flag constants.
105 * Modeled after Android's MediaCodec.BufferInfo for API compatibility.
106 */
107 enum BufferFlags : uint32_t
108 {
109 /// The buffer has no special property.
111 /// Indicates that the (encoded) buffer marked as such contains the data for a key frame.
113 /// Indicates that the buffer marked as such contains codec initialization / codec specific data instead of media data.
115 /// Indicates that the buffer is the last buffer in the stream.
117 /// Indicates that the buffer only contains part of a frame.
119 };
120
121 /**
122 * Definition of an encoded sample.
123 */
124 class Sample
125 {
126 friend class VideoEncoder;
127
128 public:
129
130 /**
131 * Creates an invalid sample.
132 */
133 Sample() = default;
134
135 /**
136 * Move constructor.
137 * @param sample The sample to be moved
138 */
139 inline Sample(Sample&& sample) noexcept;
140
141 /**
142 * Returns whether this sample is valid.
143 * @return True, if so
144 */
145 inline bool isValid() const;
146
147 /**
148 * Returns the encoded data.
149 * @return The encoded data
150 */
151 inline const std::vector<uint8_t>& data() const;
152
153 /**
154 * Returns the presentation time in microseconds.
155 * @return The presentation time
156 */
157 inline int64_t presentationTime() const;
158
159 /**
160 * Returns whether this sample is a key frame.
161 * @return True, if so
162 */
163 inline bool isKeyFrame() const;
164
165 /**
166 * Returns whether this sample contains codec configuration data instead of media data.
167 * @return True, if so
168 */
169 inline bool isConfiguration() const;
170
171 /**
172 * Returns whether this sample marks the end of the stream.
173 * @return True, if so
174 */
175 inline bool isEndOfStream() const;
176
177 /**
178 * Returns whether this sample contains only part of a frame.
179 * @return True, if so
180 */
181 inline bool isPartialFrame() const;
182
183 /**
184 * Move operator.
185 * @param sample The sample to be moved
186 * @return Reference to this object
187 */
188 inline Sample& operator=(Sample&& sample) noexcept;
189
190 /**
191 * Returns whether this sample is valid.
192 * @return True, if so
193 */
194 inline explicit operator bool() const;
195
196 protected:
197
198 /**
199 * Creates a sample with specified data.
200 * @param data The encoded data, will be moved
201 * @param presentationTime The presentation time in microseconds, with range [0, infinity)
202 * @param bufferFlags The buffer flags of the sample
203 */
204 inline Sample(std::vector<uint8_t>&& data, const int64_t presentationTime, const BufferFlags bufferFlags);
205
206 /**
207 * Disabled copy constructor.
208 */
209 Sample(const Sample&) = delete;
210
211 /**
212 * Disabled copy operator.
213 * @return Reference to this object
214 */
215 Sample& operator=(const Sample&) = delete;
216
217 protected:
218
219 /// The encoded data.
220 std::vector<uint8_t> data_;
221
222 /// The presentation time in microseconds.
224
225 /// The buffer flags.
227 };
228
229 /**
230 * Definition of a vector holding sample objects.
231 */
232 using Samples = std::vector<Sample>;
233
234 protected:
235
236 /// Definition of the maximal image width.
237 static constexpr unsigned int maximalWidth_ = 1920u * 8u;
238
239 /// Definition of the maximal image height.
240 static constexpr unsigned int maximalHeight_ = 1080u * 8u;
241
242 /// Definition of the maximal bit rate.
243 static constexpr int maximalBitrate_ = bitrateMbps10_ * 10;
244
245 /**
246 * Release function for VTCompressionSessionRef that invalidates and releases the session.
247 * @param session The session to release
248 */
249 static inline void releaseVTCompressionSession(VTCompressionSessionRef session);
250
251 /**
252 * Definition of a scoped object holding a VTCompressionSessionRef object.
253 * The wrapped VTCompressionSessionRef object will be invalidated and released automatically once the scoped object does not exist anymore.
254 */
256
257 public:
258
259 /**
260 * Default constructor creating an un-initialized encoder.
261 */
263
264 /**
265 * Move constructor.
266 * @param videoEncoder The encoder to be moved
267 */
268 inline VideoEncoder(VideoEncoder&& videoEncoder) noexcept;
269
270 /**
271 * Destructs the video encoder and releases all associated resources.
272 */
274
275 /**
276 * Initializes the video encoder with the specified configuration.
277 * @param width The width of the video to be encoded, in pixel, with range [1, infinity)
278 * @param height The height of the video to be encoded, in pixel, with range [1, infinity)
279 * @param mime The MIME type (Multipurpose Internet Mail Extensions) of the video to be encoded, e.g., "video/avc", "video/hevc", ...
280 * @param frameRate The target frame rate in frames per second, with range (0, infinity), e.g., 30.0
281 * @param bitrate The target bitrate in bits per second, with range [1, infinity), e.g., 5000000 for 5 Mbps
282 * @param iFrameInterval The interval between I-frames (key frames) in seconds: negative value = no key frames after first frame, 0 = all frames are key frames, positive value = key frames every N seconds
283 * @return True, if succeeded
284 * @see isInitialized().
285 */
286 bool initialize(const unsigned int width, const unsigned int height, const std::string& mime = "video/avc", const double frameRate = 30.0, const unsigned int bitrate = bitrateMbps2_, const int iFrameInterval = 1);
287
288 /**
289 * Starts the video encoder.
290 * @return True, if succeeded
291 * @see isStarted().
292 */
293 bool start();
294
295 /**
296 * Stops the video encoder.
297 * @return True, if succeeded
298 */
299 bool stop();
300
301 /**
302 * Adds a new frame which needs to be encoded to the video encoder.
303 * The encoder needs to be initialized and started.
304 * The presentation time is mainly intended to allow associating the provided frame with the resulting encoded sample when calling popSample().
305 * However, it's recommended to define a reasonable presentation time for each frame (e.g., let the first frame start at 0 and increment the time by 1^6/fps for each following frame).
306 * @param frame The frame to be encoded, must be valid
307 * @param presentationTime The presentation time of the frame, in microseconds, with range [0, infinity)
308 * @return True, if succeeded
309 * @see start(), isInitialized(), isStarted().
310 */
311 bool pushFrame(const Frame& frame, const uint64_t presentationTime);
312
313 /**
314 * Returns the next encoded sample if available.
315 * @return The resulting encoded sample, invalid if currently no encoded sample is available
316 * @see pushFrame().
317 */
319
320 /**
321 * Returns whether this encoder is initialized.
322 * @return True, if so
323 * @see initialize().
324 */
325 inline bool isInitialized() const;
326
327 /**
328 * Returns whether this encoder is currently running.
329 * @return True, if so
330 * @see start().
331 */
332 inline bool isStarted() const;
333
334 /**
335 * Explicitly releases this video encoder.
336 * If the encoder is still running, the encoder will be stopped as well.
337 */
338 void release();
339
340 /**
341 * Move operator.
342 * @param videoEncoder The video encoder to be moved
343 * @return Reference to this object
344 */
345 inline VideoEncoder& operator=(VideoEncoder&& videoEncoder) noexcept;
346
347 protected:
348
349 /**
350 * Definition of a scoped object holding a CFNumberRef object.
351 * The wrapped CFNumberRef object will be released automatically once the scoped object does not exist anymore.
352 */
354
355 /**
356 * Disabled copy constructor.
357 */
358 VideoEncoder(const VideoEncoder&) = delete;
359
360 /**
361 * Disabled copy operator.
362 * @return Reference to this object
363 */
365
366 /**
367 * Callback function for encoded samples from VideoToolbox.
368 * @param outputCallbackRefCon Reference to this encoder
369 * @param sourceFrameRefCon Reference containing the presentation time
370 * @param status The status of the compression operation
371 * @param infoFlags Information flags
372 * @param sampleBuffer The encoded sample buffer, may be nullptr on error
373 */
374 static void compressionOutputCallback(void* outputCallbackRefCon, void* sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer);
375
376 /**
377 * Translates a MIME type to a CMVideoCodecType.
378 * @param mime The MIME type
379 * @return The corresponding codec type, 0 if not supported
380 */
381 static CMVideoCodecType mimeToCodecType(const std::string& mime);
382
383 protected:
384
385 /// The compression session.
387
388 /// The queue of encoded samples.
389 std::deque<Sample> encodedSamples_;
390
391 /// The width of the video.
392 unsigned int width_ = 0u;
393
394 /// The height of the video.
395 unsigned int height_ = 0u;
396
397 /// True, if the encoder is currently started.
398 bool isStarted_ = false;
399
400 /// The encoder's lock.
401 mutable Lock lock_;
402
403 /// The lock for the encoded samples queue.
405
406#ifdef OCEAN_DEBUG
407 /// The previous presentation timestamp submitted via pushFrame(), in microseconds, -1 if no frame has been submitted yet.
409
410 /// The previous presentation timestamp of an encoded sample in the compression callback, in microseconds, NumericT<int64_t>::minValue() if no sample has been encoded yet.
412#endif
413};
414
415inline VideoEncoder::Sample::Sample(std::vector<uint8_t>&& data, const int64_t presentationTime, const BufferFlags bufferFlags) :
416 data_(std::move(data)),
417 presentationTime_(presentationTime),
418 bufferFlags_(bufferFlags)
419{
420 // nothing to do here
421}
422
423inline VideoEncoder::Sample::Sample(Sample&& sample) noexcept
424{
425 *this = std::move(sample);
426}
427
429{
430 return !data_.empty();
431}
432
433inline const std::vector<uint8_t>& VideoEncoder::Sample::data() const
434{
435 return data_;
436}
437
439{
440 return presentationTime_;
441}
442
444{
445 return bufferFlags_ & BUFFER_FLAG_KEY_FRAME;
446}
447
449{
450 return bufferFlags_ & BUFFER_FLAG_CODEC_CONFIG;
451}
452
454{
455 return bufferFlags_ & BUFFER_FLAG_END_OF_STREAM;
456}
457
459{
460 return bufferFlags_ & BUFFER_FLAG_PARTIAL_FRAME;
461}
462
464{
465 if (this != &sample)
466 {
467 data_ = std::move(sample.data_);
468 presentationTime_ = sample.presentationTime_;
469 bufferFlags_ = sample.bufferFlags_;
470
471 sample.presentationTime_ = NumericT<int64_t>::minValue();
472 sample.bufferFlags_ = BUFFER_FLAG_NONE;
473 }
474
475 return *this;
476}
477
478inline VideoEncoder::Sample::operator bool() const
479{
480 return isValid();
481}
482
484{
485 if (session != nullptr)
486 {
487 VTCompressionSessionInvalidate(session);
488 CFRelease(session);
489 }
490}
491
492inline VideoEncoder::VideoEncoder(VideoEncoder&& videoEncoder) noexcept
493{
494 *this = std::move(videoEncoder);
495}
496
498{
499 const ScopedLock scopedLock(lock_);
500
502}
503
504inline bool VideoEncoder::isStarted() const
505{
506 const ScopedLock scopedLock(lock_);
507
508 ocean_assert(!isStarted_ || isInitialized());
509
510 return isStarted_;
511}
512
513inline VideoEncoder& VideoEncoder::operator=(VideoEncoder&& videoEncoder) noexcept
514{
515 if (this != &videoEncoder)
516 {
517 release();
518
519 compressionSession_ = std::move(videoEncoder.compressionSession_);
520
521 encodedSamples_ = std::move(videoEncoder.encodedSamples_);
522
523 width_ = videoEncoder.width_;
524 videoEncoder.width_ = 0u;
525
526 height_ = videoEncoder.height_;
527 videoEncoder.height_ = 0u;
528
529 isStarted_ = videoEncoder.isStarted_;
530 videoEncoder.isStarted_ = false;
531
532#ifdef OCEAN_DEBUG
533 debugPreviousSubmittedTimestamp_ = videoEncoder.debugPreviousSubmittedTimestamp_;
534 videoEncoder.debugPreviousSubmittedTimestamp_ = NumericT<int64_t>::minValue();
535
536 debugPreviousEncodedTimestamp_ = videoEncoder.debugPreviousEncodedTimestamp_;
537 videoEncoder.debugPreviousEncodedTimestamp_ = NumericT<int64_t>::minValue();
538#endif
539 }
540
541 return *this;
542}
543
544}
545
546}
547
548}
549
550#endif // META_OCEAN_MEDIA_AVF_VIDEO_ENCODER_H
This class implements Ocean's image class.
Definition Frame.h:1879
This class implements a recursive lock object.
Definition Lock.h:31
Definition of an encoded sample.
Definition avfoundation/VideoEncoder.h:125
bool isPartialFrame() const
Returns whether this sample contains only part of a frame.
Definition avfoundation/VideoEncoder.h:458
int64_t presentationTime_
The presentation time in microseconds.
Definition avfoundation/VideoEncoder.h:223
const std::vector< uint8_t > & data() const
Returns the encoded data.
Definition avfoundation/VideoEncoder.h:433
BufferFlags bufferFlags_
The buffer flags.
Definition avfoundation/VideoEncoder.h:226
std::vector< uint8_t > data_
The encoded data.
Definition avfoundation/VideoEncoder.h:220
bool isConfiguration() const
Returns whether this sample contains codec configuration data instead of media data.
Definition avfoundation/VideoEncoder.h:448
int64_t presentationTime() const
Returns the presentation time in microseconds.
Definition avfoundation/VideoEncoder.h:438
Sample(const Sample &)=delete
Disabled copy constructor.
Sample & operator=(const Sample &)=delete
Disabled copy operator.
bool isValid() const
Returns whether this sample is valid.
Definition avfoundation/VideoEncoder.h:428
bool isEndOfStream() const
Returns whether this sample marks the end of the stream.
Definition avfoundation/VideoEncoder.h:453
Sample()=default
Creates an invalid sample.
Sample & operator=(Sample &&sample) noexcept
Move operator.
Definition avfoundation/VideoEncoder.h:463
bool isKeyFrame() const
Returns whether this sample is a key frame.
Definition avfoundation/VideoEncoder.h:443
This class implements a simple video encoder for iOS/macOS using Ocean::Frame objects as input.
Definition avfoundation/VideoEncoder.h:88
static CMVideoCodecType mimeToCodecType(const std::string &mime)
Translates a MIME type to a CMVideoCodecType.
static void releaseVTCompressionSession(VTCompressionSessionRef session)
Release function for VTCompressionSessionRef that invalidates and releases the session.
Definition avfoundation/VideoEncoder.h:483
VideoEncoder(const VideoEncoder &)=delete
Disabled copy constructor.
VideoEncoder()
Default constructor creating an un-initialized encoder.
static constexpr int bitrateMbps5_
Definition of a 5 Mbps bit rate.
Definition avfoundation/VideoEncoder.h:98
int64_t debugPreviousEncodedTimestamp_
The previous presentation timestamp of an encoded sample in the compression callback,...
Definition avfoundation/VideoEncoder.h:411
Lock encodedSamplesLock_
The lock for the encoded samples queue.
Definition avfoundation/VideoEncoder.h:404
static constexpr unsigned int maximalWidth_
Definition of the maximal image width.
Definition avfoundation/VideoEncoder.h:237
static constexpr int bitrateMbps10_
Definition of a 10 Mbps bit rate.
Definition avfoundation/VideoEncoder.h:101
static void compressionOutputCallback(void *outputCallbackRefCon, void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer)
Callback function for encoded samples from VideoToolbox.
bool isInitialized() const
Returns whether this encoder is initialized.
Definition avfoundation/VideoEncoder.h:497
Lock lock_
The encoder's lock.
Definition avfoundation/VideoEncoder.h:401
std::vector< Sample > Samples
Definition of a vector holding sample objects.
Definition avfoundation/VideoEncoder.h:232
bool isStarted() const
Returns whether this encoder is currently running.
Definition avfoundation/VideoEncoder.h:504
bool initialize(const unsigned int width, const unsigned int height, const std::string &mime="video/avc", const double frameRate=30.0, const unsigned int bitrate=bitrateMbps2_, const int iFrameInterval=1)
Initializes the video encoder with the specified configuration.
static constexpr unsigned int maximalHeight_
Definition of the maximal image height.
Definition avfoundation/VideoEncoder.h:240
int64_t debugPreviousSubmittedTimestamp_
The previous presentation timestamp submitted via pushFrame(), in microseconds, -1 if no frame has be...
Definition avfoundation/VideoEncoder.h:408
unsigned int height_
The height of the video.
Definition avfoundation/VideoEncoder.h:395
bool pushFrame(const Frame &frame, const uint64_t presentationTime)
Adds a new frame which needs to be encoded to the video encoder.
~VideoEncoder()
Destructs the video encoder and releases all associated resources.
std::deque< Sample > encodedSamples_
The queue of encoded samples.
Definition avfoundation/VideoEncoder.h:389
ScopedVTCompressionSessionRef compressionSession_
The compression session.
Definition avfoundation/VideoEncoder.h:386
bool start()
Starts the video encoder.
bool stop()
Stops the video encoder.
static constexpr int maximalBitrate_
Definition of the maximal bit rate.
Definition avfoundation/VideoEncoder.h:243
VideoEncoder & operator=(const VideoEncoder &)=delete
Disabled copy operator.
unsigned int width_
The width of the video.
Definition avfoundation/VideoEncoder.h:392
void release()
Explicitly releases this video encoder.
static constexpr int bitrateMbps2_
Definition of a 2 Mbps bit rate.
Definition avfoundation/VideoEncoder.h:95
VideoEncoder & operator=(VideoEncoder &&videoEncoder) noexcept
Move operator.
Definition avfoundation/VideoEncoder.h:513
BufferFlags
Definition of individual buffer flag constants.
Definition avfoundation/VideoEncoder.h:108
@ BUFFER_FLAG_KEY_FRAME
Indicates that the (encoded) buffer marked as such contains the data for a key frame.
Definition avfoundation/VideoEncoder.h:112
@ BUFFER_FLAG_PARTIAL_FRAME
Indicates that the buffer only contains part of a frame.
Definition avfoundation/VideoEncoder.h:118
@ BUFFER_FLAG_END_OF_STREAM
Indicates that the buffer is the last buffer in the stream.
Definition avfoundation/VideoEncoder.h:116
@ BUFFER_FLAG_NONE
The buffer has no special property.
Definition avfoundation/VideoEncoder.h:110
@ BUFFER_FLAG_CODEC_CONFIG
Indicates that the buffer marked as such contains codec initialization / codec specific data instead ...
Definition avfoundation/VideoEncoder.h:114
static constexpr int bitrateMbps1_
Definition of a 1 Mbps bit rate.
Definition avfoundation/VideoEncoder.h:92
bool isStarted_
True, if the encoder is currently started.
Definition avfoundation/VideoEncoder.h:398
Sample popSample()
Returns the next encoded sample if available.
static constexpr T minValue()
Returns the min scalar value.
Definition Numeric.h:3259
This class implements a scoped lock object for recursive lock objects.
Definition Lock.h:147
bool isValid() const
Returns whether this scoped object holds a valid object.
Definition ScopedObject.h:460
The namespace covering the entire Ocean framework.
Definition Accessor.h:15