Skip to content

Commit b02f110

Browse files
authored
Merge pull request #167 from h4tr3d/fix_possible_mem_leak_with_custom_io
Fix possible mem leak with custom io
2 parents 75ead61 + b9f0699 commit b02f110

4 files changed

Lines changed: 611 additions & 30 deletions

File tree

src/avcpp/formatcontext.cpp

Lines changed: 155 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void set_uri(AVFormatContext *ctx, string_view uri)
6969
#if AVCPP_API_AVFORMAT_URL
7070
if (ctx->url)
7171
av_free(ctx->url);
72-
ctx->url = av_strdup(uri.data());
72+
ctx->url = av_strndup(uri.data(), uri.size());
7373
#else
7474
av_strlcpy(ctx->filename, uri.data(), std::min<size_t>(sizeof(ctx->filename), uri.size() + 1));
7575
ctx->filename[uri.size()] = '\0';
@@ -195,10 +195,10 @@ void FormatContext::close()
195195
m_headerWriten = false;
196196

197197
// To prevent free not out custom IO, e.g. setted via raw pointer access
198-
if (m_customIO) {
198+
if (m_customIO && avio) {
199199
// Close custom IO
200200
av_freep(&avio->buffer);
201-
av_freep(&avio);
201+
avio_context_free(&avio);
202202
m_customIO = false;
203203
}
204204
}
@@ -606,29 +606,42 @@ Packet FormatContext::readPacket(OptionalErrorCode ec)
606606

607607
void FormatContext::openOutput(const string &uri, OptionalErrorCode ec)
608608
{
609-
return openOutput(uri, OutputFormat(), nullptr, ec);
609+
openOutput(uri, OutputFormat(), nullptr, ec);
610610
}
611611

612612
void FormatContext::openOutput(const string &uri, Dictionary &options, OptionalErrorCode ec)
613613
{
614614
auto ptr = options.release();
615-
try
616-
{
617-
openOutput(uri, OutputFormat(), &ptr, ec);
618-
options.assign(ptr);
619-
}
620-
catch (const Exception&)
621-
{
615+
ScopeOutAction onScopeExit{[ptr, &options] {
622616
options.assign(ptr);
623-
throw;
624-
}
617+
}};
618+
openOutput(uri, OutputFormat(), &ptr, ec);
625619
}
626620

627621
void FormatContext::openOutput(const string &uri, Dictionary &&options, OptionalErrorCode ec)
628622
{
629623
return openOutput(uri, options, ec);
630624
}
631625

