mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Cut video messages round with white bg.
This commit is contained in:
parent
17996757fd
commit
4cf6173d25
1 changed files with 90 additions and 27 deletions
|
@ -97,11 +97,17 @@ private:
|
||||||
crl::time _lastUpdateDuration = 0;
|
crl::time _lastUpdateDuration = 0;
|
||||||
rpl::event_stream<Update, rpl::empty_error> _updates;
|
rpl::event_stream<Update, rpl::empty_error> _updates;
|
||||||
|
|
||||||
|
void cutCircleFromYUV420P(not_null<AVFrame*> frame);
|
||||||
|
void initCircleMask();
|
||||||
|
|
||||||
|
std::vector<bool> _circleMask; // Always nice to use vector<bool>! :D
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RoundVideoRecorder::Private::Private(crl::weak_on_queue<Private> weak)
|
RoundVideoRecorder::Private::Private(crl::weak_on_queue<Private> weak)
|
||||||
: _weak(std::move(weak)) {
|
: _weak(std::move(weak)) {
|
||||||
initEncoding();
|
initEncoding();
|
||||||
|
initCircleMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
RoundVideoRecorder::Private::~Private() {
|
RoundVideoRecorder::Private::~Private() {
|
||||||
|
@ -416,8 +422,15 @@ void RoundVideoRecorder::Private::push(const Media::Capture::Chunk &chunk) {
|
||||||
void RoundVideoRecorder::Private::encodeVideoFrame(
|
void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
int64 mcstimestamp,
|
int64 mcstimestamp,
|
||||||
const QImage &frame) {
|
const QImage &frame) {
|
||||||
|
const auto fwidth = frame.width();
|
||||||
|
const auto fheight = frame.height();
|
||||||
|
const auto fmin = std::min(fwidth, fheight);
|
||||||
|
const auto fx = (fwidth > fheight) ? (fwidth - fheight) / 2 : 0;
|
||||||
|
const auto fy = (fwidth < fheight) ? (fheight - fwidth) / 2 : 0;
|
||||||
|
const auto crop = QRect(fx, fy, fmin, fmin);
|
||||||
|
|
||||||
_swsContext = MakeSwscalePointer(
|
_swsContext = MakeSwscalePointer(
|
||||||
QSize(kSide, kSide),
|
QSize(fmin, fmin),
|
||||||
AV_PIX_FMT_BGRA,
|
AV_PIX_FMT_BGRA,
|
||||||
QSize(kSide, kSide),
|
QSize(kSide, kSide),
|
||||||
AV_PIX_FMT_YUV420P,
|
AV_PIX_FMT_YUV420P,
|
||||||
|
@ -431,41 +444,91 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
_videoFirstTimestamp = mcstimestamp;
|
_videoFirstTimestamp = mcstimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fwidth = frame.width();
|
const auto cdata = frame.constBits()
|
||||||
const auto fheight = frame.height();
|
+ (frame.bytesPerLine() * fy)
|
||||||
const auto fmin = std::min(fwidth, fheight);
|
+ (fx * frame.depth() / 8);
|
||||||
const auto fx = (fwidth > fheight) ? (fwidth - fheight) / 2 : 0;
|
|
||||||
const auto fy = (fwidth < fheight) ? (fheight - fwidth) / 2 : 0;
|
|
||||||
const auto crop = QRect(fx, fy, fmin, fmin);
|
|
||||||
const auto cropped = frame.copy(crop).scaled(
|
|
||||||
kSide,
|
|
||||||
kSide,
|
|
||||||
Qt::KeepAspectRatio,
|
|
||||||
Qt::SmoothTransformation);
|
|
||||||
|
|
||||||
// Convert QImage to RGB32 format
|
const uint8_t *srcSlice[1] = { cdata };
|
||||||
// QImage rgbImage = cropped.convertToFormat(QImage::Format_ARGB32);
|
int srcStride[1] = { int(frame.bytesPerLine()) };
|
||||||
|
|
||||||
// Prepare source data
|
|
||||||
const uint8_t *srcSlice[1] = { cropped.constBits() };
|
|
||||||
int srcStride[1] = { cropped.bytesPerLine() };
|
|
||||||
|
|
||||||
// Perform the color space conversion
|
|
||||||
sws_scale(
|
sws_scale(
|
||||||
_swsContext.get(),
|
_swsContext.get(),
|
||||||
srcSlice,
|
srcSlice,
|
||||||
srcStride,
|
srcStride,
|
||||||
0,
|
0,
|
||||||
kSide,
|
fmin,
|
||||||
_videoFrame->data,
|
_videoFrame->data,
|
||||||
_videoFrame->linesize);
|
_videoFrame->linesize);
|
||||||
|
|
||||||
|
cutCircleFromYUV420P(_videoFrame.get());
|
||||||
|
|
||||||
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
||||||
if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::initCircleMask() {
|
||||||
|
const auto width = kSide;
|
||||||
|
const auto height = kSide;
|
||||||
|
const auto centerX = width / 2;
|
||||||
|
const auto centerY = height / 2;
|
||||||
|
const auto radius = std::min(centerX, centerY) + 3; // Add some padding.
|
||||||
|
const auto radiusSquared = radius * radius;
|
||||||
|
|
||||||
|
_circleMask.resize(width * height);
|
||||||
|
auto index = 0;
|
||||||
|
for (auto y = 0; y != height; ++y) {
|
||||||
|
for (auto x = 0; x != width; ++x) {
|
||||||
|
const auto dx = x - centerX;
|
||||||
|
const auto dy = y - centerY;
|
||||||
|
_circleMask[index++] = (dx * dx + dy * dy > radiusSquared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::cutCircleFromYUV420P(
|
||||||
|
not_null<AVFrame*> frame) {
|
||||||
|
const auto width = frame->width;
|
||||||
|
const auto height = frame->height;
|
||||||
|
|
||||||
|
auto yMaskIndex = 0;
|
||||||
|
auto yData = frame->data[0];
|
||||||
|
const auto ySkip = frame->linesize[0] - width;
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
if (_circleMask[yMaskIndex]) {
|
||||||
|
*yData = 255;
|
||||||
|
}
|
||||||
|
++yData;
|
||||||
|
++yMaskIndex;
|
||||||
|
}
|
||||||
|
yData += ySkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto whalf = width / 2;
|
||||||
|
const auto hhalf = height / 2;
|
||||||
|
|
||||||
|
auto uvMaskIndex = 0;
|
||||||
|
auto uData = frame->data[1];
|
||||||
|
auto vData = frame->data[2];
|
||||||
|
const auto uSkip = frame->linesize[1] - whalf;
|
||||||
|
for (auto y = 0; y != hhalf; ++y) {
|
||||||
|
for (auto x = 0; x != whalf; ++x) {
|
||||||
|
if (_circleMask[uvMaskIndex]) {
|
||||||
|
*uData = 128;
|
||||||
|
*vData = 128;
|
||||||
|
}
|
||||||
|
++uData;
|
||||||
|
++vData;
|
||||||
|
uvMaskIndex += 2;
|
||||||
|
}
|
||||||
|
uData += uSkip;
|
||||||
|
vData += uSkip;
|
||||||
|
uvMaskIndex += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::encodeAudioFrame(
|
void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
const Media::Capture::Chunk &chunk) {
|
const Media::Capture::Chunk &chunk) {
|
||||||
updateMaxLevel(chunk);
|
updateMaxLevel(chunk);
|
||||||
|
@ -476,18 +539,18 @@ void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
_audioTail.append(chunk.samples);
|
_audioTail.append(chunk.samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int inSamples = _audioTail.size() / sizeof(int16_t);
|
const auto inSamples = int(_audioTail.size() / sizeof(int16_t));
|
||||||
const uint8_t *inData = reinterpret_cast<const uint8_t*>(
|
const auto inData = reinterpret_cast<const uint8_t*>(
|
||||||
_audioTail.constData());
|
_audioTail.constData());
|
||||||
int samplesProcessed = 0;
|
auto samplesProcessed = 0;
|
||||||
|
|
||||||
while (samplesProcessed + _audioCodec->frame_size <= inSamples) {
|
while (samplesProcessed + _audioCodec->frame_size <= inSamples) {
|
||||||
int remainingSamples = inSamples - samplesProcessed;
|
const auto remainingSamples = inSamples - samplesProcessed;
|
||||||
int outSamples = av_rescale_rnd(
|
auto outSamples = int(av_rescale_rnd(
|
||||||
swr_get_delay(_swrContext.get(), 48000) + remainingSamples,
|
swr_get_delay(_swrContext.get(), 48000) + remainingSamples,
|
||||||
_audioCodec->sample_rate,
|
_audioCodec->sample_rate,
|
||||||
48000,
|
48000,
|
||||||
AV_ROUND_UP);
|
AV_ROUND_UP));
|
||||||
|
|
||||||
// Ensure we don't exceed the frame's capacity
|
// Ensure we don't exceed the frame's capacity
|
||||||
outSamples = std::min(outSamples, _audioCodec->frame_size);
|
outSamples = std::min(outSamples, _audioCodec->frame_size);
|
||||||
|
@ -725,4 +788,4 @@ void RoundVideoRecorder::setPaused(bool paused) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
Loading…
Add table
Reference in a new issue