mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Add author to the top of Reply in Another Chat.
This commit is contained in:
parent
511cfc524f
commit
f74dd3ca1e
8 changed files with 201 additions and 13 deletions
|
@ -3684,6 +3684,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
"lng_reply_in_another_title" = "Reply in...";
|
"lng_reply_in_another_title" = "Reply in...";
|
||||||
"lng_reply_in_another_chat" = "Reply in Another Chat";
|
"lng_reply_in_another_chat" = "Reply in Another Chat";
|
||||||
|
"lng_reply_in_author" = "Message author";
|
||||||
|
"lng_reply_in_chats_list" = "Your chats";
|
||||||
"lng_reply_show_in_chat" = "Show in Chat";
|
"lng_reply_show_in_chat" = "Show in Chat";
|
||||||
"lng_reply_remove" = "Do Not Reply";
|
"lng_reply_remove" = "Do Not Reply";
|
||||||
"lng_reply_about_quote" = "You can select a specific part to quote.";
|
"lng_reply_about_quote" = "You can select a specific part to quote.";
|
||||||
|
|
|
@ -878,6 +878,13 @@ peerListWithInviteViaLink: PeerList(peerListBox) {
|
||||||
peerListSingleRow: PeerList(peerListBox) {
|
peerListSingleRow: PeerList(peerListBox) {
|
||||||
padding: margins(0px, 0px, 0px, 0px);
|
padding: margins(0px, 0px, 0px, 0px);
|
||||||
}
|
}
|
||||||
|
peerListSmallSkips: PeerList(peerListBox) {
|
||||||
|
padding: margins(
|
||||||
|
0px,
|
||||||
|
defaultVerticalListSkip,
|
||||||
|
0px,
|
||||||
|
defaultVerticalListSkip);
|
||||||
|
}
|
||||||
|
|
||||||
scheduleHeight: 95px;
|
scheduleHeight: 95px;
|
||||||
scheduleDateTop: 38px;
|
scheduleDateTop: 38px;
|
||||||
|
|
|
@ -1944,6 +1944,13 @@ PeerListContent::SkipResult PeerListContent::selectSkip(int direction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_controller->overrideKeyboardNavigation(
|
||||||
|
direction,
|
||||||
|
_selected.index.value,
|
||||||
|
newSelectedIndex)) {
|
||||||
|
return { _selected.index.value, _selected.index.value };
|
||||||
|
}
|
||||||
|
|
||||||
_selected.index.value = newSelectedIndex;
|
_selected.index.value = newSelectedIndex;
|
||||||
_selected.element = 0;
|
_selected.element = 0;
|
||||||
if (newSelectedIndex >= 0) {
|
if (newSelectedIndex >= 0) {
|
||||||
|
|
|
@ -357,6 +357,8 @@ public:
|
||||||
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
|
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
|
||||||
virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
|
virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
|
||||||
|
|
||||||
|
virtual void peerListSelectSkip(int direction) = 0;
|
||||||
|
|
||||||
virtual void peerListPressLeftToContextMenu(bool shown) = 0;
|
virtual void peerListPressLeftToContextMenu(bool shown) = 0;
|
||||||
virtual bool peerListTrackRowPressFromGlobal(QPoint globalPosition) = 0;
|
virtual bool peerListTrackRowPressFromGlobal(QPoint globalPosition) = 0;
|
||||||
|
|
||||||
|
@ -573,6 +575,13 @@ public:
|
||||||
Unexpected("PeerListController::customRowRippleMaskGenerator.");
|
Unexpected("PeerListController::customRowRippleMaskGenerator.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool overrideKeyboardNavigation(
|
||||||
|
int direction,
|
||||||
|
int fromIndex,
|
||||||
|
int toIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
return _lifetime;
|
return _lifetime;
|
||||||
}
|
}
|
||||||
|
@ -1016,6 +1025,10 @@ public:
|
||||||
bool highlightRow,
|
bool highlightRow,
|
||||||
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
|
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
|
||||||
|
|
||||||
|
void peerListSelectSkip(int direction) override {
|
||||||
|
_content->selectSkip(direction);
|
||||||
|
}
|
||||||
|
|
||||||
void peerListPressLeftToContextMenu(bool shown) override {
|
void peerListPressLeftToContextMenu(bool shown) override {
|
||||||
_content->pressLeftToContextMenu(shown);
|
_content->pressLeftToContextMenu(shown);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "base/timer_rpl.h"
|
#include "base/timer_rpl.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
|
#include "boxes/filters/edit_filter_chats_list.h"
|
||||||
#include "boxes/peer_list_box.h"
|
#include "boxes/peer_list_box.h"
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
#include "chat_helpers/compose/compose_show.h"
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
|
@ -41,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/themes/window_theme.h"
|
#include "window/themes/window_theme.h"
|
||||||
#include "window/section_widget.h"
|
#include "window/section_widget.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
@ -911,6 +913,117 @@ void DraftOptionsBox(
|
||||||
}, box->lifetime());
|
}, box->lifetime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AuthorSelector {
|
||||||
|
object_ptr<Ui::RpWidget> content = { nullptr };
|
||||||
|
Fn<bool(int, int, int)> overrideKey;
|
||||||
|
};
|
||||||
|
[[nodiscard]] AuthorSelector AuthorRowSelector(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
FullReplyTo reply,
|
||||||
|
Fn<void(not_null<Data::Thread*>)> chosen) {
|
||||||
|
const auto item = session->data().message(reply.messageId);
|
||||||
|
if (!item) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto displayFrom = item->displayFrom();
|
||||||
|
const auto from = displayFrom ? displayFrom : item->from().get();
|
||||||
|
if (!from->isUser() || from == item->history()->peer || from->isSelf()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthorController final : public PeerListController {
|
||||||
|
public:
|
||||||
|
AuthorController(not_null<PeerData*> peer, Fn<void()> click)
|
||||||
|
: _peer(peer)
|
||||||
|
, _click(std::move(click)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare() override {
|
||||||
|
delegate()->peerListAppendRow(
|
||||||
|
std::make_unique<ChatsListBoxController::Row>(
|
||||||
|
_peer->owner().history(_peer),
|
||||||
|
&computeListSt().item));
|
||||||
|
delegate()->peerListRefreshRows();
|
||||||
|
TrackPremiumRequiredChanges(this, _lifetime);
|
||||||
|
}
|
||||||
|
void loadMoreRows() override {
|
||||||
|
}
|
||||||
|
void rowClicked(not_null<PeerListRow*> row) override {
|
||||||
|
if (RecipientRow::ShowLockedError(this, row, WritePremiumRequiredError)) {
|
||||||
|
return;
|
||||||
|
} else if (const auto onstack = _click) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Main::Session &session() const override {
|
||||||
|
return _peer->session();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<PeerData*> _peer;
|
||||||
|
Fn<void()> _click;
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
|
||||||
|
const auto container = result.data();
|
||||||
|
|
||||||
|
container->add(CreatePeerListSectionSubtitle(
|
||||||
|
container,
|
||||||
|
tr::lng_reply_in_author()));
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
|
||||||
|
const auto delegate = container->lifetime().make_state<
|
||||||
|
PeerListContentDelegateSimple
|
||||||
|
>();
|
||||||
|
const auto controller = container->lifetime().make_state<
|
||||||
|
AuthorController
|
||||||
|
>(from, [=] { chosen(from->owner().history(from)); });
|
||||||
|
controller->setStyleOverrides(&st::peerListSingleRow);
|
||||||
|
const auto content = container->add(object_ptr<PeerListContent>(
|
||||||
|
container,
|
||||||
|
controller));
|
||||||
|
delegate->setContent(content);
|
||||||
|
controller->setDelegate(delegate);
|
||||||
|
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
container->add(CreatePeerListSectionSubtitle(
|
||||||
|
container,
|
||||||
|
tr::lng_reply_in_chats_list()));
|
||||||
|
|
||||||
|
const auto overrideKey = [=](int direction, int from, int to) {
|
||||||
|
if (!content->isVisible()) {
|
||||||
|
return false;
|
||||||
|
} else if (direction > 0 && from < 0 && to >= 0) {
|
||||||
|
if (content->hasSelection()) {
|
||||||
|
const auto was = content->selectedIndex();
|
||||||
|
const auto now = content->selectSkip(1).reallyMovedTo;
|
||||||
|
if (was != now) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
content->clearSelection();
|
||||||
|
} else {
|
||||||
|
content->selectSkip(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (direction < 0 && to < 0) {
|
||||||
|
if (!content->hasSelection()) {
|
||||||
|
content->selectLast();
|
||||||
|
} else if (from >= 0 || content->hasSelection()) {
|
||||||
|
content->selectSkip(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
.content = std::move(result),
|
||||||
|
.overrideKey = overrideKey,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ShowReplyToChatBox(
|
void ShowReplyToChatBox(
|
||||||
|
@ -921,7 +1034,7 @@ void ShowReplyToChatBox(
|
||||||
public:
|
public:
|
||||||
using Chosen = not_null<Data::Thread*>;
|
using Chosen = not_null<Data::Thread*>;
|
||||||
|
|
||||||
Controller(not_null<Main::Session*> session)
|
Controller(not_null<Main::Session*> session, FullReplyTo reply)
|
||||||
: ChooseRecipientBoxController({
|
: ChooseRecipientBoxController({
|
||||||
.session = session,
|
.session = session,
|
||||||
.callback = [=](Chosen thread) {
|
.callback = [=](Chosen thread) {
|
||||||
|
@ -929,6 +1042,13 @@ void ShowReplyToChatBox(
|
||||||
},
|
},
|
||||||
.premiumRequiredError = WritePremiumRequiredError,
|
.premiumRequiredError = WritePremiumRequiredError,
|
||||||
}) {
|
}) {
|
||||||
|
_authorRow = AuthorRowSelector(
|
||||||
|
session,
|
||||||
|
reply,
|
||||||
|
[=](Chosen thread) { _singleChosen.fire_copy(thread); });
|
||||||
|
if (_authorRow.content) {
|
||||||
|
setStyleOverrides(&st::peerListSmallSkips);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<Chosen> singleChosen() const {
|
[[nodiscard]] rpl::producer<Chosen> singleChosen() const {
|
||||||
|
@ -939,13 +1059,26 @@ void ShowReplyToChatBox(
|
||||||
return tr::lng_saved_quote_here(tr::now);
|
return tr::lng_saved_quote_here(tr::now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool overrideKeyboardNavigation(
|
||||||
|
int direction,
|
||||||
|
int fromIndex,
|
||||||
|
int toIndex) override {
|
||||||
|
return _authorRow.overrideKey
|
||||||
|
&& _authorRow.overrideKey(direction, fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void prepareViewHook() override {
|
void prepareViewHook() override {
|
||||||
|
if (_authorRow.content) {
|
||||||
|
delegate()->peerListSetAboveWidget(
|
||||||
|
std::move(_authorRow.content));
|
||||||
|
}
|
||||||
ChooseRecipientBoxController::prepareViewHook();
|
ChooseRecipientBoxController::prepareViewHook();
|
||||||
delegate()->peerListSetTitle(tr::lng_reply_in_another_title());
|
delegate()->peerListSetTitle(tr::lng_reply_in_another_title());
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::event_stream<Chosen> _singleChosen;
|
rpl::event_stream<Chosen> _singleChosen;
|
||||||
|
AuthorSelector _authorRow;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -956,7 +1089,7 @@ void ShowReplyToChatBox(
|
||||||
};
|
};
|
||||||
const auto session = &show->session();
|
const auto session = &show->session();
|
||||||
const auto state = [&] {
|
const auto state = [&] {
|
||||||
auto controller = std::make_unique<Controller>(session);
|
auto controller = std::make_unique<Controller>(session, reply);
|
||||||
const auto controllerRaw = controller.get();
|
const auto controllerRaw = controller.get();
|
||||||
auto box = Box<PeerListBox>(std::move(controller), [=](
|
auto box = Box<PeerListBox>(std::move(controller), [=](
|
||||||
not_null<PeerListBox*> box) {
|
not_null<PeerListBox*> box) {
|
||||||
|
|
|
@ -1704,6 +1704,9 @@ void VoiceRecordBar::startRecording() {
|
||||||
) | rpl::start_with_next_error([=](const Update &update) {
|
) | rpl::start_with_next_error([=](const Update &update) {
|
||||||
_recordingTipRequired = (update.samples < kMinSamples);
|
_recordingTipRequired = (update.samples < kMinSamples);
|
||||||
recordUpdated(update.level, update.samples);
|
recordUpdated(update.level, update.samples);
|
||||||
|
if (update.finished) {
|
||||||
|
stop(true);
|
||||||
|
}
|
||||||
}, [=] {
|
}, [=] {
|
||||||
stop(false);
|
stop(false);
|
||||||
}, _recordingLifetime);
|
}, _recordingLifetime);
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace Capture {
|
||||||
struct Update {
|
struct Update {
|
||||||
int samples = 0;
|
int samples = 0;
|
||||||
ushort level = 0;
|
ushort level = 0;
|
||||||
|
|
||||||
|
bool finished = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Chunk {
|
struct Chunk {
|
||||||
|
|
|
@ -24,6 +24,7 @@ constexpr auto kUpdateEach = crl::time(100);
|
||||||
constexpr auto kAudioFrequency = 48'000;
|
constexpr auto kAudioFrequency = 48'000;
|
||||||
constexpr auto kAudioBitRate = 32'000;
|
constexpr auto kAudioBitRate = 32'000;
|
||||||
constexpr auto kVideoBitRate = 3 * 1024 * 1024;
|
constexpr auto kVideoBitRate = 3 * 1024 * 1024;
|
||||||
|
constexpr auto kMaxDuration = 10 * crl::time(1000); AssertIsDebug();
|
||||||
|
|
||||||
using namespace FFmpeg;
|
using namespace FFmpeg;
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ private:
|
||||||
void initEncoding();
|
void initEncoding();
|
||||||
bool initVideo();
|
bool initVideo();
|
||||||
bool initAudio();
|
bool initAudio();
|
||||||
|
void notifyFinished();
|
||||||
void deinitEncoding();
|
void deinitEncoding();
|
||||||
void finishEncoding();
|
void finishEncoding();
|
||||||
void fail();
|
void fail();
|
||||||
|
@ -66,6 +68,9 @@ private:
|
||||||
void updateMaxLevel(const Media::Capture::Chunk &chunk);
|
void updateMaxLevel(const Media::Capture::Chunk &chunk);
|
||||||
void updateResultDuration(int64 pts, AVRational timeBase);
|
void updateResultDuration(int64 pts, AVRational timeBase);
|
||||||
|
|
||||||
|
void cutCircleFromYUV420P(not_null<AVFrame*> frame);
|
||||||
|
void initCircleMask();
|
||||||
|
|
||||||
const crl::weak_on_queue<Private> _weak;
|
const crl::weak_on_queue<Private> _weak;
|
||||||
|
|
||||||
FormatPointer _format;
|
FormatPointer _format;
|
||||||
|
@ -95,14 +100,12 @@ private:
|
||||||
QByteArray _result;
|
QByteArray _result;
|
||||||
int64_t _resultOffset = 0;
|
int64_t _resultOffset = 0;
|
||||||
crl::time _resultDuration = 0;
|
crl::time _resultDuration = 0;
|
||||||
|
bool _finished = false;
|
||||||
|
|
||||||
ushort _maxLevelSinceLastUpdate = 0;
|
ushort _maxLevelSinceLastUpdate = 0;
|
||||||
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
|
std::vector<bool> _circleMask; // Always nice to use vector<bool>! :D
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -400,7 +403,7 @@ void RoundVideoRecorder::Private::deinitEncoding() {
|
||||||
void RoundVideoRecorder::Private::push(
|
void RoundVideoRecorder::Private::push(
|
||||||
int64 mcstimestamp,
|
int64 mcstimestamp,
|
||||||
const QImage &frame) {
|
const QImage &frame) {
|
||||||
if (!_format) {
|
if (!_format || _finished) {
|
||||||
return;
|
return;
|
||||||
} else if (!_firstAudioChunkFinished) {
|
} else if (!_firstAudioChunkFinished) {
|
||||||
// Skip frames while we didn't start receiving audio.
|
// Skip frames while we didn't start receiving audio.
|
||||||
|
@ -412,7 +415,7 @@ void RoundVideoRecorder::Private::push(
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::push(const Media::Capture::Chunk &chunk) {
|
void RoundVideoRecorder::Private::push(const Media::Capture::Chunk &chunk) {
|
||||||
if (!_format) {
|
if (!_format || _finished) {
|
||||||
return;
|
return;
|
||||||
} else if (!_firstAudioChunkFinished || !_firstVideoFrameTime) {
|
} else if (!_firstAudioChunkFinished || !_firstVideoFrameTime) {
|
||||||
_firstAudioChunkFinished = chunk.finished;
|
_firstAudioChunkFinished = chunk.finished;
|
||||||
|
@ -425,6 +428,11 @@ 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) {
|
||||||
|
Expects(!_finished);
|
||||||
|
|
||||||
|
if (_videoFirstTimestamp == -1) {
|
||||||
|
_videoFirstTimestamp = mcstimestamp;
|
||||||
|
}
|
||||||
const auto fwidth = frame.width();
|
const auto fwidth = frame.width();
|
||||||
const auto fheight = frame.height();
|
const auto fheight = frame.height();
|
||||||
const auto fmin = std::min(fwidth, fheight);
|
const auto fmin = std::min(fwidth, fheight);
|
||||||
|
@ -443,10 +451,6 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_videoFirstTimestamp == -1) {
|
|
||||||
_videoFirstTimestamp = mcstimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto cdata = frame.constBits()
|
const auto cdata = frame.constBits()
|
||||||
+ (frame.bytesPerLine() * fy)
|
+ (frame.bytesPerLine() * fy)
|
||||||
+ (fx * frame.depth() / 8);
|
+ (fx * frame.depth() / 8);
|
||||||
|
@ -466,7 +470,10 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
cutCircleFromYUV420P(_videoFrame.get());
|
cutCircleFromYUV420P(_videoFrame.get());
|
||||||
|
|
||||||
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
||||||
if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
if (_videoFrame->pts >= kMaxDuration * int64(1000)) {
|
||||||
|
notifyFinished();
|
||||||
|
return;
|
||||||
|
} else if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,6 +541,8 @@ void RoundVideoRecorder::Private::cutCircleFromYUV420P(
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::encodeAudioFrame(
|
void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
const Media::Capture::Chunk &chunk) {
|
const Media::Capture::Chunk &chunk) {
|
||||||
|
Expects(!_finished);
|
||||||
|
|
||||||
updateMaxLevel(chunk);
|
updateMaxLevel(chunk);
|
||||||
|
|
||||||
if (_audioTail.isEmpty()) {
|
if (_audioTail.isEmpty()) {
|
||||||
|
@ -578,7 +587,10 @@ void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
|
|
||||||
_audioFrame->pts = _audioPts;
|
_audioFrame->pts = _audioPts;
|
||||||
_audioPts += _audioFrame->nb_samples;
|
_audioPts += _audioFrame->nb_samples;
|
||||||
if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
if (_audioPts >= kMaxDuration * int64(kAudioFrequency) / 1000) {
|
||||||
|
notifyFinished();
|
||||||
|
return;
|
||||||
|
} else if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,6 +605,15 @@ void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::notifyFinished() {
|
||||||
|
_finished = true;
|
||||||
|
_updates.fire({
|
||||||
|
.samples = int(_resultDuration * 48),
|
||||||
|
.level = base::take(_maxLevelSinceLastUpdate),
|
||||||
|
.finished = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool RoundVideoRecorder::Private::writeFrame(
|
bool RoundVideoRecorder::Private::writeFrame(
|
||||||
const FramePointer &frame,
|
const FramePointer &frame,
|
||||||
const CodecPointer &codec,
|
const CodecPointer &codec,
|
||||||
|
|
Loading…
Add table
Reference in a new issue