626+
void FormatContext::openOutput(const std::string &uri, OutputFormat format, OptionalErrorCode ec)
627+
{
628+
openOutput(uri, format, nullptr, ec);
629+
}
630+
631+
void FormatContext::openOutput(const std::string &uri, Dictionary &options, OutputFormat format, OptionalErrorCode ec)
632+
{
633+
auto ptr = options.release();
634+
ScopeOutAction onScopeExit{[ptr, &options] {
635+
options.assign(ptr);
636+
}};
637+
openOutput(uri, format, &ptr, ec);
638+
}
639+
640+
void FormatContext::openOutput(const std::string &uri, Dictionary &&options, OutputFormat format, OptionalErrorCode ec)
641+
{
642+
openOutput(uri, options, format, ec);
643+
}
644+
632645
void FormatContext::openOutput(const string &uri, OutputFormat format, AVDictionary **options, OptionalErrorCode ec)
633646
{
634647
clear_if(ec);
@@ -693,6 +706,57 @@ void FormatContext::openOutput(const string &uri, OutputFormat format, AVDiction
693706
m_isOpened = true;
694707
}
695708

709+
bool FormatContext::initOutput(Dictionary &options, bool closeOnError, OptionalErrorCode ec)
710+
{
711+
auto dict = options.release();
712+
ScopeOutAction onScopeExit([this, &dict, &options, ec, closeOnError](){
713+
options.assign(dict);
714+
//fflog(AV_LOG_ERROR, "init output.... done with %s\n", (is_error(ec) || std::uncaught_exceptions() > 0) ? "error" : "no error");
715+
if (closeOnError && (is_error(ec) || std::uncaught_exceptions() > 0)) {
716+
close();
717+
}
718+
});
719+
return initOutput(&dict, ec);
720+
}
721+
722+
bool FormatContext::initOutput(AVDictionary **options, OptionalErrorCode ec)
723+
{
724+
clear_if(ec);
725+
726+
if (!isOpened()) {
727+
throws_if(ec, Errors::FormatNotOpened);
728+
return false;
729+
}
730+
731+
if (!isOutput()) {
732+
throws_if(ec, Errors::FormatInvalidDirection);
733+
return false;
734+
}
735+
736+
// just silent it???
737+
if (m_headerWriten) {
738+
return true;
739+
}
740+
741+
resetSocketAccess();
742+
int ret = avformat_init_output(m_raw, options);
743+
ret = checkPbError(ret);
744+
if (ret < 0) {
745+
throws_if(ec, ret, ffmpeg_category());
746+
return false;
747+
}
748+
749+
fflog(AV_LOG_ERROR, "avformat_init_output: ret = %d\n", ret);
750+
751+
switch (ret) {
752+
case AVSTREAM_INIT_IN_INIT_OUTPUT:
753+
return true;
754+
case AVSTREAM_INIT_IN_WRITE_HEADER:
755+
default:
756+
return false;
757+
}
758+
}
759+
696760
void FormatContext::openOutput(CustomIO *io, OptionalErrorCode ec, size_t internalBufferSize)
697761
{
698762
openCustomIOOutput(io, internalBufferSize, ec);
@@ -702,6 +766,66 @@ void FormatContext::openOutput(CustomIO *io, OptionalErrorCode ec, size_t intern
702766
}
703767
}
704768

769+
bool FormatContext::openOutput(CustomIO *io, Dictionary &options, OptionalErrorCode ec, size_t internalBufferSize)
770+
{
771+
openOutput(io, ec, internalBufferSize);
772+
if (!is_error(ec)) {
773+
return initOutput(options, true, ec);
774+
}
775+
return false;
776+
}
777+
778+
bool FormatContext::openOutput(CustomIO *io, Dictionary &&formatOptions, OptionalErrorCode ec, size_t internalBufferSize)
779+
{
780+
openOutput(io, ec, internalBufferSize);
781+
if (!is_error(ec)) {
782+
return initOutput(formatOptions, true, ec);
783+
}
784+
return false;
785+
}
786+
787+
void FormatContext::openOutput(CustomIO *io, OutputFormat format, OptionalErrorCode ec, size_t internalBufferSize)
788+
{
789+
if (format.isNull())
790+
format = outputFormat();
791+
else
792+
setFormat(format);
793+
openOutput(io, ec, internalBufferSize);
794+
}
795+
796+
bool FormatContext::openOutput(CustomIO *io, Dictionary &formatOptions, OutputFormat format, OptionalErrorCode ec, size_t internalBufferSize)
797+
{
798+
openOutput(io, format, ec, internalBufferSize);
799+
if (!is_error(ec)) {
800+
return initOutput(formatOptions, true, ec);
801+
}
802+
return false;
803+
}
804+
805+
bool FormatContext::openOutput(CustomIO *io, Dictionary &&formatOptions, OutputFormat format, OptionalErrorCode ec, size_t internalBufferSize)
806+
{
807+
openOutput(io, format, ec, internalBufferSize);
808+
if (!is_error(ec)) {
809+
return initOutput(formatOptions, true, ec);
810+
}
811+
return false;
812+
}
813+
814+
bool FormatContext::initOutput(OptionalErrorCode ec)
815+
{
816+
return initOutput(nullptr, ec);
817+
}
818+
819+
bool FormatContext::initOutput(Dictionary &options, OptionalErrorCode ec)
820+
{
821+
return initOutput(options, false, ec);
822+
}
823+
824+
bool FormatContext::initOutput(Dictionary &&options, OptionalErrorCode ec)
825+
{
826+
return initOutput(options, false, ec);
827+
}
828+
705829
void FormatContext::writeHeader(OptionalErrorCode ec)
706830
{
707831
writeHeader(nullptr, ec);
@@ -729,6 +853,11 @@ void FormatContext::writeHeader(AVDictionary **options, OptionalErrorCode ec)
729853
{
730854
clear_if(ec);
731855

856+
if (m_headerWriten) {
857+
// TBD: just silent it?
858+
return;
859+
}
860+
732861
if (!isOpened())
733862
{
734863
throws_if(ec, Errors::FormatNotOpened);
@@ -1051,6 +1180,12 @@ void FormatContext::openCustomIO(CustomIO *io, size_t internalBufferSize, bool i
10511180
{
10521181
clear_if(ec);
10531182

1183+
if (!io) {
1184+
fflog(AV_LOG_ERROR, "Open CustomIO with null io context");
1185+
throws_if(ec, Errors::InvalidArgument);
1186+
return;
1187+
}
1188+
10541189
if (!m_raw)
10551190
{
10561191
throws_if(ec, Errors::Unallocated);
@@ -1067,26 +1202,24 @@ void FormatContext::openCustomIO(CustomIO *io, size_t internalBufferSize, bool i
10671202

10681203
AVIOContext *ctx = nullptr;
10691204
// Note: buffer must be allocated only with av_malloc() and friends
1070-
uint8_t *internalBuffer = (uint8_t*)av_mallocz(internalBufferSize);
1071-
if (!internalBuffer)
1072-
{
1205+
auto internalBuffer = av::mallocz<uint8_t>(internalBufferSize);
1206+
if (!internalBuffer) {
10731207
throws_if(ec, ENOMEM, std::system_category());
10741208
return;
10751209
}
10761210

1077-
ctx = avio_alloc_context(internalBuffer, internalBufferSize, isWritable, (void*)(io), custom_io_read, custom_io_write, custom_io_seek);
1078-
if (ctx)
1079-
{
1211+
ctx = avio_alloc_context(internalBuffer.get(), internalBufferSize, isWritable, (void*)(io), custom_io_read, custom_io_write, custom_io_seek);
1212+
if (ctx) {
10801213
ctx->seekable = io->seekable();
10811214
m_raw->flags |= AVFMT_FLAG_CUSTOM_IO;
10821215
m_customIO = true;
1083-
}
1084-
else
1085-
{
1216+
} else {
10861217
throws_if(ec, ENOMEM, std::system_category());
10871218
return;
10881219
}
10891220

1221+
internalBuffer.release(); // drop owning
1222+
10901223
m_raw->pb = ctx;
10911224
}
10921225

0 commit comments

Comments
 (0)