xpra icon
Bug tracker and wiki

Ticket #1341: html5-mpeg4video-v3.patch

File html5-mpeg4video-v3.patch, 44.7 KB (added by Antoine Martin, 22 months ago)

this works! (on Firefox, with one frame of latency, no cleanup code, etc..)

  • html5/js/Window.js

     
    364364                //copy previous pixels to new image, ignoring bit gravity
    365365        //      this.offscreen_canvas.getContext('2d').putImageData(previous_image, 0, 0);
    366366        //}
    367 
    368367        // this should copy canvas pixel data since a canvas is the backing!
    369368};
    370369
     
    744743        };
    745744};
    746745
     746XpraWindow.prototype._init_video = function(width, height, profile, level) {
     747        var me = this;
     748        var ms = window.MediaSource || window.WebKitMediaSource;
     749        this.media_source = new ms();
     750        this.media_source.addEventListener('sourceopen',        function(e) { console.log('source open: ' + me.media_source.readyState); });
     751        this.media_source.addEventListener('sourceended',       function(e) { console.log('source ended: ' + me.media_source.readyState); });
     752        this.media_source.addEventListener('sourceclose',       function(e) { console.log('source close: ' + me.media_source.readyState); });
     753        this.media_source.addEventListener('error',             function(e) { console.log('source error: ' + me.media_source.readyState); });
     754       
     755        this.video = document.createElement("video");
     756        this.video.setAttribute('autoplay', true);
     757        this.video.setAttribute('muted', "muted");
     758        this.video.setAttribute('width', width);
     759        this.video.setAttribute('height', height);
     760        this.video.setAttribute('display', "none");
     761        //for debugging:
     762        //this.video.setAttribute('controls', "controls");
     763        //this.video.setAttribute('position', "absolute");
     764        //this.video.setAttribute('z-index', 100000);
     765        //this.video.setAttribute('margin', "auto");
     766        //this.video.setAttribute('top', "50%");
     767    this.video.addEventListener('waiting', function(){
     768        console.log("video waiting");
     769    },false);
     770    this.video.addEventListener('stalled', function(){
     771        console.log("video stalled");
     772    },false);
     773    this.video.addEventListener('playing', function(){
     774        console.log("video playing");
     775        me.offscreen_canvas_ctx.drawImage(me.video, 0, 0, width, height);
     776    },false);
     777    this.video.addEventListener('loadstart', function(){
     778        console.log("video loadstart");
     779    },false);
     780    this.video.addEventListener('loadedmetadata', function(){
     781        console.log("video loadedmetadata");
     782    },false);
     783    this.video.addEventListener('loadeddata', function(){
     784        console.log("video loadeddata");
     785    },false);
     786    this.video.addEventListener('error', function(){
     787        console.log("video error");
     788    },false);
     789    this.video.addEventListener('canplay', function(){
     790        console.log("video canplay");
     791    },false);
     792    this.video.addEventListener('play', function(){
     793        console.log("video play! ************************************");
     794        /*cw = v.clientWidth;
     795        ch = v.clientHeight;
     796        canvas.width = cw;
     797        canvas.height = ch;
     798        back.width = cw;
     799        back.height = ch;
     800        draw(v,context,backcontext,cw,ch);*/
     801    },false);
     802    this.video.src = window.URL.createObjectURL(this.media_source);
     803        //this.video.src = "https://html5-demos.appspot.com/static/test.webm"
     804        this.video_buffers = []
     805        this.video_source_ready = false;
     806        this.media_source.addEventListener('sourceopen', function() {
     807                console.log("media source open");
     808                //var vsb = me.media_source.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
     809                var profile_value  = {
     810                                "baseline"      : "42E0",
     811                                "main"          : "4D40",
     812                                "high"          : "6400",
     813                                "extended"      : "58A0",
     814                }
     815                var level_value = {
     816                                "3.0"           : "1E",
     817                                "3.1"           : "1F",
     818                                "4.1"           : "29",
     819                                "5.1"           : "33",
     820                }
     821                var codecs_string = "avc1." + profile_value[profile] + level_value[level];
     822                console.log("using mp4 video with codecs string: "+codecs_string);
     823                var vsb = me.media_source.addSourceBuffer('video/mp4; codecs="'+codecs_string+'"');
     824            me.video_source_buffer = vsb;
     825            vsb.addEventListener('updatestart', function(e) { console.log('buffer updatestart: ' + me.media_source.readyState); });
     826            //vsb.addEventListener('update',            function(e) { console.log('buffer update: ' + me.media_source.readyState); });
     827            vsb.addEventListener('updateend',   function(e) { console.log('buffer updateend: ' + me.media_source.readyState);
     828                //me.video.play();
     829            });
     830            vsb.addEventListener('error',               function(e) { console.log('buffer error: ' + me.media_source.readyState); });
     831            vsb.addEventListener('abort',               function(e) { console.log('buffer abort: ' + me.media_source.readyState); });
    747832
     833            vsb.addEventListener('updateend', function() {
     834                        console.log("updateend: queue length="+me.video_buffers.length+", updating="+vsb.updating);
     835                if(me.video_buffers.length > 0 && !vsb.updating) {
     836                        vsb.appendBuffer(me.video_buffers.shift());
     837                }
     838            });
     839            me.video_source_ready = true;
     840            //add any buffers that may have accumulated since we initialized the video element:
     841                while(me.video_buffers.length>0 && !vsb.updating) {
     842                        console.log("initial: pushing one buffer from queue of size "+me.video_buffers.length);
     843                        me.video_source_buffer.appendBuffer(me.video_buffers.shift());
     844                }
     845        });
     846        //var div = document.getElementById("screen");
     847        //div.appendChild(this.video);
     848        document.body.appendChild(this.video);
     849};
     850
     851
    748852/**
    749853 * Updates the window image with new pixel data
    750854 * we have received from the server.
     
    833937                decode_callback(this.client);
    834938                //this._h264_process_raw(img_data);
    835939        }
     940        else if (coding=="h264+mp4") {
     941                if(!this.video) {
     942                        var profile = options["profile"] || "baseline";
     943                        var level  = options["level"] || "3.0";
     944                        this._init_video(width, height, profile, level);
     945                }
     946                if(img_data.length>0) {
     947                        var ready_state = {
     948                                0       : "NOTHING",
     949                                1       : "METADATA",
     950                                2       : "CURRENT DATA",
     951                                3       : "FUTURE DATA",
     952                                4       : "ENOUGH DATA",
     953                        }
     954                        var network_state = {
     955                                0       : "EMPTY",
     956                                1       : "IDLE",
     957                                2       : "LOADING",
     958                                3       : "NO_SOURCE",
     959                        };
     960                        console.log("video state="+ready_state[this.video.readyState]+", network state="+network_state[this.video.networkState]);
     961                        console.log("video paused="+this.video.paused);
     962                        var buf = new Uint8Array(img_data).buffer;
     963                        if(this.video_source_ready && !this.video_source_buffer.updating) {
     964                                this.video_source_buffer.appendBuffer(buf);
     965                        }
     966                        else {
     967                                this.video_buffers.push(buf);
     968                        }
     969                        if(this.video.paused) {
     970                                this.video.play();
     971                        }
     972                }
     973                decode_callback(this.client);
     974        }
    836975        else if (coding=="scroll") {
    837976                if(this.offscreen_canvas_mode!='2d') {
    838977                        this._init_2d_canvas();
  • xpra/codecs/enc_ffmpeg/encoder.pyx

     
    44# later version. See the file COPYING for details.
    55
    66import os
     7import time
     8import errno
    79import weakref
    810from xpra.log import Logger
    911log = Logger("encoder", "ffmpeg")
     
    1214from xpra.codecs.codec_constants import get_subsampling_divs, video_spec
    1315from xpra.codecs.libav_common.av_log cimport override_logger, restore_logger #@UnresolvedImport
    1416from xpra.codecs.libav_common.av_log import suspend_nonfatal_logging, resume_nonfatal_logging
    15 from xpra.util import AtomicInteger, csv, print_nested_dict, envint
     17from xpra.util import AtomicInteger, csv, print_nested_dict, envint, envbool
    1618from xpra.os_util import bytestostr
    1719
    1820SAVE_TO_FILE = os.environ.get("XPRA_SAVE_TO_FILE")
     
    1921
    2022THREAD_TYPE = envint("XPRA_FFMPEG_THREAD_TYPE", 2)
    2123THREAD_COUNT= envint("XPRA_FFMPEG_THREAD_COUNT")
     24AUDIO = envbool("XPRA_FFMPEG_MPEG4_AUDIO", False)
    2225
    2326
    24 from libc.stdint cimport uint8_t, int64_t
     27from libc.stdint cimport uint8_t, int64_t, uint32_t
    2528
    2629cdef extern from "../../inline.h":
    2730    pass
     
    5255#why can't we define this inside the avcodec.h section? (beats me)
    5356ctypedef unsigned int AVCodecID
    5457ctypedef long AVPixelFormat
     58ctypedef long AVSampleFormat
    5559ctypedef int AVPictureType
    5660
    5761
     
    6569    int AV_PICTURE_TYPE_SP
    6670    int AV_PICTURE_TYPE_BI
    6771
     72cdef extern from "libavutil/dict.h":
     73    int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
     74    void av_dict_free(AVDictionary **m)
     75
    6876cdef extern from "libavutil/pixfmt.h":
    6977    AVPixelFormat AV_PIX_FMT_NONE
    7078    AVPixelFormat AV_PIX_FMT_YUV420P
     
    7785    AVPixelFormat AV_PIX_FMT_BGRA
    7886    AVPixelFormat AV_PIX_FMT_GBRP
    7987
     88cdef extern from "libavutil/samplefmt.h":
     89    AVSampleFormat AV_SAMPLE_FMT_S16
     90    AVSampleFormat AV_SAMPLE_FMT_FLTP
    8091
     92
     93cdef extern from "libavformat/avio.h":
     94    ctypedef int AVIODataMarkerType
     95    int AVIO_FLAG_WRITE
     96
     97    ctypedef struct AVIOContext:
     98        const AVClass *av_class
     99        unsigned char *buffer       #Start of the buffer
     100        int buffer_size             #Maximum buffer size
     101        unsigned char *buf_ptr      #Current position in the buffer
     102        unsigned char *buf_end      #End of the data, may be less than
     103                                    #buffer+buffer_size if the read function returned
     104                                    #less data than requested, e.g. for streams where
     105                                    #no more data has been received yet.
     106        int64_t     pos             #position in the file of the current buffer
     107        int         must_flush      #true if the next seek should flush
     108        int         error           #contains the error code or 0 if no error happened
     109        int         seekable
     110        int64_t     maxsize
     111        int         direct
     112        int64_t     bytes_read
     113
     114    AVIOContext *avio_alloc_context(unsigned char *buffer, int buffer_size, int write_flag,
     115                  void *opaque,
     116                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
     117                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
     118                  int64_t (*seek)(void *opaque, int64_t offset, int whence))
     119
     120
    81121cdef extern from "libavcodec/avcodec.h":
    82122    int FF_THREAD_SLICE     #allow more than one thread per frame
    83123    int FF_THREAD_FRAME     #Decode more than one frame at once
     
    174214        AVRational time_base
    175215        unsigned int codec_tag
    176216        int64_t bit_rate
     217        AVSampleFormat sample_fmt
     218        int sample_rate
     219        int channels
    177220
     221    ctypedef struct AVFormatContext:
     222        const AVClass   *av_class
     223        AVOutputFormat  *oformat
     224        void            *priv_data
     225        AVIOContext     *pb
     226        int             ctx_flags
     227        unsigned int    nb_streams
     228        AVStream        **streams
     229        int64_t         start_time
     230        int64_t         duration
     231        int             bit_rate
     232        unsigned int    packet_size
     233        int             max_delay
     234        int             flags
     235        unsigned int    probesize
     236        int             max_analyze_duration
     237        AVCodecID       video_codec_id
     238        AVCodecID       audio_codec_id
     239        AVCodecID       subtitle_codec_id
     240        unsigned int    max_index_size
     241        unsigned int    max_picture_buffer
     242        unsigned int    nb_chapters
     243        AVDictionary    *metadata
     244        int64_t         start_time_realtime
     245        int             strict_std_compliance
     246
     247    ctypedef int AVFieldOrder
     248    ctypedef int AVColorRange
     249    ctypedef int AVColorPrimaries
     250    ctypedef int AVColorTransferCharacteristic
     251    ctypedef int AVColorSpace
     252    ctypedef int AVChromaLocation
     253    ctypedef struct AVCodecParameters:
     254        AVCodecID       codec_id
     255        uint32_t        codec_tag
     256        int64_t         bit_rate
     257        int             bits_per_coded_sample
     258        int             bits_per_raw_sample
     259        int             profile
     260        int             level
     261        int             width
     262        int             height
     263        AVFieldOrder    field_order
     264        AVColorRange    color_range
     265        AVColorPrimaries    color_primaries
     266        AVColorTransferCharacteristic color_trc
     267        AVColorSpace        color_space
     268        AVChromaLocation    chroma_location
     269        int             sample_rate
     270        int             frame_size
     271
    178272    AVCodecID AV_CODEC_ID_H264
    179273    AVCodecID AV_CODEC_ID_H265
    180274    AVCodecID AV_CODEC_ID_VP8
     
    181275    AVCodecID AV_CODEC_ID_VP9
    182276    AVCodecID AV_CODEC_ID_MPEG4
    183277
     278    AVCodecID AV_CODEC_ID_AAC
     279
    184280    #init and free:
    185281    void avcodec_register_all()
    186282    AVCodec *avcodec_find_encoder(AVCodecID id)
     
    189285    int avcodec_send_frame(AVCodecContext *avctx,const AVFrame *frame) nogil
    190286    int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) nogil
    191287
     288    int av_write_frame(AVFormatContext *s, AVPacket *pkt)
    192289    AVFrame* av_frame_alloc()
    193290    void av_frame_free(AVFrame **frame)
    194291    int avcodec_close(AVCodecContext *avctx)
     292    void av_frame_unref(AVFrame *frame) nogil
    195293    void av_init_packet(AVPacket *pkt) nogil
    196294    void av_packet_unref(AVPacket *pkt) nogil
    197295
     
    247345        #AVClassCategory (*get_category)(void *ctx)
    248346
    249347
     348cdef extern from "libavformat/avformat.h":
     349    int AVFMTCTX_NOHEADER           #signal that no header is present
     350
     351    int AVFMT_FLAG_GENPTS           #Generate missing pts even if it requires parsing future frames
     352    int AVFMT_FLAG_IGNIDX           #Ignore index
     353    int AVFMT_FLAG_NONBLOCK         #Do not block when reading packets from input
     354    int AVFMT_FLAG_IGNDTS           #Ignore DTS on frames that contain both DTS & PTS
     355    int AVFMT_FLAG_NOFILLIN         #Do not infer any values from other values, just return what is stored in the container
     356    int AVFMT_FLAG_NOPARSE          #Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled
     357    int AVFMT_FLAG_NOBUFFER         #Do not buffer frames when possible
     358    int AVFMT_FLAG_CUSTOM_IO        #The caller has supplied a custom AVIOContext, don't avio_close() it
     359    int AVFMT_FLAG_DISCARD_CORRUPT  #Discard frames marked corrupted
     360    int AVFMT_FLAG_FLUSH_PACKETS    #Flush the AVIOContext every packet
     361    int AVFMT_FLAG_BITEXACT
     362    int AVFMT_FLAG_MP4A_LATM        #Enable RTP MP4A-LATM payload
     363    int AVFMT_FLAG_SORT_DTS         #try to interleave outputted packets by dts (using this flag can slow demuxing down)
     364    int AVFMT_FLAG_PRIV_OPT         #Enable use of private options by delaying codec open (this could be made default once all code is converted)
     365    int AVFMT_FLAG_KEEP_SIDE_DATA   #Don't merge side data but keep it separate.
     366    int AVFMT_FLAG_FAST_SEEK        #Enable fast, but inaccurate seeks for some formats
     367
     368    int AVFMT_NOFILE                #Demuxer will use avio_open, no opened file should be provided by the caller
     369    int AVFMT_NEEDNUMBER            #Needs '%d' in filename
     370    int AVFMT_SHOW_IDS              #Show format stream IDs numbers
     371    int AVFMT_RAWPICTURE            #Format wants AVPicture structure for raw picture data. @deprecated Not used anymore
     372    int AVFMT_GLOBALHEADER          #Format wants global header
     373    int AVFMT_NOTIMESTAMPS          #Format does not need / have any timestamps
     374    int AVFMT_GENERIC_INDEX         #Use generic index building code
     375    int AVFMT_TS_DISCONT            #Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps
     376    int AVFMT_VARIABLE_FPS          #Format allows variable fps
     377    int AVFMT_NODIMENSIONS          #Format does not need width/height
     378    int AVFMT_NOSTREAMS             #Format does not require any streams
     379    int AVFMT_NOBINSEARCH           #Format does not allow to fall back on binary search via read_timestamp
     380    int AVFMT_NOGENSEARCH           #Format does not allow to fall back on generic search
     381    int AVFMT_NO_BYTE_SEEK          #Format does not allow seeking by bytes
     382    int AVFMT_ALLOW_FLUSH           #Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function
     383    int AVFMT_TS_NONSTRICT          #Format does not require strictly increasing timestamps, but they must still be monotonic
     384    int AVFMT_TS_NEGATIVE           #Format allows muxing negative timestamps.
     385    int AVFMT_SEEK_TO_PTS           #Seeking is based on PTS
     386
     387    ctypedef struct AVStream:
     388        int         index           #stream index in AVFormatContext
     389        int         id
     390        AVCodecContext *codec
     391        AVRational  time_base
     392        int64_t     start_time
     393        int64_t     duration
     394        int64_t     nb_frames       #number of frames in this stream if known or 0
     395        #AVDiscard   discard         #Selects which packets can be discarded at will and do not need to be demuxed.
     396        AVRational  avg_frame_rate
     397        AVCodecParameters *codecpar
     398
     399    ctypedef struct AVOutputFormat:
     400        const char  *name
     401        const char  *long_name
     402        const char  *mime_type
     403        const char  *extensions
     404        AVCodecID   audio_codec
     405        AVCodecID   video_codec
     406        AVCodecID   subtitle_codec
     407        int         flags       #AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS, AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH, AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE More...
     408        int         (*query_codec)(AVCodecID id, int std_compliance)
     409
     410    void av_register_all()
     411    AVOutputFormat *av_oformat_next(const AVOutputFormat *f)
     412    int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename)
     413    void avformat_free_context(AVFormatContext *s)
     414
     415    int avcodec_parameters_from_context(AVCodecParameters *par, const AVCodecContext *codec)
     416    AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
     417    int avformat_write_header(AVFormatContext *s, AVDictionary **options)
     418    int av_write_trailer(AVFormatContext *s)
     419    int av_write_frame(AVFormatContext *s, AVPacket *pkt)
     420
     421
    250422AV_OPT_TYPES = {
    251423                AV_OPT_TYPE_FLAGS       : "FLAGS",
    252424                AV_OPT_TYPE_INT         : "INT",
     
    274446             AV_PKT_FLAG_CORRUPT    : "CORRUPT",
    275447             }
    276448
     449AVFMTCTX = {
     450            AVFMTCTX_NOHEADER       : "NOHEADER",
     451            }
     452
    277453CODEC_FLAGS = {
    278454               CODEC_FLAG_UNALIGNED         : "UNALIGNED",
    279455               CODEC_FLAG_QSCALE            : "QSCALE",
     
    298474                CODEC_FLAG2_FAST : "FAST",
    299475                }
    300476
     477FMT_FLAGS = {
     478             AVFMT_FLAG_GENPTS          : "GENPTS",
     479             AVFMT_FLAG_IGNIDX          : "IGNIDX",
     480             AVFMT_FLAG_NONBLOCK        : "NONBLOCK",
     481             AVFMT_FLAG_IGNDTS          : "IGNDTS",
     482             AVFMT_FLAG_NOFILLIN        : "NOFILLIN",
     483             AVFMT_FLAG_NOPARSE         : "NOPARSE",
     484             AVFMT_FLAG_NOBUFFER        : "NOBUFFER",
     485             AVFMT_FLAG_CUSTOM_IO       : "CUSTOM_IO",
     486             AVFMT_FLAG_DISCARD_CORRUPT : "DISCARD_CORRUPT",
     487             AVFMT_FLAG_FLUSH_PACKETS   : "FLUSH_PACKETS",
     488             AVFMT_FLAG_BITEXACT        : "BITEXACT",
     489             AVFMT_FLAG_MP4A_LATM       : "MP4A_LATM",
     490             AVFMT_FLAG_SORT_DTS        : "SORT_DTS",
     491             AVFMT_FLAG_PRIV_OPT        : "PRIV_OPT",
     492             AVFMT_FLAG_KEEP_SIDE_DATA  : "KEEP_SIDE_DATA",
     493             AVFMT_FLAG_FAST_SEEK       : "FAST_SEEK",
     494             }
     495
     496AVFMT = {
     497         AVFMT_NOFILE           : "NOFILE",
     498         AVFMT_NEEDNUMBER       : "NEEDNUMBER",
     499         AVFMT_SHOW_IDS         : "SHOW_IDS",
     500         AVFMT_RAWPICTURE       : "RAWPICTURE",
     501         AVFMT_GLOBALHEADER     : "GLOBALHEADER",
     502         AVFMT_NOTIMESTAMPS     : "NOTIMESTAMPS",
     503         AVFMT_GENERIC_INDEX    : "GENERIC_INDEX",
     504         AVFMT_TS_DISCONT       : "TS_DISCONT",
     505         AVFMT_VARIABLE_FPS     : "VARIABLE_FPS",
     506         AVFMT_NODIMENSIONS     : "NODIMENSIONS",
     507         AVFMT_NOSTREAMS        : "NOSTREAMS",
     508         AVFMT_NOBINSEARCH      : "NOBINSEARCH",
     509         AVFMT_NOGENSEARCH      : "NOGENSEARCH",
     510         AVFMT_NO_BYTE_SEEK     : "NO_BYTE_SEEK",
     511         AVFMT_ALLOW_FLUSH      : "ALLOW_FLUSH",
     512         AVFMT_TS_NONSTRICT     : "TS_NONSTRICT",
     513         AVFMT_TS_NEGATIVE      : "TS_NEGATIVE",
     514         AVFMT_SEEK_TO_PTS      : "SEEK_TO_PTS",
     515         }
     516
     517
    301518CAPS = {
    302519        CODEC_CAP_DRAW_HORIZ_BAND       : "DRAW_HORIZ_BAND",
    303520        CODEC_CAP_DR1                   : "DR1",
     
    356573def flagscsv(flag_dict, value=0):
    357574    return csv([v for k,v in flag_dict.items() if k&value])
    358575
     576
     577def get_muxer_formats():
     578    av_register_all()
     579    cdef AVOutputFormat *fmt = NULL
     580    formats = {}
     581    while True:
     582        fmt = av_oformat_next(fmt)
     583        if fmt==NULL:
     584            break
     585        name = fmt.name
     586        long_name = fmt.long_name
     587        formats[name] = long_name
     588    return formats
     589log("AV Output Formats:")
     590print_nested_dict(get_muxer_formats(), print_fn=log.debug)
     591
     592cdef AVOutputFormat* get_av_output_format(name):
     593    cdef AVOutputFormat *fmt = NULL
     594    while True:
     595        fmt = av_oformat_next(fmt)
     596        if fmt==NULL:
     597            break
     598        if name==fmt.name:
     599            return fmt
     600    return NULL
     601
     602
    359603def get_version():
    360604    return (LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO)
    361605
     
    362606avcodec_register_all()
    363607CODECS = []
    364608if avcodec_find_encoder(AV_CODEC_ID_H264)!=NULL:
    365     CODECS.append("h264")
     609    CODECS.append("h264+mp4")
    366610#if avcodec_find_encoder(AV_CODEC_ID_VP8)!=NULL:
    367611#    CODECS.append("vp8")
    368612#if avcodec_find_encoder(AV_CODEC_ID_VP9)!=NULL:
     
    370614#if avcodec_find_encoder(AV_CODEC_ID_H265)!=NULL:
    371615#    CODECS.append("h265")
    372616if avcodec_find_encoder(AV_CODEC_ID_MPEG4)!=NULL:
    373     CODECS.append("mpeg4")
     617    CODECS.append("mpeg4+mp4")
    374618log("enc_ffmpeg CODECS=%s", csv(CODECS))
    375619
    376620cdef av_error_str(int errnum):
     
    382626        return bytestostr(err_str[:i])
    383627    return "error %s" % errnum
    384628
    385 DEF EAGAIN = -11
     629DEF DEFAULT_BUF_LEN = 64*1024
    386630
    387631
    388632def init_module():
     
    405649    return  {
    406650             "version"      : get_version(),
    407651             "encodings"    : get_encodings(),
     652             "muxers"       : get_muxer_formats(),
    408653             "buffer_api"   : get_buffer_api_version(),
    409654             "formats"      : f,
    410655             "generation"   : generation.get(),
     
    423668    return ["YUV420P"]
    424669
    425670
     671GEN_TO_ENCODER = weakref.WeakValueDictionary()
     672
     673
     674cdef list_options(void *obj, const AVClass *av_class):
     675    if av_class==NULL:
     676        return
     677    cdef const AVOption *option = <const AVOption*> av_class.option
     678    options = []
     679    while option!=NULL:
     680        oname = option.name
     681        options.append(oname)
     682        option = av_opt_next(obj, option)
     683    log("%s options: %s", av_class.class_name, csv(options))
     684    cdef void *child = NULL
     685    cdef const AVClass *child_class = NULL
     686    while True:
     687        child = av_opt_child_next(obj, child)
     688        if child==NULL:
     689            return
     690        child_class = (<AVClass**> child)[0]
     691        list_options(child, child_class)
     692
     693
     694cdef int write_packet(void *opaque, uint8_t *buf, int buf_size):
     695    global GEN_TO_ENCODER
     696    encoder = GEN_TO_ENCODER.get(<unsigned long> opaque)
     697    #log.warn("write_packet(%#x, %#x, %#x) encoder=%s", <unsigned long> opaque, <unsigned long> buf, buf_size, type(encoder))
     698    if not encoder:
     699        log.error("Error: write_packet called for unregistered encoder %i!", <unsigned long> opaque)
     700        return -1
     701    return encoder.write_packet(<unsigned long> buf, buf_size)
     702
    426703MAX_WIDTH, MAX_HEIGHT = 4096, 4096
    427704def get_spec(encoding, colorspace):
    428705    assert encoding in get_encodings(), "invalid encoding: %s (must be one of %s" % (encoding, get_encodings())
     
    437714    """
    438715        This wraps the AVCodecContext and its configuration,
    439716    """
    440     cdef AVCodecID codec_id
    441     cdef AVCodec *codec
    442     cdef AVCodecContext *codec_ctx
     717    #muxer:
     718    cdef AVFormatContext *muxer_ctx
     719    cdef unsigned char *buffer
     720    cdef object buffers
     721    cdef int64_t offset
     722    cdef object muxer_format
     723    cdef object file
     724    #video:
     725    cdef AVCodec *video_codec
     726    cdef AVStream *video_stream
     727    cdef AVCodecContext *video_ctx
    443728    cdef AVPixelFormat pix_fmt
    444729    cdef object src_format
    445730    cdef AVFrame *av_frame
    446     #this is the actual number of images we have returned
    447731    cdef unsigned long frames
    448732    cdef unsigned int width
    449733    cdef unsigned int height
    450734    cdef object encoding
    451     cdef object file
     735    #audio:
     736    cdef AVCodec *audio_codec
     737    cdef AVStream *audio_stream
     738    cdef AVCodecContext *audio_ctx
    452739
    453740    cdef object __weakref__
    454741
     
    458745        assert encoding in CODECS
    459746        assert src_format in get_input_colorspaces(encoding), "invalid colorspace: %s" % src_format
    460747        self.encoding = encoding
     748        self.muxer_format = encoding.split("+")[1]  #ie: "mp4"   #"mov", "f4v"
     749        assert self.muxer_format=="mp4"
    461750        self.width = width
    462751        self.height = height
    463752        self.src_format = src_format
     
    464753        self.pix_fmt = FORMAT_TO_ENUM.get(src_format, AV_PIX_FMT_NONE)
    465754        if self.pix_fmt==AV_PIX_FMT_NONE:
    466755            raise Exception("invalid pixel format: %s", src_format)
     756        self.buffers = []
    467757
     758        codec = self.encoding.split("+")[0]
    468759        avcodec_register_all()
    469         if self.encoding=="h264":
    470             self.codec_id = AV_CODEC_ID_H264
    471         elif self.encoding=="h265":
    472             self.codec_id = AV_CODEC_ID_H265
    473         elif self.encoding=="vp8":
    474             self.codec_id = AV_CODEC_ID_VP8
    475         elif self.encoding=="vp9":
    476             self.codec_id = AV_CODEC_ID_VP9
    477         elif self.encoding=="mpeg4":
    478             self.codec_id = AV_CODEC_ID_MPEG4
     760        cdef AVCodecID video_codec_id
     761        if codec=="h264":
     762            video_codec_id = AV_CODEC_ID_H264
     763        elif codec=="h265":
     764            video_codec_id = AV_CODEC_ID_H265
     765        elif codec=="vp8":
     766            video_codec_id = AV_CODEC_ID_VP8
     767        elif codec=="vp9":
     768            video_codec_id = AV_CODEC_ID_VP9
     769        elif codec=="mpeg4":
     770            video_codec_id = AV_CODEC_ID_MPEG4
    479771        else:
    480772            raise Exception("invalid codec; %s" % self.encoding)
    481         self.codec = avcodec_find_encoder(self.codec_id)
    482         if self.codec==NULL:
     773        self.video_codec = avcodec_find_encoder(video_codec_id)
     774        if self.video_codec==NULL:
    483775            raise Exception("codec %s not found!" % self.encoding)
    484         log("%s: \"%s\", codec flags: %s", self.codec.name, self.codec.long_name, flagscsv(CAPS, self.codec.capabilities))
     776        log("%s: \"%s\", codec flags: %s", self.video_codec.name, self.video_codec.long_name, flagscsv(CAPS, self.video_codec.capabilities))
    485777
     778        #TODO: the muxer should be configurable
    486779        cdef int b_frames = 0   #int(options.get("b-frames"))
    487780        try:
    488781            self.init_encoder(b_frames)
     
    494787            log("enc_ffmpeg.Encoder.init_context(%s, %s, %s) self=%s", self.width, self.height, self.src_format, self.get_info())
    495788
    496789    def init_encoder(self, int b_frames):
     790        global GEN_TO_ENCODER
     791        cdef AVOutputFormat *oformat = get_av_output_format(self.muxer_format)
     792        if oformat==NULL:
     793            raise Exception("libavformat does not support %s" % self.muxer_format)
     794        log("init_encoder() AVOutputFormat(%s)=%#x, flags=%s", self.muxer_format, <unsigned long> oformat, flagscsv(AVFMT, oformat.flags))
     795        if oformat.flags & AVFMT_ALLOW_FLUSH==0:
     796            raise Exception("AVOutputFormat(%s) does not support flushing!" % self.muxer_format)
     797        r = avformat_alloc_output_context2(&self.muxer_ctx, oformat, self.muxer_format, NULL)
     798        if r!=0:
     799            msg = av_error_str(r)
     800            raise Exception("libavformat cannot allocate context: %s" % msg)
     801        log("init_encoder() avformat_alloc_output_context2 returned %i, format context=%#x, flags=%s, ctx_flags=%s", r, <unsigned long> self.muxer_ctx,
     802            flagscsv(FMT_FLAGS, self.muxer_ctx.flags), flagscsv(AVFMTCTX, self.muxer_ctx.ctx_flags))
     803        list_options(self.muxer_ctx, self.muxer_ctx.av_class)
     804
     805        cdef int64_t v = 0
     806        r = av_opt_get_int(self.muxer_ctx, "movflags", AV_OPT_SEARCH_CHILDREN, &v)
     807        if r==0:
     808            FF_MOV_FLAG_FRAGMENT = 1<<1
     809            FF_MOV_FLAG_EMPTY_MOOV = 1<<2
     810            FF_MOV_FLAG_FRAG_KEYFRAME = 1<<3
     811            FF_MOV_FLAG_OMIT_TFHD_OFFSET = 1<<8
     812            FF_MOV_FLAG_DEFAULT_BASE_MOOF = 1<<10
     813            v |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_FRAG_KEYFRAME | FF_MOV_FLAG_OMIT_TFHD_OFFSET | FF_MOV_FLAG_DEFAULT_BASE_MOOF
     814            log("movflags=%#x", v)
     815            r = av_opt_set_int(self.muxer_ctx, "movflags", v, AV_OPT_SEARCH_CHILDREN)
     816            if r!=0:
     817                log.error("Error: failed to set movflags")
     818                log.error(" %s", av_error_str(r))
     819
    497820        cdef unsigned long gen = generation.increase()
     821        GEN_TO_ENCODER[gen] = self
     822        self.buffer = <unsigned char*> av_malloc(DEFAULT_BUF_LEN)
     823        if self.buffer==NULL:
     824            raise Exception("failed to allocate %iKB of memory" % (DEFAULT_BUF_LEN//1024))
     825        self.muxer_ctx.pb = avio_alloc_context(self.buffer, DEFAULT_BUF_LEN, 1, <void *> gen, NULL, write_packet, NULL)
     826        if self.muxer_ctx.pb==NULL:
     827            raise Exception("libavformat failed to allocate io context")
     828        log("init_encoder() saving %s stream to bitstream buffer %#x", self.encoding, <unsigned long> self.buffer)
     829        self.muxer_ctx.bit_rate = 500000
     830        self.muxer_ctx.start_time_realtime = int(time.time())
    498831
    499         self.codec_ctx = avcodec_alloc_context3(self.codec)
    500         if self.codec_ctx==NULL:
    501             raise Exception("failed to allocate codec context!")
     832        self.video_stream = avformat_new_stream(self.muxer_ctx, NULL)    #self.video_codec
     833        self.video_stream.id = 0
     834        log("init_encoder() video: avformat_new_stream=%#x, nb streams=%i", <unsigned long> self.video_stream, self.muxer_ctx.nb_streams)
    502835
     836        self.video_ctx = avcodec_alloc_context3(self.video_codec)
     837        if self.video_ctx==NULL:
     838            raise Exception("failed to allocate video codec context!")
     839
    503840        #we need a framerate.. make one up:
    504         self.codec_ctx.framerate.num = 1
    505         self.codec_ctx.framerate.den = 25
    506         self.codec_ctx.time_base.num = 1
    507         self.codec_ctx.time_base.den = 25
    508         self.codec_ctx.refcounted_frames = 1
    509         self.codec_ctx.max_b_frames = b_frames*1
    510         self.codec_ctx.has_b_frames = b_frames
    511         self.codec_ctx.delay = 0
    512         self.codec_ctx.gop_size = 1
    513         self.codec_ctx.width = self.width
    514         self.codec_ctx.height = self.height
    515         self.codec_ctx.bit_rate = 500000
    516         self.codec_ctx.pix_fmt = self.pix_fmt
    517         self.codec_ctx.thread_safe_callbacks = 1
    518         self.codec_ctx.thread_type = THREAD_TYPE
    519         self.codec_ctx.thread_count = THREAD_COUNT     #0=auto
    520         self.codec_ctx.flags2 |= CODEC_FLAG2_FAST   #may cause "no deblock across slices" - which should be fine
     841        self.video_ctx.framerate.num = 1
     842        self.video_ctx.framerate.den = 25
     843        self.video_ctx.time_base.num = 1
     844        self.video_ctx.time_base.den = 25
     845        self.video_ctx.refcounted_frames = 1
     846        self.video_ctx.max_b_frames = b_frames*1
     847        self.video_ctx.has_b_frames = b_frames
     848        self.video_ctx.delay = 0
     849        self.video_ctx.gop_size = 1
     850        self.video_ctx.width = self.width
     851        self.video_ctx.height = self.height
     852        self.video_ctx.bit_rate = 500000
     853        self.video_ctx.pix_fmt = self.pix_fmt
     854        self.video_ctx.thread_safe_callbacks = 1
     855        self.video_ctx.thread_type = THREAD_TYPE
     856        self.video_ctx.thread_count = THREAD_COUNT     #0=auto
     857        #if oformat.flags & AVFMT_GLOBALHEADER:
     858        self.video_ctx.flags |= CODEC_FLAG_GLOBAL_HEADER
     859        self.video_ctx.flags2 |= CODEC_FLAG2_FAST   #may cause "no deblock across slices" - which should be fine
    521860        #av_opt_set(c->priv_data, "preset", "slow", 0)
    522861        log("init_encoder() b-frames=%i, thread-type=%i, thread-count=%i", b_frames, THREAD_TYPE, THREAD_COUNT)
    523         log("init_encoder() codec flags: %s", flagscsv(CODEC_FLAGS, self.codec_ctx.flags))
    524         log("init_encoder() codec flags2: %s", flagscsv(CODEC_FLAGS2, self.codec_ctx.flags2))
     862        log("init_encoder() codec flags: %s", flagscsv(CODEC_FLAGS, self.video_ctx.flags))
     863        log("init_encoder() codec flags2: %s", flagscsv(CODEC_FLAGS2, self.video_ctx.flags2))
    525864
    526         r = avcodec_open2(self.codec_ctx, self.codec, NULL)   #NULL, NULL)
     865        r = avcodec_open2(self.video_ctx, self.video_codec, NULL)   #NULL, NULL)
    527866        if r!=0:
    528867            raise Exception("could not open %s encoder context: %s" % (self.encoding, av_error_str(r)))
    529868
     869        r = avcodec_parameters_from_context(self.video_stream.codecpar, self.video_ctx)
     870        if r<0:
     871            raise Exception("could not copy video context parameters %#x: %s" % (<unsigned long> self.video_stream.codecpar, av_error_str(r)))
     872
     873        if AUDIO:
     874            #audio:
     875            self.audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC)
     876            if self.audio_codec==NULL:
     877                raise Exception("cannot find audio codec!")
     878            log("init_encoder() audio_codec=%#x", <unsigned long> self.audio_codec)
     879            self.audio_stream = avformat_new_stream(self.muxer_ctx, NULL)
     880            self.audio_stream.id = 1
     881            log("init_encoder() audio: avformat_new_stream=%#x, nb streams=%i", <unsigned long> self.audio_stream, self.muxer_ctx.nb_streams)
     882            self.audio_ctx = avcodec_alloc_context3(self.audio_codec)
     883            log("init_encoder() audio_context=%#x", <unsigned long> self.audio_ctx)
     884            self.audio_ctx.sample_fmt = AV_SAMPLE_FMT_FLTP
     885            self.audio_ctx.time_base.den = 25
     886            self.audio_ctx.time_base.num = 1
     887            self.audio_ctx.bit_rate = 64000
     888            self.audio_ctx.sample_rate = 44100
     889            self.audio_ctx.channels = 2
     890            #if audio_codec.capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE:
     891            #    pass
     892            #cdef AVDictionary *opts = NULL
     893            #av_dict_set(&opts, "strict", "experimental", 0)
     894            #r = avcodec_open2(audio_ctx, audio_codec, &opts)
     895            #av_dict_free(&opts)
     896            r = avcodec_open2(self.audio_ctx, self.audio_codec, NULL)
     897            if r!=0:
     898                raise Exception("could not open %s encoder context: %s" % (self.encoding, av_error_str(r)))
     899            if r!=0:
     900                raise Exception("could not open %s encoder context: %s" % ("aac", av_error_str(r)))
     901            r = avcodec_parameters_from_context(self.audio_stream.codecpar, self.audio_ctx)
     902            if r<0:
     903                raise Exception("could not copy audio context parameters %#x: %s" % (<unsigned long> self.audio_stream.codecpar, av_error_str(r)))
     904
     905        log("init_encoder() writing %s header", self.muxer_format)
     906        r = avformat_write_header(self.muxer_ctx, NULL)
     907        if r!=0:
     908            msg = av_error_str(r)
     909            raise Exception("libavformat failed to write header: %s" % msg)
     910
    530911        self.av_frame = av_frame_alloc()
    531912        if self.av_frame==NULL:
    532913            raise Exception("could not allocate an AVFrame for encoding")
     
    533914        self.frames = 0
    534915
    535916        if SAVE_TO_FILE is not None:
    536             filename = SAVE_TO_FILE+"-"+str(gen)+".%s" % self.encoding
     917            filename = SAVE_TO_FILE+"-"+self.encoding+"-"+str(gen)+".%s" % self.muxer_format
    537918            self.file = open(filename, 'wb')
    538             log.info("saving stream to %s", filename)
     919            log.info("saving %s stream to %s", self.encoding, filename)
    539920
    540921
    541922    def clean(self):
     923        #import traceback
     924        #traceback.print_stack()
    542925        try:
    543926            self.clean_encoder()
    544927        except:
    545928            log.error("cleanup failed", exc_info=True)
    546         self.codec = NULL
     929        self.video_codec = NULL
     930        self.audio_codec = NULL
    547931        self.pix_fmt = 0
    548932        self.src_format = ""
    549933        self.av_frame = NULL                        #should be redundant
     
    551935        self.width = 0
    552936        self.height = 0
    553937        self.encoding = ""
     938        self.buffers = []
    554939        f = self.file
    555940        if f:
    556941            self.file = None
     
    562947        if self.av_frame!=NULL:
    563948            log("clean_encoder() freeing AVFrame: %#x", <unsigned long> self.av_frame)
    564949            av_frame_free(&self.av_frame)
    565         log("clean_encoder() freeing AVCodecContext: %#x", <unsigned long> self.codec_ctx)
    566         if self.codec_ctx!=NULL:
    567             r = avcodec_close(self.codec_ctx)
     950        if self.muxer_ctx!=NULL:
     951            if self.frames>0:
     952                log("clean_encoder() writing trailer to stream")
     953                av_write_trailer(self.muxer_ctx)
     954                if self.muxer_ctx.pb!=NULL:
     955                    av_free(self.muxer_ctx.pb)
     956                    self.muxer_ctx.pb = NULL
     957            log("clean_encoder() freeing av format context %#x", <unsigned long> self.muxer_ctx)
     958            avformat_free_context(self.muxer_ctx)
     959            self.muxer_ctx = NULL
     960            log("clean_encoder() freeing bitstream buffer %#x", <unsigned long> self.buffer)
     961            if self.buffer!=NULL:
     962                av_free(self.buffer)
     963                self.buffer = NULL
     964        cdef unsigned long ctx_key          #@DuplicatedSignature
     965        log("clean_encoder() freeing AVCodecContext: %#x", <unsigned long> self.video_ctx)
     966        if self.video_ctx!=NULL:
     967            r = avcodec_close(self.video_ctx)
    568968            if r!=0:
    569                 log.error("Error: failed to close encoder context %#x", <unsigned long> self.codec_ctx)
     969                log.error("Error: failed to close video encoder context %#x", <unsigned long> self.video_ctx)
    570970                log.error(" %s", av_error_str(r))
    571             av_free(self.codec_ctx)
    572             self.codec_ctx = NULL
     971            av_free(self.video_ctx)
     972            self.video_ctx = NULL
     973        if self.audio_ctx!=NULL:
     974            r = avcodec_close(self.audio_ctx)
     975            if r!=0:
     976                log.error("Error: failed to close audio encoder context %#x", <unsigned long> self.audio_ctx)
     977                log.error(" %s", av_error_str(r))
     978            av_free(self.audio_ctx)
     979            self.audio_ctx = NULL
    573980        log("clean_encoder() done")
    574981
    575982    def __repr__(self):                      #@DuplicatedSignature
     
    581988        info = {
    582989                "version"   : get_version(),
    583990                "encoding"  : self.encoding,
     991                "muxer"     : self.muxer_format,
    584992                "formats"   : get_input_colorspaces(self.encoding),
    585993                "type"      : self.get_type(),
    586994                "frames"    : self.frames,
     
    587995                "width"     : self.width,
    588996                "height"    : self.height,
    589997                }
    590         if self.codec:
    591             info["codec"] = self.codec.name[:]
    592             info["description"] = self.codec.long_name[:]
     998        if self.video_codec:
     999            info["video-codec"] = self.video_codec.name[:]
     1000            info["video-description"] = self.video_codec.long_name[:]
     1001        if self.audio_codec:
     1002            info["audio-codec"] = self.audio_codec.name[:]
     1003            info["audio-description"] = self.audio_codec.long_name[:]
    5931004        if self.src_format:
    5941005            info["src_format"] = self.src_format
    5951006        if not self.is_closed():
    596             info["encoder_width"] = self.codec_ctx.width
    597             info["encoder_height"] = self.codec_ctx.height
     1007            info["encoder_width"] = self.video_ctx.width
     1008            info["encoder_height"] = self.video_ctx.height
    5981009        else:
    5991010            info["closed"] = True
    6001011        return info
    6011012
    6021013    def is_closed(self):
    603         return self.codec_ctx==NULL
     1014        return self.video_ctx==NULL
    6041015
    6051016    def __dealloc__(self):                          #@DuplicatedSignature
    6061017        self.clean()
     
    6451056        cdef int ret
    6461057        cdef AVPacket avpkt
    6471058        cdef AVFrame *frame
    648         assert self.codec_ctx!=NULL, "no codec context! (not initialized or already closed)"
    649         assert self.codec!=NULL
     1059        assert self.video_ctx!=NULL, "no codec context! (not initialized or already closed)"
     1060        assert self.video_codec!=NULL
    6501061
    6511062        if image:
    6521063            assert image.get_pixel_format()==self.src_format, "invalid input format %s, expected %s" % (image.get_pixel_format, self.src_format)
     
    6721083            self.av_frame.coded_picture_number = self.frames+1
    6731084            self.av_frame.display_picture_number = self.frames+1
    6741085            #if self.frames==0:
    675             #    self.av_frame.pict_type = AV_PICTURE_TYPE_I
     1086            self.av_frame.pict_type = AV_PICTURE_TYPE_I
    6761087            #else:
    6771088            #    self.av_frame.pict_type = AV_PICTURE_TYPE_P
    6781089            #self.av_frame.quality = 1
     
    6821093            frame = NULL
    6831094
    6841095        with nogil:
    685             ret = avcodec_send_frame(self.codec_ctx, frame)
     1096            ret = avcodec_send_frame(self.video_ctx, frame)
    6861097        if ret!=0:
    6871098            self.log_av_error(image, ret, options)
    6881099            raise Exception(av_error_str(ret))
     
    6921103        avpkt.data = <uint8_t *> xmemalign(buf_len)
    6931104        avpkt.size = buf_len
    6941105        assert ret==0
    695         bufs = []
    6961106        client_options = {}
    6971107        while ret==0:
    6981108            with nogil:
    699                 ret = avcodec_receive_packet(self.codec_ctx, &avpkt)
    700             if ret==EAGAIN:
     1109                ret = avcodec_receive_packet(self.video_ctx, &avpkt)
     1110            if ret==--errno.EAGAIN:
    7011111                client_options["delayed"] = 1
    7021112                log("ffmpeg EAGAIN: delayed picture")
    7031113                break
    704             if ret!=0 and bufs:
    705                 log("avcodec_receive_packet returned error '%s' for image %s, returning existing buffer", av_error_str(ret), image)
     1114            if ret!=0:
     1115                if not image:
     1116                    log("avcodec_receive_packet returned error '%s' for flush request", av_error_str(ret))
     1117                else:
     1118                    log("avcodec_receive_packet returned error '%s' for image %s, returning existing buffer", av_error_str(ret), image)
    7061119                break
    707             if ret!=0 and not image:
    708                 log("avcodec_receive_packet returned error '%s' for flush request", av_error_str(ret))
    709                 break
    7101120            if ret<0:
    7111121                free(avpkt.data)
    7121122                self.log_av_error(image, ret, options)
     
    7201130                free(avpkt.data)
    7211131                self.log_error(image, "packet", options, "av packet is corrupt")
    7221132                raise Exception("av packet is corrupt")
    723             packet_data = avpkt.data[:avpkt.size]
    724             bufs.append(packet_data)
     1133
     1134            avpkt.stream_index = self.video_stream.index
     1135            r = av_write_frame(self.muxer_ctx, &avpkt)
     1136            log("av_write_frame packet returned %i", r)
     1137            if ret<0:
     1138                free(avpkt.data)
     1139                self.log_av_error(image, ret, options)
     1140                raise Exception(av_error_str(ret))
     1141            while True:
     1142                r = av_write_frame(self.muxer_ctx, NULL)
     1143                log("av_write_frame flush returned %i", r)
     1144                if r==1:
     1145                    break
     1146                if ret<0:
     1147                    free(avpkt.data)
     1148                    self.log_av_error(image, ret, options)
     1149                    raise Exception(av_error_str(ret))
    7251150        av_packet_unref(&avpkt)
    7261151        free(avpkt.data)
    7271152        self.frames += 1
    728         data = b"".join(bufs)
    729         log("compress_image(%s) %5i bytes (%i buffers) for %4s frame %-3i, client options: %s", image, len(data), len(bufs), self.encoding, self.frames, client_options)
    730         if data and self.file:
    731             self.file.write(data)
     1153        data = b"".join(self.buffers)
     1154        log("compress_image(%s) %5i bytes (%i buffers) for %4s frame %-3i, client options: %s", image, len(data), len(self.buffers), self.encoding, self.frames, client_options)
     1155        #client_options["raw-buffers"] = self.buffers
     1156        if self.buffers and self.file:
     1157            for x in self.buffers:
     1158                self.file.write(x)
     1159        self.buffers = []
    7321160        return data, client_options
    7331161
    7341162    def flush(self, delayed):
     
    7371165        self.clean()
    7381166        return v
    7391167
     1168    def write_packet(self, unsigned long buf, int buf_size):
     1169        log("write_packet(%#x, %#x)", <unsigned long> buf, buf_size)
     1170        cdef uint8_t *cbuf = <uint8_t*> buf
     1171        buffer = cbuf[:buf_size]
     1172        self.buffers.append(buffer)
     1173        return buf_size
    7401174
     1175
    7411176def selftest(full=False):
    7421177    global CODECS
    7431178    from xpra.codecs.codec_checks import testencoder
    7441179    from xpra.codecs.enc_ffmpeg import encoder
    7451180    try:
    746         suspend_nonfatal_logging()
     1181        #suspend_nonfatal_logging()
    7471182        CODECS = testencoder(encoder, full)
    7481183    finally:
    749         resume_nonfatal_logging()
     1184        pass
     1185        #resume_nonfatal_logging()