Overview
If you’ve been following my previous blog posts, you may recall that I’ve been working on adding multi-format rendering support to Kdenlive. This feature allows users to export their videos in different aspect ratios, such as horizontal (16:9), vertical (9:16), and square (1:1), catering to the requirements of various platforms and use cases.
In the previous blog, I talked about the core implementation of the aspect ratio conversion logic, which calculates the necessary cropping parameters to achieve the desired format.
My Progess so far…
> Temporary File Handling and Refactoring
I initially implemented QTemporaryFile
for cross-platform handling of temporary files, replacing hardcoded temp paths. However, this approach involved creating a new temporary file each time the method was called, which was inefficient. Based on the mentor’s feedback, I refactored the code to create an empty QTemporaryFile
object and only set it up with a specific file template when an aspect ratio change is required. This improved the efficiency of the temporary file handling.1
> User Interface
My next goal was to create user interface elements where the user can select the desired aspect ratio, and then connect them to the backend logic, which I discussed in my first blog.
First, I added a setAspectRatio
method in the RenderRequest
class to store the selected aspect ratio.
void RenderRequest::setAspectRatio(const QString &aspectRatio)
{
m_aspectRatio = aspectRatio;
}
This aspect ratio would then be passed to the projectSceneList
method in the ProjectManager
class:
QString ProjectManager::projectSceneList(const QString &outputFolder, const QString &overlayData, const QString &aspectRatio)
{
// ...
}
In the render widget’s file, I added a new row with a label and a combo box for selecting the aspect ratio. In the rendering widget’s constructor.
Then, in the rendering widget’s constructor, I created the combo box with the available options:
m_view.aspect_ratio_type->addItem(i18n("Default"));
m_view.aspect_ratio_type->addItem(i18n("Horizontal (16:9)"), QStringLiteral("horizontal"));
m_view.aspect_ratio_type->addItem(i18n("Vertical (9:16)"), QStringLiteral("vertical"));
m_view.aspect_ratio_type->addItem(i18n("Square (1:1)"), QStringLiteral("square"));
m_view.aspect_ratio_type->setCurrentIndex(0);
Profiles with width or height that are not a multiple of 2 cause crashes. I’ve fixed the issue by ensuring that the video profile width and height are multiples of 2.
[libx264 @ 0x733910204380] width not divisible by 2 (607x1080)
I tried to find the reason behind why we need width and height as divisible of 2 in the first place.
As required by x264, the “divisible by 2 for width and height” is needed for YUV 4:2:0 chroma subsampled outputs. 4:2:2 would need “divisible by 2 for width”, and 4:4:4 does not have these restrictions. However, most non-FFmpeg based players can only properly decode 4:2:0, so that is why you often see ffmpeg commands with the -pix_fmt yuv420p option when outputting H.264 video. —source
Finally, the selected aspect ratio is retrieved from the combo box and passed to the setAspectRatio
method of the RenderRequest
object:
request->setAspectRatio(m_view.aspect_ratio_type->currentData().toString());
With these changes, users can now select the desired aspect ratio from the combo box in the rendering widget before exporting their video.
> What I learned:
It was a learning experience; I’ve gained confidence to work on a large codebase and navigate through it, a better understanding of object-oriented programming (OOP) concepts, and the realization that I needed to get better at it. I learned the importance of efficient resource management, such as handling temporary files and much more. Beyond code, I learned how to better communicate with the mentors.
I’m grateful to the mentors who provided guidance and support throughout the project, helping me overcome the challenges I faced.