Skip to content

Commit 15c71c7

Browse files
authored
Merge pull request #274 from ffmpeginteropx/fix-leaks
2 parents bbada84 + eb427cb commit 15c71c7

File tree

3 files changed

+111
-25
lines changed

3 files changed

+111
-25
lines changed

Source/D3D11VideoSampleProvider.h

+82-8
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ namespace FFmpegInteropX
4545
}
4646
}
4747

48+
public:
49+
50+
virtual ~D3D11VideoSampleProvider()
51+
{
52+
ReleaseTrackedSamples();
53+
}
54+
55+
virtual void Flush() override
56+
{
57+
UncompressedVideoSampleProvider::Flush();
58+
ReturnTrackedSamples();
59+
}
60+
61+
internal:
62+
4863
virtual HRESULT CreateBufferFromFrame(IBuffer^* pBuffer, IDirect3DSurface^* surface, AVFrame* avFrame, int64_t& framePts, int64_t& frameDuration) override
4964
{
5065
HRESULT hr = S_OK;
@@ -120,20 +135,46 @@ namespace FFmpegInteropX
120135
{
121136
if (sample->Direct3D11Surface)
122137
{
123-
samples.insert(reinterpret_cast<IUnknown*>(sample));
124-
sample->Processed += ref new Windows::Foundation::TypedEventHandler<Windows::Media::Core::MediaStreamSample^, Platform::Object^>(this, &D3D11VideoSampleProvider::OnProcessed);
138+
std::lock_guard<std::mutex> lock(samplesMutex);
139+
140+
// AddRef the sample on native interface to prevent it from being collected before Processed is called
141+
auto sampleNative = reinterpret_cast<IUnknown*>(sample);
142+
sampleNative->AddRef();
143+
144+
// Attach Processed event and store in samples list
145+
auto token = sample->Processed += ref new Windows::Foundation::TypedEventHandler<Windows::Media::Core::MediaStreamSample^, Platform::Object^>(this, &D3D11VideoSampleProvider::OnProcessed);
146+
trackedSamples[sampleNative] = token;
125147
}
126148

127149
return UncompressedVideoSampleProvider::SetSampleProperties(sample);
128150
};
129151

130152
void OnProcessed(Windows::Media::Core::MediaStreamSample^ sender, Platform::Object^ args)
131153
{
132-
auto unknown = reinterpret_cast<IUnknown*>(sender->Direct3D11Surface);
154+
std::lock_guard<std::mutex> lock(samplesMutex);
155+
156+
auto sampleNative = reinterpret_cast<IUnknown*>(sender);
157+
auto mapEntry = trackedSamples.find(sampleNative);
158+
if (mapEntry == trackedSamples.end())
159+
{
160+
// sample was already released during Flush() or destructor
161+
}
162+
else
163+
{
164+
// Release the sample's native interface and return texture to pool
165+
sampleNative->Release();
166+
trackedSamples.erase(mapEntry);
167+
168+
ReturnTextureToPool(sender);
169+
}
170+
}
171+
172+
void ReturnTextureToPool(MediaStreamSample^ sample)
173+
{
133174
IDXGISurface* surface = NULL;
134175
ID3D11Texture2D* texture = NULL;
135176

136-
HRESULT hr = DirectXInteropHelper::GetDXGISurface(sender->Direct3D11Surface, &surface);
177+
HRESULT hr = DirectXInteropHelper::GetDXGISurface(sample->Direct3D11Surface, &surface);
137178

138179
if (SUCCEEDED(hr))
139180
{
@@ -145,12 +186,45 @@ namespace FFmpegInteropX
145186
texturePool->ReturnTexture(texture);
146187
}
147188

148-
samples.erase(reinterpret_cast<IUnknown*>(sender));
149-
150189
SAFE_RELEASE(surface);
151190
SAFE_RELEASE(texture);
152191
}
153192

193+
void ReleaseTrackedSamples()
194+
{
195+
std::lock_guard<std::mutex> lock(samplesMutex);
196+
for (auto entry : trackedSamples)
197+
{
198+
// detach Processed event and release native interface
199+
auto sampleNative = entry.first;
200+
auto sample = reinterpret_cast<MediaStreamSample^>(sampleNative);
201+
202+
sample->Processed -= entry.second;
203+
sampleNative->Release();
204+
}
205+
206+
trackedSamples.clear();
207+
}
208+
209+
void ReturnTrackedSamples()
210+
{
211+
std::lock_guard<std::mutex> lock(samplesMutex);
212+
for (auto entry : trackedSamples)
213+
{
214+
// detach Processed event and release native interface
215+
auto sampleNative = entry.first;
216+
auto sample = reinterpret_cast<MediaStreamSample^>(sampleNative);
217+
218+
sample->Processed -= entry.second;
219+
sampleNative->Release();
220+
221+
// return texture to pool
222+
ReturnTextureToPool(sample);
223+
}
224+
225+
trackedSamples.clear();
226+
}
227+
154228
virtual HRESULT SetHardwareDevice(ID3D11Device* device, ID3D11DeviceContext* context, AVBufferRef* avHardwareContext) override
155229
{
156230
HRESULT hr = S_OK;
@@ -379,8 +453,8 @@ namespace FFmpegInteropX
379453
}
380454

381455
TexturePool^ texturePool;
382-
std::set<IUnknown*> samples;
383-
456+
std::map<IUnknown*,EventRegistrationToken> trackedSamples;
457+
std::mutex samplesMutex;
384458
};
385459
}
386460

