11 Commits
6.1.0 ... 6.1.1

Author SHA1 Message Date
k4yt3x
0d6a6abce2 chore(release): bump version to 6.1.1
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-07 00:00:00 +00:00
k4yt3x
943b446d26 fix(encoder): make the encoder use the right color space
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-07 00:00:00 +00:00
k4yt3x
1b81f7d1e2 ci(build): make the build pipeline only run on code changes
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-07 00:00:00 +00:00
k4yt3x
64697a9385 ci(issues): added the issues auto-labeling pipeline
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-07 00:00:00 +00:00
k4yt3x
e8b0b0ec21 docs(readme): updated installation instructions for Linux
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-06 00:00:00 +00:00
k4yt3x
05b275dd82 feat(video2x): added time remaining and processing speed to the progress bar
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-06 00:00:00 +00:00
k4yt3x
33b7c53e16 fix(encoder): fixed incorrect stream mapping for multi-stream files
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-06 00:00:00 +00:00
k4yt3x
3b7921a774 docs(readme): added a badge for GitHub sponsors
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-05 00:00:00 +00:00
k4yt3x
dfb29e05b7 docs(readme): updated README for 6.1.0
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-05 00:00:00 +00:00
k4yt3x
3bae03f403 docs(changelog): updated the changelog for 6.1.0
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-05 00:00:00 +00:00
k4yt3x
9ff320721f ci(build): updated the name of the build pipeline
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-11-05 00:00:00 +00:00
11 changed files with 235 additions and 76 deletions

View File

@@ -1,10 +1,17 @@
name: build
name: Build
on:
push:
branches:
- master
- dev
pull_request: {}
paths:
- "include/**"
- "src/**"
- "third_party/**"
- "CMakeLists.txt"
pull_request:
paths:
- "include/**"
- "src/**"
- "third_party/**"
- "CMakeLists.txt"
workflow_dispatch: {}
jobs:

