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(30.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);
46 const bool explicitTargetRate = m_settings.videoFrameRate() > 0.;
47 if (!explicitTargetRate)
48 m_settings.setVideoFrameRate(m_sourceParams.frameRate);
50 if (m_fixedSourceFrameRate || explicitTargetRate)
51 m_frameRateAdapter.setRates(m_fixedSourceFrameRate, m_settings.videoFrameRate());
58 if (!frame.isValid()) {
59 bool hasFlushed =
false;
61 auto guard = lockLoopData();
62 std::optional<
FrameInfo> flushed = m_frameRateAdapter.flush();
63 if (flushed && m_videoFrameQueue.size() < m_maxQueueSize) {
64 m_videoFrameQueue.push(*flushed);
70 setEndOfSourceStream();
75 auto guard = lockLoopData();
76 resetEndOfSourceStream();
79 m_shouldAdjustTimeBaseForNextFrame =
true;
80 m_frameRateAdapter.reset();
84 const bool adjustTimeBase = m_shouldAdjustTimeBaseForNextFrame;
85 m_shouldAdjustTimeBaseForNextFrame =
false;
87 if (m_videoFrameQueue.size() >= m_maxQueueSize) {
88 qCDebug(qLcFFmpegVideoEncoder)
89 <<
"RecordingEngine frame queue full. Frame lost. This might mean we're trying "
90 "to encode at too high of a frame rate.";
94 std::vector<FrameInfo> adapted = m_frameRateAdapter.adapt(frame, adjustTimeBase);
98 for (FrameInfo &adaptedFrame : adapted)
99 m_videoFrameQueue.push(std::move(adaptedFrame));
120 m_frameEncoder = VideoFrameEncoder::create(m_settings, m_sourceParams,
121 m_recordingEngine.avFormatContext());
123 qCDebug(qLcFFmpegVideoEncoder) <<
"VideoEncoder::init started video device thread.";
124 if (!m_frameEncoder) {
125 emit m_recordingEngine.sessionError(QMediaRecorder::ResourceError,
126 u"Could not initialize encoder"_s);
132 return EncoderThread::init();
165 Q_ASSERT(m_frameEncoder);
170 QVideoFrame &frame = frameInfo.frame;
171 Q_ASSERT(frame.isValid());
177 auto *videoBuffer =
dynamic_cast<QFFmpegVideoBuffer *>(QVideoFramePrivate::hwBuffer(frame));
180 auto *hwFrame = videoBuffer->getHWFrame();
181 if (hwFrame && hwFrame->format == m_frameEncoder->sourceFormat())
182 avFrame.reset(av_frame_clone(hwFrame));
186 frame.map(QVideoFrame::ReadOnly);
187 auto size = frame.size();
188 avFrame = makeAVFrame();
189 avFrame->format = m_frameEncoder->sourceFormat();
190 avFrame->width = size.width();
191 avFrame->height = size.height();
193 for (
int i = 0; i < 4; ++i) {
194 avFrame->data[i] =
const_cast<uint8_t *>(frame.bits(i));
195 avFrame->linesize[i] = frame.bytesPerLine(i);
208 if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
211 img = frame.toImage();
212 avFrame->data[0] = (uint8_t *)img.bits();
213 avFrame->linesize[0] = img.bytesPerLine();
216 Q_ASSERT(avFrame->data[0]);
222 auto [startTime, endTime] = frameTimeStamps(frame);
224 if (frameInfo.overriddenPts) {
225 startTime = *frameInfo.overriddenPts;
226 endTime = startTime + m_frameRateAdapter.frameDuration();
230 m_baseTime += startTime - m_lastFrameTime;
231 qCDebug(qLcFFmpegVideoEncoder)
232 <<
">>>> adjusting base time to" << m_baseTime << startTime << m_lastFrameTime;
235 const qint64 time = startTime - m_baseTime;
236 m_lastFrameTime = endTime;
238 setAVFrameTime(*avFrame, m_frameEncoder->getPts(time), m_frameEncoder->getTimeBase());
240 m_recordingEngine.newTimeStamp(time / 1000);
242 qCDebug(qLcFFmpegVideoEncoder)
243 <<
">>> sending frame" << avFrame->pts << time << m_lastFrameTime;
244 int ret = m_frameEncoder->sendFrame(std::move(avFrame));
246 qCDebug(qLcFFmpegVideoEncoder) <<
"error sending frame" << ret << AVError(ret);
247 emit m_recordingEngine.sessionError(QMediaRecorder::ResourceError, err2str(ret));