mirror of
https://github.com/k4yt3x/video2x.git
synced 2026-02-10 14:54:46 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d6a6abce2 | ||
|
|
943b446d26 | ||
|
|
1b81f7d1e2 | ||
|
|
64697a9385 | ||
|
|
e8b0b0ec21 | ||
|
|
05b275dd82 | ||
|
|
33b7c53e16 | ||
|
|
3b7921a774 | ||
|
|
dfb29e05b7 | ||
|
|
3bae03f403 | ||
|
|
9ff320721f |
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@@ -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
52
.github/workflows/issues.yml
vendored
Normal 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
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.**
|
||||
|
||||

|
||||

|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user