File indexing completed on 2025-01-19 03:57:05
0001 /********************************************************* 0002 * Copyright (C) 2021, Val Doroshchuk <valbok@gmail.com> * 0003 * * 0004 * This file is part of QtAVPlayer. * 0005 * Free Qt Media Player based on FFmpeg. * 0006 *********************************************************/ 0007 0008 #include "qaviodevice.h" 0009 #include <QMutex> 0010 #include <QWaitCondition> 0011 0012 extern "C" { 0013 #include <libavformat/avio.h> 0014 #include <libavutil/mem.h> 0015 #include <libavutil/error.h> 0016 } 0017 0018 QT_BEGIN_NAMESPACE 0019 0020 struct ReadRequest 0021 { 0022 ReadRequest() = default; 0023 ReadRequest(unsigned char *data, int maxSize): data(data), maxSize(maxSize) { } 0024 int wroteBytes = 0; 0025 unsigned char *data = nullptr; 0026 int maxSize = 0; 0027 }; 0028 0029 class QAVIODevicePrivate 0030 { 0031 Q_DECLARE_PUBLIC(QAVIODevice) 0032 public: 0033 explicit QAVIODevicePrivate(QAVIODevice *q, const QSharedPointer<QIODevice> &device) 0034 : q_ptr(q) 0035 , device(device) 0036 , buffer(static_cast<unsigned char*>(av_malloc(buffer_size))) 0037 , ctx(avio_alloc_context(buffer, static_cast<int>(buffer_size), 0, this, &QAVIODevicePrivate::read, nullptr, !device->isSequential() ? &QAVIODevicePrivate::seek : nullptr)) 0038 { 0039 if (!device->isSequential()) 0040 ctx->seekable = AVIO_SEEKABLE_NORMAL; 0041 } 0042 0043 ~QAVIODevicePrivate() 0044 { 0045 av_free(ctx); 0046 } 0047 0048 void readData() 0049 { 0050 QMutexLocker locker(&mutex); 0051 // if no request or it is being processed 0052 if (readRequest.data == nullptr || readRequest.wroteBytes) 0053 return; 0054 0055 readRequest.wroteBytes = !device->atEnd() ? device->read((char *)readRequest.data, readRequest.maxSize) : AVERROR_EOF; 0056 // Unblock the decoder thread when there is available bytes 0057 if (readRequest.wroteBytes) { 0058 waitCond.wakeAll(); 0059 wakeRead = true; 0060 } 0061 } 0062 0063 static int read(void *opaque, unsigned char *data, int maxSize) 0064 { 0065 auto d = static_cast<QAVIODevicePrivate *>(opaque); 0066 QMutexLocker locker(&d->mutex); 0067 if (d->aborted) 0068 return ECANCELED; 0069 0070 d->readRequest = { data, maxSize }; 0071 // When decoder thread is the same as current 0072 d->wakeRead = false; 0073 locker.unlock(); 0074 // Reading is done on thread where the object is created 0075 QMetaObject::invokeMethod(d->q_ptr, [d] { d->readData(); }, nullptr); 0076 locker.relock(); 0077 // Blocks until data is available 0078 if (!d->wakeRead) 0079 d->waitCond.wait(&d->mutex); 0080 0081 int bytes = d->readRequest.wroteBytes; 0082 d->readRequest = {}; 0083 return bytes; 0084 } 0085 0086 static int64_t seek(void *opaque, int64_t offset, int whence) 0087 { 0088 auto d = static_cast<QAVIODevicePrivate *>(opaque); 0089 QMutexLocker locker(&d->mutex); 0090 if (d->aborted) 0091 return ECANCELED; 0092 0093 int64_t pos = 0; 0094 bool wake = false; 0095 locker.unlock(); 0096 QMetaObject::invokeMethod(d->q_ptr, [&] { 0097 QMutexLocker locker(&d->mutex); 0098 if (whence == AVSEEK_SIZE) { 0099 pos = d->device->size() > 0 ? d->device->size() : 0; 0100 } else { 0101 if (whence == SEEK_END) 0102 offset = d->device->size() - offset; 0103 else if (whence == SEEK_CUR) 0104 offset = d->device->pos() + offset; 0105 0106 pos = d->device->seek(offset) ? d->device->pos() : -1; 0107 } 0108 d->waitCond.wakeAll(); 0109 wake = true; 0110 }, nullptr); 0111 0112 locker.relock(); 0113 if (!wake) 0114 d->waitCond.wait(&d->mutex); 0115 0116 return pos; 0117 } 0118 0119 size_t buffer_size = 64 * 1024; 0120 QAVIODevice *q_ptr = nullptr; 0121 QSharedPointer<QIODevice> device; 0122 unsigned char *buffer = nullptr; 0123 AVIOContext *ctx = nullptr; 0124 mutable QMutex mutex; 0125 QWaitCondition waitCond; 0126 bool aborted = false; 0127 bool wakeRead = false; 0128 ReadRequest readRequest; 0129 }; 0130 0131 QAVIODevice::QAVIODevice(const QSharedPointer<QIODevice> &device, QObject *parent) 0132 : QObject(parent) 0133 , d_ptr(new QAVIODevicePrivate(this, device)) 0134 { 0135 connect(device.get(), &QIODevice::readyRead, this, [this] { 0136 Q_D(QAVIODevice); 0137 d->readData(); 0138 }); 0139 } 0140 0141 QAVIODevice::~QAVIODevice() 0142 { 0143 abort(true); 0144 } 0145 0146 AVIOContext *QAVIODevice::ctx() const 0147 { 0148 return d_func()->ctx; 0149 } 0150 0151 void QAVIODevice::abort(bool aborted) 0152 { 0153 Q_D(QAVIODevice); 0154 QMutexLocker locker(&d->mutex); 0155 d->aborted = aborted; 0156 d->waitCond.wakeAll(); 0157 } 0158 0159 void QAVIODevice::setBufferSize(size_t size) 0160 { 0161 Q_D(QAVIODevice); 0162 QMutexLocker locker(&d->mutex); 0163 d->buffer_size = size; 0164 } 0165 0166 size_t QAVIODevice::bufferSize() const 0167 { 0168 Q_D(const QAVIODevice); 0169 QMutexLocker locker(&d->mutex); 0170 return d->buffer_size; 0171 } 0172 0173 QT_END_NAMESPACE