Source/FFmpegMediaSource.cpp

+17-9
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,11 @@ FFmpegMediaSource::~FFmpegMediaSource()
120120
videoStreams.clear();
121121

122122
avformat_close_input(&avFormatCtx);
123-
av_free(avIOCtx);
123+
124+
// must set opaque pointer to NULL, othewise ffmpeg will assume it is some internal URLContext and crash trying to free it
125+
avIOCtx->opaque = NULL;
126+
avio_closep(&avIOCtx);
127+
124128
av_dict_free(&avDict);
125129

126130
if (fileStreamData != nullptr)
@@ -143,7 +147,11 @@ FFmpegMediaSource::~FFmpegMediaSource()
143147
SAFE_RELEASE(deviceContext);
144148
SAFE_RELEASE(deviceManager);
145149

146-
PlaybackSession = nullptr;
150+
if (PlaybackSession)
151+
{
152+
PlaybackSession->PositionChanged -= sessionPositionEvent;
153+
PlaybackSession = nullptr;
154+
}
147155

148156
mutexGuard.unlock();
149157
}
@@ -1600,13 +1608,6 @@ void FFmpegMediaSource::OnStarting(MediaStreamSource^ sender, MediaStreamSourceS
16001608
// Perform seek operation when MediaStreamSource received seek event from MediaElement
16011609
if (request->StartPosition && request->StartPosition->Value.Duration <= mediaDuration.Duration && (!isFirstSeek || request->StartPosition->Value.Duration > 0))
16021610
{
1603-
TimeSpan actualPosition = request->StartPosition->Value;
1604-
auto hr = Seek(request->StartPosition->Value, actualPosition, true);
1605-
if (SUCCEEDED(hr))
1606-
{
1607-
request->SetActualStartPosition(actualPosition);
1608-
}
1609-
16101611
if (currentVideoStream && !currentVideoStream->IsEnabled)
16111612
{
16121613
currentVideoStream->EnableStream();
@@ -1616,6 +1617,13 @@ void FFmpegMediaSource::OnStarting(MediaStreamSource^ sender, MediaStreamSourceS
16161617
{
16171618
currentAudioStream->EnableStream();
16181619
}
1620+
1621+
TimeSpan actualPosition = request->StartPosition->Value;
1622+
auto hr = Seek(request->StartPosition->Value, actualPosition, true);
1623+
if (SUCCEEDED(hr))
1624+
{
1625+
request->SetActualStartPosition(actualPosition);
1626+
}
16191627
}
16201628

16211629
isFirstSeek = false;

Source/StringUtils.h

+12-8
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ namespace FFmpegInteropX
2222
{
2323
if (!char_array) return std::wstring(L"");
2424

25-
std::string s_str = std::string(char_array);
26-
std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
27-
std::wstring wid_str = myconv.from_bytes(s_str);
25+
auto required_size = MultiByteToWideChar(CP_UTF8, 0, char_array, -1, NULL, 0);
26+
wchar_t* buffer = (wchar_t*)calloc(required_size, sizeof(wchar_t));
27+
auto result = MultiByteToWideChar(CP_UTF8, 0, char_array, -1, buffer, required_size);
28+
std::wstring wid_str = std::wstring(buffer);
29+
free(buffer);
2830
return wid_str;
2931
}
3032

@@ -39,13 +41,15 @@ namespace FFmpegInteropX
3941
return ref new Platform::String(input.c_str(), (unsigned int)input.length());
4042
}
4143

42-
4344
static std::string PlatformStringToUtf8String(String^ value)
4445
{
45-
std::wstring strW(value->Begin(), value->Length());
46-
std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
47-
return myconv.to_bytes(strW);
46+
int required_size = WideCharToMultiByte(CP_UTF8, 0, value->Data(), -1, NULL, 0, NULL, NULL);
47+
char* buffer = (char*)calloc(required_size, sizeof(char));
48+
auto result = WideCharToMultiByte(CP_UTF8, 0, value->Data(), -1, buffer, required_size, NULL, NULL);
49+
std::string s_str = std::string(buffer);
50+
free(buffer);
51+
return s_str;
4852
}
4953

5054
};
51-
}
55+
}

0 commit comments

Comments
 (0)