2,045 changes: 1,034 additions & 1,011 deletions mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp

Large diffs are not rendered by default.

439 changes: 206 additions & 233 deletions mythtv/libs/libmythtv/DVD/dvdringbuffer.h

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions mythtv/libs/libmythtv/DVD/mythdvdplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ bool MythDVDPlayer::DoJumpChapter(int Chapter)
}
}

bool success = m_playerCtx->m_buffer->DVD()->playTrack(Chapter);
bool success = m_playerCtx->m_buffer->DVD()->PlayTrack(Chapter);
if (success)
{
if (m_decoder)
Expand Down Expand Up @@ -715,7 +715,7 @@ bool MythDVDPlayer::SwitchAngle(int Angle)
if (Angle < 1 || Angle > total)
Angle = 1;

return m_playerCtx->m_buffer->DVD()->SwitchAngle(static_cast<uint>(Angle));
return m_playerCtx->m_buffer->DVD()->SwitchAngle(Angle);
}

void MythDVDPlayer::ResetStillFrameTimer(void)
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/HLS/httplivestreambuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2657,7 +2657,7 @@ void HLSRingBuffer::WaitUntilBuffered(void)
m_streamworker->Unlock();
}

int HLSRingBuffer::safe_read(void *data, uint sz)
int HLSRingBuffer::SafeRead(void *data, uint sz)
{
if (m_error)
return -1;
Expand Down
3 changes: 1 addition & 2 deletions mythtv/libs/libmythtv/HLS/httplivestreambuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,12 @@ class HLSRingBuffer : public RingBuffer
static bool TestForHTTPLiveStreaming(const QString &filename);
bool SaveToDisk(const QString &filename, int segstart = 0, int segend = -1);
int NumStreams(void) const;
int Read(void *data, uint i_read) { return safe_read(data, i_read); }
void Interrupt(void);
void Continue(void);
int DurationForBytes(uint size);

protected:
int safe_read(void *data, uint sz) override; // RingBuffer
int SafeRead(void *data, uint sz) override; // RingBuffer
long long GetRealFileSizeInternal(void) const override; // RingBuffer
long long SeekInternal(long long pos, int whence) override; // RingBuffer

Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/avformatwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ bool AVFormatWriter::OpenFile(void)

m_avfRingBuffer = new AVFRingBuffer(m_ringBuffer);
auto *uc = (URLContext *)m_ctx->pb->opaque;
uc->prot = AVFRingBuffer::GetRingBufferURLProtocol();
uc->prot = AVFRingBuffer::GetURLProtocol();
uc->priv_data = (void *)m_avfRingBuffer;

if (avformat_write_header(m_ctx, nullptr) < 0)
Expand Down
93 changes: 52 additions & 41 deletions mythtv/libs/libmythtv/avfringbuffer.cpp
Original file line number Diff line number Diff line change
@@ -1,85 +1,94 @@
// MythTV
#include "avfringbuffer.h"
#include "mythcorecontext.h"

URLProtocol AVFRingBuffer::s_avfrURL;

int AVFRingBuffer::AVF_Open(URLContext *h, const char *filename, int flags)
AVFRingBuffer::AVFRingBuffer(RingBuffer *Buffer)
: m_ringBuffer(Buffer)
{
(void)filename;
(void)flags;
}

void AVFRingBuffer::SetRingBuffer(RingBuffer *Buffer)
{
m_ringBuffer = Buffer;
}

RingBuffer* AVFRingBuffer::GetRingBuffer(void)
{
return m_ringBuffer;
}

h->priv_data = nullptr;
int AVFRingBuffer::Open(URLContext *Context, const char*, int)
{
Context->priv_data = nullptr;
return 0;
}

int AVFRingBuffer::AVF_Read(URLContext *h, uint8_t *buf, int buf_size)
int AVFRingBuffer::Read(URLContext *Context, uint8_t *Buffer, int Size)
{
auto *avfr = (AVFRingBuffer *)h->priv_data;
auto *avfr = reinterpret_cast<AVFRingBuffer*>(Context->priv_data);
if (!avfr)
return 0;

int ret = avfr->GetRingBuffer()->Read(buf, buf_size);
int ret = avfr->GetRingBuffer()->Read(Buffer, Size);

if (ret == 0)
ret = AVERROR_EOF;
return ret;
}

int AVFRingBuffer::AVF_Write(URLContext *h, const uint8_t *buf, int buf_size)
int AVFRingBuffer::Write(URLContext *h, const uint8_t *Buffer, int Size)
{
auto *avfr = (AVFRingBuffer *)h->priv_data;
auto *avfr = reinterpret_cast<AVFRingBuffer*>(h->priv_data);
if (!avfr)
return 0;

return avfr->GetRingBuffer()->Write(buf, buf_size);
return avfr->GetRingBuffer()->Write(Buffer, static_cast<uint>(Size));
}

int64_t AVFRingBuffer::AVF_Seek(URLContext *h, int64_t offset, int whence)
int64_t AVFRingBuffer::Seek(URLContext *Context, int64_t Offset, int Whence)
{
auto *avfr = (AVFRingBuffer *)h->priv_data;
auto *avfr = reinterpret_cast<AVFRingBuffer*>(Context->priv_data);
if (!avfr)
return 0;

if (whence == AVSEEK_SIZE)
if (Whence == AVSEEK_SIZE)
return avfr->GetRingBuffer()->GetRealFileSize();

if (whence == SEEK_END)
return avfr->GetRingBuffer()->GetRealFileSize() + offset;
if (Whence == SEEK_END)
return avfr->GetRingBuffer()->GetRealFileSize() + Offset;

return avfr->GetRingBuffer()->Seek(offset, whence);
return avfr->GetRingBuffer()->Seek(Offset, Whence);
}

int AVFRingBuffer::AVF_Close(URLContext *h)
int AVFRingBuffer::Close(URLContext*)
{
(void)h;
return 0;
}

int AVFRingBuffer::AVF_Write_Packet(void *opaque, uint8_t *buf, int buf_size)
int AVFRingBuffer::WritePacket(void *Context, uint8_t *Buffer, int Size)
{
if (!opaque)
if (!Context)
return 0;

return ffurl_write((URLContext *)opaque, buf, buf_size);
return ffurl_write(reinterpret_cast<URLContext*>(Context), Buffer, Size);
}

int AVFRingBuffer::AVF_Read_Packet(void *opaque, uint8_t *buf, int buf_size)
int AVFRingBuffer::ReadPacket(void *Context, uint8_t *Buffer, int Size)
{
if (!opaque)
if (!Context)
return 0;

return ffurl_read((URLContext *)opaque, buf, buf_size);
return ffurl_read(reinterpret_cast<URLContext*>(Context), Buffer, Size);
}

int64_t AVFRingBuffer::AVF_Seek_Packet(void *opaque, int64_t offset, int whence)
int64_t AVFRingBuffer::SeekPacket(void *Context, int64_t Offset, int Whence)
{
if (!opaque)
if (!Context)
return 0;

return ffurl_seek((URLContext *)opaque, offset, whence);
return ffurl_seek(reinterpret_cast<URLContext*>(Context), Offset, Whence);
}

URLProtocol *AVFRingBuffer::GetRingBufferURLProtocol(void)
URLProtocol *AVFRingBuffer::GetURLProtocol(void)
{
static QMutex s_avringbufferLock(QMutex::Recursive);
static bool s_avringbufferInitialised = false;
Expand All @@ -90,23 +99,25 @@ URLProtocol *AVFRingBuffer::GetRingBufferURLProtocol(void)
// just in case URLProtocol's members do not have default constructor
memset(static_cast<void*>(&s_avfrURL), 0, sizeof(s_avfrURL));
s_avfrURL.name = "rbuffer";
s_avfrURL.url_open = AVF_Open;
s_avfrURL.url_read = AVF_Read;
s_avfrURL.url_write = AVF_Write;
s_avfrURL.url_seek = AVF_Seek;
s_avfrURL.url_close = AVF_Close;
s_avfrURL.url_open = Open;
s_avfrURL.url_read = Read;
s_avfrURL.url_write = Write;
s_avfrURL.url_seek = Seek;
s_avfrURL.url_close = Close;
s_avfrURL.priv_data_size = 0;
s_avfrURL.flags = URL_PROTOCOL_FLAG_NETWORK;
s_avringbufferInitialised = true;
}
return &s_avfrURL;
}

