Files
iDescriptor/src/mediastreamer.h
T
2025-11-03 19:55:48 -08:00

120 lines
3.4 KiB
C++

/*
* iDescriptor: A free and open-source idevice management tool.
*
* Copyright (C) 2025 Uncore <https://github.com/uncor3>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MEDIASTREAMER_H
#define MEDIASTREAMER_H
#include "iDescriptor.h"
#include <QMap>
#include <QMutex>
#include <QTcpServer>
#include <QUrl>
#include <libimobiledevice/afc.h>
QT_BEGIN_NAMESPACE
class QTcpSocket;
QT_END_NAMESPACE
/**
* @brief A lightweight HTTP server for streaming media files from iOS devices
*
* This class implements an HTTP server that supports:
* - Basic HTTP GET requests
* - HTTP Range requests for video scrubbing
* - Streaming from AFC (Apple File Conduit) without loading entire file into
* memory
* - Thread-safe operation
*
* The server automatically shuts down when the client disconnects.
*/
class MediaStreamer : public QTcpServer
{
Q_OBJECT
public:
explicit MediaStreamer(iDescriptorDevice *device, afc_client_t afcClient,
const QString &filePath, QObject *parent = nullptr);
~MediaStreamer();
/**
* @brief Get the URL that clients should use to connect to this server
* @return URL in format http://127.0.0.1:port
*/
QUrl getUrl() const;
/**
* @brief Check if the server started successfully
* @return true if server is listening, false otherwise
*/
bool isListening() const;
protected:
void incomingConnection(qintptr socketDescriptor) override;
private slots:
void handleClientDisconnected();
private:
struct HttpRequest {
QString method;
QString path;
QString httpVersion;
QMap<QString, QString> headers;
bool hasRange = false;
qint64 rangeStart = 0;
qint64 rangeEnd = -1;
};
struct StreamingContext {
QTcpSocket *socket;
iDescriptorDevice *device;
QString filePath;
qint64 startByte;
qint64 endByte;
qint64 bytesRemaining;
uint64_t afcHandle;
};
HttpRequest parseHttpRequest(const QByteArray &requestData);
void handleRequest(QTcpSocket *socket, const HttpRequest &request);
void sendErrorResponse(QTcpSocket *socket, int statusCode,
const QString &statusText);
void streamFileRange(QTcpSocket *socket, qint64 startByte, qint64 endByte);
void streamNextChunk(StreamingContext *context);
void cleanupStreamingContext(StreamingContext *context);
qint64 getFileSize();
QString getMimeType() const;
// Core data
iDescriptorDevice *m_device;
QString m_filePath;
// File info cache
mutable QMutex m_fileSizeMutex;
mutable qint64 m_cachedFileSize;
mutable bool m_fileSizeCached;
// Connection management
QList<QTcpSocket *> m_activeConnections;
QMutex m_connectionsMutex;
afc_client_t m_afcClient;
};
#endif // MEDIASTREAMER_H