22 const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat)
23 : EncoderThread(recordingEngine), m_settings(settings)
25 setObjectName(QLatin1String(
"VideoEncoder"));
27 const AVPixelFormat swFormat = QFFmpegVideoBuffer::toAVPixelFormat(format.pixelFormat());
28 if (format.streamFrameRate() > 0.)
29 m_fixedSourceFrameRate = format.streamFrameRate();
31 m_sourceParams.size = format.frameSize();
32 m_sourceParams.format = hwFormat && *hwFormat != AV_PIX_FMT_NONE ? *hwFormat : swFormat;
35 m_sourceParams.swFormat =
36 isSwPixelFormat(m_sourceParams.format) ? m_sourceParams.format : swFormat;
37 m_sourceParams.transform = qNormalizedSurfaceTransformation(format);
38 m_sourceParams.frameRate = m_fixedSourceFrameRate.value_or(0.f);
39 m_sourceParams.colorTransfer = QFFmpeg::toAvColorTransfer(format.colorTransfer());
40 m_sourceParams.colorSpace = QFFmpeg::toAvColorSpace(format.colorSpace());
41 m_sourceParams.colorRange = QFFmpeg::toAvColorRange(format.colorRange());
43 if (!m_settings.videoResolution().isValid())
44 m_settings.setVideoResolution(m_sourceParams.size);
48 const qreal provisionalTargetRate = m_settings.videoFrameRate() > 0.
49 ? m_settings.videoFrameRate()
50 : m_sourceParams.frameRate;
51 m_frameRateAdapter.setRates(m_fixedSourceFrameRate, provisionalTargetRate);
58 if (!frame.isValid()) {
59 bool hasFlushed =
false;
61 auto guard = lockLoopData();
62 if (isInitialized()) {
65 std::optional<
FrameInfo> flushed = m_frameRateAdapter.flush();
66 if (flushed && m_videoFrameQueue.size() < m_maxQueueSize) {
67 m_videoFrameQueue.push(*flushed);
74 setEndOfSourceStream();
79 auto guard = lockLoopData();
80 resetEndOfSourceStream();
83 m_shouldAdjustTimeBaseForNextFrame =
true;
84 m_frameRateAdapter.reset();
85 m_framesBeforeInit = {};
89 if (!isInitialized()) {
93 if (m_framesBeforeInit.size() >= m_maxFramesBeforeInit) {
94 qCWarning(qLcFFmpegVideoEncoder) <<
"Too many frames received before encoder "
95 "initialization. Buffer full, dropping "
99 m_framesBeforeInit.push(frame);
103 const bool adjustTimeBase = m_shouldAdjustTimeBaseForNextFrame;
104 m_shouldAdjustTimeBaseForNextFrame =
false;
106 if (m_videoFrameQueue.size() >= m_maxQueueSize) {
107 qCDebug(qLcFFmpegVideoEncoder)
108 <<
"RecordingEngine frame queue full. Frame lost. This might mean we're trying "
109 "to encode at too high of a frame rate.";
113 std::vector<FrameInfo> adapted = m_frameRateAdapter.adapt(frame, adjustTimeBase);
117 for (FrameInfo &adaptedFrame : adapted)
118 m_videoFrameQueue.push(std::move(adaptedFrame));
139 m_frameEncoder = VideoFrameEncoder::create(m_settings, m_sourceParams,
140 m_recordingEngine.avFormatContext());
142 qCDebug(qLcFFmpegVideoEncoder) <<
"VideoEncoder::init started video device thread.";
143 if (!m_frameEncoder) {
145 emit m_recordingEngine.sessionError(QMediaRecorder::ResourceError,
146 u"Could not initialize encoder"_s);
151 auto guard = lockLoopData();
152 m_frameRateAdapter.setRates(m_fixedSourceFrameRate, m_frameEncoder->codecFrameRate());
154 while (!m_framesBeforeInit.empty()) {
155 const QVideoFrame bufferedFrame = std::move(m_framesBeforeInit.front());
156 m_framesBeforeInit.pop();
158 std::vector<FrameInfo> adapted = m_frameRateAdapter.adapt(
159 bufferedFrame, std::exchange(m_shouldAdjustTimeBaseForNextFrame,
false));
160 for (FrameInfo &adaptedFrame : adapted)
161 m_videoFrameQueue.push(std::move(adaptedFrame));
166 if (isEndOfSourceStream()) {
167 std::optional<
FrameInfo> flushed = m_frameRateAdapter.flush();
168 if (flushed && m_videoFrameQueue.size() < m_maxQueueSize)
169 m_videoFrameQueue.push(std::move(*flushed));
173 return EncoderThread::init();
206 Q_ASSERT(m_frameEncoder);
211 QVideoFrame &frame = frameInfo.frame;
212 Q_ASSERT(frame.isValid());
218 auto *videoBuffer =
dynamic_cast<QFFmpegVideoBuffer *>(QVideoFramePrivate::hwBuffer(frame));
221 auto *hwFrame = videoBuffer->getHWFrame();
222 if (hwFrame && hwFrame->format == m_frameEncoder->sourceFormat())
223 avFrame.reset(av_frame_clone(hwFrame));
227 frame.map(QVideoFrame::ReadOnly);
228 auto size = frame.size();
229 avFrame = makeAVFrame();
230 avFrame->format = m_frameEncoder->sourceFormat();
231 avFrame->width = size.width();
232 avFrame->height = size.height();
234 for (
int i = 0; i < 4; ++i) {
235 avFrame->data[i] =
const_cast<uint8_t *>(frame.bits(i));
236 avFrame->linesize[i] = frame.bytesPerLine(i);
249 if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
252 img = frame.toImage();
253 avFrame->data[0] = (uint8_t *)img.bits();
254 avFrame->linesize[0] = img.bytesPerLine();
257 Q_ASSERT(avFrame->data[0]);
263 auto [startTime, endTime] = frameTimeStamps(frame);
265 if (frameInfo.overriddenPts) {
266 startTime = *frameInfo.overriddenPts;
267 endTime = startTime + m_frameRateAdapter.frameDuration();
271 m_baseTime += startTime - m_lastFrameTime;
272 qCDebug(qLcFFmpegVideoEncoder)
273 <<
">>>> adjusting base time to" << m_baseTime << startTime << m_lastFrameTime;
276 const qint64 time = startTime - m_baseTime;
277 m_lastFrameTime = endTime;
279 setAVFrameTime(*avFrame, m_frameEncoder->getPts(time), m_frameEncoder->getTimeBase());
281 m_recordingEngine.newTimeStamp(time / 1000);
283 qCDebug(qLcFFmpegVideoEncoder)
284 <<
">>> sending frame" << avFrame->pts << time << m_lastFrameTime;
285 int ret = m_frameEncoder->sendFrame(std::move(avFrame));
287 qCDebug(qLcFFmpegVideoEncoder) <<
"error sending frame" << ret << AVError(ret);
288 emit m_recordingEngine.sessionError(QMediaRecorder::ResourceError, err2str(ret));