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