mirror of
https://github.com/telegramdesktop/tdesktop
synced 2024-12-26 00:12:25 +00:00
Apply sample_aspect_ratio in streaming.
This commit is contained in:
parent
99e96a5b13
commit
e2eb9cea00
@ -111,6 +111,7 @@ Stream File::Context::initStream(AVMediaType type) {
|
||||
const auto info = _format->streams[index];
|
||||
if (type == AVMEDIA_TYPE_VIDEO) {
|
||||
result.rotation = ReadRotationFromMetadata(info);
|
||||
result.aspect = ValidateAspectRatio(info->sample_aspect_ratio);
|
||||
} else if (type == AVMEDIA_TYPE_AUDIO) {
|
||||
result.frequency = info->codecpar->sample_rate;
|
||||
if (!result.frequency) {
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
[[nodiscard]] rpl::producer<Update, Error> updates() const;
|
||||
|
||||
[[nodiscard]] QImage frame(const FrameRequest &request) const;
|
||||
//[[nodiscard]] int videoRotation() const;
|
||||
|
||||
[[nodiscard]] Media::Player::TrackState prepareLegacyState() const;
|
||||
|
||||
|
@ -23,17 +23,25 @@ constexpr auto kAlignImageBy = 16;
|
||||
constexpr auto kPixelBytesSize = 4;
|
||||
constexpr auto kImageFormat = QImage::Format_ARGB32_Premultiplied;
|
||||
constexpr auto kAvioBlockSize = 4096;
|
||||
constexpr auto kMaxScaleByAspectRatio = 16;
|
||||
|
||||
void AlignedImageBufferCleanupHandler(void* data) {
|
||||
const auto buffer = static_cast<uchar*>(data);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
bool IsAlignedImage(const QImage &image) {
|
||||
[[nodiscard]] bool IsAlignedImage(const QImage &image) {
|
||||
return !(reinterpret_cast<uintptr_t>(image.bits()) % kAlignImageBy)
|
||||
&& !(image.bytesPerLine() % kAlignImageBy);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsValidAspectRatio(AVRational aspect) {
|
||||
return (aspect.num > 0)
|
||||
&& (aspect.den > 0)
|
||||
&& (aspect.num <= aspect.den * kMaxScaleByAspectRatio)
|
||||
&& (aspect.den <= aspect.num * kMaxScaleByAspectRatio);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool GoodStorageForFrame(const QImage &storage, QSize size) {
|
||||
@ -303,6 +311,16 @@ int ReadRotationFromMetadata(not_null<AVStream*> stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVRational ValidateAspectRatio(AVRational aspect) {
|
||||
return IsValidAspectRatio(aspect) ? aspect : kNormalAspect;
|
||||
}
|
||||
|
||||
QSize CorrectByAspect(QSize size, AVRational aspect) {
|
||||
Expects(IsValidAspectRatio(aspect));
|
||||
|
||||
return QSize(size.width() * aspect.num / aspect.den, size.height());
|
||||
}
|
||||
|
||||
bool RotationSwapWidthHeight(int rotation) {
|
||||
return (rotation == 90 || rotation == 270);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ namespace Media {
|
||||
namespace Streaming {
|
||||
|
||||
constexpr auto kUniversalTimeBase = AVRational{ 1, AV_TIME_BASE };
|
||||
constexpr auto kNormalAspect = AVRational{ 1, 1 };
|
||||
|
||||
struct TimePoint {
|
||||
crl::time trackTime = kTimeUnknown;
|
||||
@ -176,6 +177,7 @@ struct Stream {
|
||||
|
||||
// Video only.
|
||||
int rotation = 0;
|
||||
AVRational aspect = kNormalAspect;
|
||||
SwscalePointer swscale;
|
||||
};
|
||||
|
||||
@ -191,7 +193,9 @@ void LogError(QLatin1String method, AvErrorWrap error);
|
||||
AVRational timeBase);
|
||||
[[nodiscard]] crl::time FramePosition(const Stream &stream);
|
||||
[[nodiscard]] int ReadRotationFromMetadata(not_null<AVStream*> stream);
|
||||
[[nodiscard]] AVRational ValidateAspectRatio(AVRational aspect);
|
||||
[[nodiscard]] bool RotationSwapWidthHeight(int rotation);
|
||||
[[nodiscard]] QSize CorrectByAspect(QSize size, AVRational aspect);
|
||||
[[nodiscard]] AvErrorWrap ProcessPacket(Stream &stream, Packet &&packet);
|
||||
[[nodiscard]] AvErrorWrap ReadNextFrame(Stream &stream);
|
||||
|
||||
|
@ -394,7 +394,7 @@ void VideoTrackObject::callReady() {
|
||||
Assert(frame != nullptr);
|
||||
|
||||
auto data = VideoInformation();
|
||||
data.size = frame->original.size();
|
||||
data.size = CorrectByAspect(frame->original.size(), _stream.aspect);
|
||||
if (RotationSwapWidthHeight(_stream.rotation)) {
|
||||
data.size.transpose();
|
||||
}
|
||||
@ -586,6 +586,7 @@ VideoTrack::VideoTrack(
|
||||
, _streamTimeBase(stream.timeBase)
|
||||
, _streamDuration(stream.duration)
|
||||
//, _streamRotation(stream.rotation)
|
||||
//, _streamAspect(stream.aspect)
|
||||
, _shared(std::make_unique<Shared>())
|
||||
, _wrapped(
|
||||
options,
|
||||
|
@ -123,6 +123,7 @@ private:
|
||||
const AVRational _streamTimeBase;
|
||||
const crl::time _streamDuration = 0;
|
||||
//const int _streamRotation = 0;
|
||||
//AVRational _streamAspect = kNormalAspect;
|
||||
std::unique_ptr<Shared> _shared;
|
||||
|
||||
using Implementation = VideoTrackObject;
|
||||
|
@ -1199,7 +1199,15 @@ void OverlayWidget::onCopy() {
|
||||
if (!_current.isNull()) {
|
||||
QApplication::clipboard()->setPixmap(_current);
|
||||
} else if (videoShown()) {
|
||||
QApplication::clipboard()->setImage(videoFrame());
|
||||
// #TODO streaming later apply rotation
|
||||
auto image = videoFrame();
|
||||
if (image.size() != _streamed->info.video.size) {
|
||||
image = image.scaled(
|
||||
_streamed->info.video.size,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
QApplication::clipboard()->setImage(std::move(image));
|
||||
}
|
||||
} else {
|
||||
if (!_photo || !_photo->loaded()) return;
|
||||
@ -1900,13 +1908,14 @@ void OverlayWidget::initStreamingThumbnail() {
|
||||
} else if (thumb && !useThumb) {
|
||||
thumb->load(fileOrigin());
|
||||
}
|
||||
const auto size = useGood ? good->size() : _doc->dimensions;
|
||||
if (!useGood && !thumb && !blurred) {
|
||||
return;
|
||||
} else if (_doc->dimensions.isEmpty()) {
|
||||
} else if (size.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto w = _doc->dimensions.width();
|
||||
const auto h = _doc->dimensions.height();
|
||||
const auto w = size.width();
|
||||
const auto h = size.height();
|
||||
const auto options = VideoThumbOptions(_doc);
|
||||
const auto goodOptions = (options & ~Images::Option::Blurred);
|
||||
_current = (useGood
|
||||
@ -1962,10 +1971,17 @@ void OverlayWidget::validateStreamedGoodThumbnail() {
|
||||
Expects(_doc != nullptr);
|
||||
|
||||
const auto good = _doc->goodThumbnail();
|
||||
const auto &image = _streamed->info.video.cover;
|
||||
auto image = _streamed->info.video.cover;
|
||||
if (image.isNull() || (good && good->loaded()) || _doc->uploading()) {
|
||||
return;
|
||||
}
|
||||
// #TODO streaming later apply rotation
|
||||
if (image.size() != _streamed->info.video.size) {
|
||||
image = image.scaled(
|
||||
_streamed->info.video.size,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
auto bytes = QByteArray();
|
||||
{
|
||||
auto buffer = QBuffer(&bytes);
|
||||
@ -1976,7 +1992,7 @@ void OverlayWidget::validateStreamedGoodThumbnail() {
|
||||
LOG(("App Error: Bad thumbnail data for saving to cache."));
|
||||
} else if (_doc->uploading()) {
|
||||
_doc->setGoodThumbnailOnUpload(
|
||||
base::duplicate(image),
|
||||
std::move(image),
|
||||
std::move(bytes));
|
||||
} else {
|
||||
_doc->owner().cache().putIfEmpty(
|
||||
@ -2187,7 +2203,7 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) {
|
||||
}
|
||||
auto options = Streaming::PlaybackOptions();
|
||||
options.position = position;
|
||||
if (_doc->isAnimation() || true) {
|
||||
if (_doc->isAnimation()) {
|
||||
options.mode = Streaming::Mode::Video;
|
||||
options.loop = true;
|
||||
}
|
||||
@ -2348,17 +2364,13 @@ void OverlayWidget::paintEvent(QPaintEvent *e) {
|
||||
if (rect.intersects(r)) {
|
||||
if (videoShown()) {
|
||||
const auto image = videoFrame();
|
||||
if (image.width() != _w) {
|
||||
//if (_fullScreenVideo) {
|
||||
// const auto fill = rect.intersected(this->rect());
|
||||
// PaintImageProfile(p, image, rect, fill);
|
||||
//} else {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawImage(rect, image);
|
||||
//}
|
||||
} else {
|
||||
p.drawImage(rect.topLeft(), image);
|
||||
}
|
||||
//if (_fullScreenVideo) {
|
||||
// const auto fill = rect.intersected(this->rect());
|
||||
// PaintImageProfile(p, image, rect, fill);
|
||||
//} else {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawImage(rect, image);
|
||||
//}
|
||||
} else if (!_current.isNull()) {
|
||||
if ((!_doc || !_doc->getStickerLarge()) && _current.hasAlpha()) {
|
||||
p.fillRect(rect, _transparentBrush);
|
||||
|
Loading…
Reference in New Issue
Block a user