Data Flow for Sending Media Files in Telegram Desktop: A Complete Technical Guide
Telegram Desktop sends media files through a seven-stage pipeline that prepares the file, uploads it to Telegram's servers, constructs an MTProto inputMedia object, and transmits it via MTPmessages_SendMedia before processing the server's Updates response.
The data flow for sending media files like photos, documents, and voice messages in Telegram Desktop follows a strict pipeline from UI selection to network transmission. This article examines the exact implementation in the telegramdesktop/tdesktop repository, tracing how user-selected files become encrypted MTProto messages. Understanding this data flow is essential for developers contributing to the client or building compatible implementations.
Overview of the Media Sending Pipeline
When a user attaches a media file, the application executes a well-defined sequence across multiple subsystems:
- UI Capture – The attach widget captures file selection and triggers preparation
- Media Preparation – Files are validated, classified, and wrapped in a
PreparedList - Upload Streaming –
Storage::Uploaderstreams file parts to Telegram's upload infrastructure - Input Media Construction – Uploaded files are converted to
MTP_inputMedia*objects - Request Assembly –
ApiWrap::sendMediaserializes captions, entities, and send options - Network Dispatch –
Histories::sendPreparedMessagetransmitsMTPmessages_SendMedia - Response Handling – Server-returned
MTPupdatesare applied to the local history
Step-by-Step Data Flow Analysis
1. UI File Selection and Media Preparation
The process begins in Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp. When the user presses Send, the preview widget invokes the preparation layer to transform raw file paths into upload-ready structures.
The UI layer does not handle validation directly; instead, it delegates to Storage::PrepareMediaList to ensure consistent handling across all entry points.
2. File Validation and Metadata Extraction
The core preparation logic resides in Telegram/SourceFiles/storage/storage_media_prepare.cpp. The PrepareMediaList function validates each path, classifies the media type, and extracts essential metadata:
PreparedList PrepareMediaList(
const QStringList &files,
int previewWidth,
bool premium);
This function performs several critical operations:
- Validates local file existence, non-empty paths, and size limits
- Classifies files as photos, videos, music, or generic documents
- Extracts dimensions and MIME types via
FileLoadTask::ReadMediaInformation - Returns a
PreparedListcontainingPreparedFileobjects that drive the subsequent upload
Files that fail validation are flagged with specific error codes, allowing the UI to present appropriate feedback before network operations begin.
3. Uploading to Telegram Servers
Once prepared, files enter the upload subsystem in Telegram/SourceFiles/storage/file_upload.cpp. The Uploader class manages the actual data transmission:
Uploader::Entrydetermines whether to treat the file as a photo, document, or audio, selecting appropriate part containers (filepartsvsthumbparts)- The uploader handles multipart streaming, retry logic, and encryption of file segments
- Upon completion,
Uploader::photoReadyorUploader::documentReadyemits anUploadedMediasignal
The upload layer operates asynchronously, emitting completion signals that trigger the next phase without blocking the UI thread.
4. Constructing MTProto Input Media Objects
When the upload completes, Telegram/SourceFiles/apiwrap.cpp receives the UploadedMedia signal and converts the uploaded data into MTProto-compatible structures. Two specialized functions handle this conversion:
void ApiWrap::sendUploadedPhoto(
FullMsgId fullId,
const UploadedPhoto &info,
const Api::SendOptions &options);
void ApiWrap::sendUploadedDocument(
FullMsgId fullId,
const UploadedDocument &info,
const Api::SendOptions &options);
These functions construct MTP_inputMediaPhoto or MTP_inputMediaDocument objects, embedding the file references returned by Telegram's upload servers. Both functions ultimately forward to the generic sendMedia method, ensuring uniform handling of captions, entities, and send options regardless of media type.
5. Building the Send Media Request
The ApiWrap::sendMedia function in Telegram/SourceFiles/apiwrap.cpp serves as the central coordinator for assembling the final network payload:
void ApiWrap::sendMedia(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
Api::SendOptions options,
Fn<void(bool)> done);
This method:
- Generates a cryptographically secure
randomIdand registers it viaregisterMessageRandomId - Serializes the message caption, entities, and formatting
- Encodes send options including silent mode, scheduling, message effects, and paid star transactions
- Delegates to the history subsystem for actual transmission
6. History Subsystem and Network Transmission
The final network dispatch occurs in Telegram/SourceFiles/data/data_histories.cpp via Histories::sendPreparedMessage:
int Histories::sendPreparedMessage(
History *history,
const ReplyTo *replyTo,
uint64 randomId,
MTP::Request<MTPmessages_SendMedia> request,
Fn<void(const MTPUpdates&, const MTP::Response&)> done,
Fn<void(const MTP::Error&, const MTP::Response&)> fail);
This function:
- Inserts a pending message into the local history cache for immediate UI feedback
- Dispatches the
MTPmessages_SendMediarequest through the MTProto connection layer - Registers success and failure callbacks to handle the asynchronous server response
The history subsystem maintains message state throughout the transmission lifecycle, ensuring that pending messages survive application restarts and network interruptions.
7. Server Response and UI Updates
Upon successful transmission, the server returns MTPupdates containing the finalized message objects. The client processes these updates in Telegram/SourceFiles/history/history.cpp:
History::addNewMessagecreates concreteHistoryIteminstances (photos, documents, voice notes) from the server data- The pending local message is replaced with the authoritative server version
- UI widgets receive update notifications and repaint to reflect the confirmed message state
Practical Implementation: Sending a Photo Programmatically
The following example demonstrates the complete data flow when sending a photo from a custom action:
void sendMyPhoto(
not_null<Main::Session*> session,
const QString &filePath,
const QString &caption) {
// 1️⃣ Prepare the media list (single file → photo)
const auto list = Storage::PrepareMediaList(
QStringList{filePath},
st::sendMediaPreviewSize, // preview width from style
session->premium()); // premium flag
if (list.error != Storage::PreparedList::Error::None) {
LOG(("Failed to prepare media: %1").arg(list.error));
return;
}
// 2️⃣ Upload via the uploader (creates an UploadedMedia signal)
session->uploader().upload(
std::make_shared<Storage::FilePrepareResult>(list.files.front()),
[&](const Storage::UploadedMedia &media) {
// 3️⃣ Build a HistoryItem that will be sent
const auto item = session->data().createItem(
session->data().peer( // target chat
session->user().id),
QString(), // empty text, media will be attached
{ .options = {} });
// 4️⃣ Call ApiWrap – the uploader already called sendUploadedPhoto,
// but we can also call it ourselves:
session->api().sendUploadedPhoto(
item->fullId(),
media.photoInfo,
Api::SendOptions()); // default options
});
}
This snippet directly utilizes Storage::PrepareMediaList for validation, initiates upload via Uploader, and invokes ApiWrap::sendUploadedPhoto upon completion. All thumbnail generation, multipart streaming, and MTProto serialization occur within the framework's internal pipeline.
Summary
- Preparation Phase:
Storage::PrepareMediaListinstorage_media_prepare.cppvalidates files and extracts metadata before any network activity begins. - Upload Phase: The
Uploaderclass infile_upload.cpphandles encrypted multipart uploads and emitsUploadedMediasignals upon completion. - API Construction:
ApiWrapmethods (sendUploadedPhoto,sendUploadedDocument) convert uploaded data intoMTP_inputMedia*objects for the MTProto protocol. - Network Dispatch:
Histories::sendPreparedMessageindata_histories.cpptransmitsMTPmessages_SendMediarequests and manages pending message states. - Response Handling: Server
MTPupdatesare processed by the history subsystem to finalize message objects and update the UI.
Frequently Asked Questions
How does Telegram Desktop handle file uploads for large media files?
Large files are automatically divided into parts by the Uploader class in Telegram/SourceFiles/storage/file_upload.cpp. The system streams these parts sequentially to Telegram's upload servers, with Uploader::Entry managing separate containers for file data and thumbnail previews. This multipart approach allows transmission to resume after network interruptions without re-uploading completed segments.
What is the difference between sendUploadedPhoto and sendUploadedDocument?
sendUploadedPhoto constructs an MTP_inputMediaPhoto object optimized for image files with automatic compression and dimension metadata, while sendUploadedDocument creates an MTP_inputMediaDocument suitable for files requiring preservation of exact bytes, such as PDFs, voice messages, or video files. Both functions reside in Telegram/SourceFiles/apiwrap.cpp and converge on the common sendMedia path after constructing their respective input media types.
How does the MTProto API receive media messages from Telegram Desktop?
The client transmits media via the MTPmessages_SendMedia method, constructed in Telegram/SourceFiles/data/data_histories.cpp by sendPreparedMessage. This request includes the MTPInputMedia reference (pointing to the uploaded file on Telegram's servers), message caption, entities, and flags for options like silent sending or scheduling. The MTProto layer encrypts this payload and delivers it to Telegram's datacenters.
Where is the media upload state managed in the Telegram Desktop codebase?
Upload state is primarily managed by the Storage::Uploader class in Telegram/SourceFiles/storage/file_upload.cpp, which tracks progress for each Uploader::Entry. The history subsystem in Telegram/SourceFiles/data/data_histories.cpp maintains the association between pending local messages and their corresponding upload operations, ensuring that UI state remains synchronized with background transmission progress.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →