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);
147 m_frameEncoder = VideoFrameEncoder::create(m_settings, m_sourceParams,
148 m_recordingEngine.avFormatContext());
150 qCDebug(qLcFFmpegVideoEncoder) <<
"VideoEncoder::init started video device thread.";
151 if (!m_frameEncoder) {
153 emit m_recordingEngine.sessionError(QMediaRecorder::ResourceError,
154 u"Could not initialize encoder"_s);
159 auto guard = lockLoopData();
160 m_frameRateAdapter.setRates(m_fixedSourceFrameRate, m_frameEncoder->codecFrameRate());
161 saturateEncoderQueue();
164 return EncoderThread::init();
198 Q_ASSERT(m_frameEncoder);
202 FrameInfo frameInfo = dequeueAdaptedFrame();
203 QVideoFrame &frame = frameInfo.frame;
204 if (!frame.isValid())
211 auto *videoBuffer =
dynamic_cast<QFFmpegVideoBuffer *>(QVideoFramePrivate::hwBuffer(frame));
214 auto *hwFrame = videoBuffer->getHWFrame();
215 if (hwFrame && hwFrame->format == m_frameEncoder->sourceFormat())
216 avFrame.reset(av_frame_clone(hwFrame));
220 frame.map(QVideoFrame::ReadOnly);
221 auto size = frame.size();
222 avFrame = makeAVFrame();
223 avFrame->format = m_frameEncoder->sourceFormat();
224 avFrame->width = size.width();
225 avFrame->height = size.height();
227 for (
int i = 0; i < 4; ++i) {
228 avFrame->data[i] =
const_cast<uint8_t *>(frame.bits(i));
229 avFrame->linesize[i] = frame.bytesPerLine(i);
242 if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
245 img = frame.toImage();
246 avFrame->data[0] = (uint8_t *)img.bits();
247 avFrame->linesize[0] = img.bytesPerLine();
250 Q_ASSERT(avFrame->data[0]);
256 auto [startTime, endTime] = frameTimeStamps(frame);
258 if (frameInfo.overriddenPts) {
259 startTime = *frameInfo.overriddenPts;
260 endTime = startTime + m_frameRateAdapter.frameDuration();
264 m_baseTime += startTime - m_lastFrameTime;
265 qCDebug(qLcFFmpegVideoEncoder)
266 <<
">>>> adjusting base time to" << m_baseTime << startTime << m_lastFrameTime;
269 const qint64 time = startTime - m_baseTime;
270 m_lastFrameTime = endTime;
272 setAVFrameTime(*avFrame, m_frameEncoder->getPts(time), m_frameEncoder->getTimeBase());
274 m_recordingEngine.newTimeStamp(time / 1000);
276 qCDebug(qLcFFmpegVideoEncoder)
277 <<
">>> sending frame" << avFrame->pts << time << m_lastFrameTime;
278 int ret = m_frameEncoder->sendFrame(std::move(avFrame));
280 qCDebug(qLcFFmpegVideoEncoder) <<
"error sending frame" << ret << AVError(ret);
281 emit m_recordingEngine.sessionError(QMediaRecorder::ResourceError, err2str(ret));