avcpp  2.0
Wrapper for the FFmpeg that simplify usage from C++ projects.
frame.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <vector>
4 #include <memory>
5 #include <stdexcept>
6 
7 #include "avcompat.h"
8 
9 #if AVCPP_CXX_STANDARD >= 20
10 #if __has_include(<span>)
11 #include <span>
12 #endif
13 #endif
14 
15 #include "ffmpeg.h"
16 #include "rational.h"
17 #include "timestamp.h"
18 #include "pixelformat.h"
19 #include "sampleformat.h"
20 #include "avutils.h"
21 
22 extern "C" {
23 #include <libavutil/imgutils.h>
24 #include <libavutil/attributes.h>
25 }
26 
27 namespace av
28 {
29 
30 namespace frame {
31 
32 namespace priv {
33 void channel_layout_copy(AVFrame &dst, const AVFrame &src);
34 }
35 
36 static inline int64_t get_best_effort_timestamp(const AVFrame* frame) {
37 #if AVCPP_AVUTIL_VERSION_MAJOR < 56 // < FFmpeg 4.0
38  return av_frame_get_best_effort_timestamp(frame);
39 #else
40  return frame->best_effort_timestamp;
41 #endif
42 }
43 
44 // Based on db6efa1815e217ed76f39aee8b15ee5c64698537
45 static inline uint64_t get_channel_layout(const AVFrame* frame) {
46 #if AVCPP_AVUTIL_VERSION_MAJOR < 56 // < FFmpeg 4.0
47  return static_cast<uint64_t>(av_frame_get_channel_layout(frame));
48 #elif AVCPP_API_NEW_CHANNEL_LAYOUT
49  return frame->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? frame->ch_layout.u.mask : 0;
50 #else
51  return frame->channel_layout;
52 #endif
53 }
54 
55 static inline void set_channel_layout(AVFrame* frame, uint64_t layout) {
56 #if AVCPP_AVUTIL_VERSION_MAJOR < 56 // < FFmpeg 4.0
57  av_frame_set_channel_layout(frame, static_cast<int64_t>(layout));
58 #elif AVCPP_API_NEW_CHANNEL_LAYOUT
59  av_channel_layout_uninit(&frame->ch_layout);
60  av_channel_layout_from_mask(&frame->ch_layout, layout);
61 #else
62  frame->channel_layout = layout;
63 #endif
64 }
65 
66 
67 static inline int get_channels(const AVFrame* frame) {
68 #if AVCPP_AVUTIL_VERSION_MAJOR < 56 // < FFmpeg 4.0
69  return av_frame_get_channels(frame);
70 #elif AVCPP_API_NEW_CHANNEL_LAYOUT
71  return frame->ch_layout.nb_channels;
72 #else
73  return frame->channels;
74 #endif
75 }
76 
77 static inline bool is_valid_channel_layout(const AVFrame *frame)
78 {
79 #if AVCPP_API_NEW_CHANNEL_LAYOUT
80  return av_channel_layout_check(&frame->ch_layout);
81 #else
82  return frame->channel_layout;
83 #endif
84 }
85 
86 static inline int get_sample_rate(const AVFrame* frame) {
87 #if AVCPP_AVUTIL_VERSION_MAJOR < 56 // < FFmpeg 4.0
88  return av_frame_get_sample_rate(frame);
89 #else
90  return frame->sample_rate;
91 #endif
92 }
93 
94 static inline void set_sample_rate(AVFrame* frame, int sampleRate) {
95 #if AVCPP_AVUTIL_VERSION_MAJOR < 56 // < FFmpeg 4.0
96  av_frame_set_sample_rate(frame, sampleRate);
97 #else
98  frame->sample_rate = sampleRate;
99 #endif
100 }
101 
102 
103 } // ::frame
104 
105 template<typename T>
106 class Frame : public FFWrapperPtr<AVFrame>
107 {
108 protected:
109  T& assignOperator(const T &rhs) {
110  if (this == &rhs)
111  return static_cast<T&>(*this);
112  T(rhs).swap(static_cast<T&>(*this));
113  return static_cast<T&>(*this);
114  }
115 
116  T& moveOperator(T &&rhs) {
117  if (this == &rhs)
118  return static_cast<T&>(*this);
119  T(std::move(rhs)).swap(static_cast<T&>(*this));
120  return static_cast<T&>(*this);
121  }
122 
123 public:
124  Frame() {
125  m_raw = av_frame_alloc();
126  m_raw->opaque = this;
127  }
128 
129  ~Frame() {
130  av_frame_free(&m_raw);
131  }
132 
133  Frame(const AVFrame *frame) {
134  if (frame) {
135  m_raw = av_frame_alloc();
136  m_raw->opaque = this;
137  av_frame_ref(m_raw, frame);
138  }
139  }
140 
141  // Helper ctors to implement move/copy ctors
142  Frame(const T& other) : Frame(other.m_raw) {
143  if (m_raw)
144  copyInfoFrom(other);
145  }
146 
147  Frame(T&& other) : FFWrapperPtr<AVFrame>(nullptr) {
148  if (other.m_raw) {
149  m_raw = av_frame_alloc();
150  m_raw->opaque = this;
151  av_frame_move_ref(m_raw, other.m_raw);
152  copyInfoFrom(other);
153  }
154  }
155 
156  static T null() {
157  return T(nullptr);
158  }
159 
160  // You must implement operators in deveritive classes using assignOperator() and moveOperator()
161  void operator=(const Frame&) = delete;
162 
163  void swap(Frame &other) {
164  using std::swap;
165 #define FRAME_SWAP(x) swap(x, other.x)
166  FRAME_SWAP(m_raw);
170 #undef FRAME_SWAP
171  }
172 
173  void copyInfoFrom(const T& other) {
174  m_timeBase = other.m_timeBase;
175  m_streamIndex = other.m_streamIndex;
176  m_isComplete = other.m_isComplete;
177  }
178 
179  bool isReferenced() const {
180  return m_raw && m_raw->buf[0];
181  }
182 
183  int refCount() const {
184  if (m_raw && m_raw->buf[0])
185  return av_buffer_get_ref_count(m_raw->buf[0]);
186  else
187  return 0;
188  }
189 
190  AVFrame* makeRef() const {
191  return m_raw ? av_frame_clone(m_raw) : nullptr;
192  }
193 
194  T clone(size_t align = 1) const {
195  T result;
196 
197  // Setup data required for buffer allocation
198  result.m_raw->format = m_raw->format;
199  result.m_raw->width = m_raw->width;
200  result.m_raw->height = m_raw->height;
201  result.m_raw->nb_samples = m_raw->nb_samples;
202 
203  frame::priv::channel_layout_copy(*result.m_raw, *m_raw);
204 
205  result.copyInfoFrom(static_cast<const T&>(*this));
206 
207  av_frame_get_buffer(result.m_raw, align);
208  av_frame_copy(result.m_raw, m_raw);
209  av_frame_copy_props(result.m_raw, m_raw);
210  return result;
211  }
212 
213  Timestamp pts() const
214  {
215  return {RAW_GET(pts, av::NoPts), m_timeBase};
216  }
217 
218  attribute_deprecated void setPts(int64_t pts, Rational ptsTimeBase)
219  {
220  RAW_SET(pts, ptsTimeBase.rescale(pts, m_timeBase));
221  }
222 
223  void setPts(const Timestamp &ts)
224  {
225  if (m_timeBase == Rational())
226  m_timeBase = ts.timebase();
228  }
229 
230  const Rational& timeBase() const { return m_timeBase; }
231 
232  void setTimeBase(const Rational &value) {
233  if (m_timeBase == value)
234  return;
235 
236  if (!m_raw)
237  return;
238 
239  int64_t rescaledPts = NoPts;
240  int64_t rescaledBestEffortTs = NoPts;
241 
242  if (m_timeBase != Rational() && value != Rational()) {
243  if (m_raw->pts != av::NoPts)
244  rescaledPts = m_timeBase.rescale(m_raw->pts, value);
245 
246  if (m_raw->best_effort_timestamp != av::NoPts)
247  rescaledBestEffortTs = m_timeBase.rescale(m_raw->best_effort_timestamp, value);
248  } else {
249  rescaledPts = m_raw->pts;
250  rescaledBestEffortTs = m_raw->best_effort_timestamp;
251  }
252 
253  if (m_timeBase != Rational()) {
254  m_raw->pts = rescaledPts;
255  m_raw->best_effort_timestamp = rescaledBestEffortTs;
256  }
257 
258  m_timeBase = value;
259  }
260 
261  int streamIndex() const {
262  return m_streamIndex;
263  }
264 
267  }
268 
269  void setComplete(bool isComplete) {
271  }
272 
273  bool isComplete() const { return m_isComplete; }
274 
275  bool isValid() const {
276  return (!isNull() &&
277  ((m_raw->data[0] && m_raw->linesize[0]) ||
278  ((m_raw->format == AV_PIX_FMT_VAAPI) && ((intptr_t)m_raw->data[3] > 0)))
279  );
280  }
281 
282  operator bool() const { return isValid() && isComplete(); }
283 
284  uint8_t *data(size_t plane = 0) {
285  if (!m_raw || plane >= size_t(AV_NUM_DATA_POINTERS + m_raw->nb_extended_buf))
286  return nullptr;
287  return m_raw->extended_data[plane];
288  }
289 
290  const uint8_t *data(size_t plane = 0) const {
291  if (!m_raw || plane >= size_t(AV_NUM_DATA_POINTERS + m_raw->nb_extended_buf))
292  return nullptr;
293  return m_raw->extended_data[plane];;
294  }
295 
296  size_t size(size_t plane) const {
297  if (!m_raw || plane >= size_t(AV_NUM_DATA_POINTERS + m_raw->nb_extended_buf))
298  return 0;
299  AVBufferRef *buf = plane < AV_NUM_DATA_POINTERS ?
300  m_raw->buf[plane] :
301  m_raw->extended_buf[plane - AV_NUM_DATA_POINTERS];
302  if (buf == nullptr)
303  return 0;
304  return size_t(buf->size);
305  }
306 
307  size_t size() const {
308  if (!m_raw)
309  return 0;
310  size_t total = 0;
311  if (m_raw->buf[0]) {
312  for (auto i = 0; i < AV_NUM_DATA_POINTERS && m_raw->buf[i]; i++) {
313  total += m_raw->buf[i]->size;
314  }
315 
316  for (auto i = 0; i < m_raw->nb_extended_buf; ++i) {
317  total += m_raw->extended_buf[i]->size;
318  }
319  } else if (m_raw->data[0]) {
320  if (m_raw->width && m_raw->height) {
321  uint8_t data[4] = {0};
322  int linesizes[4] = {
323  m_raw->linesize[0],
324  m_raw->linesize[1],
325  m_raw->linesize[2],
326  m_raw->linesize[3],
327  };
328  total = av_image_fill_pointers(reinterpret_cast<uint8_t**>(&data),
329  static_cast<AVPixelFormat>(m_raw->format),
330  m_raw->height,
331  nullptr,
332  linesizes);
333  } else if (m_raw->nb_samples && frame::is_valid_channel_layout(m_raw)) {
334  for (auto i = 0; i < m_raw->nb_extended_buf + AV_NUM_DATA_POINTERS && m_raw->extended_data[i]; ++i) {
335  // According docs, all planes must have same size
336  total += m_raw->linesize[0];
337  }
338  }
339  }
340  return total;
341  }
342 
343  void dump() const {
344  if (!m_raw)
345  return;
346  if (m_raw->buf[0]) {
347  for (size_t i = 0; i < AV_NUM_DATA_POINTERS && m_raw->buf[i]; i++) {
348  av::hex_dump(stdout, m_raw->buf[i]->data, m_raw->buf[i]->size);
349  }
350  } else if (m_raw->data[0]) {
351  av_hex_dump(stdout, m_raw->data[0], size());
352  }
353  }
354 
355 protected:
357  int m_streamIndex {-1};
358  bool m_isComplete {false};
359 };
360 
361 
362 class VideoFrame : public Frame<VideoFrame>
363 {
364 public:
366 
367  VideoFrame() = default;
368  VideoFrame(PixelFormat pixelFormat, int width, int height, int align = 1);
369  VideoFrame(const uint8_t *data, size_t size, PixelFormat pixelFormat, int width, int height, int align = 1);
370 
371  VideoFrame(const VideoFrame &other);
372  VideoFrame(VideoFrame &&other);
373 
374  VideoFrame& operator=(const VideoFrame &rhs);
376 
377  PixelFormat pixelFormat() const;
378  int width() const;
379  int height() const;
380 
381  bool isKeyFrame() const;
382  void setKeyFrame(bool isKey);
383 
384  int quality() const;
385  void setQuality(int quality);
386 
387  AVPictureType pictureType() const;
388  void setPictureType(AVPictureType type = AV_PICTURE_TYPE_NONE);
389 
390  Rational sampleAspectRatio() const;
392 
393  size_t bufferSize(int align = 1, OptionalErrorCode ec = throws()) const;
394  bool copyToBuffer(uint8_t *dst, size_t size, int align = 1, OptionalErrorCode ec = throws());
395  bool copyToBuffer(std::vector<uint8_t>& dst, int align = 1, OptionalErrorCode ec = throws());
396 
397 
412  static VideoFrame wrap(const void *data, size_t size, PixelFormat pixelFormat, int width, int height, int align = 1);
413 
414 #ifdef __cpp_lib_span
427  static VideoFrame wrap(std::span<const std::byte> data, PixelFormat pixelFormat, int width, int height, int align = 1);
428 
443  static VideoFrame wrap(std::span<const std::byte> data, void (*deleter)(void *opaque, uint8_t *data),
444  void *opaque, PixelFormat pixelFormat, int width, int height, int align = 1);
445 #endif
446 };
447 
448 
449 class AudioSamples : public Frame<AudioSamples>
450 {
451 public:
453 
454  AudioSamples() = default;
455  AudioSamples(SampleFormat sampleFormat, int samplesCount, uint64_t channelLayout, int sampleRate, int align = SampleFormat::AlignDefault);
456  AudioSamples(const uint8_t *data, size_t size,
457  SampleFormat sampleFormat, int samplesCount, uint64_t channelLayout, int sampleRate, int align = SampleFormat::AlignDefault);
458 
459  AudioSamples(const AudioSamples &other);
460  AudioSamples(AudioSamples &&other);
461 
462  AudioSamples& operator=(const AudioSamples &rhs);
464 
465  int init(SampleFormat sampleFormat, int samplesCount, uint64_t channelLayout, int sampleRate, int align = SampleFormat::AlignDefault);
466 
467  SampleFormat sampleFormat() const;
468  int samplesCount() const;
469  int channelsCount() const;
470  uint64_t channelsLayout() const;
471  int sampleRate() const;
472  size_t sampleBitDepth(OptionalErrorCode ec = throws()) const;
473  bool isPlanar() const;
474 
475  std::string channelsLayoutString() const;
476 };
477 
478 
479 } // ::av
480 
Definition: frame.h:450
uint64_t channelsLayout() const
Definition: frame.cpp:371
SampleFormat sampleFormat() const
Definition: frame.cpp:356
size_t sampleBitDepth(OptionalErrorCode ec=throws()) const
Definition: frame.cpp:381
int channelsCount() const
Definition: frame.cpp:366
int sampleRate() const
Definition: frame.cpp:376
std::string channelsLayoutString() const
Definition: frame.cpp:393
bool isPlanar() const
Definition: frame.cpp:388
int samplesCount() const
Definition: frame.cpp:361
AudioSamples()=default
AudioSamples & operator=(const AudioSamples &rhs)
Definition: frame.cpp:346
int init(SampleFormat sampleFormat, int samplesCount, uint64_t channelLayout, int sampleRate, int align=SampleFormat::AlignDefault)
Definition: frame.cpp:287
Definition: frame.h:107
void setTimeBase(const Rational &value)
Definition: frame.h:232
void setStreamIndex(int streamIndex)
Definition: frame.h:265
bool isReferenced() const
Definition: frame.h:179
T & moveOperator(T &&rhs)
Definition: frame.h:116
bool isValid() const
Definition: frame.h:275
size_t size(size_t plane) const
Definition: frame.h:296
Frame(const T &other)
Definition: frame.h:142
attribute_deprecated void setPts(int64_t pts, Rational ptsTimeBase)
Definition: frame.h:218
void copyInfoFrom(const T &other)
Definition: frame.h:173
int m_streamIndex
Definition: frame.h:357
bool isComplete() const
Definition: frame.h:273
T clone(size_t align=1) const
Definition: frame.h:194
int refCount() const
Definition: frame.h:183
bool m_isComplete
Definition: frame.h:358
~Frame()
Definition: frame.h:129
Timestamp pts() const
Definition: frame.h:213
size_t size() const
Definition: frame.h:307
void setPts(const Timestamp &ts)
Definition: frame.h:223
Frame(T &&other)
Definition: frame.h:147
AVFrame * makeRef() const
Definition: frame.h:190
void dump() const
Definition: frame.h:343
T & assignOperator(const T &rhs)
Definition: frame.h:109
Frame()
Definition: frame.h:124
const uint8_t * data(size_t plane=0) const
Definition: frame.h:290
void swap(Frame &other)
Definition: frame.h:163
int streamIndex() const
Definition: frame.h:261
Rational m_timeBase
Definition: frame.h:356
uint8_t * data(size_t plane=0)
Definition: frame.h:284
const Rational & timeBase() const
Definition: frame.h:230
void setComplete(bool isComplete)
Definition: frame.h:269
Frame(const AVFrame *frame)
Definition: frame.h:133
void operator=(const Frame &)=delete
Definition: averror.h:64
The PixelFormat class is a simple wrapper for AVPixelFormat that allow to acces it it properties.
Definition: pixelformat.h:27
Definition: rational.h:26
int64_t rescale(int64_t srcValue, const Rational &dstBase) const noexcept
Definition: rational.cpp:36
The SampleFormat class is a simple proxy class for AVSampleFormat.
Definition: sampleformat.h:22
@ AlignDefault
Definition: sampleformat.h:26
The Timestamp class represents timestamp value and it timebase.
Definition: timestamp.h:14
int64_t timestamp() const noexcept
Definition: timestamp.cpp:19
const Rational & timebase() const noexcept
Definition: timestamp.cpp:29
Definition: frame.h:363
VideoFrame()=default
bool isKeyFrame() const
Definition: frame.cpp:153
VideoFrame & operator=(const VideoFrame &rhs)
Definition: frame.cpp:128
void setQuality(int quality)
Definition: frame.cpp:182
Rational sampleAspectRatio() const
Definition: frame.cpp:197
void setSampleAspectRatio(const Rational &sampleAspectRatio)
Definition: frame.cpp:202
size_t bufferSize(int align=1, OptionalErrorCode ec=throws()) const
Definition: frame.cpp:207
bool copyToBuffer(uint8_t *dst, size_t size, int align=1, OptionalErrorCode ec=throws())
Definition: frame.cpp:218
int height() const
Definition: frame.cpp:148
int width() const
Definition: frame.cpp:143
static VideoFrame wrap(const void *data, size_t size, PixelFormat pixelFormat, int width, int height, int align=1)
Wrap external data into VideoFrame object ready to use with FFmpeg/AvCpp.
Definition: frame.cpp:264
void setPictureType(AVPictureType type=AV_PICTURE_TYPE_NONE)
Definition: frame.cpp:192
PixelFormat pixelFormat() const
Definition: frame.cpp:138
void setKeyFrame(bool isKey)
Definition: frame.cpp:162
int quality() const
Definition: frame.cpp:177
AVPictureType pictureType() const
Definition: frame.cpp:187
#define RAW_GET(field, def)
Definition: ffmpeg.h:47
#define RAW_SET(field, val)
Definition: ffmpeg.h:48
#define FRAME_SWAP(x)
void channel_layout_copy(AVFrame &dst, const AVFrame &src)
Definition: frame.cpp:409
static int get_sample_rate(const AVFrame *frame)
Definition: frame.h:86
static int64_t get_best_effort_timestamp(const AVFrame *frame)
Definition: frame.h:36
static int get_channels(const AVFrame *frame)
Definition: frame.h:67
static void set_sample_rate(AVFrame *frame, int sampleRate)
Definition: frame.h:94
static bool is_valid_channel_layout(const AVFrame *frame)
Definition: frame.h:77
static void set_channel_layout(AVFrame *frame, uint64_t layout)
Definition: frame.h:55
static uint64_t get_channel_layout(const AVFrame *frame)
Definition: frame.h:45
Definition: audioresampler.cpp:8
void hex_dump(FILE *f, const uint8_t *buf, std::size_t size)
Definition: avutils.cpp:319
constexpr auto NoPts
Definition: avutils.h:35
Definition: ffmpeg.h:22
AVFrame * m_raw
Definition: ffmpeg.h:44
bool isNull() const
Definition: ffmpeg.h:30