Skip to content

Commit a613ddd

Browse files
Merge pull request #21 from darbyjohnston/otioz_mmap2
Memory-map support for .otioz
2 parents 67d8935 + a8f82a8 commit a613ddd

25 files changed

+861
-103
lines changed

bin/toucan-render/App.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#include <toucan/ImageEffectHost.h>
99
#include <toucan/ImageGraph.h>
10-
#include <toucan/Timeline.h>
10+
#include <toucan/TimelineWrapper.h>
1111
#include <toucan/Util.h>
1212

1313
#include <OpenImageIO/imagebufalgo.h>
@@ -148,10 +148,10 @@ namespace toucan
148148
const size_t outputNumberPadding = getNumberPadding(outputSplit.second);
149149

150150
// Open the timeline.
151-
auto timeline = std::make_shared<Timeline>(inputPath);
151+
auto timelineWrapper = std::make_shared<TimelineWrapper>(inputPath);
152152

153153
// Get time values.
154-
const OTIO_NS::TimeRange& timeRange = timeline->getTimeRange();
154+
const OTIO_NS::TimeRange& timeRange = timelineWrapper->getTimeRange();
155155
const OTIO_NS::RationalTime timeInc(1.0, timeRange.duration().rate());
156156
const int frames = timeRange.duration().value();
157157

@@ -165,7 +165,7 @@ namespace toucan
165165
imageGraphOptions.log = log;
166166
const auto graph = std::make_shared<ImageGraph>(
167167
inputPath.parent_path(),
168-
timeline,
168+
timelineWrapper,
169169
imageGraphOptions);
170170
const IMATH_NAMESPACE::V2d imageSize = graph->getImageSize();
171171

