Blackmagic RAW SDK OS X crash

Ask software engineering and SDK questions for developers working on Mac OS X, Windows or Linux.
  • Author
  • Message
Offline

ggarra13

  • Posts: 3
  • Joined: Mon May 25, 2020 1:32 pm
  • Real Name: Gonzalo Garramuño

Blackmagic RAW SDK OS X crash

PostMon May 25, 2020 1:52 pm

I am getting crashes on the codec->SetCallback() call on OS X for similar code that works fine on Windows and Linux. I must be doing something obviously wrong, but cannot figure out why. Apparently braw gets a zero pointer from somewhere.

Here's the stack trace:
Code: Select all

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000013
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Segmentation fault: 11
Termination Reason:    Namespace SIGNAL, Code 0xb
Terminating Process:   exc handler [3590]

VM Regions Near 0x13:
-->
    __TEXT                 00000001030af000-0000000103714000 [ 6548K] r-x/r-x SM=COW  /Users/USER/*/mrViewer.app/Contents/MacOS/mrViewer

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.blackmagic-design.blackmagicraw.api   0x00000001813b3f7c 0x1813b2000 + 8060
1                                    0x00000001032f83b1 mrv::brawImage::fetch(boost::shared_ptr<mrv::VideoFrame>&, long long) + 785 (brawImage.cpp:871)
2                                    0x00000001032fa42f mrv::brawImage::do_seek() + 303 (brawImage.cpp:1302)
3                                    0x000000010314ee33 mrv::CMedia::seek(long long) + 147 (CMedia.cpp:2854)
4                                    0x000000010347be4f mrv::ImageBrowser::load_image(char const*, long long, long long, long long, long long, bool) + 559 (mrvImageBrowser.cpp:1679)
5                                    0x000000010347f09b mrv::ImageBrowser::load(std::__1::vector<mrv::LoadInfo, std::__1::allocator<mrv::LoadInfo> > const&, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, bool) + 3627 (mrvImageBrowser.cpp:1874)
6                                    0x00000001035cc197 load_files(std::__1::vector<mrv::LoadInfo, std::__1::allocator<mrv::LoadInfo> >&, ViewerUI*, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool) + 135 (main.cpp:91)
7                                    0x00000001035ce79d main + 7021 (main.cpp:343)
8   libdyld.dylib                    0x00007fff72775cc9 start + 1


Here's my .cpp code:
Code: Select all
#include "brawImage.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#if !defined(WIN32) && !defined(WIN64)
#  include <arpa/inet.h>
#else
#  include <winsock2.h>    // for htonl
#  include <comutil.h>
#endif

#ifdef OSX
#include <CoreServices/CoreServices.h>
#define STDMETHODCALLTYPE
#endif


#ifdef DEBUG
#include <cassert>
#define VERIFY(condition) assert(SUCCEEDED(condition))
#else
#define VERIFY(condition) condition
#endif

extern "C" {
#include <libavutil/time.h>
#ifdef WIN32
#  pragma warning(push)
#  pragma warning (disable: 4244 )
#endif
#include <libavutil/intreadwrite.h>
#ifdef WIN32
#  pragma warning(pop)
#endif
}

#include "ImfFloatAttribute.h"
#include "ImfIntAttribute.h"
#include "ImfStringAttribute.h"
#include "ImfTimeCodeAttribute.h"

#include "core/mrvMath.h"
#include "core/mrvFrameFunctors.h"
#include "gui/mrvPreferences.h"
#include "mrvPreferencesUI.h"



namespace {
    const char* kModule = "braw";
}


namespace mrv {


#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE
#  define AVCODEC_MAX_AUDIO_FRAME_SIZE 198000
#endif

    static const BlackmagicRawResourceFormat s_resourceFormat = blackmagicRawResourceFormatRGBAU8;

    class CameraCodecCallback : public IBlackmagicRawCallback
    {
        brawImage* img;
        mrv::image_type_ptr& canvas;
        int64_t frame;
        BlackmagicRawResolutionScale scale;
        IBlackmagicRawFrame* m_frame = nullptr;
    public:
        explicit CameraCodecCallback( brawImage* b,
                                      const mrv::image_type_ptr& c,
                                      const int64_t f,
                                      const BlackmagicRawResolutionScale s =
                                      blackmagicRawResolutionScaleEighth) :
            IBlackmagicRawCallback(),
            img( b ),
            canvas( c ),
            frame( f ),
            scale( s )
            {
                std::cerr << "init callback " << f << " " << img << std::endl;
            };
        virtual ~CameraCodecCallback()
            {
                std::cerr << "exit callback" << std::endl;
                if (m_frame != nullptr)
                    m_frame->Release();
            }

        IBlackmagicRawFrame*   GetFrame() {
            return m_frame;
        }

        virtual
        void STDMETHODCALLTYPE
        ReadComplete(IBlackmagicRawJob* readJob, HRESULT result,
                     IBlackmagicRawFrame* Frame)
            {
                std::cerr << "ReadComplete" << std::endl;
                if (result == S_OK)
                {
                    m_frame = Frame;
                    m_frame->AddRef();
                }

                IBlackmagicRawJob* decodeAndProcessJob = nullptr;

                if (result == S_OK)
                    result = Frame->SetResourceFormat(s_resourceFormat);

                if (result == S_OK)
                    result = Frame->SetResolutionScale( scale );

                if (result == S_OK)
                    result = Frame->CreateJobDecodeAndProcessFrame(nullptr,
                                                                   nullptr,
                                                                   &decodeAndProcessJob);

                if (result == S_OK)
                    result = decodeAndProcessJob->Submit();

                if (result != S_OK)
                {
                    LOG_ERROR( _("ReadComplete failed!") );
                    if (decodeAndProcessJob)
                        decodeAndProcessJob->Release();
                }

                readJob->Release();
            }

        virtual
        void STDMETHODCALLTYPE
        ProcessComplete(IBlackmagicRawJob* job, HRESULT result,
                        IBlackmagicRawProcessedImage* processedImage)
            {
                std::cerr << "ProcessComplete" << std::endl;
                unsigned int width = 0;
                unsigned int height = 0;
                void* imageData = nullptr;

                if (result == S_OK)
                    result = processedImage->GetWidth(&width);

                if (result == S_OK)
                    result = processedImage->GetHeight(&height);

                if (result == S_OK)
                    result = processedImage->GetResource(&imageData);

                if ( result == S_OK )
                    img->store_image( canvas, frame, width, height, imageData );

                job->Release();
            }

        virtual void STDMETHODCALLTYPE DecodeComplete(IBlackmagicRawJob*, HRESULT) {}
        virtual void STDMETHODCALLTYPE TrimProgress(IBlackmagicRawJob*, float) {}
        virtual void STDMETHODCALLTYPE TrimComplete(IBlackmagicRawJob*, HRESULT) {}

#ifdef WIN32
        virtual void STDMETHODCALLTYPE SidecarMetadataParseWarning(IBlackmagicRawClip*, BSTR, uint32_t, BSTR) {}
        virtual void STDMETHODCALLTYPE SidecarMetadataParseError(IBlackmagicRawClip*, BSTR, uint32_t, BSTR) {}
#elif LINUX
        virtual void SidecarMetadataParseWarning(IBlackmagicRawClip*, const char*, uint32_t, const char*) {}
        virtual void SidecarMetadataParseError(IBlackmagicRawClip*, const char*, uint32_t, const char*) {}
#elif OSX
        virtual void SidecarMetadataParseWarning(IBlackmagicRawClip*, CFStringRef, uint32_t, CFStringRef) {}
        virtual void SidecarMetadataParseError(IBlackmagicRawClip*, CFStringRef, uint32_t, CFStringRef) {}
#endif
        virtual void STDMETHODCALLTYPE PreparePipelineComplete(void*, HRESULT) {}

        virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
            {
                return E_NOTIMPL;
            }

        virtual ULONG STDMETHODCALLTYPE AddRef(void)
            {
                std::cerr << "this " << this << " AddRef" << std::endl;
                return 0;
            }

        virtual ULONG STDMETHODCALLTYPE Release(void)
            {
                std::cerr << "this " << this << " Release" << std::endl;
                return 0;
            }
    };

    bool brawImage::init = true;
    IBlackmagicRawFactory* brawImage::factory = nullptr;

    brawImage::brawImage() :
        CMedia(),
        _scale( Preferences::BRAWScale ),
        _old_scale( Preferences::BRAWScale ),
        bitDepth( 0 ),
        audiobuffer( NULL ),
        audioinit( false ),
        codec( NULL ),
        clip( NULL ),
        readJob( NULL ),
        audio( NULL )
    {
        initialize();
        _gamma = 1.0f;
        _audio_last_frame = 0;
    }

    brawImage::~brawImage()
    {
        finalize();
    }

    bool brawImage::store_image( mrv::image_type_ptr& canvas,
                                 int64_t frame, unsigned dw, unsigned dh,
                                 void* imageData )
    {
        image_size( dw, dh );
        const image_type::PixelType pixel_type = image_type::kByte;
        allocate_pixels( canvas, frame, 4, image_type::kRGBA, pixel_type,
                         dw, dh );

        std::cerr << "canvas now " << canvas << std::endl;
        memcpy( canvas->data().get(), imageData, dw*dh*4 );

        // Store in queue
        SCOPED_LOCK( _mutex );
        if ( _images.empty() || _images.back()->frame() < frame )
        {
            _images.push_back( canvas );
        }
        else
        {
            video_cache_t::iterator at = std::lower_bound( _images.begin(),
                                                           _images.end(),
                                                           frame,
                                                           LessThanFunctor() );


            // Avoid storing duplicate frames, replace old frame with this one
            if ( at != _images.end() )
            {
                if ( (*at)->frame() == frame )
                {
                    at = _images.erase(at);
                }
            }

            _images.insert( at, canvas );
        }

        return true;
    }

    bool brawImage::test( const char* file )
    {
        if ( file == NULL ) return false;

        HRESULT result;

        std::string libpath = mrv::Preferences::root;
        libpath += "/lib";

#ifdef WIN32
        _bstr_t clib( libpath.c_str() );
#elif LINUX
        const char* clib = libpath.c_str();
#elif OSX
        CFStringRef clib = CFStringCreateWithCString( NULL,
                                                      libpath.c_str(),
                                                      kCFStringEncodingUTF8 );
#endif

        if ( factory == nullptr )
        {
            factory = CreateBlackmagicRawFactoryInstanceFromPath( clib );
            if (factory == nullptr)
            {
#if defined(LINUX) || defined(_WIN64) || defined(OSX)
                LOG_ERROR( _("Failed to create IBlackmagicRawFactory!") );
#endif
                return false;
            }
        }

#ifdef OSX
        CFRelease( clib );
#endif


        IBlackmagicRaw* codec;
        result = factory->CreateCodec(&codec);
        if (result != S_OK)
        {
            LOG_ERROR( _("Failed to create IBlackmagicRaw!") );
            return false;
        }

#ifdef WIN32
        _bstr_t filename( file );
#elif LINUX
        const char* filename = file;
#elif OSX
        CFStringRef filename = CFStringCreateWithCString( NULL,
                                                          file,
                                                          kCFStringEncodingUTF8 );
#endif

        IBlackmagicRawClip* localclip = nullptr;
        result = codec->OpenClip(filename, &localclip);
        if (result != S_OK)
        {
            return false;
        }

#ifdef OSX
        CFRelease( filename );
#endif

        localclip->Release();
        localclip = nullptr;

        codec->Release();
        codec = nullptr;

        return true;
    }

    void brawImage::parse_metadata(int64_t frame,
                                   IBlackmagicRawMetadataIterator*
                                   metadataIterator)
    {
#ifdef WIN32
        BSTR keyStr;
        VARIANT value;
#elif LINUX
        const char* keyStr = nullptr;
        Variant value;
#else
        CFStringRef keyStr;
        Variant value;
#endif

        if ( _attrs.find( frame ) == _attrs.end() )
        {
            _attrs.insert( std::make_pair( frame, Attributes() ) );
        }

        HRESULT result;
        while (SUCCEEDED(metadataIterator->GetKey(&keyStr)))
        {
            VariantInit(&value);
            result = metadataIterator->GetData(&value);
            if (result != S_OK)
            {
                LOG_ERROR( _("Failed to get data from "
                             "IBlackmagicRawMetadataIterator!") );
                break;
            }


#ifdef LINUX
            const char* key = (const char*) keyStr;
            BlackmagicRawVariantType variantType = value.vt;
#elif WIN32
            // or use true to get original BSTR released through wrapper
            _bstr_t interim(keyStr, false);
            const char* key((const char*) interim);
            VARTYPE variantType = value.vt;
#else
            const char* key = CFStringGetCStringPtr( keyStr,
                                                     kCFStringEncodingUTF8 );
            BlackmagicRawVariantType variantType = value.vt;
#endif

            if ( key == NULL || strlen(key) == 0 ) {
                VariantClear(&value);
                metadataIterator->Next();
                continue;
            }

            switch (variantType)
            {
            case blackmagicRawVariantTypeS16:
            {
                short s16 = value.iVal;
                Imf::IntAttribute attr( s16 );
                if ( frame < 0 )
                {
                    _clip_attrs.insert( std::make_pair( key, attr.copy() ) );
                }
                else
                {
                    _attrs[frame].insert( std::make_pair( key, attr.copy() ) );
                }
                break;
            }
            case blackmagicRawVariantTypeU16:
            {
                unsigned short u16 = value.uiVal;
                Imf::IntAttribute attr( u16 );
                if ( frame < 0 )
                {
                    _clip_attrs.insert( std::make_pair( key, attr.copy() ) );
                }
                else
                {
                    _attrs[frame].insert( std::make_pair( key, attr.copy() ) );
                }
                break;
            }
            case blackmagicRawVariantTypeS32:
            {
                int i32 = value.intVal;
                Imf::IntAttribute attr( i32 );
                if ( frame < 0 )
                {
                    _clip_attrs.insert( std::make_pair( key, attr.copy() ) );
                }
                else
                {
                    _attrs[frame].insert( std::make_pair( key, attr.copy() ) );
                }
                break;
            }
            case blackmagicRawVariantTypeU32:
            {
                unsigned int u32 = value.uintVal;
                Imf::IntAttribute attr( u32 );
                if ( frame < 0 )
                {
                    _clip_attrs.insert( std::make_pair( key, attr.copy() ) );
                }
                else
                {
                    _attrs[frame].insert( std::make_pair( key, attr.copy() ) );
                }
                break;
            }
            case blackmagicRawVariantTypeFloat32:
            {
                float f32 = value.fltVal;
                Imf::FloatAttribute attr( f32 );
                if ( frame < 0 )
                {
                    _clip_attrs.insert( std::make_pair( key, attr.copy() ) );
                }
                else
                {
                    _attrs[frame].insert( std::make_pair( key, attr.copy() ) );
                }
                break;
            }
            case blackmagicRawVariantTypeString:
            {
#ifdef LINUX
                const char* str = value.bstrVal;
#elif WIN32
                BSTR tmp = value.bstrVal;
                // or use true to get original BSTR released through wrapper
                _bstr_t interim(tmp, false);
                const char* str((const char*) interim);
#else
                const char* str = CFStringGetCStringPtr( value.bstrVal,
                                                         kCFStringEncodingUTF8 );
#endif
                if ( str == NULL ) str = "";

                Imf::StringAttribute attr( str );
                if ( frame < 0 )
                {
                    _clip_attrs.insert( std::make_pair( key, attr.copy() ) );
                }
                else
                {
                    _attrs[frame].insert( std::make_pair( key, attr.copy() ) );
                }
                break;
            }
            }

            VariantClear(&value);

            metadataIterator->Next();
        }

        image_damage( image_damage() | kDamageData );
    }

    bool brawImage::initialize()
    {
        init = true;
        HRESULT result;

        result = factory->CreateCodec(&codec);
        if (result != S_OK)
        {
            LOG_ERROR( _("Failed to create IBlackmagicRaw Codec!") );
            return false;
        }

#ifdef WIN32
        _bstr_t file = filename();
#elif LINUX
        const char* file = filename();
#elif OSX
        const char* fname = filename();
        if ( fname == NULL || strlen(fname) == 0 ) return false;
        CFStringRef file = CFStringCreateWithCString( NULL,
                                                      fname,
                                                      kCFStringEncodingUTF8 );
#endif

        result = codec->OpenClip( file, &clip );
        if (result != S_OK)
        {
            IMG_ERROR( _("Could not open clip" ) );
            return false;
        }

#ifdef OSX
        CFRelease( file );
#endif




        rgb_layers();
        lumma_layers();
        alpha_layers();

        _frameStart = _frame_start = 0;
        uint64_t duration;
        result = clip->GetFrameCount( &duration );
        if (result != S_OK)
        {
            IMG_ERROR( _("Could not obtain frame count" ) );
            return false;
        }
        _frameEnd = _frame_end = (int64_t)duration - 1;
        float f;
        result = clip->GetFrameRate( &f );
        if ( result != S_OK )
        {
            IMG_ERROR( _("Could not obtain frame rate" ) );
            return false;
        }
        _fps = _orig_fps = _play_fps = f;

        video_info_t info;
        info.has_codec = true;
        info.codec_name = "BRAW";
        info.fourcc = "BRAW";
        info.fps = _fps;
        info.pixel_format = "RGBA";
        info.has_b_frames = true;
        info.start = 0;
        info.duration = (double)(_frame_end - _frame_start) /
                        (double) _fps;
        _video_info.push_back( info );


        IBlackmagicRawMetadataIterator* clipMetadataIterator = nullptr;

        result = clip->GetMetadataIterator(&clipMetadataIterator);
        if (result != S_OK)
        {
            LOG_ERROR(_("Failed to get clip IBlackmagicRawMetadataIterator!"));
        }

        parse_metadata( -1, clipMetadataIterator );

        image_damage( image_damage() | kDamageData );

        result = clip->QueryInterface(IID_IBlackmagicRawClipAudio,
                                      (void**)&audio);
        if ( result != S_OK ) return true;

        uint32_t channelCount;
        uint32_t sampleRate;

        BlackmagicRawAudioFormat format;
        result = audio->GetAudioFormat(&format);
        if (result != S_OK)
        {
            IMG_ERROR( _("Failed to get Audio Format!") );
            return false;
        }

        result = audio->GetAudioSampleCount(&audioSamples);
        if (result != S_OK)
        {
            IMG_ERROR( _("Failed to get total audio sample count!") );
            return false;
        }

        result = audio->GetAudioBitDepth(&bitDepth);
        if (result != S_OK)
        {
            IMG_ERROR( _("Failed to get Audio Bit Depth!") );
            return false;
        }

        result = audio->GetAudioChannelCount(&channelCount);
        if (result != S_OK)
        {
            IMG_ERROR( _("Failed to get Audio Channel Count!") );
            return false;
        }

        result = audio->GetAudioSampleRate(&sampleRate);
        if (result != S_OK)
        {
            IMG_ERROR( _("Failed to get Audio Sample Rate!") );
            return false;
        }

        audio_info_t a;
        a.has_codec = bitDepth == 0 ? false : true;
        a.codec_name = "BRAW Audio";
        a.fourcc = "BRAW";
        if ( format == blackmagicRawAudioFormatPCMLittleEndian )
        {
            switch( bitDepth )
            {
            case 8:
                _audio_format = AudioEngine::kU8;
                a.format = "u8";
                break;
            case 16:
                _audio_format = AudioEngine::kS16LSB;
                a.format = "s16le";
                break;
            case 24:
                _audio_format = AudioEngine::kS32LSB;
                a.format = "s24le";
                break;
            case 32:
                _audio_format = AudioEngine::kS32LSB;
                a.format = "s32le";
                break;
            default:
                LOG_ERROR( _("Unknown audio bit depth ") << bitDepth );
                _audio_format = AudioEngine::kS16MSB;
                a.format = "s16be";
                break;
            }
        }
        else
        {
            switch( bitDepth )
            {
            case 8:
                _audio_format = AudioEngine::kU8;
                a.format = "u8";
                break;
            case 16:
                _audio_format = AudioEngine::kS16MSB;
                a.format = "s16be";
                break;
            case 24:
                _audio_format = AudioEngine::kS32MSB;
                a.format = "s24be";
                break;
            case 32:
                _audio_format = AudioEngine::kS32MSB;
                a.format = "s32be";
                break;
            default:
                LOG_ERROR( _("Unknown audio bit depth ") << bitDepth );
                _audio_format = AudioEngine::kS16MSB;
                a.format = "s16be";
                break;
            }
        }


        a.channels = _audio_channels = channelCount;
        a.frequency = frequency = sampleRate;
        a.bitrate = sampleRate * channelCount * bitDepth;
        a.language = _("und");
        a.start = 0;
        a.duration = audioSamples / (double) frequency;

        _audio_info.push_back( a );

        if ( a.has_codec )
        {
            _audio_index = (int) _audio_info.size() - 1;
            audio_initialize();

            if ( !_audio_buf )
            {
                _audio_max = AVCODEC_MAX_AUDIO_FRAME_SIZE * 2;
                // Final buffer for 32 bit audio
                _audio_buf = new aligned16_uint8_t[ _audio_max ];
                memset( _audio_buf, 0, _audio_max );

                static constexpr uint32_t maxSampleCount = 48000;
                uint32_t bufferSize = (maxSampleCount*_audio_channels*
                                       bitDepth)/8;

                // temporary buffer for 24 bit audio
                audiobuffer = new int8_t[ bufferSize ];

                char buf[256];
                uint64_t  in_ch_layout =
                    get_valid_channel_layout( 0, _audio_channels);
                if ( in_ch_layout == 0 )
                    in_ch_layout = get_valid_channel_layout( AV_CH_LAYOUT_STEREO,
                                                             _audio_channels);

                if ( in_ch_layout == 0 )
                    in_ch_layout = get_valid_channel_layout( AV_CH_LAYOUT_MONO,
                                                             _audio_channels);

                if ( ! _is_thumbnail )
                {
                    av_get_channel_layout_string( buf, 256, _audio_channels,
                                                  in_ch_layout );
                    IMG_INFO( _("Audio ") << buf << _(", channels ")
                              << _audio_channels );
                    IMG_INFO( _("format ") << a.format
                              << _(", frequency ") << frequency );
                }
            }
        }

        return true;
    }

    bool brawImage::finalize()
    {
        std::cerr << ">>>> FiNALIZE" << std::endl;
        if ( audio )
            audio->Release();
        audio = nullptr;
        if ( clip )
            clip->Release();
        clip = nullptr;
        if ( codec )
            codec->Release();
        codec = nullptr;
        if ( factory )
            factory->Release();
        factory = nullptr;

        delete [] audiobuffer;
        audiobuffer = NULL;

        init = false;

        return true;
    }


    void brawImage::clear_cache()
    {
        SCOPED_LOCK( _mutex );
        _images.clear();
        image_damage( image_damage() | kDamageCache | kDamageContents );
    }

    CMedia::Cache brawImage::is_cache_filled( int64_t frame )
    {
        SCOPED_LOCK( _mutex );
        bool ok = false;

        // Check if video is already in video store
        video_cache_t::iterator i = std::find_if( _images.begin(),
                                                  _images.end(),
                                                  EqualFunctor(frame) );
        if ( i != _images.end() ) ok = true;

        if ( ok && _stereo_input != kSeparateLayersInput ) return kStereoCache;
        return (CMedia::Cache) ok;
    }


    bool brawImage::frame( const int64_t f )
    {

        if ( Preferences::max_memory <= CMedia::memory_used )
        {
            int64_t max_frames = (int64_t) max_image_frames();
            if ( std::abs( f - _frame ) >= max_frames )
                return false;
            limit_video_store( f );
            if ( has_audio() ) limit_audio_store( f );
        }


        if ( f < _frameStart )    _dts = _adts = _frameStart;
        else if ( f > _frameEnd ) _dts = _adts = _frameEnd;
        else                      _dts = _adts = f;

        AVPacket pkt;
        av_init_packet( &pkt );
        pkt.dts = pkt.pts = _dts;
        pkt.size = 0;
        pkt.data = NULL;

        if ( ! is_cache_filled( _dts ) )
        {
            image_type_ptr canvas;
            if ( fetch( canvas, _dts ) )
            {
                default_color_corrections();
            }
        }

        _video_packets.push_back( pkt );

        if ( has_audio() )
        {
            _audio_packets.push_back( pkt );
        }


        _expected = _dts + 1;
        _expected_audio = _expected + _audio_offset;
        return true;
    }

    void brawImage::copy_values()
    {
        _old_scale = _scale;
    }

    bool brawImage::fetch( mrv::image_type_ptr& canvas,
                           const int64_t frame )
    {
        std::cerr << "fetch " << frame << std::endl;
        if ( frame < _frame_start || frame > _frame_end )
            return false;

        if ( _scale != _old_scale )
        {
            clear_cache();
            copy_values();
        }

        BlackmagicRawResolutionScale s = blackmagicRawResolutionScaleEighth;
        switch ( _scale )
        {
        case 0:
            s = blackmagicRawResolutionScaleFull;
            break;
        case 1:
            s = blackmagicRawResolutionScaleHalf;
            break;
        case 2:
            s = blackmagicRawResolutionScaleQuarter;
            break;
        case 3:
        default:
            s = blackmagicRawResolutionScaleEighth;
            break;
        }

        HRESULT result = S_OK;
        std::cerr << "EXITED ALL " << name() << " " << this << " "
                  << canvas << std::endl;

        CameraCodecCallback callback( this, canvas, frame, s );
        std::cerr << codec << " callback " << &callback << std::endl;
        result = codec->SetCallback(&callback);
        std::cerr << codec << " callback returned!! " << &callback << std::endl;
        if (result != S_OK)
        {
            LOG_ERROR( "Failed to set IBlackmagicRawCallback!" );
            return false;
        }

        result = clip->CreateJobReadFrame(frame, &readJob);
        if (result != S_OK)
        {
            LOG_ERROR( "Failed to create IBlackmagicRawJob!" );
            return false;
        }

        result = readJob->Submit();
        if (result != S_OK)
        {
            // submit has failed, the ReadComplete callback won't be called,
            // release the job here instead
            readJob->Release();
            LOG_ERROR( "Failed to submit IBlackmagicRawJob!" );
            return false;
        }

        codec->FlushJobs();

/*
        IBlackmagicRawFrame* f = callback.GetFrame();

        if (f == nullptr)
        {
            LOG_ERROR( _("Failed to get IBlackmagicRawFrame!") );
            return true;
        }

        IBlackmagicRawMetadataIterator* frameMetadataIterator = nullptr;
        result = f->GetMetadataIterator(&frameMetadataIterator);
        if (result != S_OK)
        {
            LOG_ERROR( _("Failed to get frame "
                         "IBlackmagicRawMetadataIterator!") );
            return true;
        }

        parse_metadata(frame, frameMetadataIterator);
*/

        return true;
    }

    bool brawImage::find_image( int64_t& frame )
    {

        if ( _right_eye && (stopped() || saving() ) )
            _right_eye->find_image( frame );

        assert0( frame != AV_NOPTS_VALUE );
        if ( frame < _frame_start || frame > _frame_end )
            return false;

#ifdef DEBUG_VIDEO_PACKETS
        debug_video_packets(frame, "find_image");
#endif

#ifdef DEBUG_VIDEO_STORES
        debug_video_stores(frame, "find_image", true);
#endif


        _frame = frame;



        {
            SCOPED_LOCK( _mutex );

            int64_t f = frame - _start_number;

            video_cache_t::iterator end = _images.end();
            video_cache_t::iterator i;

            if ( playback() == kBackwards )
            {
                i = std::upper_bound( _images.begin(), end,
                                      f, LessThanFunctor() );
            }
            else
            {
                i = std::lower_bound( _images.begin(), end,
                                      f, LessThanFunctor() );
            }

            if ( i != end && *i )
            {
                _hires = *i;

                int64_t distance = f - _hires->frame();


                if ( distance > _hires->repeat() )
                {
                    int64_t first = (*_images.begin())->frame();
                    video_cache_t::iterator end = std::max_element( _images.begin(),
                                                                    _images.end() );
                    int64_t last  = (*end)->frame();
                    boost::uint64_t diff = last - first + 1;
                    IMG_ERROR( _("Video Sync master frame ") << f
                               << " != " << _hires->frame()
                               << _(" video frame, cache ") << first << "-" << last
                               << " (" << diff << _(") cache size: ") << _images.size()
                               << " dts: " << _dts );
                    //  debug_video_stores(frame);
                    //  debug_video_packets(frame);
                }
            }
            else
            {
                // Hmm... no close image was found.  If we have some images in
                // cache, we choose the last one in it.  This avoids problems if
                // the last frame is the one with problem.
                // If not, we fail.

                if ( ! _images.empty() )
                {
                    _hires = _images.back();

                    uint64_t diff = abs(f - _hires->frame() );

                    static short counter = 0;

                    if ( _hires->frame() != f &&
                         diff > 1 && diff < 10 && counter < 10 &&
                         f <= _frameEnd )
                    {
                        frame = _frame = _hires->frame();
                        // ++counter;
                        // IMG_WARNING( _("find_image: frame ") << frame
                        //              << _(" not found, choosing ")
                        //              << _hires->frame()
                        //              << _(" instead") );
                    }
                    else
                    {
                        if ( diff == 0 ) counter = 0;
                    }
                }
                else
                {
                    IMG_ERROR( _("find_image: frame ") << frame << _(" not found") );
                    return false;
                }
            }



            // Limit (clean) the video store as we play it
            limit_video_store( f );

            _video_pts   = f  / _fps; //av_q2d( get_video_stream()->avg_frame_rate );
            _video_clock = double(av_gettime_relative()) / 1000000.0;

            update_video_pts(this, _video_pts, 0, 0);


#ifdef WIN32
            BSTR timeCode;
#elif LINUX
            const char* timeCode = nullptr;
#elif OSX // APPLE
            CFStringRef timeCode;
#endif
            HRESULT result = clip->GetTimecodeForFrame( frame, &timeCode );
            if ( result != S_OK )
            {
                LOG_ERROR( _("Could not get timecode for frame ") << frame );
            }

#ifdef WIN32
            _bstr_t tmp( timeCode, false );
            const char* timecode( (const char*)tmp );
#elif LINUX
            const char* timecode = timeCode;
#elif OSX  // APPLE
            const char* timecode = CFStringGetCStringPtr( timeCode,
                                                          kCFStringEncodingUTF8 );
            if ( timecode == NULL ) return false;
#endif

            Imf::TimeCode t = str2timecode( timecode );
            if ( frame == start_frame() )
            {
                process_timecode( t );
                _tc_frame++;
            }
            Imf::TimeCodeAttribute attr( t );
            _attrs[frame].insert( std::make_pair( "timecode", attr.copy() ) );

#ifdef OSX
            CFRelease( timeCode );
#endif

            refresh();
            image_damage( image_damage() | kDamageTimecode );

        }  // release lock


        return true;
    }

    void brawImage::timed_limit_store( const int64_t frame )
    {
        uint64_t max_frames = max_image_frames();

#undef timercmp
# define timercmp(a, b, CMP)                    \
        (((a).tv_sec == (b).tv_sec) ?           \
         ((a).tv_usec CMP (b).tv_usec) :        \
         ((a).tv_sec CMP (b).tv_sec))

        struct customMore {
            inline bool operator()( const timeval& a,
                                    const timeval& b ) const
                {
                    return timercmp( a, b, < );
                }
        };

        typedef std::map< timeval, int64_t, customMore > TimedSeqMap;
        {
            SCOPED_LOCK( _mutex );
            TimedSeqMap tmp;
            {
                video_cache_t::iterator  it = _images.begin();
                video_cache_t::iterator end = _images.end();
                for ( ; it != end; ++it )
                {
                    tmp.insert( std::make_pair( (*it)->ptime(), (*it)->frame() ) );
                }
            }

            TimedSeqMap::iterator it = tmp.begin();
            for ( ; it != tmp.end() &&
                      memory_used >= Preferences::max_memory; ++it )
            {
                if ( _images.size() < max_frames ) break;
                auto start = std::remove_if( _images.begin(), _images.end(),
                                             EqualFunctor( it->second ) );
                _images.erase( start, _images.end() );
            }

        }

    }