52
.github/workflows/issues.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Issues
on:
issues:
types:
- opened
- reopened
- closed
jobs:
label_issues:
runs-on: ubuntu-latest
steps:
- name: Remove all 'state:' labels
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE_NUMBER=${{ github.event.issue.number }}
REPO=${{ github.repository }}
EXISTING_LABELS=$(gh issue view $ISSUE_NUMBER --repo $REPO --json labels --jq '.labels[].name')
for label in $EXISTING_LABELS; do
if [[ $label == state:* ]]; then
gh issue edit $ISSUE_NUMBER --remove-label "$label" --repo $REPO
fi
done
- name: Add 'state:Backlog' label on issue opened or reopened
if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
LABEL_NAME="state:Backlog"
REPO=${{ github.repository }}
ISSUE_NUMBER=${{ github.event.issue.number }}
if gh label list --repo $REPO | grep -q "$LABEL_NAME"; then
gh issue edit $ISSUE_NUMBER --add-label "$LABEL_NAME" --repo $REPO
fi
- name: Add 'state:Done' label on issue closed
if: ${{ github.event.action == 'closed' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
LABEL_NAME="state:Done"
REPO=${{ github.repository }}
ISSUE_NUMBER=${{ github.event.issue.number }}
if gh label list --repo $REPO | grep -q "$LABEL_NAME"; then
gh issue edit $ISSUE_NUMBER --add-label "$LABEL_NAME" --repo $REPO
fi

View File

@@ -5,4 +5,39 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [6.1.1] - 2024-11-07
### Added
- Time remaining, and processing speed to the status bar.
### Fixed
- Stream mapping for cases where the video stream is not the first stream in the input file (#1217).
- The encoder using the wrong color space for the output video.
## [6.1.0] - 2024-11-04
### Added
- A better timer that gets paused when the processing is paused.
- Detection for the validity of the provided GPU ID.
- The `--listgpus` option to list available Vulkan GPU devices.
- Vulkan device selection for libplacebo.
- Status bar and processing statistics. (Video2X Qt6)
### Fixed
- Wide character string paths support on Windows systems without UTF-8 support enabled (#1201).
### Changed
- Automatically detect if options `colorspace` and `range` are supported by the buffer filter.
- Resource file missing error messages.
- Rewritten the CLI with C++.
## [6.0.0] - 2024-10-29
### Added
- The initial release of the 6.0.0 version of Video2X.

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(video2x VERSION 6.1.0 LANGUAGES CXX)
project(video2x VERSION 6.1.1 LANGUAGES CXX)
# Set the C++ standard
set(CMAKE_CXX_STANDARD 17)

View File

@@ -5,6 +5,7 @@
<img src="https://img.shields.io/github/actions/workflow/status/k4yt3x/video2x/build.yml?label=Build&style=flat-square"/>
<img src="https://img.shields.io/github/downloads/k4yt3x/video2x/total?style=flat-square"/>
<img src="https://img.shields.io/github/license/k4yt3x/video2x?style=flat-square"/>
<img src="https://img.shields.io/github/sponsors/k4yt3x?style=flat-square&link=https%3A%2F%2Fgithub.com%2Fsponsors%2Fk4yt3x"/>
<img src="https://img.shields.io/badge/dynamic/json?color=%23e85b46&label=Patreon&query=data.attributes.patron_count&suffix=%20patrons&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F4507807&style=flat-square"/>
</p>
@@ -13,11 +14,11 @@
## 🌟 Version 6.0.0
**[Download Windows Installer](https://github.com/k4yt3x/video2x/releases/download/6.0.0/video2x-qt6-windows-amd64-installer.exe)**
**[Download Windows Installer](https://github.com/k4yt3x/video2x/releases/download/6.1.0/video2x-qt6-windows-amd64-installer.exe)**
**TL;DR: Version 6.0.0 is a complete rewrite of the Video2X project in C/C++, featuring a faster, more efficient architecture, cross-platform support, vastly improved output quality, and a new GUI and installer for easy setup on Windows.**
![6.0.0-screenshot](https://github.com/user-attachments/assets/7c6f4cd9-2cca-4deb-a886-f9f225962866)
![6.1.0-screenshot](https://github.com/user-attachments/assets/57aa11d0-dd01-49e9-b6b0-2d2f21a363ac)
Version 6.0.0 is a complete rewrite of this project in C/C++. It:
@@ -43,7 +44,7 @@ You can download the latest Windows release from the [releases page](https://git
## [🐧 Install on Linux](https://aur.archlinux.org/packages/video2x-git)
You can install Video2X on Arch Linux using the [video2x-git](https://aur.archlinux.org/packages/video2x-git) package or download pre-compiled binaries from the [releases page](https://github.com/k4yt3x/video2x/releases/latest). If you'd like to build from source, refer to the [PKGBUILD](packaging/arch/PKGBUILD) file for a general overview of the required packages and commands. If you'd prefer not to compile the program from source, consider using the container image below.
You can install Video2X on Arch Linux using the [video2x-git](https://aur.archlinux.org/packages/video2x-git) AUR package or on Ubuntu/Debian using the `.deb` package from the [releases page](https://github.com/k4yt3x/video2x/releases/latest). If you'd like to build from source, refer to the [PKGBUILD](packaging/arch/PKGBUILD) file for a general overview of the required packages and commands. If you'd prefer not to compile the program from source, consider using the container image below.
## [📦 Container Image](https://github.com/k4yt3x/video2x/pkgs/container/video2x)

View File

@@ -14,7 +14,7 @@ int init_decoder(
std::filesystem::path in_fpath,
AVFormatContext **fmt_ctx,
AVCodecContext **dec_ctx,
int *vstream_idx
int *in_vstream_idx
);
#endif // DECODER_H

View File

@@ -19,7 +19,8 @@ int init_encoder(
AVCodecContext **enc_ctx,
AVCodecContext *dec_ctx,
EncoderConfig *encoder_config,
int vstream_idx,
int in_vstream_idx,
int *out_vstream_idx,
int **stream_map
);
@@ -27,9 +28,9 @@ int write_frame(
AVFrame *frame,
AVCodecContext *enc_ctx,
AVFormatContext *ofmt_ctx,
int vstream_idx
int out_vstream_idx
);
int flush_encoder(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx);
int flush_encoder(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx, int out_vstream_idx);
#endif // ENCODER_H

View File

@@ -25,7 +25,7 @@ int init_decoder(
std::filesystem::path in_fpath,
AVFormatContext **fmt_ctx,
AVCodecContext **dec_ctx,
int *vstream_idx
int *in_vstream_idx
) {
AVFormatContext *ifmt_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
@@ -110,7 +110,7 @@ int init_decoder(
*fmt_ctx = ifmt_ctx;
*dec_ctx = codec_ctx;
*vstream_idx = stream_index;
*in_vstream_idx = stream_index;
return 0;
}

View File

@@ -25,12 +25,12 @@ int init_encoder(
AVCodecContext **enc_ctx,
AVCodecContext *dec_ctx,
EncoderConfig *encoder_config,
int vstream_idx,
int in_vstream_idx,
int *out_vstream_idx,
int **stream_map
) {
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
int stream_index = 0;
int ret;
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, out_fpath.u8string().c_str());
@@ -49,11 +49,12 @@ int init_encoder(
}
// Create a new video stream in the output file
AVStream *out_stream = avformat_new_stream(fmt_ctx, NULL);
if (!out_stream) {
AVStream *out_vstream = avformat_new_stream(fmt_ctx, NULL);
if (!out_vstream) {
spdlog::error("Failed to allocate the output video stream");
return AVERROR_UNKNOWN;
}
*out_vstream_idx = out_vstream->index;
codec_ctx = avcodec_alloc_context3(encoder);
if (!codec_ctx) {
@@ -72,6 +73,13 @@ int init_encoder(
codec_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
codec_ctx->bit_rate = encoder_config->bit_rate;
// Set the color properties
codec_ctx->color_range = dec_ctx->color_range;
codec_ctx->color_primaries = dec_ctx->color_primaries;
codec_ctx->color_trc = dec_ctx->color_trc;
codec_ctx->colorspace = dec_ctx->colorspace;
codec_ctx->chroma_sample_location = dec_ctx->chroma_sample_location;
// Set the pixel format
if (encoder_config->pix_fmt != AV_PIX_FMT_NONE) {
// Use the specified pixel format
@@ -89,14 +97,14 @@ int init_encoder(
if (dec_ctx->time_base.num > 0 && dec_ctx->time_base.den > 0) {
codec_ctx->time_base = dec_ctx->time_base;
} else {
codec_ctx->time_base = av_inv_q(av_guess_frame_rate(ifmt_ctx, out_stream, NULL));
codec_ctx->time_base = av_inv_q(av_guess_frame_rate(ifmt_ctx, out_vstream, NULL));
}
// Set the output video's frame rate
if (dec_ctx->framerate.num > 0 && dec_ctx->framerate.den > 0) {
codec_ctx->framerate = dec_ctx->framerate;
} else {
codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, out_stream, NULL);
codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, out_vstream, NULL);
}
// Set the CRF and preset for any codecs that support it
@@ -113,15 +121,15 @@ int init_encoder(
return ret;
}
ret = avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);
ret = avcodec_parameters_from_context(out_vstream->codecpar, codec_ctx);
if (ret < 0) {
spdlog::error("Failed to copy encoder parameters to output video stream");
return ret;
}
out_stream->time_base = codec_ctx->time_base;
out_stream->avg_frame_rate = codec_ctx->framerate;
out_stream->r_frame_rate = codec_ctx->framerate;
out_vstream->time_base = codec_ctx->time_base;
out_vstream->avg_frame_rate = codec_ctx->framerate;
out_vstream->r_frame_rate = codec_ctx->framerate;
if (encoder_config->copy_streams) {
// Allocate the stream map
@@ -132,43 +140,46 @@ int init_encoder(
return AVERROR(ENOMEM);
}
// Map the video stream
(*stream_map)[vstream_idx] = stream_index++;
// Loop through each stream in the input file
// Map each input stream to an output stream
for (int i = 0; i < static_cast<int>(ifmt_ctx->nb_streams); i++) {
AVStream *in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (i == vstream_idx) {
// Video stream is already handled
// Skip the input video stream as it's already processed
if (i == in_vstream_idx) {
(*stream_map)[i] = *out_vstream_idx;
continue;
}
// Map only audio and subtitle streams (skip other types)
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
(*stream_map)[i] = -1;
(*stream_map)[i] = -1; // Stream not mapped
spdlog::warn("Skipping unsupported stream type at index: {}", i);
continue;
}
// Create corresponding output stream
AVStream *out_copied_stream = avformat_new_stream(fmt_ctx, NULL);
if (!out_copied_stream) {
// Create corresponding output stream for audio and subtitle streams
AVStream *out_stream = avformat_new_stream(fmt_ctx, NULL);
if (!out_stream) {
spdlog::error("Failed allocating output stream");
return AVERROR_UNKNOWN;
}
ret = avcodec_parameters_copy(out_copied_stream->codecpar, in_codecpar);
// Copy codec parameters from input to output
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0) {
spdlog::error("Failed to copy codec parameters");
return ret;
}
out_copied_stream->codecpar->codec_tag = 0;
out_stream->codecpar->codec_tag = 0;
// Copy time base
out_copied_stream->time_base = in_stream->time_base;
out_stream->time_base = in_stream->time_base;
(*stream_map)[i] = stream_index++;
// Map input stream index to output stream index
spdlog::debug("Stream mapping: {} (in) -> {} (out)", i, out_stream->index);
(*stream_map)[i] = out_stream->index;
}
}
@@ -191,7 +202,7 @@ int write_frame(
AVFrame *frame,
AVCodecContext *enc_ctx,
AVFormatContext *ofmt_ctx,
int vstream_idx
int out_vstream_idx
) {
AVFrame *converted_frame = nullptr;
int ret;
@@ -238,9 +249,9 @@ int write_frame(
// Rescale packet timestamps
av_packet_rescale_ts(
enc_pkt, enc_ctx->time_base, ofmt_ctx->streams[vstream_idx]->time_base
enc_pkt, enc_ctx->time_base, ofmt_ctx->streams[out_vstream_idx]->time_base
);
enc_pkt->stream_index = vstream_idx;
enc_pkt->stream_index = out_vstream_idx;
// Write the packet
ret = av_interleaved_write_frame(ofmt_ctx, enc_pkt);
@@ -256,7 +267,7 @@ int write_frame(
return 0;
}
int flush_encoder(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx) {
int flush_encoder(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx, int out_vstream_idx) {
int ret;
AVPacket *enc_pkt = av_packet_alloc();
if (!enc_pkt) {
@@ -265,26 +276,35 @@ int flush_encoder(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx) {
}
ret = avcodec_send_frame(enc_ctx, NULL);
while (ret >= 0) {
if (ret < 0) {
spdlog::error("Error sending NULL frame to encoder during flush");
av_packet_free(&enc_pkt);
return ret;
}
// Write the packets to the output file
while (true) {
ret = avcodec_receive_packet(enc_ctx, enc_pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
av_packet_unref(enc_pkt);
break;
} else if (ret < 0) {
spdlog::error("Error encoding frame");
spdlog::error("Error encoding packet during flush");
av_packet_free(&enc_pkt);
return ret;
}
// Rescale packet timestamps
av_packet_rescale_ts(enc_pkt, enc_ctx->time_base, ofmt_ctx->streams[0]->time_base);
enc_pkt->stream_index = ofmt_ctx->streams[0]->index;
av_packet_rescale_ts(
enc_pkt, enc_ctx->time_base, ofmt_ctx->streams[out_vstream_idx]->time_base
);
enc_pkt->stream_index = out_vstream_idx;
// Write the packet
ret = av_interleaved_write_frame(ofmt_ctx, enc_pkt);
av_packet_unref(enc_pkt);
if (ret < 0) {
spdlog::error("Error muxing packet");
spdlog::error("Error muxing packet during flush");
av_packet_free(&enc_pkt);
return ret;
}

View File

@@ -26,7 +26,8 @@ static int process_frames(
AVCodecContext *dec_ctx,
AVCodecContext *enc_ctx,
Filter *filter,
int vstream_idx,
int in_vstream_idx,
int out_vstream_idx,
int *stream_map,
bool benchmark = false
) {
@@ -36,7 +37,7 @@ static int process_frames(
// Get the total number of frames in the video with OpenCV
spdlog::debug("Reading total number of frames");
proc_ctx->total_frames = ifmt_ctx->streams[vstream_idx]->nb_frames;
proc_ctx->total_frames = ifmt_ctx->streams[in_vstream_idx]->nb_frames;
if (proc_ctx->total_frames > 0) {
spdlog::debug("Read total number of frames from 'nb_frames': {}", proc_ctx->total_frames);
} else {
@@ -47,27 +48,27 @@ static int process_frames(
if (ifmt_ctx->duration != AV_NOPTS_VALUE) {
duration_secs =
static_cast<double>(ifmt_ctx->duration) / static_cast<double>(AV_TIME_BASE);
} else if (ifmt_ctx->streams[vstream_idx]->duration != AV_NOPTS_VALUE) {
duration_secs = static_cast<double>(ifmt_ctx->streams[vstream_idx]->duration) *
av_q2d(ifmt_ctx->streams[vstream_idx]->time_base);
} else if (ifmt_ctx->streams[in_vstream_idx]->duration != AV_NOPTS_VALUE) {
duration_secs = static_cast<double>(ifmt_ctx->streams[in_vstream_idx]->duration) *
av_q2d(ifmt_ctx->streams[in_vstream_idx]->time_base);
} else {
spdlog::warn("Unable to determine video duration");
}
spdlog::debug("Video duration: {}s", duration_secs);
// Calculate average FPS
double fps = av_q2d(ifmt_ctx->streams[vstream_idx]->avg_frame_rate);
double fps = av_q2d(ifmt_ctx->streams[in_vstream_idx]->avg_frame_rate);
if (fps <= 0) {
spdlog::debug("Unable to read the average frame rate from 'avg_frame_rate'");
fps = av_q2d(ifmt_ctx->streams[vstream_idx]->r_frame_rate);
fps = av_q2d(ifmt_ctx->streams[in_vstream_idx]->r_frame_rate);
}
if (fps <= 0) {
spdlog::debug("Unable to read the average frame rate from 'r_frame_rate'");
fps = av_q2d(av_guess_frame_rate(ifmt_ctx, ifmt_ctx->streams[vstream_idx], nullptr));
fps = av_q2d(av_guess_frame_rate(ifmt_ctx, ifmt_ctx->streams[in_vstream_idx], nullptr));
}
if (fps <= 0) {
spdlog::debug("Unable to estimate the average frame rate with 'av_guess_frame_rate'");
fps = av_q2d(ifmt_ctx->streams[vstream_idx]->time_base);
fps = av_q2d(ifmt_ctx->streams[in_vstream_idx]->time_base);
}
if (fps <= 0 || duration_secs <= 0) {
spdlog::warn("Unable to estimate the video's average frame rate");
@@ -123,7 +124,7 @@ static int process_frames(
return ret;
}
if (packet->stream_index == vstream_idx) {
if (packet->stream_index == in_vstream_idx) {
ret = avcodec_send_packet(dec_ctx, packet);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
@@ -161,7 +162,7 @@ static int process_frames(
return ret;
} else if (ret == 0 && processed_frame != nullptr) {
if (!benchmark) {
ret = write_frame(processed_frame, enc_ctx, ofmt_ctx, vstream_idx);
ret = write_frame(processed_frame, enc_ctx, ofmt_ctx, out_vstream_idx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing frame: {}", errbuf);
@@ -191,7 +192,7 @@ static int process_frames(
ret = av_interleaved_write_frame(ofmt_ctx, packet);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error muxing packet: {}", errbuf);
spdlog::critical("Error muxing audio/subtitle packet: {}", errbuf);
av_packet_unref(packet);
cleanup();
return ret;
@@ -211,7 +212,7 @@ static int process_frames(
// Encode and write all flushed frames
for (AVFrame *&flushed_frame : flushed_frames) {
ret = write_frame(flushed_frame, enc_ctx, ofmt_ctx, vstream_idx);
ret = write_frame(flushed_frame, enc_ctx, ofmt_ctx, out_vstream_idx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing flushed frame: {}", errbuf);
@@ -226,7 +227,7 @@ static int process_frames(
}
// Flush the encoder
ret = flush_encoder(enc_ctx, ofmt_ctx);
ret = flush_encoder(enc_ctx, ofmt_ctx, out_vstream_idx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error flushing encoder: {}", errbuf);
@@ -256,7 +257,8 @@ extern "C" int process_video(
AVBufferRef *hw_ctx = nullptr;
int *stream_map = nullptr;
Filter *filter = nullptr;
int vstream_idx = -1;
int in_vstream_idx = -1;
int out_vstream_idx = -1;
char errbuf[AV_ERROR_MAX_STRING_SIZE];
int ret = 0;
@@ -348,7 +350,7 @@ extern "C" int process_video(
}
// Initialize input
ret = init_decoder(hw_type, hw_ctx, in_fpath, &ifmt_ctx, &dec_ctx, &vstream_idx);
ret = init_decoder(hw_type, hw_ctx, in_fpath, &ifmt_ctx, &dec_ctx, &in_vstream_idx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Failed to initialize decoder: {}", errbuf);
@@ -385,7 +387,8 @@ extern "C" int process_video(
&enc_ctx,
dec_ctx,
encoder_config,
vstream_idx,
in_vstream_idx,
&out_vstream_idx,
&stream_map
);
if (ret < 0) {
@@ -461,7 +464,8 @@ extern "C" int process_video(
dec_ctx,
enc_ctx,
filter,
vstream_idx,
in_vstream_idx,
out_vstream_idx,
stream_map,
benchmark
);

View File

@@ -318,7 +318,15 @@ void process_video_thread(
#ifdef _WIN32
int wmain(int argc, wchar_t *argv[]) {
// Set console output code page to UTF-8
SetConsoleOutputCP(CP_UTF8);
// Enable ANSI escape codes
HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD console_mode = 0;
GetConsoleMode(console_handle, &console_mode);
console_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(console_handle, console_mode);
#else
int main(int argc, char **argv) {
#endif
@@ -593,7 +601,7 @@ int main(int argc, char **argv) {
&encoder_config,
&proc_ctx
);
spdlog::info("Press SPACE to pause/resume, 'q' to abort.");
spdlog::info("Press [space] to pause/resume, [q] to abort.");
// Setup timer
Timer timer;
@@ -633,18 +641,23 @@ int main(int argc, char **argv) {
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
proc_ctx.pause = !proc_ctx.pause;
if (proc_ctx.pause) {
putchar('\n');
spdlog::info("Processing paused. Press SPACE to resume, 'q' to abort.");
std::cout
<< "\r\033[KProcessing paused; press [space] to resume, [q] to abort.";
std::cout.flush();
timer.pause();
} else {
spdlog::info("Resuming processing...");
std::cout << "\r\033[KProcessing resumed.";
std::cout.flush();
timer.resume();
}
newline_required = true;
}
} else if (ch == 'q' || ch == 'Q') {
// Abort processing
putchar('\n');
spdlog::info("Aborting processing...");
if (newline_required) {
putchar('\n');
}
spdlog::warn("Aborting gracefully; press Ctrl+C to terminate forcefully.");
{
std::lock_guard<std::mutex> lock(proc_ctx_mutex);
proc_ctx.abort = true;
@@ -667,9 +680,35 @@ int main(int argc, char **argv) {
double percentage = total_frames > 0 ? static_cast<double>(processed_frames) *
100.0 / static_cast<double>(total_frames)
: 0.0;
int64_t time_elapsed = timer.get_elapsed_time() / 1000;
std::cout << "\rProcessing frame " << processed_frames << "/" << total_frames
<< " (" << percentage << "%); time elapsed: " << time_elapsed << "s";
int time_elapsed = static_cast<int>(timer.get_elapsed_time() / 1000);
// Calculate hours, minutes, and seconds elapsed
int hours_elapsed = time_elapsed / 3600;
int minutes_elapsed = (time_elapsed % 3600) / 60;
int seconds_elapsed = time_elapsed % 60;
// Calculate estimated time remaining
int64_t frames_remaining = total_frames - processed_frames;
double processing_rate = static_cast<double>(processed_frames) / time_elapsed;
int time_remaining =
static_cast<int>(static_cast<double>(frames_remaining) / processing_rate);
time_remaining = std::max<int>(time_remaining, 0);
// Calculate hours, minutes, and seconds remaining
int hours_remaining = time_remaining / 3600;
int minutes_remaining = (time_remaining % 3600) / 60;
int seconds_remaining = time_remaining % 60;
// Print the progress bar
std::cout << "\r\033[Kframe=" << processed_frames << "/" << total_frames << " ("
<< std::fixed << std::setprecision(2) << percentage
<< "%); fps=" << std::fixed << std::setprecision(2) << processing_rate
<< "; elapsed=" << std::setw(2) << std::setfill('0') << hours_elapsed
<< ":" << std::setw(2) << std::setfill('0') << minutes_elapsed << ":"
<< std::setw(2) << std::setfill('0') << seconds_elapsed
<< "; remaining=" << std::setw(2) << std::setfill('0') << hours_remaining
<< ":" << std::setw(2) << std::setfill('0') << minutes_remaining << ":"
<< std::setw(2) << std::setfill('0') << seconds_remaining;
std::cout.flush();
newline_required = true;
}