1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QFFMPEGFRAME_P_H |
5 | #define QFFMPEGFRAME_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QtFFmpegMediaPluginImpl/private/qffmpeg_p.h> |
19 | #include <QtFFmpegMediaPluginImpl/private/qffmpegcodeccontext_p.h> |
20 | #include <QtFFmpegMediaPluginImpl/private/qffmpegplaybackutils_p.h> |
21 | #include <QtFFmpegMediaPluginImpl/private/qffmpegtime_p.h> |
22 | #include "QtCore/qsharedpointer.h" |
23 | #include "qpointer.h" |
24 | #include "qobject.h" |
25 | |
26 | #include <optional> |
27 | |
28 | QT_BEGIN_NAMESPACE |
29 | |
30 | namespace QFFmpeg { |
31 | |
32 | struct Frame |
33 | { |
34 | struct Data |
35 | { |
36 | Data(const LoopOffset &offset, AVFrameUPtr f, const CodecContext &codecContext, quint64 sourceId) |
37 | : loopOffset(offset), |
38 | codecContext(codecContext), |
39 | frame(std::move(f)), |
40 | sourceId(sourceId) |
41 | { |
42 | Q_ASSERT(frame); |
43 | if (frame->pts != AV_NOPTS_VALUE) |
44 | startTime = codecContext.toTrackPosition(streamPosition: AVStreamPosition(frame->pts)); |
45 | else |
46 | startTime = codecContext.toTrackPosition( |
47 | streamPosition: AVStreamPosition(frame->best_effort_timestamp)); |
48 | |
49 | if (auto frameDuration = getAVFrameDuration(frame: *frame)) { |
50 | duration = codecContext.toTrackDuration(duration: AVStreamDuration(frameDuration)); |
51 | } else { |
52 | // Estimate frame duration for audio stream |
53 | if (codecContext.context()->codec_type == AVMEDIA_TYPE_AUDIO) { |
54 | if (frame->sample_rate) |
55 | duration = TrackDuration(qint64(1000000) * frame->nb_samples |
56 | / frame->sample_rate); |
57 | else |
58 | duration = TrackDuration(0); // Fallback |
59 | } else { |
60 | // Estimate frame duration for video stream |
61 | const auto &avgFrameRate = codecContext.stream()->avg_frame_rate; |
62 | duration = TrackDuration( |
63 | mul(a: qint64(1000000), b: { .num: avgFrameRate.den, .den: avgFrameRate.num }) |
64 | .value_or(u: 0)); |
65 | } |
66 | } |
67 | } |
68 | Data(const LoopOffset &offset, const QString &text, TrackPosition pts, |
69 | TrackDuration duration, quint64 sourceId) |
70 | : loopOffset(offset), text(text), startTime(pts), duration(duration), sourceId(sourceId) |
71 | { |
72 | } |
73 | |
74 | QAtomicInt ref; |
75 | LoopOffset loopOffset; |
76 | std::optional<CodecContext> codecContext; |
77 | AVFrameUPtr frame; |
78 | QString text; |
79 | TrackPosition startTime = 0; |
80 | TrackDuration duration = 0; |
81 | quint64 sourceId = 0; |
82 | }; |
83 | Frame() = default; |
84 | |
85 | Frame(const LoopOffset &offset, AVFrameUPtr f, const CodecContext &codecContext, |
86 | quint64 sourceIndex) |
87 | : d(new Data(offset, std::move(f), codecContext, sourceIndex)) |
88 | { |
89 | } |
90 | Frame(const LoopOffset &offset, const QString &text, TrackPosition pts, TrackDuration duration, |
91 | quint64 sourceIndex) |
92 | : d(new Data(offset, text, pts, duration, sourceIndex)) |
93 | { |
94 | } |
95 | bool isValid() const { return !!d; } |
96 | |
97 | AVFrame *avFrame() const { return data().frame.get(); } |
98 | AVFrameUPtr takeAVFrame() { return std::move(data().frame); } |
99 | const CodecContext *codecContext() const |
100 | { |
101 | return data().codecContext ? &data().codecContext.value() : nullptr; |
102 | } |
103 | TrackPosition startTime() const { return data().startTime; } |
104 | TrackDuration duration() const { return data().duration; } |
105 | TrackPosition endTime() const { return data().startTime + data().duration; } |
106 | QString text() const { return data().text; } |
107 | quint64 sourceId() const { return data().sourceId; }; |
108 | const LoopOffset &loopOffset() const { return data().loopOffset; }; |
109 | TrackPosition absolutePts() const |
110 | { |
111 | return startTime() + loopOffset().loopStartTimeUs.asDuration(); |
112 | } |
113 | TrackPosition absoluteEnd() const |
114 | { |
115 | return endTime() + loopOffset().loopStartTimeUs.asDuration(); |
116 | } |
117 | |
118 | private: |
119 | Data &data() const |
120 | { |
121 | Q_ASSERT(d); |
122 | return *d; |
123 | } |
124 | |
125 | private: |
126 | QExplicitlySharedDataPointer<Data> d; |
127 | }; |
128 | |
129 | } // namespace QFFmpeg |
130 | |
131 | QT_END_NAMESPACE |
132 | |
133 | Q_DECLARE_METATYPE(QFFmpeg::Frame); |
134 | |
135 | #endif // QFFMPEGFRAME_P_H |
136 | |