@@ -245,13 +245,13 @@ namespace toucan
245245
{
246246
if (!_args.outputRaw)
247247
{
248-
const std::filesystem::path path = getSequenceFrame(
249-
outputPath.parent_path(),
248+
const std::string fileName = getSequenceFrame(
249+
outputPath.parent_path().string(),
250250
outputSplit.first,
251251
outputStartFrame + time.to_frames(),
252252
outputNumberPadding,
253253
outputPath.extension().string());
254-
buf.write(path.string());
254+
buf.write(fileName);
255255
}
256256
else
257257
{
@@ -278,14 +278,14 @@ namespace toucan
278278
// Write the graph.
279279
if (_options.graph)
280280
{
281-
const std::filesystem::path path = getSequenceFrame(
282-
outputPath.parent_path(),
281+
const std::string fileName = getSequenceFrame(
282+
outputPath.parent_path().string(),
283283
outputSplit.first,
284284
outputStartFrame + time.to_frames(),
285285
outputNumberPadding,
286286
".dot");
287287
const std::vector<std::string> lines = node->graph(inputPath.stem().string());
288-
if (FILE* f = fopen(path.string().c_str(), "w"))
288+
if (FILE* f = fopen(fileName.c_str(), "w"))
289289
{
290290
for (const auto& line : lines)
291291
{

lib/toucan/CMakeLists.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ set(HEADERS
55
ImageEffectHost.h
66
ImageGraph.h
77
ImageNode.h
8+
MemoryMap.h
89
MessageLog.h
910
Plugin.h
1011
PropertySet.h
1112
Read.h
1213
TimeWarp.h
13-
Timeline.h
14+
TimelineWrapper.h
1415
Util.h)
1516
set(HEADERS_PRIVATE
1617
ImageEffect_p.h)
@@ -21,19 +22,22 @@ set(SOURCE
2122
ImageEffectHost.cpp
2223
ImageGraph.cpp
2324
ImageNode.cpp
25+
MemoryMap.cpp
2426
MessageLog.cpp
2527
Plugin.cpp
2628
PropertySet.cpp
2729
Read.cpp
2830
TimeWarp.cpp
29-
Timeline.cpp
31+
TimelineWrapper.cpp
3032
Util.cpp)
3133
if(WIN32)
3234
list(APPEND SOURCE
35+
MemoryMapWin32.cpp
3336
PluginWin32.cpp
3437
UtilWin32.cpp)
3538
else()
3639
list(APPEND SOURCE
40+
MemoryMapUnix.cpp
3741
PluginUnix.cpp
3842
UtilUnix.cpp)
3943
endif()

lib/toucan/FFmpegRead.cpp

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace toucan
1818
{
1919
namespace
2020
{
21+
const size_t avIOContextBufferSize = 4096;
22+
2123
void logCallback(void*, int level, const char* fmt, va_list vl)
2224
{
2325
switch (level)
@@ -70,13 +72,44 @@ namespace toucan
7072
}
7173
}
7274

73-
FFmpegRead::FFmpegRead(const std::filesystem::path& path) :
74-
_path(path)
75+
FFmpegRead::FFmpegRead(
76+
const std::filesystem::path& path,
77+
const MemoryReference& memoryReference) :
78+
_path(path),
79+
_memoryReference(memoryReference)
7580
{
7681
av_log_set_level(AV_LOG_QUIET);
7782
//av_log_set_level(AV_LOG_VERBOSE);
7883
//av_log_set_callback(logCallback);
7984

85+
if (memoryReference.isValid())
86+
{
87+
_avFormatContext = avformat_alloc_context();
88+
if (!_avFormatContext)
89+
{
90+
throw std::runtime_error("Cannot allocate format context");
91+
}
92+
93+
_avIOBufferData = AVIOBufferData(
94+
reinterpret_cast<const uint8_t*>(memoryReference.getData()),
95+
memoryReference.getSize());
96+
_avIOContextBuffer = static_cast<uint8_t*>(av_malloc(avIOContextBufferSize));
97+
_avIOContext = avio_alloc_context(
98+
_avIOContextBuffer,
99+
avIOContextBufferSize,
100+
0,
101+
&_avIOBufferData,
102+
&_avIOBufferRead,
103+
nullptr,
104+
&_avIOBufferSeek);
105+
if (!_avIOContext)
106+
{
107+
throw std::runtime_error("Cannot allocate I/O context");
108+
}
109+
110+
_avFormatContext->pb = _avIOContext;
111+
}
112+
80113
const std::string fileName = path.string();
81114
int r = avformat_open_input(
82115
&_avFormatContext,
@@ -578,4 +611,49 @@ namespace toucan
578611
}
579612
return out;
580613
}
614+
615+
FFmpegRead::AVIOBufferData::AVIOBufferData()
616+
{}
617+
618+
FFmpegRead::AVIOBufferData::AVIOBufferData(const uint8_t* data, size_t size) :
619+
data(data),
620+
size(size)
621+
{}
622+
623+
int FFmpegRead::_avIOBufferRead(void* opaque, uint8_t* buf, int bufSize)
624+
{
625+
AVIOBufferData* bufferData = static_cast<AVIOBufferData*>(opaque);
626+
627+
const int64_t remaining = bufferData->size - bufferData->offset;
628+
int bufSizeClamped = std::min(std::max(
629+
static_cast<int64_t>(bufSize),
630+
static_cast<int64_t>(0)),
631+
remaining);
632+
if (!bufSizeClamped)
633+
{
634+
return AVERROR_EOF;
635+
}
636+
637+
memcpy(buf, bufferData->data + bufferData->offset, bufSizeClamped);
638+
bufferData->offset += bufSizeClamped;
639+
640+
return bufSizeClamped;
641+
}
642+
643+
int64_t FFmpegRead::_avIOBufferSeek(void* opaque, int64_t offset, int whence)
644+
{
645+
AVIOBufferData* bufferData = static_cast<AVIOBufferData*>(opaque);
646+
647+
if (whence & AVSEEK_SIZE)
648+
{
649+
return bufferData->size;
650+
}
651+
652+
bufferData->offset = std::min(std::max(
653+
offset,
654+
static_cast<int64_t>(0)),
655+
static_cast<int64_t>(bufferData->size));
656+
657+
return offset;
658+
}
581659
}

lib/toucan/FFmpegRead.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#pragma once
55

6+
#include "MemoryMap.h"
7+
68
#include <opentimelineio/anyVector.h>
79

810
#include <OpenImageIO/imagebuf.h>
@@ -23,7 +25,9 @@ namespace toucan
2325
class FFmpegRead : public std::enable_shared_from_this<FFmpegRead>
2426
{
2527
public:
26-
FFmpegRead(const std::filesystem::path&);
28+
FFmpegRead(
29+
const std::filesystem::path&,
30+
const MemoryReference& = {});
2731

2832
virtual ~FFmpegRead();
2933

@@ -37,11 +41,25 @@ namespace toucan
3741
OIIO::ImageBuf _read();
3842

3943
std::filesystem::path _path;
44+
MemoryReference _memoryReference;
4045
OIIO::ImageSpec _spec;
4146
OTIO_NS::TimeRange _timeRange;
4247
OTIO_NS::RationalTime _currentTime;
4348

49+
struct AVIOBufferData
50+
{
51+
AVIOBufferData();
52+
AVIOBufferData(const uint8_t*, size_t size);
53+
54+
const uint8_t* data = nullptr;
55+
size_t size = 0;
56+
size_t offset = 0;
57+
};
58+
static int _avIOBufferRead(void* opaque, uint8_t* buf, int bufSize);
59+
static int64_t _avIOBufferSeek(void* opaque, int64_t offset, int whence);
60+
4461
AVFormatContext* _avFormatContext = nullptr;
62+
AVIOBufferData _avIOBufferData;
4563
uint8_t* _avIOContextBuffer = nullptr;
4664
AVIOContext* _avIOContext = nullptr;
4765
AVRational _avSpeed = { 24, 1 };

lib/toucan/ImageGraph.cpp

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "TimeWarp.h"
1010
#include "Util.h"
1111

12+
#include <opentimelineio/clip.h>
1213
#include <opentimelineio/externalReference.h>
1314
#include <opentimelineio/gap.h>
1415
#include <opentimelineio/generatorReference.h>
@@ -24,27 +25,28 @@ namespace toucan
2425

2526
ImageGraph::ImageGraph(
2627
const std::filesystem::path& path,
27-
const std::shared_ptr<Timeline>& timeline,
28+
const std::shared_ptr<TimelineWrapper>& timelineWrapper,
2829
const ImageGraphOptions& options) :
2930
_path(path),
30-
_timeline(timeline),
31-
_timeRange(timeline->getTimeRange()),
31+
_timelineWrapper(timelineWrapper),
32+
_timeRange(timelineWrapper->getTimeRange()),
3233
_options(options)
3334
{
3435
_loadCache.setMax(10);
3536

3637
// Get the image size from the first video clip.
37-
for (auto track : _timeline->otio()->find_children<OTIO_NS::Track>())
38+
for (auto track : _timelineWrapper->getTimeline()->find_children<OTIO_NS::Track>())
3839
{
3940
if (OTIO_NS::Track::Kind::video == track->kind())
4041
{
4142
for (auto clip : track->find_clips())
4243
{
4344
if (auto externalRef = dynamic_cast<OTIO_NS::ExternalReference*>(clip->media_reference()))
4445
{
45-
const std::filesystem::path path = _timeline->getMediaPath(externalRef->target_url());
46-
const OIIO::ImageBuf buf(path.string());
47-
const auto& spec = buf.spec();
46+
auto read = std::make_shared<ReadNode>(
47+
_timelineWrapper->getMediaPath(externalRef->target_url()),
48+
_timelineWrapper->getMemoryReference(externalRef->target_url()));
49+
const auto& spec = read->getSpec();
4850
if (spec.width > 0)
4951
{
5052
_imageSize.x = spec.width;
@@ -54,14 +56,16 @@ namespace toucan
5456
}
5557
else if (auto sequenceRef = dynamic_cast<OTIO_NS::ImageSequenceReference*>(clip->media_reference()))
5658
{
57-
const std::filesystem::path path = getSequenceFrame(
58-
_timeline->getMediaPath(sequenceRef->target_url_base()),
59+
auto read = std::make_shared<SequenceReadNode>(
60+
_timelineWrapper->getMediaPath(sequenceRef->target_url_base()),
5961
sequenceRef->name_prefix(),
62+
sequenceRef->name_suffix(),
6063
sequenceRef->start_frame(),
64+
sequenceRef->frame_step(),
65+
sequenceRef->rate(),
6166
sequenceRef->frame_zero_padding(),
62-
sequenceRef->name_suffix());
63-
const OIIO::ImageBuf buf(path.string());
64-
const auto& spec = buf.spec();
67+
_timelineWrapper->getMemoryReferences());
68+
const auto& spec = read->getSpec();
6569
if (spec.width > 0)
6670
{
6771
_imageSize.x = spec.width;
@@ -102,7 +106,7 @@ namespace toucan
102106
std::shared_ptr<IImageNode> node = host->createNode("toucan:Fill", metaData);
103107

104108
// Loop over the tracks.
105-
auto stack = _timeline->otio()->tracks();
109+
auto stack = _timelineWrapper->getTimeline()->tracks();
106110
for (const auto& i : stack->children())
107111
{
108112
if (auto track = OTIO_NS::dynamic_retainer_cast<OTIO_NS::Track>(i))
@@ -294,8 +298,9 @@ namespace toucan
294298
std::shared_ptr<ReadNode> read;
295299
if (!_loadCache.get(externalRef, read))
296300
{
297-
const std::filesystem::path path = _timeline->getMediaPath(externalRef->target_url());
298-
read = std::make_shared<ReadNode>(path);
301+
read = std::make_shared<ReadNode>(
302+
_timelineWrapper->getMediaPath(externalRef->target_url()),
303+
_timelineWrapper->getMemoryReference(externalRef->target_url()));
299304
_loadCache.add(externalRef, read);
300305
}
301306
out = read;
@@ -312,15 +317,15 @@ namespace toucan
312317
}
313318
else if (auto sequenceRef = dynamic_cast<OTIO_NS::ImageSequenceReference*>(clip->media_reference()))
314319
{
315-
const std::filesystem::path path = _timeline->getMediaPath(sequenceRef->target_url_base());
316320
auto read = std::make_shared<SequenceReadNode>(
317-
path,
321+
_timelineWrapper->getMediaPath(sequenceRef->target_url_base()),
318322
sequenceRef->name_prefix(),
319323
sequenceRef->name_suffix(),
320324
sequenceRef->start_frame(),
321325
sequenceRef->frame_step(),
322326
sequenceRef->rate(),
323-
sequenceRef->frame_zero_padding());
327+
sequenceRef->frame_zero_padding(),
328+
_timelineWrapper->getMemoryReferences());
324329
out = read;
325330
}
326331
else if (auto generatorRef = dynamic_cast<OTIO_NS::GeneratorReference*>(clip->media_reference()))

lib/toucan/ImageGraph.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55

66
#include <toucan/ImageNode.h>
77
#include <toucan/MessageLog.h>
8-
#include <toucan/Timeline.h>
8+
#include <toucan/TimelineWrapper.h>
99

1010
#include <dtk/core/LRUCache.h>
1111

12+
#include <opentimelineio/mediaReference.h>
1213
#include <opentimelineio/track.h>
1314
#include <opentimelineio/transition.h>
1415

@@ -31,7 +32,7 @@ namespace toucan
3132
public:
3233
ImageGraph(
3334
const std::filesystem::path&,
34-
const std::shared_ptr<Timeline>&,
35+
const std::shared_ptr<TimelineWrapper>&,
3536
const ImageGraphOptions& = ImageGraphOptions());
3637

3738
~ImageGraph();
@@ -62,7 +63,7 @@ namespace toucan
6263
const std::shared_ptr<IImageNode>&);
6364

6465
std::filesystem::path _path;
65-
std::shared_ptr<Timeline> _timeline;
66+
std::shared_ptr<TimelineWrapper> _timelineWrapper;
6667
OTIO_NS::TimeRange _timeRange;
6768
ImageGraphOptions _options;
6869
IMATH_NAMESPACE::V2i _imageSize = IMATH_NAMESPACE::V2i(0, 0);

0 commit comments

Comments
 (0)