xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #415: ffmpeg2-v3.patch

File ffmpeg2-v3.patch, 23.3 KB (added by Antoine Martin, 8 years ago)

patch using refcounted_frames - not working, crashes in av_frame_unref dereferencing a NULL pointer for side_data...

  • xpra/codecs/dec_avcodec/decoder.pyx

     
    99debug = debug_if_env(log, "XPRA_AVCODEC_DEBUG")
    1010error = log.error
    1111
     12DEF GET_BUFFER_V2 = True
     13
     14
    1215#some consumers need a writeable buffer (ie: OpenCL...)
    1316READ_ONLY = False
    1417
     
    6467        uint8_t *data
    6568        int      size
    6669
    67     ctypedef struct AVCodecContext:
    68         int width
    69         int height
    70         AVPixelFormat pix_fmt
    71         int (*get_buffer)(AVCodecContext *c, AVFrame *pic)
    72         void (*release_buffer)(AVCodecContext *avctx, AVFrame *frame)
    73         int thread_safe_callbacks
    74         int thread_count
    75         int thread_type
    76         int flags
    77         int flags2
     70    IF not GET_BUFFER_V2:
     71        ctypedef struct AVCodecContext:
     72            int width
     73            int height
     74            AVPixelFormat pix_fmt
     75            int (*get_buffer)(AVCodecContext *c, AVFrame *pic)
     76            void (*release_buffer)(AVCodecContext *avctx, AVFrame *frame)
     77            int thread_safe_callbacks
     78            int thread_count
     79            int thread_type
     80            int flags
     81            int flags2
     82    ELSE:
     83        ctypedef struct AVCodecContext:                 #@DuplicateSignature
     84            int width
     85            int height
     86            AVPixelFormat pix_fmt
     87            int thread_safe_callbacks
     88            int thread_count
     89            int thread_type
     90            int flags
     91            int flags2
     92            int refcounted_frames
    7893
    7994    AVPixelFormat PIX_FMT_NONE
    8095    AVCodecID CODEC_ID_H264
     
    96111                                int *got_picture_ptr, const AVPacket *avpkt) nogil
    97112
    98113    #buffer management:
    99     int avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic)
    100     void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic)
     114    IF not GET_BUFFER_V2:
     115        int avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic)
     116        void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic)
     117    ELSE:
     118        AVFrame *av_frame_alloc()
     119        void av_frame_free(AVFrame **frame)
     120        void av_frame_unref(AVFrame *frame)
    101121
    102122
    103123def get_version():
     
    156176    return _CODECS
    157177
    158178
    159 #maps AVCodecContext to the Decoder that manages it
    160 #the key must be obtained using get_context_key()
    161 #The Decoder manages everything.
    162 DECODERS = {}
    163179
    164180
    165 #these two functions convert pointers to longs
    166 #so we can use a context or frame as a dictionary key
    167 #NOTE: we can't simply use the "Frame" pointer as key
    168 #(the one we create and pass to avcodec_decode_video2)
    169 #because avcodec will pass a different "Frame" pointing
    170 #to the same memory. So we have to use frame.data[0] instead.
    171 cdef unsigned long get_context_key(AVCodecContext *avctx):
    172     cdef unsigned long ctx_key
    173     assert avctx!=NULL, "context is not set!"
    174     ctx_key = <unsigned long> avctx
    175     return ctx_key
     181IF not GET_BUFFER_V2:
     182    #maps AVCodecContext to the Decoder that manages it
     183    #the key must be obtained using get_context_key()
     184    #The Decoder manages everything.
     185    DECODERS = {}
    176186
    177 cdef unsigned long get_frame_key(AVFrame *frame):
    178     cdef unsigned long frame_key
    179     assert frame!=NULL, "frame is not set!"
    180     frame_key = <unsigned long> frame.data[0]
    181     return frame_key
     187    #these two functions convert pointers to longs
     188    #so we can use a context or frame as a dictionary key
     189    #NOTE: we can't simply use the "Frame" pointer as key
     190    #(the one we create and pass to avcodec_decode_video2)
     191    #because avcodec will pass a different "Frame" pointing
     192    #to the same memory. So we have to use frame.data[0] instead.
     193    cdef unsigned long get_context_key(AVCodecContext *avctx):
     194        cdef unsigned long ctx_key
     195        assert avctx!=NULL, "context is not set!"
     196        ctx_key = <unsigned long> avctx
     197        return ctx_key
    182198
    183 cdef void clear_frame(AVFrame *frame):
    184     assert frame!=NULL, "frame is not set!"
    185     for i in xrange(4):
    186         frame.data[i] = NULL
     199    cdef unsigned long get_frame_key(AVFrame *frame):
     200        cdef unsigned long frame_key
     201        assert frame!=NULL, "frame is not set!"
     202        frame_key = <unsigned long> frame.data[0]
     203        return frame_key
    187204
     205    cdef void clear_frame(AVFrame *frame):
     206        assert frame!=NULL, "frame is not set!"
     207        for i in xrange(4):
     208            frame.data[i] = NULL
    188209
    189 cdef get_decoder(AVCodecContext *avctx):
    190     cdef unsigned long ctx_key = get_context_key(avctx)
    191     global DECODERS
    192     decoder = DECODERS.get(ctx_key)
    193     assert decoder is not None, "decoder not found for context %s" % hex(ctx_key)
    194     return decoder
    195210
     211    cdef get_decoder(AVCodecContext *avctx):
     212        cdef unsigned long ctx_key = get_context_key(avctx)
     213        global DECODERS
     214        decoder = DECODERS.get(ctx_key)
     215        assert decoder is not None, "decoder not found for context %s" % hex(ctx_key)
     216        return decoder
    196217
    197218
    198 cdef int avcodec_get_buffer(AVCodecContext *avctx, AVFrame *frame) with gil:
    199     """ This function overrides AVCodecContext.get_buffer:
    200         we create an AVFrameWrapper object and
    201         register it with the Decoder for this context.
    202     """
    203     cdef unsigned long frame_key = 0
    204     cdef AVFrameWrapper frame_wrapper
    205     cdef int ret
    206     decoder = get_decoder(avctx)
    207     ret = avcodec_default_get_buffer(avctx, frame)
    208     if ret==0:
    209         frame_key = get_frame_key(frame)
    210         frame_wrapper = AVFrameWrapper()
    211         frame_wrapper.set_context(avctx, frame)
    212         decoder.add_framewrapper(frame_key, frame_wrapper)
    213     #debug("avcodec_get_buffer(%s, %s) ret=%s, decoder=%s, frame pointer=%s",
    214     #        hex(<unsigned long> avctx), hex(frame_key), ret, decoder, hex(frame_key))
    215     return ret
     219    cdef int avcodec_get_buffer(AVCodecContext *avctx, AVFrame *frame) with gil:
     220        """ This function overrides AVCodecContext.get_buffer:
     221            we create an AVFrameWrapper object and
     222            register it with the Decoder for this context.
     223        """
     224        cdef unsigned long frame_key = 0        #@DuplicateSignature
     225        cdef AVFrameWrapper frame_wrapper       #@DuplicateSignature
     226        cdef int ret                            #@DuplicateSignature
     227        decoder = get_decoder(avctx)
     228        ret = avcodec_default_get_buffer(avctx, frame)
     229        if ret==0:
     230            frame_key = get_frame_key(frame)
     231            frame_wrapper = AVFrameWrapper()
     232            frame_wrapper.set_context(avctx, frame)
     233            decoder.add_framewrapper(frame_key, frame_wrapper)
     234        #debug("avcodec_get_buffer(%s, %s) ret=%s, decoder=%s, frame pointer=%s",
     235        #        hex(<unsigned long> avctx), hex(frame_key), ret, decoder, hex(frame_key))
     236        return ret
    216237
    217 cdef void avcodec_release_buffer(AVCodecContext *avctx, AVFrame *frame) with gil:
    218     """ when avcodec releases the buffer,
    219         we tell the Decoder to manage it.
    220     """
    221     cdef unsigned long frame_key = get_frame_key(frame)
    222     decoder = get_decoder(avctx)
    223     decoder.av_free(frame_key)
    224238
     239    cdef void avcodec_release_buffer(AVCodecContext *avctx, AVFrame *frame) with gil:
     240        """ when avcodec releases the buffer,
     241            we tell the Decoder to manage it.
     242        """
     243        cdef unsigned long frame_key = get_frame_key(frame)
     244        decoder = get_decoder(avctx)
     245        decoder.av_free(frame_key)
    225246
    226 cdef class AVFrameWrapper:
    227     """
    228         Wraps an AVFrame so we can free it
    229         once both xpra and avcodec are done with it.
    230     """
    231     cdef AVCodecContext *avctx
    232     cdef AVFrame *frame
    233     cdef int av_freed
    234     cdef int xpra_freed
    235247
    236     cdef set_context(self, AVCodecContext *avctx, AVFrame *frame):
    237         self.avctx = avctx
    238         self.frame = frame
    239         self.av_freed = 0
    240         self.xpra_freed = 0
     248    cdef class AVFrameWrapper:
     249        """
     250            Wraps an AVFrame so we can free it
     251            once both xpra and avcodec are done with it.
     252        """
     253        cdef AVCodecContext *avctx
     254        cdef AVFrame *frame
     255        cdef int av_freed
     256        cdef int xpra_freed
    241257
    242     def __dealloc__(self):
    243         #debug("CSCImage.__dealloc__()")
    244         #By the time this wrapper is garbage collected,
    245         #we must have freed it!
    246         assert self.av_freed, "AVFrameWrapper falling out of scope before being freed by avcodec!"
    247         assert self.xpra_freed, "AVFrameWrapper falling out of scope before being freed by xpra!"
    248         assert self.frame==NULL and self.avctx==NULL, "frame was freed by both, but not actually freed!"
     258        cdef set_context(self, AVCodecContext *avctx, AVFrame *frame):
     259            self.avctx = avctx
     260            self.frame = frame
     261            self.av_freed = 0
     262            self.xpra_freed = 0
    249263
    250     def __str__(self):
    251         return "AVFrameWrapper(%s)" % hex(self.get_key())
     264        def __dealloc__(self):
     265            #debug("CSCImage.__dealloc__()")
     266            #By the time this wrapper is garbage collected,
     267            #we must have freed it!
     268            assert self.av_freed, "AVFrameWrapper falling out of scope before being freed by avcodec!"
     269            assert self.xpra_freed, "AVFrameWrapper falling out of scope before being freed by xpra!"
     270            assert self.frame==NULL and self.avctx==NULL, "frame was freed by both, but not actually freed!"
    252271
    253     def xpra_free(self):
    254         debug("%s.xpra_free()", self)
    255         self.xpra_freed = 1
    256         if self.av_freed==0:
    257             return False
    258         self.free()
    259         return True
     272        def __str__(self):
     273            return "AVFrameWrapper(%s)" % hex(self.get_key())
    260274
    261     def av_free(self):
    262         debug("%s.av_free()", self)
    263         self.av_freed = 1
    264         if self.xpra_freed==0:
    265             return False
    266         self.free()
    267         return True
     275        def xpra_free(self):
     276            debug("%s.xpra_free()", self)
     277            self.xpra_freed = 1
     278            if self.av_freed==0:
     279                return False
     280            self.free()
     281            return True
    268282
    269     def free(self):
    270         debug("%s.free()", self)
    271         if self.avctx!=NULL and self.frame!=NULL:
    272             avcodec_default_release_buffer(self.avctx, self.frame)
    273             #do we need this?
    274             #avcodec_free_frame(frame)
    275             self.avctx = NULL
    276             self.frame = NULL
     283        def av_free(self):
     284            debug("%s.av_free()", self)
     285            self.av_freed = 1
     286            if self.xpra_freed==0:
     287                return False
     288            self.free()
     289            return True
    277290
    278     def get_key(self):
    279         return get_frame_key(self.frame)
     291        def free(self):
     292            debug("%s.free()", self)
     293            if self.avctx!=NULL and self.frame!=NULL:
     294                avcodec_default_release_buffer(self.avctx, self.frame)
     295                #do we need this?
     296                #avcodec_free_frame(frame)
     297                self.avctx = NULL
     298                self.frame = NULL
    280299
     300        def get_key(self):
     301            return get_frame_key(self.frame)
    281302
    282 class AVImageWrapper(ImageWrapper):
    283     """
    284         Wrapper which allows us to call xpra_free on the decoder
    285         when the image is freed, or once we have made a copy of the pixels.
    286     """
    287303
    288     def __str__(self):                          #@DuplicatedSignature
    289         return ImageWrapper.__str__(self)+"-(%s)" % self.av_frame
     304    class AVImageWrapper(ImageWrapper):
     305        """
     306            Wrapper which allows us to call xpra_free on the decoder
     307            when the image is freed, or once we have made a copy of the pixels.
     308        """
    290309
    291     def free(self):                             #@DuplicatedSignature
    292         debug("AVImageWrapper.free() av_frame=%s", self.av_frame)
    293         ImageWrapper.free(self)
    294         self.xpra_free_frame()
     310        def __str__(self):                          #@DuplicatedSignature
     311            return ImageWrapper.__str__(self)+"-(%s)" % self.av_frame
    295312
    296     def clone_pixel_data(self):
    297         ImageWrapper.clone_pixel_data(self)
    298         self.xpra_free_frame()
     313        def free(self):                             #@DuplicatedSignature
     314            debug("AVImageWrapper.free() av_frame=%s", self.av_frame)
     315            ImageWrapper.free(self)
     316            self.xpra_free_frame()
    299317
    300     def xpra_free_frame(self):
    301         if self.av_frame:
    302             assert self.decoder, "no decoder set!"
    303             self.decoder.xpra_free(self.av_frame.get_key())
    304             self.av_frame = None
     318        def clone_pixel_data(self):
     319            ImageWrapper.clone_pixel_data(self)
     320            self.xpra_free_frame()
    305321
     322        def xpra_free_frame(self):
     323            if self.av_frame:
     324                assert self.decoder, "no decoder set!"
     325                self.decoder.xpra_free(self.av_frame.get_key())
     326                self.av_frame = None
    306327
     328ELSE:
     329    class AVImageWrapper(ImageWrapper):             #@DuplicatedSignature
     330        """
     331            Wrapper which allows us to call xpra_free on the decoder
     332            when the image is freed, or once we have made a copy of the pixels.
     333        """
    307334
     335        def __str__(self):                          #@DuplicatedSignature
     336            return ImageWrapper.__str__(self)+"-(%s)" % hex(self.av_frame)
     337
     338        def free(self):                             #@DuplicatedSignature
     339            import traceback
     340            traceback.print_stack()
     341            debug("AVImageWrapper.free() av_frame=%s", hex(self.av_frame))
     342            if self.av_frame:
     343                av_frame_unref(<AVFrame*> self.av_frame)
     344                self.av_frame = None
     345
     346        def clone_pixel_data(self):                 #@DuplicatedSignature
     347            ImageWrapper.clone_pixel_data(self)
     348            self.free()
     349
     350
    308351cdef class Decoder:
    309352    """
    310353        This wraps the AVCodecContext and its configuration,
     
    368411        self.codec_ctx.width = width
    369412        self.codec_ctx.height = height
    370413        self.codec_ctx.pix_fmt = self.pix_fmt
    371         self.codec_ctx.get_buffer = avcodec_get_buffer
    372         self.codec_ctx.release_buffer = avcodec_release_buffer
     414        IF GET_BUFFER_V2:
     415            self.codec_ctx.refcounted_frames = 1
     416        ELSE:
     417            self.frame = avcodec_alloc_frame()
     418            if self.frame==NULL:
     419                error("could not allocate an AVFrame for decoding")
     420                self.clean_decoder()
     421                return  False
     422            self.codec_ctx.get_buffer2 = avcodec_get_buffer2
     423            self.codec_ctx.release_buffer = avcodec_release_buffer
    373424        self.codec_ctx.thread_safe_callbacks = 1
    374425        self.codec_ctx.thread_type = 2      #FF_THREAD_SLICE: allow more than one thread per frame
    375426        self.codec_ctx.thread_count = 0     #auto
     
    378429            error("could not open codec")
    379430            self.clean_decoder()
    380431            return  False
    381         self.frame = avcodec_alloc_frame()
    382         if self.frame==NULL:
    383             error("could not allocate an AVFrame for decoding")
    384             self.clean_decoder()
    385             return  False
    386432        self.frames = 0
    387433        #to keep track of frame wrappers:
    388434        self.framewrappers = {}
     
    390436        #(we want a weakref.WeakSet() but this is python2.7+ only..)
    391437        self.weakref_images = []
    392438        #register this decoder in the global dictionary:
    393         global DECODERS
    394         cdef unsigned long ctx_key = get_context_key(self.codec_ctx)
    395         DECODERS[ctx_key] = self
     439        IF not GET_BUFFER_V2:
     440            global DECODERS
     441            cdef unsigned long ctx_key = get_context_key(self.codec_ctx)
     442            DECODERS[ctx_key] = self
    396443        debug("dec_avcodec.Decoder.init_context(%s, %s, %s) self=%s", width, height, colorspace, self.get_info())
    397444        return True
    398445
     
    414461
    415462        debug("clean_decoder() freeing AVFrame: %s", hex(<unsigned long> self.frame))
    416463        if self.frame!=NULL:
    417             avcodec_free_frame(&self.frame)
    418             #redundant: self.frame = NULL
     464            IF not GET_BUFFER_V2:
     465                #FIXME: unsafe??
     466                avcodec_free_frame(&self.frame)
     467                #redundant: self.frame = NULL
    419468
    420469        cdef unsigned long ctx_key          #@DuplicatedSignature
    421470        debug("clean_decoder() freeing AVCodecContext: %s", hex(<unsigned long> self.codec_ctx))
    422471        if self.codec_ctx!=NULL:
    423472            avcodec_close(self.codec_ctx)
    424473            av_free(self.codec_ctx)
    425             global DECODERS
    426             ctx_key = get_context_key(self.codec_ctx)
    427             if ctx_key in DECODERS:
    428                 DECODERS[ctx_key] = self
     474            IF not GET_BUFFER_V2:
     475                global DECODERS
     476                ctx_key = get_context_key(self.codec_ctx)
     477                if ctx_key in DECODERS:
     478                    DECODERS[ctx_key] = self
    429479            self.codec_ctx = NULL
    430480        debug("clean_decoder() done")
    431481
     
    470520    def get_type(self):                             #@DuplicatedSignature
    471521        return "avcodec"
    472522
     523    def get_colorspace(self):
     524        return self.colorspace
     525
     526    def get_actual_colorspace(self):
     527        return ENUM_TO_FORMAT.get(self.actual_pix_fmt, "unknown/invalid")
     528
     529
    473530    def decompress_image(self, input, options):
    474531        cdef unsigned char * padded_buf = NULL
    475532        cdef const unsigned char * buf = NULL
     
    487544        memcpy(padded_buf, buf, buf_len)
    488545        memset(padded_buf+buf_len, 0, 128)
    489546        #ensure we can detect if the frame buffer got allocated:
    490         clear_frame(self.frame)
     547        IF GET_BUFFER_V2:
     548            self.frame = NULL
     549            self.frame = av_frame_alloc()
     550            log.info("decompress_image(..) new frame=%s", hex(int(<long> self.frame)))
     551            if self.frame==NULL:
     552                error("could not allocate an AVFrame for decoding")
     553                self.clean_decoder()
     554                return  False
     555        ELSE:
     556            clear_frame(self.frame)
    491557        #now safe to run without gil:
    492558        with nogil:
    493559            av_init_packet(&avpkt)
     
    548614        assert self.codec_ctx.width>=self.width, "codec width is smaller than our width: %s<%s" % (self.codec_ctx.width, self.width)
    549615        assert self.codec_ctx.height>=self.height, "codec height is smaller than our height: %s<%s" % (self.codec_ctx.height, self.height)
    550616        img = AVImageWrapper(0, 0, self.width, self.height, out, cs, 24, strides, nplanes)
    551         img.decoder = self
    552         img.av_frame = None
    553         #we must find the frame wrapper used by our get_buffer override:
    554         frame_key = get_frame_key(self.frame)
    555         framewrapper = self.get_framewrapper(frame_key)
    556         img.av_frame = framewrapper
     617        IF GET_BUFFER_V2:
     618            #just set the frame pointer and the AVImageWrapper will free it:
     619            img.av_frame = int(<long> self.frame)
     620        ELSE:
     621            #we must find the frame wrapper used by our get_buffer override:
     622            img.decoder = self
     623            img.av_frame = None
     624            frame_key = get_frame_key(self.frame)
     625            framewrapper = self.get_framewrapper(frame_key)
     626            img.av_frame = framewrapper
    557627        self.frames += 1
    558628        #add to weakref list after cleaning it up:
    559629        self.weakref_images = [x for x in self.weakref_images if x() is not None]
     
    563633        return img
    564634
    565635
    566     def frame_error(self):
    567         cdef unsigned long frame_key = get_frame_key(self.frame)
    568         framewrapper = self.get_framewrapper(frame_key, True)
    569         log("frame_error() freeing %s", framewrapper)
    570         if framewrapper:
    571             #avcodec had allocated a buffer, make sure we don't claim to be using it:
    572             self.av_free(frame_key)
    573         return framewrapper
    574 
    575     def get_framewrapper(self, frame_key, ignore_missing=False):
    576         framewrapper = self.framewrappers.get(int(frame_key))
    577         #debug("get_framewrapper(%s)=%s, known frame keys=%s", frame_key, framewrapper,
    578         #                        [hex(x) for x in self.framewrappers.keys()])
    579         assert ignore_missing or framewrapper is not None, "frame not found for pointer %s, known frame keys=%s" % (hex(frame_key),
    580                                 [hex(x) for x in self.framewrappers.keys()])
    581         return framewrapper
    582 
    583     def add_framewrapper(self, frame_key, frame_wrapper):
    584         debug("add_framewrapper(%s, %s) known frame keys: %s", hex(frame_key), frame_wrapper,
    585                                 [hex(x) for x in self.framewrappers.keys()])
    586         self.framewrappers[int(frame_key)] = frame_wrapper
    587 
    588     def av_free(self, frame_key):                       #@DuplicatedSignature
    589         avframe = self.get_framewrapper(frame_key)
    590         debug("av_free(%s) framewrapper=%s", hex(frame_key), avframe)
    591         if avframe.av_free():
    592             #frame has been freed - remove it from dict:
    593             del self.framewrappers[frame_key]
    594 
    595     def xpra_free(self, frame_key):                     #@DuplicatedSignature
    596         avframe = self.get_framewrapper(frame_key)
    597         debug("xpra_free(%s) framewrapper=%s", hex(frame_key), avframe)
    598         if avframe.xpra_free():
    599             #frame has been freed - remove it from dict:
    600             del self.framewrappers[frame_key]
    601 
    602     def get_colorspace(self):
    603         return self.colorspace
    604 
    605     def get_actual_colorspace(self):
    606         return ENUM_TO_FORMAT.get(self.actual_pix_fmt, "unknown/invalid")
     636    IF GET_BUFFER_V2:
     637        def frame_error(self):
     638            av_frame_free(&self.frame)
     639    ELSE:
     640        def frame_error(self):                          #@DuplicatedSignature
     641            cdef unsigned long frame_key = get_frame_key(self.frame)
     642            framewrapper = self.get_framewrapper(frame_key, True)
     643            log("frame_error() freeing %s", framewrapper)
     644            if framewrapper:
     645                #avcodec had allocated a buffer, make sure we don't claim to be using it:
     646                self.av_free(frame_key)
     647            return framewrapper
     648   
     649        def get_framewrapper(self, frame_key, ignore_missing=False):
     650            framewrapper = self.framewrappers.get(int(frame_key))
     651            #debug("get_framewrapper(%s)=%s, known frame keys=%s", frame_key, framewrapper,
     652            #                        [hex(x) for x in self.framewrappers.keys()])
     653            assert ignore_missing or framewrapper is not None, "frame not found for pointer %s, known frame keys=%s" % (hex(frame_key),
     654                                    [hex(x) for x in self.framewrappers.keys()])
     655            return framewrapper
     656   
     657        def add_framewrapper(self, frame_key, frame_wrapper):
     658            debug("add_framewrapper(%s, %s) known frame keys: %s", hex(frame_key), frame_wrapper,
     659                                    [hex(x) for x in self.framewrappers.keys()])
     660            self.framewrappers[int(frame_key)] = frame_wrapper
     661   
     662        def av_free(self, frame_key):                       #@DuplicatedSignature
     663            avframe = self.get_framewrapper(frame_key)
     664            debug("av_free(%s) framewrapper=%s", hex(frame_key), avframe)
     665            if avframe.av_free():
     666                #frame has been freed - remove it from dict:
     667                del self.framewrappers[frame_key]
     668   
     669        def xpra_free(self, frame_key):                     #@DuplicatedSignature
     670            avframe = self.get_framewrapper(frame_key)
     671            debug("xpra_free(%s) framewrapper=%s", hex(frame_key), avframe)
     672            if avframe.xpra_free():
     673                #frame has been freed - remove it from dict:
     674                del self.framewrappers[frame_key]