2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-10-03 13:07:48 +00:00

PoC video messages sending.

This commit is contained in:
John Preston
2024-10-04 14:38:49 +04:00
parent 4dc7fd8cd1
commit 552343fa37
22 changed files with 1278 additions and 209 deletions

View File

@@ -284,10 +284,12 @@ FormatPointer MakeFormatPointer(
return {};
}
result->pb = io.get();
result->flags |= AVFMT_FLAG_CUSTOM_IO;
auto options = (AVDictionary*)nullptr;
const auto guard = gsl::finally([&] { av_dict_free(&options); });
av_dict_set(&options, "usetoc", "1", 0);
const auto error = AvErrorWrap(avformat_open_input(
&result,
nullptr,
@@ -307,6 +309,54 @@ FormatPointer MakeFormatPointer(
return FormatPointer(result);
}
FormatPointer MakeWriteFormatPointer(
void *opaque,
int(*read)(void *opaque, uint8_t *buffer, int bufferSize),
#if DA_FFMPEG_CONST_WRITE_CALLBACK
int(*write)(void *opaque, const uint8_t *buffer, int bufferSize),
#else
int(*write)(void *opaque, uint8_t *buffer, int bufferSize),
#endif
int64_t(*seek)(void *opaque, int64_t offset, int whence),
const QByteArray &format) {
const AVOutputFormat *found = nullptr;
void *i = nullptr;
while ((found = av_muxer_iterate(&i))) {
if (found->name == format) {
break;
}
}
if (!found) {
LogError(
"av_muxer_iterate",
u"Format %1 not found"_q.arg(QString::fromUtf8(format)));
return {};
}
auto io = MakeIOPointer(opaque, read, write, seek);
if (!io) {
return {};
}
io->seekable = (seek != nullptr);
auto result = (AVFormatContext*)nullptr;
auto error = AvErrorWrap(avformat_alloc_output_context2(
&result,
(AVOutputFormat*)found,
nullptr,
nullptr));
if (!result || error) {
LogError("avformat_alloc_output_context2", error);
return {};
}
result->pb = io.get();
result->flags |= AVFMT_FLAG_CUSTOM_IO;
// Now FormatPointer will own and free the IO context.
io.release();
return FormatPointer(result);
}
void FormatDeleter::operator()(AVFormatContext *value) {
if (value) {
const auto deleter = IOPointer(value->pb);
@@ -448,21 +498,134 @@ SwscalePointer MakeSwscalePointer(
existing);
}
void SwresampleDeleter::operator()(SwrContext *value) {
if (value) {
swr_free(&value);
}
}
SwresamplePointer MakeSwresamplePointer(
#if DA_FFMPEG_NEW_CHANNEL_LAYOUT
AVChannelLayout *srcLayout,
#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
uint64_t srcLayout,
#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
AVSampleFormat srcFormat,
int srcRate,
#if DA_FFMPEG_NEW_CHANNEL_LAYOUT
AVChannelLayout *dstLayout,
#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
uint64_t dstLayout,
#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
AVSampleFormat dstFormat,
int dstRate,
SwresamplePointer *existing) {
// We have to use custom caching for SwsContext, because
// sws_getCachedContext checks passed flags with existing context flags,
// and re-creates context if they're different, but in the process of
// context creation the passed flags are modified before being written
// to the resulting context, so the caching doesn't work.
if (existing && (*existing) != nullptr) {
const auto &deleter = existing->get_deleter();
if (true
#if DA_FFMPEG_NEW_CHANNEL_LAYOUT
&& srcLayout->nb_channels == deleter.srcChannels
&& dstLayout->nb_channels == deleter.dstChannels
#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
&& (av_get_channel_layout_nb_channels(srcLayout)
== deleter.srcChannels)
&& (av_get_channel_layout_nb_channels(dstLayout)
== deleter.dstChannels)
#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
&& srcFormat == deleter.srcFormat
&& dstFormat == deleter.dstFormat
&& srcRate == deleter.srcRate
&& dstRate == deleter.dstRate) {
return std::move(*existing);
}
}
// Initialize audio resampler
#if DA_FFMPEG_NEW_CHANNEL_LAYOUT
auto result = (SwrContext*)nullptr;
auto error = AvErrorWrap(swr_alloc_set_opts2(
&result,
dstLayout,
dstFormat,
dstRate,
srcLayout,
srcFormat,
srcRate,
0,
nullptr));
if (error || !result) {
LogError(u"swr_alloc_set_opts2"_q, error);
return SwresamplePointer();
}
#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
auto result = swr_alloc_set_opts(
existing ? existing.get() : nullptr,
dstLayout,
dstFormat,
dstRate,
srcLayout,
srcFormat,
srcRate,
0,
nullptr);
if (!result) {
LogError(u"swr_alloc_set_opts"_q);
}
#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
error = AvErrorWrap(swr_init(result));
if (error) {
LogError(u"swr_init"_q, error);
swr_free(&result);
return SwresamplePointer();
}
return SwresamplePointer(
result,
{
srcFormat,
srcRate,
#if DA_FFMPEG_NEW_CHANNEL_LAYOUT
srcLayout->nb_channels,
#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
av_get_channel_layout_nb_channels(srcLayout),
#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
dstFormat,
dstRate,
#if DA_FFMPEG_NEW_CHANNEL_LAYOUT
dstLayout->nb_channels,
#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
av_get_channel_layout_nb_channels(dstLayout),
#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
});
}
void SwscaleDeleter::operator()(SwsContext *value) {
if (value) {
sws_freeContext(value);
}
}
void LogError(const QString &method) {
LOG(("Streaming Error: Error in %1.").arg(method));
void LogError(const QString &method, const QString &details) {
LOG(("Streaming Error: Error in %1%2."
).arg(method
).arg(details.isEmpty() ? QString() : " - " + details));
}
void LogError(const QString &method, AvErrorWrap error) {
LOG(("Streaming Error: Error in %1 (code: %2, text: %3)."
void LogError(
const QString &method,
AvErrorWrap error,
const QString &details) {
LOG(("Streaming Error: Error in %1 (code: %2, text: %3)%4."
).arg(method
).arg(error.code()
).arg(error.text()));
).arg(error.text()
).arg(details.isEmpty() ? QString() : " - " + details));
}
crl::time PtsToTime(int64_t pts, AVRational timeBase) {