//
// Limit the video store to approx. max frames images on each side.
// We have to check both where frame is as well as where _dts is.
//
    void brawImage::limit_video_store(const int64_t frame)
    {

        return timed_limit_store( frame );


    }

    CMedia::DecodeStatus brawImage::decode_audio( int64_t& f )
    {
        return kDecodeOK;

        if ( _audio_packets.is_loop_end() )
        {
            _audio_packets.pop_front();
            return kDecodeLoopEnd;
        }
        else if ( _audio_packets.is_loop_start() )
        {
            _audio_packets.pop_front();
            return kDecodeLoopStart;
        }

        if ( !_audio_packets.empty() )
            _audio_packets.pop_front();

        int64_t frame = f;

        bool ok = in_audio_store( frame );
        if ( ok ) return kDecodeOK;

        HRESULT result;


        unsigned int bytes_per_frame = audio_bytes_per_frame();

        static constexpr uint32_t maxSampleCount = 48000;
        uint32_t bufferSize = (maxSampleCount*_audio_channels*
                               bitDepth)/8;

        uint32_t startSample = (uint32_t) ( frame *
                                            ( (double) frequency / _orig_fps ));


        uint32_t samplesRead;
        uint32_t bytesRead;

        result = audio->GetAudioSamples(startSample,
                                        audiobuffer,
                                        bufferSize,
                                        maxSampleCount,
                                        &samplesRead,
                                        &bytesRead);

        if (result != S_OK)
        {
            IMG_ERROR( _("Could not get audio samples") );
            return kDecodeMissingSamples;
        }

        assert0( _audio_buf_used % 16 == 0 );
        int32_t* tmp = (int32_t*) ((int8_t*)_audio_buf + _audio_buf_used);

        size_t j = 0, i = 0;
        if ( bitDepth == 24 )
        {
            for ( size_t i = 0; i < bytesRead; i += 3, ++tmp )
            {
                uint32_t base = *((uint32_t*) ((uint8_t*)audiobuffer + i) );
                AV_WN32A( tmp, (uint32_t)(base << 8) );
            }
        }
        else
        {
            IMG_ERROR( _("Bitdepth to process is unknown") );
            return kDecodeError;
        }

        _audio_buf_used += (bytesRead / 3) * 4;

        CMedia::DecodeStatus got_audio = kDecodeMissingFrame;

        if (samplesRead == 0 && _audio_buf_used == 0 ) return got_audio;

        int64_t last = frame;
        unsigned int index = 0;

        if ( last == first_frame() || (stopped() /* || saving() */ ) )
        {
            if ( bytes_per_frame > _audio_buf_used && _audio_buf_used > 0 )
            {
                bytes_per_frame = _audio_buf_used;
            }
        }


        // Split audio read into frame chunks
        for (;;)
        {

            if ( bytes_per_frame > _audio_buf_used ) break;

#ifdef DEBUG
            if ( index + bytes_per_frame >= _audio_max )
            {
                std::cerr << "frame: " << frame << std::endl
                          << "index: " << index << std::endl
                          << "  bpf: " << bytes_per_frame << std::endl
                          << " used: " << _audio_buf_used << std::endl
                          << "  max: " << _audio_max << std::endl;
                abort();
            }
#endif

            uint32_t skip = store_audio( last,
                                         (uint8_t*)_audio_buf + index,
                                         bytes_per_frame );
            if ( skip == 0 ) break;

            index += skip;


            if ( last >= frame ) got_audio = kDecodeOK;

            assert( bytes_per_frame <= _audio_buf_used );
            _audio_buf_used -= bytes_per_frame;
            ++last;

        }

        if (_audio_buf_used > 0 && index > 0 )
        {
            //
            // NOTE: audio buffer must remain 16 bits aligned for ffmpeg.
            memmove( _audio_buf, _audio_buf + index, _audio_buf_used );
        }

        return got_audio;
    }

    bool brawImage::has_changed()
    {
        if ( is_cache_filled( _frame ) )
        {
            if ( refetch( _frame ) )
            {
                int64_t f = _frame;
                find_image( f );
            }
        }
        image_damage( image_damage() | kDamageCache | kDamageContents );
        return true;
    }

    void brawImage::do_seek() {
        // No need to set seek frame for right eye here
        if ( _right_eye )  _right_eye->do_seek();

        if ( saving() ) _seek_req = false;

        bool got_video = !has_video();
        bool got_audio = !has_audio();

        if ( !got_audio || !got_video )
        {
            if ( !saving() && _seek_frame != _expected )
                clear_packets();

            if ( ! is_cache_filled( _seek_frame ) )
            {
                image_type_ptr canvas;
                if ( fetch( canvas, _seek_frame ) )
                {
                    default_color_corrections();
                }
            }

            find_image( _seek_frame );
        }


        // Seeking done, turn flag off
        _seek_req = false;

        if ( stopped() || saving() )
        {

            DecodeStatus status;
            if ( has_audio() )
            {
                int64_t f = _seek_frame;
                f += _audio_offset;
                status = decode_audio( f );
                if ( status > kDecodeOK )
                    IMG_ERROR( _("Decode audio error: ")
                               << get_error_text( status )
                               << _(" for frame ") << _seek_frame );

                if ( !_audio_start )
                    find_audio( _seek_frame + _audio_offset );
                _audio_start = false;
            }


            // Queue thumbnail for update
            image_damage( image_damage() | kDamageThumbnail );
        }

    }
}
Offline