void AVFRingBuffer::SetInInit(bool state)
void AVFRingBuffer::SetInInit(bool State)
{
m_initState = state;

GetRingBuffer()->SetReadInternalMode(state);
m_initState = State;
GetRingBuffer()->SetReadInternalMode(State);
}

/* vim: set expandtab tabstop=4 shiftwidth=4: */
bool AVFRingBuffer::IsInInit(void)
{
return m_initState;
}
52 changes: 22 additions & 30 deletions mythtv/libs/libmythtv/avfringbuffer.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#ifndef _AVFRINGBUFFER_H_
#define _AVFRINGBUFFER_H_
#ifndef AVFRINGBUFFER_H
#define AVFRINGBUFFER_H

// MythTV
#include "ringbuffer.h"

// FFmpeg
extern "C" {
#include "libavformat/avformat.h"
#include "libavformat/url.h"
Expand All @@ -12,35 +14,25 @@ extern URLProtocol AVF_RingBuffer_Protocol;

class AVFRingBuffer
{
public:
explicit AVFRingBuffer(RingBuffer *rbuffer = nullptr)
: m_rbuffer(rbuffer) { }
void SetRingBuffer(RingBuffer *rbuffer)
{
m_rbuffer = rbuffer;
}
RingBuffer *GetRingBuffer(void)
{
return m_rbuffer;
}
static URLProtocol *GetRingBufferURLProtocol(void);
static int AVF_Write_Packet(void *opaque, uint8_t *buf, int buf_size);
static int AVF_Read_Packet(void *opaque, uint8_t *buf, int buf_size);
static int64_t AVF_Seek_Packet(void *opaque, int64_t offset, int whence);
static int AVF_Open(URLContext *h, const char *filename, int flags);
static int AVF_Read(URLContext *h, uint8_t *buf, int buf_size);
static int AVF_Write(URLContext *h, const uint8_t *buf, int buf_size);
static int64_t AVF_Seek(URLContext *h, int64_t offset, int whence);
static int AVF_Close(URLContext *h);
void SetInInit(bool state);
bool IsInInit(void) { return m_initState; }
public:
explicit AVFRingBuffer(RingBuffer *Buffer = nullptr);
void SetRingBuffer (RingBuffer *Buffer);
RingBuffer* GetRingBuffer (void);
static URLProtocol* GetURLProtocol (void);
static int WritePacket (void* Context, uint8_t *Buffer, int Size);
static int ReadPacket (void* Context, uint8_t *Buffer, int Size);
static int64_t SeekPacket (void* Context, int64_t Offset, int Whence);
static int Open (URLContext* Context, const char *Filename, int Flags);
static int Read (URLContext* Context, uint8_t *Buffer, int Size);
static int Write (URLContext* Context, const uint8_t *Buffer, int Size);
static int64_t Seek (URLContext* Context, int64_t Offset, int Whence);
static int Close (URLContext*);
void SetInInit (bool State);
bool IsInInit (void);

private:
RingBuffer *m_rbuffer {nullptr};
bool m_initState {true};
private:
RingBuffer *m_ringBuffer { nullptr };
bool m_initState { true };
static URLProtocol s_avfrURL;
};

#endif

/* vim: set expandtab tabstop=4 shiftwidth=4: */
8 changes: 4 additions & 4 deletions mythtv/libs/libmythtv/decoders/avformatdecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -898,17 +898,17 @@ void AvFormatDecoder::InitByteContext(bool forceseek)
{
int buf_size = m_ringBuffer->BestBufferSize();
int streamed = m_ringBuffer->IsStreamed();
m_readContext.prot = AVFRingBuffer::GetRingBufferURLProtocol();
m_readContext.prot = AVFRingBuffer::GetURLProtocol();
m_readContext.flags = AVIO_FLAG_READ;
m_readContext.is_streamed = streamed;
m_readContext.max_packet_size = 0;
m_readContext.priv_data = m_avfRingBuffer;
auto *buffer = (unsigned char *)av_malloc(buf_size);
m_ic->pb = avio_alloc_context(buffer, buf_size, 0,
&m_readContext,
AVFRingBuffer::AVF_Read_Packet,
AVFRingBuffer::AVF_Write_Packet,
AVFRingBuffer::AVF_Seek_Packet);
AVFRingBuffer::ReadPacket,
AVFRingBuffer::WritePacket,
AVFRingBuffer::SeekPacket);

// We can always seek during LiveTV
m_ic->pb->seekable = !streamed || forceseek;
Expand Down
176 changes: 87 additions & 89 deletions mythtv/libs/libmythtv/dvdstream.cpp
Original file line number Diff line number Diff line change
@@ -1,94 +1,103 @@
/* DVD stream
* Copyright 2011 Lawrence Rust <lvr at softsystem dot co dot uk>
*/
#include "dvdstream.h"

#include <algorithm>
#include <cstdio>

// Qt
#include <QMutexLocker>
#include <QtGlobal>
#include <QtAlgorithms>

// MythTV
#include "mythlogging.h"
#include "dvdstream.h"

// Std
#include <algorithm>
#include <cstdio>

// DVD
#include "dvdread/dvd_reader.h"
#include "dvdread/dvd_udf.h" // for UDFFindFile
#include "dvdread/dvd_udf.h"
extern "C" {
#include "dvd_input.h" // for DVDINPUT_READ_DECRYPT & DVDCSS_SEEK_KEY
#include "dvd_input.h"
}

#include "mythlogging.h"

#define LOC QString("DVDStream: ")

// A range of block numbers
/// \class DVDStream::BlockRange A range of block numbers
class DVDStream::BlockRange
{
uint32_t m_start;
uint32_t m_end;
int m_title;
public:
BlockRange(uint32_t Start, uint32_t Count, int Title)
: m_start(Start),
m_end(Start + Count),
m_title(Title)
{
}

public:
BlockRange(uint32_t b, uint32_t n, int t) : m_start(b), m_end(b+n), m_title(t) { }
bool operator < (const BlockRange& rhs) const
{
return m_end <= rhs.m_start;
}

bool operator < (const BlockRange& rhs) const { return m_end <= rhs.m_start; }
uint32_t Start (void) const { return m_start; }
uint32_t End (void) const { return m_end; }
int Title (void) const { return m_title; }

uint32_t Start() const { return m_start; }
uint32_t End() const { return m_end; }
int Title() const { return m_title; }
private:
uint32_t m_start { 0 };
uint32_t m_end { 0 };
int m_title { 0 };
};


// Private but located in/shared with dvd_reader.c
extern "C" int InternalUDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
size_t block_count, unsigned char *data,
int encrypted );

extern "C" int InternalUDFReadBlocksRaw(dvd_reader_t *device, uint32_t lb_number,
size_t block_count, unsigned char *data,
int encrypted);

// Roundup bytes to DVD blocks
inline uint32_t Len2Blocks(uint32_t len)
/// \brief Roundup bytes to DVD blocks
inline uint32_t Len2Blocks(uint32_t Length)
{
return (len + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN;
return (Length + (DVD_VIDEO_LB_LEN - 1)) / DVD_VIDEO_LB_LEN;
}

DVDStream::DVDStream(const QString& filename)
: RingBuffer(kRingBuffer_File)
/// \class DVDStream Stream content from a DVD image file
DVDStream::DVDStream(const QString& Filename)
: RingBuffer(kRingBuffer_File)
{
DVDStream::OpenFile(filename);
OpenFile(Filename);
}

DVDStream::~DVDStream()
{
KillReadAheadThread();

m_rwLock.lockForWrite();

if (m_reader)
DVDClose(m_reader);

m_rwLock.unlock();
}

/** \fn DVDStream::OpenFile(const QString &, uint)
* \brief Opens a dvd device for streaming.
*
* \param lfilename Path of the dvd device to read.
* \param retry_ms Ignored. This value is part of the API
* inherited from the parent class.
* \param Filename Path of the dvd device to read.
* \return Returns true if the dvd was opened.
*/
bool DVDStream::OpenFile(const QString &filename, uint /*retry_ms*/)
bool DVDStream::OpenFile(const QString &Filename, uint /*Retry*/)
{
m_rwLock.lockForWrite();

const QString root = filename.section("/VIDEO_TS/", 0, 0);
const QString path = filename.section(root, 1);
const QString root = Filename.section("/VIDEO_TS/", 0, 0);
const QString path = Filename.section(root, 1);

if (m_reader)
DVDClose(m_reader);

m_reader = DVDOpen(qPrintable(root));
if (!m_reader)
{
LOG(VB_GENERAL, LOG_ERR, QString("DVDStream DVDOpen(%1) failed").arg(filename));
LOG(VB_GENERAL, LOG_ERR, LOC + QString("DVDOpen(%1) failed").arg(Filename));
m_rwLock.unlock();
return false;
}
Expand All @@ -100,14 +109,14 @@ bool DVDStream::OpenFile(const QString &filename, uint /*retry_ms*/)
m_start = UDFFindFile(m_reader, qPrintable(path), &len);
if (m_start == 0)
{
LOG(VB_GENERAL, LOG_ERR, QString("DVDStream(%1) UDFFindFile(%2) failed").
LOG(VB_GENERAL, LOG_ERR, LOC + QString("(%1) UDFFindFile(%2) failed").
arg(root).arg(path));
DVDClose(m_reader);
m_reader = nullptr;
m_rwLock.unlock();
return false;
}
m_list.append(BlockRange(0, Len2Blocks(len), 0));
m_blocks.append(BlockRange(0, Len2Blocks(len), 0));
}
else
{
Expand All @@ -118,42 +127,41 @@ bool DVDStream::OpenFile(const QString &filename, uint /*retry_ms*/)
char name[64] = "VIDEO_TS/VIDEO_TS.VOB";
uint32_t start = UDFFindFile(m_reader, name, &len);
if( start != 0 && len != 0 )
m_list.append(BlockRange(start, Len2Blocks(len), 0));
m_blocks.append(BlockRange(start, Len2Blocks(len), 0));

const int kTitles = 100;
for ( int title = 1; title < kTitles; ++title)
for (int title = 1; title < kTitles; ++title)
{
// Menu
snprintf(name, sizeof name, "/VIDEO_TS/VTS_%02d_0.VOB", title);
start = UDFFindFile(m_reader, name, &len);
if( start != 0 && len != 0 )
m_list.append(BlockRange(start, Len2Blocks(len), title));
m_blocks.append(BlockRange(start, Len2Blocks(len), title));

for ( int part = 1; part < 10; ++part)
{
// A/V track
snprintf(name, sizeof name, "/VIDEO_TS/VTS_%02d_%d.VOB", title, part);
start = UDFFindFile(m_reader, name, &len);
if( start != 0 && len != 0 )
m_list.append(BlockRange(start, Len2Blocks(len), title + part * kTitles));
m_blocks.append(BlockRange(start, Len2Blocks(len), title + part * kTitles));
}
}

std::sort(m_list.begin(), m_list.end());
std::sort(m_blocks.begin(), m_blocks.end());

// Open the root menu so that CSS keys are generated now
dvd_file_t *file = DVDOpenFile(m_reader, 0, DVD_READ_MENU_VOBS);
if (file)
DVDCloseFile(file);
else
LOG(VB_GENERAL, LOG_ERR, "DVDStream DVDOpenFile(VOBS_1) failed");
LOG(VB_GENERAL, LOG_ERR, LOC + "DVDOpenFile(VOBS_1) failed");
}

m_rwLock.unlock();
return true;
}

//virtual
bool DVDStream::IsOpen(void) const
{
m_rwLock.lockForRead();
Expand All @@ -162,13 +170,12 @@ bool DVDStream::IsOpen(void) const
return ret;
}

//virtual
int DVDStream::safe_read(void *data, uint size)
int DVDStream::SafeRead(void *Buffer, uint Size)
{
uint32_t lb = size / DVD_VIDEO_LB_LEN;
if (lb < 1)
uint32_t block = Size / DVD_VIDEO_LB_LEN;
if (block < 1)
{
LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read too small");
LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead too small");
return -1;
}

Expand All @@ -178,30 +185,29 @@ int DVDStream::safe_read(void *data, uint size)
int ret = 0;

// Are any blocks in the range encrypted?
list_t::const_iterator it;
it = std::lower_bound(m_list.begin(), m_list.end(), BlockRange(m_pos, lb, -1));
uint32_t b = it == m_list.end() ? lb : m_pos < it->Start() ? it->Start() - m_pos : 0;
auto it = std::lower_bound(m_blocks.begin(), m_blocks.end(), BlockRange(m_pos, block, -1));
uint32_t b = (it == m_blocks.end()) ? block : (m_pos < it->Start() ? it->Start() - m_pos : 0);
if (b)
{
// Read the beginning unencrypted blocks
ret = InternalUDFReadBlocksRaw(m_reader, m_pos, b, (unsigned char*)data, DVDINPUT_NOFLAGS);
ret = InternalUDFReadBlocksRaw(m_reader, m_pos, b, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
if (ret == -1)
{
LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error");
return -1;
LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
return ret;
}

m_pos += ret;
lb -= ret;
if (it == m_list.end())
m_pos += static_cast<uint>(ret);
block -= static_cast<uint>(ret);
if (it == m_blocks.end())
return ret * DVD_VIDEO_LB_LEN;

data = (unsigned char*)data + ret * DVD_VIDEO_LB_LEN;
Buffer = static_cast<unsigned char*>(Buffer) + ret * DVD_VIDEO_LB_LEN;
}

b = it->End() - m_pos;
if (b > lb)
b = lb;
if (b > block)
b = block;

// Request new key if change in title
int flags = DVDINPUT_READ_DECRYPT;
Expand All @@ -212,73 +218,65 @@ int DVDStream::safe_read(void *data, uint size)
}

// Read the encrypted blocks
int ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos + m_start, b, (unsigned char*)data, flags);
int ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos + m_start, b, static_cast<unsigned char*>(Buffer), flags);
if (ret2 == -1)
{
LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error");
LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
m_title = -1;
return -1;
}

m_pos += ret2;
m_pos += static_cast<uint>(ret2);
ret += ret2;
lb -= ret2;
data = (unsigned char*)data + ret2 * DVD_VIDEO_LB_LEN;
block -= static_cast<uint>(ret2);
Buffer = static_cast<unsigned char*>(Buffer) + ret2 * DVD_VIDEO_LB_LEN;

if (lb > 0 && m_start == 0)
if (block > 0 && m_start == 0)
{
// Read the last unencrypted blocks
ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos, lb, (unsigned char*)data, DVDINPUT_NOFLAGS);
ret2 = InternalUDFReadBlocksRaw(m_reader, m_pos, block, static_cast<unsigned char*>(Buffer), DVDINPUT_NOFLAGS);
if (ret2 == -1)
{
LOG(VB_GENERAL, LOG_ERR, "DVDStream::safe_read DVDReadBlocks error");
LOG(VB_GENERAL, LOG_ERR, LOC + "SafeRead DVDReadBlocks error");
return -1;
}

m_pos += ret2;
m_pos += static_cast<uint>(ret2);
ret += ret2;;
}

return ret * DVD_VIDEO_LB_LEN;
}

//virtual
long long DVDStream::SeekInternal(long long pos, int whence)
long long DVDStream::SeekInternal(long long Position, int Whence)
{
if (!m_reader)
return -1;

if (SEEK_END == whence)
if (SEEK_END == Whence)
{
errno = EINVAL;
return -1;
}

uint32_t lb = pos / DVD_VIDEO_LB_LEN;
if ((qlonglong)lb * DVD_VIDEO_LB_LEN != pos)
uint32_t block = static_cast<uint32_t>(Position / DVD_VIDEO_LB_LEN);
if (static_cast<int64_t>(block) * DVD_VIDEO_LB_LEN != Position)
{
LOG(VB_GENERAL, LOG_ERR, "DVDStream::Seek not block aligned");
LOG(VB_GENERAL, LOG_ERR, LOC + "Seek not block aligned");
return -1;
}

m_posLock.lockForWrite();

m_pos = lb;

m_pos = block;
m_posLock.unlock();

m_generalWait.wakeAll();

return pos;
return Position;
}

//virtual
long long DVDStream::GetReadPosition(void) const
{
m_posLock.lockForRead();
long long ret = (long long)m_pos * DVD_VIDEO_LB_LEN;
long long ret = static_cast<long long>(m_pos) * DVD_VIDEO_LB_LEN;
m_posLock.unlock();
return ret;
}

// End of dvdstream,.cpp
51 changes: 22 additions & 29 deletions mythtv/libs/libmythtv/dvdstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,41 @@
#ifndef DVDSTREAM_H
#define DVDSTREAM_H

#include <cstdint>

// Qt
#include <QString>
#include <QList>

// MythTV
#include "ringbuffer.h"

using dvd_reader_t = struct dvd_reader_s;
// Std
#include <cstdint>

using dvd_reader_t = struct dvd_reader_s;

/**
* Stream content from a DVD image file
*/
class MTV_PUBLIC DVDStream : public RingBuffer
{
Q_DISABLE_COPY(DVDStream);
public:
class BlockRange;

public:
explicit DVDStream(const QString &filename);
explicit DVDStream(const QString &Filename);
~DVDStream() override;

public:
// RingBuffer methods
long long GetReadPosition(void) const override; // RingBuffer
bool IsOpen(void) const override; // RingBuffer
bool OpenFile(const QString &lfilename, uint retry_ms = 0) override; // RingBuffer
long long GetReadPosition (void) const override;
bool IsOpen (void) const override;
bool OpenFile (const QString &Filename, uint Retry = 0) override;

protected:
int safe_read(void *data, uint size) override; // RingBuffer
long long SeekInternal(long long pos, int whence) override; // RingBuffer
protected:
int SafeRead (void *Buffer, uint Size) override;
long long SeekInternal (long long Position, int Whence) override;

// Implementation
private:
dvd_reader_t *m_reader {nullptr};
uint32_t m_start {0};
private:
Q_DISABLE_COPY(DVDStream)

class BlockRange;
using list_t = QList<BlockRange>;
list_t m_list; // List of possibly encryoted block ranges

uint32_t m_pos {0}; // Current read position (blocks)
int m_title {-1}; // Last title decrypted
dvd_reader_t *m_reader { nullptr };
uint32_t m_start { 0 };
QList<BlockRange> m_blocks; // List of possibly encrypted block ranges
uint32_t m_pos { 0 }; // Current read position (blocks)
int m_title { -1 }; // Last title decrypted
};

#endif /* ndef DVDSTREAM_H */
#endif
390 changes: 169 additions & 221 deletions mythtv/libs/libmythtv/fileringbuffer.cpp

Large diffs are not rendered by default.

40 changes: 13 additions & 27 deletions mythtv/libs/libmythtv/fileringbuffer.h
Original file line number Diff line number Diff line change
@@ -1,42 +1,28 @@
// Qt headers
// Qt
#include <QCoreApplication>

// MythTV headers
// MythTV
#include "ringbuffer.h"

class MTV_PUBLIC FileRingBuffer : public RingBuffer
{
Q_DECLARE_TR_FUNCTIONS(FileRingBuffer)

friend class RingBuffer;

public:
~FileRingBuffer() override;

// Gets
bool IsOpen(void) const override; // RingBuffer
long long GetReadPosition(void) const override; // RingBuffer

// General Commands
bool OpenFile(const QString &lfilename,
uint retry_ms = kDefaultOpenTimeout) override; // RingBuffer
bool ReOpen(const QString& newFilename = "") override; // RingBuffer
bool IsOpen (void) const override;
long long GetReadPosition (void) const override;
bool OpenFile (const QString &Filename, uint Retry = static_cast<uint>(kDefaultOpenTimeout)) override;
bool ReOpen (const QString& Filename = "") override;

protected:
FileRingBuffer(const QString &lfilename,
bool write, bool readahead, int timeout_ms);

int safe_read(void *data, uint sz) override // RingBuffer
{
if (m_remotefile)
return safe_read(m_remotefile, data, sz);
if (m_fd2 >= 0)
return safe_read(m_fd2, data, sz);

errno = EBADF;
return -1;
}
int safe_read(int fd, void *data, uint sz);
int safe_read(RemoteFile *rf, void *data, uint sz);
long long GetRealFileSizeInternal(void) const override; // RingBuffer
long long SeekInternal(long long pos, int whence) override; // RingBuffer
FileRingBuffer(const QString &Filename, bool Write, bool UseReadAhead, int Timeout);
int SafeRead (void *Buffer, uint Size) override;
int SafeRead (int FD, void *Buffer, uint Size);
int SafeRead (RemoteFile *Remote, void *Buffer, uint Size);
long long GetRealFileSizeInternal(void) const override;
long long SeekInternal (long long Position, int Whence) override;
};
108 changes: 45 additions & 63 deletions mythtv/libs/libmythtv/icringbuffer.cpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
#include "icringbuffer.h"

#include <cstdio> // SEEK_SET

// Qt
#include <QScopedPointer>
#include <QWriteLocker>

// Mythtv
#include "netstream.h"
#include "mythlogging.h"
#include "icringbuffer.h"

// Std
#include <cstdio>

#define LOC QString("ICRingBuf ")

#define LOC QString("ICRingBuf: ")

ICRingBuffer::ICRingBuffer(const QString &url, RingBuffer *parent)
: RingBuffer(kRingBufferType), m_parent(parent)
ICRingBuffer::ICRingBuffer(const QString &Url, RingBuffer *Parent)
: RingBuffer(kRingBuffer_MHEG),
m_parent(Parent)
{
m_startReadAhead = true;
ICRingBuffer::OpenFile(url);
OpenFile(Url);
}

