Revision control
Copy as Markdown
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegencodingformatcontext_p.h"
#include <QtFFmpegMediaPluginImpl/private/qffmpegmediaformatinfo_p.h>
#include <QtFFmpegMediaPluginImpl/private/qffmpegioutils_p.h>
#include <QtCore/qfile.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
namespace QFFmpeg {
Q_STATIC_LOGGING_CATEGORY(qLcEncodingFormatContext, "qt.multimedia.ffmpeg.encodingformatcontext");
namespace {
// BufferSize = 4096 is suggested, however, it might be not optimal. To be investigated.
constexpr size_t DefaultBufferSize = 4096;
} // namespace
EncodingFormatContext::EncodingFormatContext(QMediaFormat::FileFormat fileFormat)
: m_avFormatContext(avformat_alloc_context())
{
const AVOutputFormat *avFormat = QFFmpegMediaFormatInfo::outputFormatForFileFormat(fileFormat);
m_avFormatContext->oformat = const_cast<AVOutputFormat *>(avFormat); // constness varies
}
EncodingFormatContext::~EncodingFormatContext()
{
closeAVIO();
}
bool EncodingFormatContext::openAVIO(const QString &filePath)
{
Q_ASSERT(!isAVIOOpen());
Q_ASSERT(!filePath.isEmpty());
const QByteArray filePathUtf8 = filePath.toUtf8();
std::unique_ptr<char, AVFreeDeleter> url(
reinterpret_cast<char *>(av_malloc(filePathUtf8.size() + 1)));
memcpy(url.get(), filePathUtf8.constData(), filePathUtf8.size() + 1);
// Initialize the AVIOContext for accessing the resource indicated by the url
auto result = avio_open2(&m_avFormatContext->pb, url.get(), AVIO_FLAG_WRITE, nullptr, nullptr);
qCDebug(qLcEncodingFormatContext)
<< "opened by file path:" << url.get() << ", result:" << result;
Q_ASSERT(m_avFormatContext->url == nullptr);
if (isAVIOOpen()) {
m_avFormatContext->url = url.release();
return true;
} else {
return openAVIOWithQFile(filePath);
}
}
bool EncodingFormatContext::openAVIOWithQFile(const QString &filePath)
{
// QTBUG-123082, To be investigated:
// - should we use the logic with QFile for all file paths?
// - does avio_open2 handle network protocols that QFile doesn't?
// - which buffer size should we set to opening with QFile to ensure the best performance?
auto file = std::make_unique<QFile>(filePath);
if (!file->open(QFile::WriteOnly)) {
qCDebug(qLcEncodingFormatContext) << "Cannot open QFile" << filePath;
return false;
}
bool success = openAVIO(file.get());
if (success)
m_outputFile = std::move(file);
return success;
}
bool EncodingFormatContext::openAVIO(QIODevice *device)
{
Q_ASSERT(!isAVIOOpen());
Q_ASSERT(device);
if (!device->isWritable())
return false;
auto buffer = static_cast<uint8_t *>(av_malloc(DefaultBufferSize));
m_avFormatContext->pb = avio_alloc_context(buffer, DefaultBufferSize, 1, device, nullptr,
&writeQIODevice, &seekQIODevice);
return bool(m_avFormatContext->pb);
}
void EncodingFormatContext::closeAVIO()
{
// Close the AVIOContext and release any file handles
if (isAVIOOpen()) {
if (m_avFormatContext->url && *m_avFormatContext->url != '\0') {
auto closeResult = avio_closep(&m_avFormatContext->pb);
Q_ASSERT(closeResult == 0);
} else {
freeAVIOContext(m_avFormatContext->pb);
}
m_outputFile.reset();
} else {
Q_ASSERT(!m_outputFile);
}
}
} // namespace QFFmpeg
QT_END_NAMESPACE