Cameron Nichols

Blackmagic Design

  • Posts: 443
  • Joined: Mon Sep 04, 2017 4:05 am

Re: Blackmagic RAW SDK OS X crash

PostWed May 27, 2020 2:07 am

Hi Gonzalo,

Can you implement AddRef/Release methods in CameraCodecCallback and see if you still see segmentation fault.

In CameraCodecCallback class declaration, add member:
Code: Select all
std::atomic<ULONG> m_refCount
Initialise m_refCount to 1 in constructor.

Change AddRef:
Code: Select all
return ++m_refCount;

Change Release:
Code: Select all
ULONG newRefCount = --m_refCount;
if (newRefCount == 0)
    delete this;
return newRefCount;

Regards
Cameron
Offline

ggarra13

  • Posts: 3
  • Joined: Mon May 25, 2020 1:32 pm
  • Real Name: Gonzalo Garramuño

Re: Blackmagic RAW SDK OS X crash

PostWed May 27, 2020 11:28 pm

I implemented the methods and I still got the crash.
I moved the factory, codec and clip creation to the fetch routine (so they are recreated on each frame with local variables instead of class variables) and I was able to read 201 frames of a movie of 350 and then got an error on creating the codec in the factory->CreateCodec() call. There were no problems on the SetCallback, thou.
Offline

ggarra13

  • Posts: 3
  • Joined: Mon May 25, 2020 1:32 pm
  • Real Name: Gonzalo Garramuño

Re: Blackmagic RAW SDK OS X crash

PostSat Jun 13, 2020 5:55 pm

I was able to make it work, with the variables being local to the fetch function (having codec and clip recreated each time a fetch is done) and with loading each frame in sequence (previously I would start at frame 1 not frame 0). I also reviewed all my memory functions to see they were okay and safe.

Return to Software Developers

Who is online

Users browsing this forum: No registered users and 11 guests