ICRingBuffer::~ICRingBuffer()
{
KillReadAheadThread();

delete m_stream;
m_stream = nullptr;

delete m_parent;
m_parent = nullptr;
}

bool ICRingBuffer::IsOpen(void) const
Expand All @@ -38,29 +35,27 @@ bool ICRingBuffer::IsOpen(void) const
/** \fn ICRingBuffer::OpenFile(const QString &, uint)
* \brief Opens a BBC NetStream for reading.
*
* \param url Url of the stream to read.
* \param retry_ms Ignored. This value is part of the API
* inherited from the parent class.
* \param Url Url of the stream to read.
* \return Returns true if the stream was opened.
*/
bool ICRingBuffer::OpenFile(const QString &url, uint /*retry_ms*/)
bool ICRingBuffer::OpenFile(const QString &Url, uint /*Retry*/)
{
if (!NetStream::IsSupported(url))
if (!NetStream::IsSupported(Url))
{
LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported URL %1").arg(url) );
LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported URL '%1'").arg(Url));
return false;
}

QScopedPointer<NetStream> stream(new NetStream(url, NetStream::kNeverCache));
if (!stream || !stream->IsOpen())
QScopedPointer<NetStream> stream(new NetStream(Url, NetStream::kNeverCache));
if (!stream || (stream && !stream->IsOpen()))
{
LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open %1").arg(url) );
LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open '%1'").arg(Url));
return false;
}

if (!stream->WaitTillReady(30000))
{
LOG(VB_GENERAL, LOG_ERR, LOC + QString("Stream not ready%1").arg(url) );
LOG(VB_GENERAL, LOG_ERR, LOC + QString("Stream not ready '%1'").arg(Url));
return false;
}

Expand All @@ -69,8 +64,8 @@ bool ICRingBuffer::OpenFile(const QString &url, uint /*retry_ms*/)

QWriteLocker locker(&m_rwLock);

m_safeFilename = url;
m_filename = url;
m_safeFilename = Url;
m_filename = Url;

delete m_stream;
m_stream = stream.take();
Expand All @@ -85,7 +80,7 @@ bool ICRingBuffer::OpenFile(const QString &url, uint /*retry_ms*/)
locker.unlock();
Reset(true, false, true);

LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened %1").arg(url));
LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened '%1'").arg(Url));
return true;
}

Expand All @@ -94,82 +89,69 @@ long long ICRingBuffer::GetReadPosition(void) const
return m_stream ? m_stream->GetReadPosition() : 0;
}

long long ICRingBuffer::SeekInternal(long long pos, int whence)
long long ICRingBuffer::SeekInternal(long long Position, int Whence)
{
long long result = -1;
if (!m_stream)
return -1;

m_posLock.lockForWrite();
return result;

long long ret = 0;
QWriteLocker locker(&m_posLock);

// Optimize no-op seeks
if (m_readAheadRunning &&
((whence == SEEK_SET && pos == m_readPos) ||
(whence == SEEK_CUR && pos == 0)))
if (m_readAheadRunning && ((Whence == SEEK_SET && Position == m_readPos) ||
(Whence == SEEK_CUR && Position == 0)))
{
ret = m_readPos;

m_posLock.unlock();

return ret;
result = m_readPos;
return result;
}

switch (whence)
switch (Whence)
{
case SEEK_SET:
break;
case SEEK_SET: break;
case SEEK_CUR:
pos += m_stream->GetReadPosition();
Position += m_stream->GetReadPosition();
break;
case SEEK_END:
pos += m_stream->GetSize();
Position += m_stream->GetSize();
break;
default:
errno = EINVAL;
ret = -1;
goto err;
m_generalWait.wakeAll();
return result;
}

ret = m_stream->Seek(pos);
if (ret >= 0)
result = m_stream->Seek(Position);
if (result >= 0)
{
m_readPos = ret;

m_readPos = result;
m_ignoreReadPos = -1;

if (m_readAheadRunning)
ResetReadAhead(m_readPos);

m_readAdjust = 0;
}

err:
m_posLock.unlock();

m_generalWait.wakeAll();

return ret;
return result;
}

int ICRingBuffer::safe_read(void *data, uint sz)
int ICRingBuffer::SafeRead(void *Buffer, uint Size)
{
return m_stream ? m_stream->safe_read(data, sz, 1000) : (m_ateof = true, 0);
if (m_stream)
return m_stream->safe_read(Buffer, Size, 1000);
m_ateof = true;
return 0;
}

long long ICRingBuffer::GetRealFileSizeInternal(void) const
{
return m_stream ? m_stream->GetSize() : -1;
}

// Take ownership of parent RingBuffer
RingBuffer *ICRingBuffer::Take()
RingBuffer *ICRingBuffer::TakeRingBuffer(void)
{
RingBuffer *parent = m_parent;
if (parent && IsOpen())
parent->Unpause();
m_parent = nullptr;
return parent;
}

// End of file
39 changes: 16 additions & 23 deletions mythtv/libs/libmythtv/icringbuffer.h
Original file line number Diff line number Diff line change
@@ -1,40 +1,33 @@
#ifndef ICRINGBUFFER_H
#define ICRINGBUFFER_H

// MythTV
#include "ringbuffer.h"

class NetStream;

class ICRingBuffer : public RingBuffer
{
public:
static enum RingBufferType const kRingBufferType = kRingBuffer_MHEG;

explicit ICRingBuffer(const QString &url, RingBuffer *parent = nullptr);
explicit ICRingBuffer(const QString &Url, RingBuffer *Parent = nullptr);
~ICRingBuffer() override;

// RingBuffer implementation
bool IsOpen(void) const override; // RingBuffer
long long GetReadPosition(void) const override; // RingBuffer
bool OpenFile(const QString &url,
uint retry_ms = kDefaultOpenTimeout) override; // RingBuffer
bool IsStreamed(void) override { return false; } // RingBuffer
bool IsSeekingAllowed(void) override { return true; } // RingBuffer
bool IsBookmarkAllowed(void) override { return false; } // RingBuffer
bool IsOpen (void) const override;
long long GetReadPosition (void) const override;
bool OpenFile (const QString &Url, uint Retry = static_cast<uint>(kDefaultOpenTimeout)) override;
bool IsStreamed (void) override { return false; }
bool IsSeekingAllowed (void) override { return true; }
bool IsBookmarkAllowed (void) override { return false; }

protected:
int safe_read(void *data, uint sz) override; // RingBuffer
long long GetRealFileSizeInternal(void) const override; // RingBuffer
long long SeekInternal(long long pos, int whence) override; // RingBuffer
RingBuffer* TakeRingBuffer(void);

// Operations
public:
// Take ownership of parent RingBuffer
RingBuffer *Take();
protected:
int SafeRead (void *Buffer, uint Size) override;
long long GetRealFileSizeInternal(void) const override;
long long SeekInternal (long long Position, int Whence) override;

private:
NetStream *m_stream {nullptr};
RingBuffer *m_parent {nullptr}; // parent RingBuffer
NetStream *m_stream { nullptr };
RingBuffer *m_parent { nullptr };
};

#endif // ICRINGBUFFER_H
#endif
2 changes: 2 additions & 0 deletions mythtv/libs/libmythtv/libmythtv.pro
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ INSTALLS += inc2
#DVD stuff
DEPENDPATH += ../../external/libmythdvdnav/
DEPENDPATH += ../../external/libmythdvdnav/dvdread # for dvd_reader.h & dvd_input.h
INCLUDEPATH += ../../external/libmythdvdnav/dvdnav
INCLUDEPATH += ../../external/libmythdvdnav/dvdread

win32-msvc*|freebsd {
INCLUDEPATH += ../../external/libmythdvdnav/dvdnav
Expand Down
1 change: 0 additions & 1 deletion mythtv/libs/libmythtv/mpeg/mpegstreamdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ using namespace std;

class EITHelper;
class PSIPTable;
class RingBuffer;

using uint_vec_t = vector<uint>;

Expand Down
5 changes: 2 additions & 3 deletions mythtv/libs/libmythtv/mythiowrapper.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#ifndef __MYTHIOWRAPPER__
#define __MYTHIOWRAPPER__
#ifndef MYTHIOWRAPPER_H
#define MYTHIOWRAPPER_H

#ifdef __cplusplus
#include <cstring>
Expand Down Expand Up @@ -45,6 +45,5 @@ MTV_PUBLIC char *mythdir_readdir(int dirID);
#ifdef __cplusplus
}
#endif

#endif

23 changes: 11 additions & 12 deletions mythtv/libs/libmythtv/mythplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2462,12 +2462,12 @@ void MythPlayer::SwitchToProgram(void)
return;
}

if (m_playerCtx->m_buffer->GetType() == ICRingBuffer::kRingBufferType)
if (m_playerCtx->m_buffer->GetType() == kRingBuffer_MHEG)
{
// Restore original ringbuffer
auto *ic = dynamic_cast< ICRingBuffer* >(m_playerCtx->m_buffer);
if (ic) // should always be true
m_playerCtx->m_buffer = ic->Take();
auto *ic = dynamic_cast<ICRingBuffer*>(m_playerCtx->m_buffer);
if (ic)
m_playerCtx->m_buffer = ic->TakeRingBuffer();
delete ic;
}

Expand Down Expand Up @@ -2623,17 +2623,16 @@ void MythPlayer::JumpToProgram(void)

SendMythSystemPlayEvent("PLAY_CHANGED", pginfo);

if (m_playerCtx->m_buffer->GetType() == ICRingBuffer::kRingBufferType)
if (m_playerCtx->m_buffer->GetType() == kRingBuffer_MHEG)
{
// Restore original ringbuffer
auto *ic = dynamic_cast< ICRingBuffer* >(m_playerCtx->m_buffer);
if (ic) // should always be true
m_playerCtx->m_buffer = ic->Take();
auto *ic = dynamic_cast<ICRingBuffer*>(m_playerCtx->m_buffer);
if (ic)
m_playerCtx->m_buffer = ic->TakeRingBuffer();
delete ic;
}

m_playerCtx->m_buffer->OpenFile(
pginfo->GetPlaybackURL(), RingBuffer::kLiveTVOpenTimeout);
m_playerCtx->m_buffer->OpenFile(pginfo->GetPlaybackURL(), RingBuffer::kLiveTVOpenTimeout);
QString subfn = m_playerCtx->m_buffer->GetSubtitleFilename();
TVState desiredState = m_playerCtx->GetState();
bool isInProgress = (desiredState == kState_WatchingRecording ||
Expand Down Expand Up @@ -5288,7 +5287,7 @@ bool MythPlayer::SetStream(const QString &stream)
// If successful will call m_interactiveTV->StreamStarted();

if (stream.isEmpty() && m_playerCtx->m_tvchain &&
m_playerCtx->m_buffer->GetType() == ICRingBuffer::kRingBufferType)
m_playerCtx->m_buffer->GetType() == kRingBuffer_MHEG)
{
// Restore livetv
SetEof(kEofStateDelayed);
Expand All @@ -5313,7 +5312,7 @@ void MythPlayer::JumpToStream(const QString &stream)
ProgramInfo pginfo(stream);
SetPlayingInfo(pginfo);

if (m_playerCtx->m_buffer->GetType() != ICRingBuffer::kRingBufferType)
if (m_playerCtx->m_buffer->GetType() != kRingBuffer_MHEG)
m_playerCtx->m_buffer = new ICRingBuffer(stream, m_playerCtx->m_buffer);
else
m_playerCtx->m_buffer->OpenFile(stream);
Expand Down
1,036 changes: 481 additions & 555 deletions mythtv/libs/libmythtv/ringbuffer.cpp

Large diffs are not rendered by default.

367 changes: 149 additions & 218 deletions mythtv/libs/libmythtv/ringbuffer.h

Large diffs are not rendered by default.

45 changes: 20 additions & 25 deletions mythtv/libs/libmythtv/streamingringbuffer.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
// Qt
#include <QUrl>

// MythTV
#include "mythcorecontext.h"
#include "mythlogging.h"

#include "streamingringbuffer.h"

#define LOC QString("StreamRingBuf(%1): ").arg(m_filename)

StreamingRingBuffer::StreamingRingBuffer(const QString &lfilename)
StreamingRingBuffer::StreamingRingBuffer(const QString &Filename)
: RingBuffer(kRingBuffer_HTTP)
{
m_startReadAhead = false;
StreamingRingBuffer::OpenFile(lfilename);
StreamingRingBuffer::OpenFile(Filename);
}

StreamingRingBuffer::~StreamingRingBuffer()
Expand All @@ -26,7 +28,7 @@ StreamingRingBuffer::~StreamingRingBuffer()
bool StreamingRingBuffer::IsOpen(void) const
{
m_rwLock.lockForRead();
bool result = (bool)m_context;
bool result = m_context;
m_rwLock.unlock();
return result;
}
Expand All @@ -44,27 +46,23 @@ long long StreamingRingBuffer::GetReadPosition(void) const
* inherited from the parent class.
* \return Returns true if the stream was opened.
*/
bool StreamingRingBuffer::OpenFile(const QString &lfilename, uint /*retry_ms*/)
bool StreamingRingBuffer::OpenFile(const QString &Filename, uint /*Retry*/)
{
RingBuffer::AVFormatInitNetwork();

m_rwLock.lockForWrite();

m_safeFilename = lfilename;
m_filename = lfilename;
m_safeFilename = Filename;
m_filename = Filename;

// TODO check whether local area file

QUrl url = m_filename;
if (url.path().endsWith(QLatin1String("m3u8"), Qt::CaseInsensitive))
{
url.setScheme("hls+http");
}

int res = ffurl_open(&m_context, url.toString().toLatin1(), AVIO_FLAG_READ,
nullptr, nullptr);
if (res >=0 && m_context &&
!m_context->is_streamed && ffurl_seek(m_context, 0, SEEK_SET) >= 0)
int res = ffurl_open(&m_context, url.toString().toLatin1(), AVIO_FLAG_READ, nullptr, nullptr);
if (res >= 0 && m_context && !m_context->is_streamed && ffurl_seek(m_context, 0, SEEK_SET) >= 0)
{
m_streamed = false;
m_allowSeeks = true;
Expand All @@ -75,50 +73,47 @@ bool StreamingRingBuffer::OpenFile(const QString &lfilename, uint /*retry_ms*/)

if (res < 0 || !m_context)
{
LOG(VB_GENERAL, LOG_ERR, LOC +
QString("Failed to open stream (error %1)") .arg(res));
LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open stream (error %1)") .arg(res));
m_lastError = QObject::tr("Failed to open stream (%1)").arg(res);
m_rwLock.unlock();
return false;
}

m_rwLock.unlock();

return true;
}

long long StreamingRingBuffer::SeekInternal(long long pos, int whence)
long long StreamingRingBuffer::SeekInternal(long long Position, int Whence)
{
if (!m_context)
return 0;

m_posLock.lockForWrite();
int seek = ffurl_seek(m_context, pos, whence);
int seek = static_cast<int>(ffurl_seek(m_context, Position, Whence));
m_posLock.unlock();

if (seek < 0)
{
m_ateof = true;
return 0;
}
return pos;
return Position;
}

int StreamingRingBuffer::safe_read(void *data, uint sz)
int StreamingRingBuffer::SafeRead(void *Buffer, uint Size)
{
uint len = 0;
int len = 0;

if (m_context)
{
while (len < sz)
while (len < static_cast<int>(Size))
{
int ret = ffurl_read(m_context, (unsigned char*)data + len, sz - len);
int ret = ffurl_read(m_context, static_cast<unsigned char*>(Buffer) + len,
static_cast<int>(Size) - len);
if (ret < 0)
{
if (ret == AVERROR_EOF)
{
m_ateof = true;
}
errno = ret;
break;
}
Expand Down
30 changes: 15 additions & 15 deletions mythtv/libs/libmythtv/streamingringbuffer.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#ifndef STREAMINGRINGBUFFER_H
#define STREAMINGRINGBUFFER_H

// MythTV
#include "ringbuffer.h"

// FFmpeg
extern "C" {
#include "libavformat/avformat.h"
#include "libavformat/url.h"
Expand All @@ -11,27 +13,25 @@ extern "C" {
class StreamingRingBuffer : public RingBuffer
{
public:
explicit StreamingRingBuffer(const QString &lfilename);
explicit StreamingRingBuffer(const QString &Filename);
~StreamingRingBuffer() override;

// RingBuffer implementation
bool IsOpen(void) const override; // RingBuffer
long long GetReadPosition(void) const override; // RingBuffer
bool OpenFile(const QString &lfilename,
uint retry_ms = kDefaultOpenTimeout) override; // RingBuffer
bool IsStreamed(void) override { return m_streamed; } // RingBuffer
bool IsSeekingAllowed(void) override { return m_allowSeeks; } // RingBuffer
bool IsBookmarkAllowed(void) override { return false; } // RingBuffer
bool IsOpen (void) const override;
long long GetReadPosition (void) const override;
bool OpenFile (const QString &Filename, uint Retry = static_cast<uint>(kDefaultOpenTimeout)) override;
bool IsStreamed (void) override { return m_streamed; }
bool IsSeekingAllowed (void) override { return m_allowSeeks; }
bool IsBookmarkAllowed (void) override { return false; }

protected:
int safe_read(void *data, uint sz) override; // RingBuffer
long long GetRealFileSizeInternal(void) const override; // RingBuffer
long long SeekInternal(long long pos, int whence) override; // RingBuffer
int SafeRead (void *Buffer, uint Size) override;
long long GetRealFileSizeInternal(void) const override;
long long SeekInternal (long long Position, int Whence) override;

private:
URLContext *m_context {nullptr};
bool m_streamed {true};
bool m_allowSeeks {false};
URLContext *m_context { nullptr };
bool m_streamed { true };
bool m_allowSeeks { false };
};

#endif // STREAMINGRINGBUFFER_H