xpra icon
Bug tracker and wiki

Ticket #1341: html5-mpeg4video-v4.patch

File html5-mpeg4video-v4.patch, 59.3 KB (added by Antoine Martin, 3 years ago)

patch updated for already merged audio bits

  • html5/js/Client.js

     
    88 *
    99 * requires:
    1010 *      Protocol.js
    11  *  Window.js
    12  *  Keycodes.js
     11 *      Window.js
     12 *      Keycodes.js
    1313 */
    1414
    1515XPRA_CLIENT_FORCE_NO_WORKER = false;
     
    2525        // some client stuff
    2626        this.capabilities = {};
    2727        this.RGB_FORMATS = ["RGBX", "RGBA"];
    28         this.supported_encodings = ["jpeg", "png", "rgb", "rgb32"];     //, "h264"];
    29         this.enabled_encodings = [];
     28        this.supported_encodings = ["jpeg", "png", "rgb", "rgb32", "vp8+webm", "h264+mp4", "mpeg4+mp4"];        //, "h264"];
     29        this.enabled_encodings = ["vp8+webm", "h264+mp4", "jpeg"];
    3030        this.normal_fullscreen_mode = false;
    3131        this.start_new_session = null;
    3232        this.username = "";
     
    610610                "platform.platform"                     : navigator.appVersion,
    611611                "namespace"                                     : true,
    612612                "share"                                         : this.sharing,
    613                 "client_type"                           : "HTML5",
     613                "client_type"                           : "HTML5",
    614614                "encoding.generic"                      : true,
    615615                "username"                                      : this.username,
    616616                "uuid"                                          : Utilities.getHexUUID(),
     
    670670                "encodings.rgb_formats"         : this.RGB_FORMATS,
    671671                "encodings.window-icon"         : ["png"],
    672672                "encodings.cursor"                      : ["png"],
    673                 "encoding.generic"                      : true,
     673                "encoding.generic"                      : true,
    674674                "encoding.transparency"         : true,
    675675                "encoding.client_options"       : true,
    676676                "encoding.csc_atoms"            : true,
     
    680680                "encoding.video_scaling"        : false,
    681681                "encoding.full_csc_modes"       : {
    682682                        "h264"          : ["YUV420P"],
    683                         //"mpeg4+mp4"   : ["YUV420P"],
    684                         //"h264+mp4"    : ["YUV420P"],
    685                         //"vp8+webm"    : ["YUV420P"],
     683                        "mpeg4+mp4"     : ["YUV420P"],
     684                        "h264+mp4"      : ["YUV420P"],
     685                        "vp8+webm"      : ["YUV420P"],
    686686                },
    687                 //"encoding.h264+mp4.YUV420P.profile" : "main",
    688                 //"encoding.h264+mp4.YUV420P.level"     : "3.0",
     687                "encoding.h264+mp4.YUV420P.profile"     : "main",
     688                "encoding.h264+mp4.YUV420P.level"       : "3.0",
    689689                "sound.receive"                         : true,
    690690                "sound.send"                            : false,
    691691                "sound.decoders"                        : Object.keys(this.audio_codecs),
     
    13721372        var win = ctx.id_to_window[wid];
    13731373        var decode_time = -1;
    13741374        if (win) {
    1375                 // win.paint draws the update to the window's off-screen buffer and returns true if it
    1376                 // was changed.
    13771375                win.paint(x, y,
    13781376                        width, height,
    13791377                        coding, data, packet_sequence, rowstride, options,
    13801378                        function (ctx) {
    13811379                                decode_time = new Date().getTime() - start;
     1380                                console.warn("decode time for ", coding, " sequence ", packet_sequence, ": ", decode_time);
    13821381                                ctx._window_send_damage_sequence(wid, packet_sequence, width, height, decode_time);
     1382                                var flush = options["flush"] || 0;
     1383                                if(flush==0) {
     1384                                        // request that drawing to screen takes place at next available opportunity if possible
     1385                                        if(requestAnimationFrame) {
     1386                                                requestAnimationFrame(function() {
     1387                                                        win.draw();
     1388                                                });
     1389                                        } else {
     1390                                                // requestAnimationFrame is not available, draw immediately
     1391                                                win.draw();
     1392                                        }
     1393                                }
    13831394                        }
    13841395                );
    1385                 // request that drawing to screen takes place at next available opportunity if possible
    1386                 if(requestAnimationFrame) {
    1387                         requestAnimationFrame(function() {
    1388                                 win.draw();
    1389                         });
    1390                 } else {
    1391                         // requestAnimationFrame is not available, draw immediately
    1392                         win.draw();
    1393                 }
    13941396        }
    13951397}
    13961398
     
    15131515                        console.log("got some data to print");
    15141516                        var b64data = btoa(uintToString(data));
    15151517                        window.open(
    1516                           'data:application/pdf;base64,'+b64data,
    1517                           '_blank'
     1518                                        'data:application/pdf;base64,'+b64data,
     1519                                        '_blank'
    15181520                        );
    15191521                }
    15201522        }
  • html5/js/MediaSourceUtil.js

     
    5353                        "mp3", "flac", "wav", "wave",
    5454                ],
    5555
     56                H264_PROFILE_CODE : {
     57                                //"baseline"    : "42E0",
     58                                "baseline"      : "42C0",
     59                                "main"          : "4D40",
     60                                "high"          : "6400",
     61                                "extended"      : "58A0",
     62                },
     63
     64                H264_LEVEL_CODE : {
     65                                "3.0"           : "1E",
     66                                "3.1"           : "1F",
     67                                "4.1"           : "29",
     68                                "5.1"           : "33",
     69                },
     70
    5671                READY_STATE : {
    5772                        0       : "NOTHING",
    5873                        1       : "METADATA",
  • html5/js/Window.js

     
    3636        //enclosing div in page DOM
    3737        this.div = jQuery("#" + String(wid));
    3838
    39         // h264 video stuff
    40         this.avc = null;
    41         this.glcanvas = null;
    42 
    4339        //callbacks start null until we finish init:
    4440        this.geometry_cb = null;
    4541        this.mouse_move_cb = null;
     
    159155        this.updateCSSGeometry();
    160156        //show("placing new window at "+this.x+","+this.y);
    161157
    162         //create the image holding the pixels (the "backing"):
    163         this.create_image_backing();
    164 
    165158        // now read all metadata
    166159        this.update_metadata(metadata);
    167160};
     
    351344};
    352345
    353346/**
    354  * Allocates the image object containing the window's pixels.
    355  */
    356 XpraWindow.prototype.create_image_backing = function() {
    357         "use strict";
    358         var previous_image = this.image;
    359         var img_geom = this.get_internal_geometry();
    360         //show("createImageData: "+img_geom.toSource());
    361         // this should draw to the offscreen canvas
    362         //this.image = this.offscreen_canvas.getContext('2d').createImageData(img_geom.w, img_geom.h);
    363         //if (previous_image) {
    364                 //copy previous pixels to new image, ignoring bit gravity
    365         //      this.offscreen_canvas.getContext('2d').putImageData(previous_image, 0, 0);
    366         //}
    367 
    368         // this should copy canvas pixel data since a canvas is the backing!
    369 };
    370 
    371 /**
    372347 * Update our metadata cache with new key-values,
    373348 * then call set_metadata with these new key-values.
    374349 */
     
    568543        }
    569544        // then update CSS and redraw backing
    570545        this.updateCSSGeometry();
    571         this.create_image_backing();
    572546        // send geometry callback
    573547        this.geometry_cb(this);
    574548};
     
    620594                        this.geometry_cb(this);
    621595                }
    622596                this.updateCSSGeometry();
    623                 this.create_image_backing();
    624597        }
    625598};
    626599
     
    728701        return window.btoa(s);
    729702}
    730703
    731 /**
    732  * The following function inits the h264 decoder
    733  */
    734 XpraWindow.prototype._init_avc = function() {
     704XpraWindow.prototype._close_video = function() {
     705        console.log("close_video: video_source_buffer="+this.video_source_buffer+", media_source="+this.media_source+", video="+this.video);
     706        this.video_source_ready = false;
     707        if(this.video) {
     708                if(this.media_source) {
     709                        try {
     710                                if(this.video_source_buffer) {
     711                                        this.media_source.removeSourceBuffer(this.video_source_buffer);
     712                                        this.video_source_buffer = null;
     713                                }
     714                                this.media_source.endOfStream();
     715                        } catch(e) {
     716                                console.warn("video media source EOS error: "+e);
     717                        }
     718                        this.media_source = null;
     719                }               
     720                this.canvas.parentElement.removeChild(this.video);
     721                this.video = null;
     722        }
     723}
     724
     725
     726XpraWindow.prototype._push_video_buffers = function() {
     727        if(this.debug) {
     728                console.debug("_push_video_buffers()");
     729        }
     730        var vsb = this.video_source_buffer;
     731        var vb = this.video_buffers;
     732        if(!vb || !vsb || !this.video_source_ready) {
     733                return;
     734        }
     735        if(vb.length==0 && this.video_buffers_count==0) {
     736                return;
     737        }
     738        while(vb.length>0 && !vsb.updating) {
     739            /*
     740            console.debug("video pushing one buffer from queue of size "+vb.length);
     741        var buffers = vb.splice(0, 24);
     742        var buffer = [].concat.apply([], buffers);
     743        vsb.appendBuffer(new Uint8Array(buffer).buffer);
     744            */
     745                var frame = vb.shift();
     746        var array = new Uint8Array(frame.data);
     747            /*if(this.video_buffers_count>0 && ms.duration) {
     748                vsb.timestampOffset = 0.0+this.video_buffers_count;
     749            }*/
     750            vsb.appendBuffer(array.buffer);
     751            frame.callback();
     752            this.video_buffers_count += 1;
     753        }
     754        setTimeout(this._ush_video_buffers, 10);
     755}
     756
     757XpraWindow.prototype._init_video = function(width, height, coding, profile, level) {
    735758        var me = this;
    736         // configure the AVC decoder
    737         this.avc = new Decoder({
    738                 rgb: true
     759        this.media_source = MediaSourceUtil.MediaSource();
     760        if(this.debug) {
     761                MediaSourceUtil.addMediaSourceEventDebugListeners(this.media_source, "video");
     762        }
     763        //<video> element:
     764        this.video = document.createElement("video");
     765        this.video.setAttribute('autoplay', true);
     766        this.video.setAttribute('muted', true);
     767        this.video.setAttribute('width', width);
     768        this.video.setAttribute('height', height);
     769        this.video.style.position = "absolute";
     770        this.video.style.zIndex = "1";
     771        this.video.style.left  = ""+this.leftoffset+"px";
     772        this.video.style.top = ""+this.topoffset+"px";
     773        if(this.debug) {
     774                MediaSourceUtil.addMediaElementEventDebugListeners(this.video, "video");
     775                this.video.setAttribute('controls', "controls");
     776        }
     777        this.video.addEventListener('error',                    function() { console.error("video error"); });
     778        this.video.src = window.URL.createObjectURL(this.media_source);
     779        //this.video.src = "https://html5-demos.appspot.com/static/test.webm"
     780        this.video_buffers = []
     781        this.video_buffers_count = 0;
     782        this.video_source_ready = false;
     783
     784        var codec_string = "";
     785        if(coding=="h264+mp4" || coding=="mpeg4+mp4") {
     786                //ie: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'
     787                codec_string = 'video/mp4; codecs="avc1.' + MediaSourceConstants.H264_PROFILE_CODE[profile] + MediaSourceConstants.H264_LEVEL_CODE[level]+'"';
     788        }
     789        else if(coding=="vp8+webm") {
     790                codec_string = 'video/webm;codecs="vp8"';
     791        }
     792        else if(coding=="vp9+webm") {
     793                codec_string = 'video/webm;codecs="vp9"';
     794        }
     795        else {
     796                throw Exception("invalid encoding: "+coding);
     797        }
     798        console.log("video codec string: "+codec_string+" for "+coding+" profile '"+profile+"', level '"+level+"'");
     799        this.media_source.addEventListener('sourceopen', function() {
     800                console.log("video media source open");
     801                var vsb = me.media_source.addSourceBuffer(codec_string);
     802            vsb.mode = "sequence";
     803                me.video_source_buffer = vsb;
     804                if(this.debug) {
     805                        MediaSourceUtil.addSourceBufferEventDebugListeners(vsb, "video");
     806                }
     807                vsb.addEventListener('error',                   function(e) { console.error("video source buffer error"); });
     808                vsb.addEventListener('waiting', function() {
     809                        me._push_video_buffers();
     810                });
     811                //push any buffers that may have accumulated since we initialized the video element:
     812                me._push_video_buffers();
     813                me.video_source_ready = true;
    739814        });
    740         this.avc.onPictureDecoded = function(buffer, bufWidth, bufHeight) {
    741                 var img = me.offscreen_canvas_ctx.createImageData(bufWidth, bufHeight);
    742                 img.data.set(buffer);
    743                 me.offscreen_canvas_ctx.putImageData(img, 0, 0);
    744         };
     815        this.canvas.parentElement.appendChild(this.video);
    745816};
    746817
     818XpraWindow.prototype._non_video_paint = function(coding) {
     819        if(this.video && this.video.style.zIndex!="-1") {
     820                if(this.debug) {
     821                        console.debug("bringing canvas above video for "+coding+" paint event");
     822                }
     823                //push video under the canvas:
     824                this.video.style.zIndex = "-1";
     825                //copy video to canvas:
     826                var width = this.video.getAttribute("width");
     827                var height = this.video.getAttribute("height");
     828        this.offscreen_canvas_ctx.drawImage(this.video, 0, 0, width, height);
     829        }
     830}
    747831
     832
    748833/**
    749834 * Updates the window image with new pixel data
    750835 * we have received from the server.
     
    755840        if (this.debug)
    756841                console.log("paint("+img_data.length+" bytes of "+("zlib" in options?"zlib ":"")+coding+" data "+width+"x"+height+" at "+x+","+y+") focused="+this.focused);
    757842
     843        if(this.offscreen_canvas_mode!='2d') {
     844                this._init_2d_canvas();
     845        }
     846
    758847        if (coding=="rgb32") {
    759                 // create image data
    760                 if(this.offscreen_canvas_mode!='2d') {
    761                         this._init_2d_canvas();
    762                 }
     848                this._non_video_paint(coding);
    763849                var img = this.offscreen_canvas_ctx.createImageData(width, height);
    764850                //if the pixel data is not in an array buffer already, convert it:
    765851                //(this happens with inlined pixel data)
     
    803889                }
    804890                img.data.set(img_data);
    805891                this.offscreen_canvas_ctx.putImageData(img, x, y);
    806                 // send decode callback once we actually decoded
    807892                decode_callback(this.client);
    808893        }
    809894        else if (coding=="jpeg" || coding=="png") {
    810                 // create image data
    811                 if(this.offscreen_canvas_mode!='2d') {
    812                         this._init_2d_canvas();
    813                 }
     895                this._non_video_paint(coding);
    814896                var img = this.offscreen_canvas_ctx.createImageData(width, height);
    815                 // decode image
     897                var me = this;
    816898                var j = new Image();
    817                 j.src = "data:image/"+coding+";base64," + this._arrayBufferToBase64(img_data);
    818                 var me = this;
    819899                j.onload = function () {
    820900                        me.offscreen_canvas_ctx.drawImage(j, x, y);
    821                         // send decode callback once we actually decoded
    822901                        decode_callback(me.client);
    823902                };
     903                j.src = "data:image/"+coding+";base64," + this._arrayBufferToBase64(img_data);
    824904        }
    825         else if (coding=="h264") {
    826                 if(!this.avc) {
    827                         this._init_avc();
     905        else if (coding=="h264+mp4" || coding=="vp8+webm" || coding=="mpeg4+mp4") {
     906                var frame = options["frame"] || -1;
     907                if(frame==0) {
     908                        this._close_video();
    828909                }
    829                 // we can pass a buffer full of NALs to avc.decode directly
    830                 // as long as they are framed properly with the NAL header
    831                 this.avc.decode(new Uint8Array(img_data));
    832                 // how do we know when the avc has finished decoding?!
    833                 decode_callback(this.client);
    834                 //this._h264_process_raw(img_data);
     910                if(!this.video) {
     911                        var profile = options["profile"] || "baseline";
     912                        var level  = options["level"] || "3.0";
     913                        this._init_video(width, height, coding, profile, level);
     914                }
     915                else if (this.video.style.zIndex != "1"){
     916                        //make sure video is on the top layer:
     917                        this.video.style.zIndex = "1";
     918                }
     919                if(img_data.length>0) {
     920                        if(this.debug) {
     921                                console.debug("video state="+MediaSourceConstants.READY_STATE[this.video.readyState]+", network state="+MediaSourceConstants.NETWORK_STATE[this.video.networkState]);
     922                                console.debug("video paused="+this.video.paused+", video buffers="+this.video_buffers.length);
     923                        }
     924                        var me = this;
     925                        var video_frame = {
     926                                        data            : img_data,
     927                                        callback        : function() {
     928                                                        decode_callback(me.client);
     929                                                },
     930                                        }
     931                        this.video_buffers.push(video_frame);
     932                        if(this.video.paused) {
     933                                this.video.play();
     934                        }
     935                        this._push_video_buffers();
     936                }
    835937        }
    836938        else if (coding=="scroll") {
    837                 if(this.offscreen_canvas_mode!='2d') {
    838                         this._init_2d_canvas();
    839                 }
     939                this._non_video_paint(coding);
    840940                for(var i=0,j=img_data.length;i<j;++i) {
    841941                        var scroll_data = img_data[i];
    842942                        var sx = scroll_data[0],
     
    845945                                sh = scroll_data[3],
    846946                                xdelta = scroll_data[4],
    847947                                ydelta = scroll_data[5];
    848             this.offscreen_canvas_ctx.drawImage(this.offscreen_canvas, sx, sy, sw, sh, sx+xdelta, y+ydelta, sw, sh);
     948                        this.offscreen_canvas_ctx.drawImage(this.offscreen_canvas, sx, sy, sw, sh, sx+xdelta, y+ydelta, sw, sh);
    849949                }
    850950        }
    851951        else {
     
    859959XpraWindow.prototype.destroy = function destroy() {
    860960        "use strict";
    861961        // remove div
     962        this._close_video()
    862963        this.div.remove()
    863964};
  • setup.py

     
    16601660            add_keywords([libffmpeg_bin_dir], [libffmpeg_include_dir],
    16611661                         [libffmpeg_lib_dir, libffmpeg_bin_dir],
    16621662                         ["avcodec", "avutil"])
     1663        if "avformat" in pkgs_options[0]:
     1664            add_keywords([libffmpeg_bin_dir], [libffmpeg_include_dir],
     1665                         [libffmpeg_lib_dir, libffmpeg_bin_dir],
     1666                         ["avformat", "avutil"])
    16631667        elif "swscale" in pkgs_options[0]:
    16641668            add_keywords([libffmpeg_bin_dir], [libffmpeg_include_dir],
    16651669                         [libffmpeg_lib_dir, libffmpeg_bin_dir],
     
    24112415
    24122416toggle_packages(enc_ffmpeg_ENABLED, "xpra.codecs.enc_ffmpeg")
    24132417if enc_ffmpeg_ENABLED:
    2414     ffmpeg_pkgconfig = pkgconfig("libavcodec")
     2418    ffmpeg_pkgconfig = pkgconfig("libavcodec", "libavformat")
    24152419    cython_add(Extension("xpra.codecs.enc_ffmpeg.encoder",
    24162420                ["xpra/codecs/enc_ffmpeg/encoder.pyx"]+membuffers_c,
    24172421                **ffmpeg_pkgconfig))
  • 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    int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags)
     75    void av_dict_free(AVDictionary **m)
     76
    6877cdef extern from "libavutil/pixfmt.h":
    6978    AVPixelFormat AV_PIX_FMT_NONE
    7079    AVPixelFormat AV_PIX_FMT_YUV420P
     
    7786    AVPixelFormat AV_PIX_FMT_BGRA
    7887    AVPixelFormat AV_PIX_FMT_GBRP
    7988
     89cdef extern from "libavutil/samplefmt.h":
     90    AVSampleFormat AV_SAMPLE_FMT_S16
     91    AVSampleFormat AV_SAMPLE_FMT_FLTP
    8092
     93
     94cdef extern from "libavformat/avio.h":
     95    ctypedef int AVIODataMarkerType
     96    int AVIO_FLAG_WRITE
     97
     98    ctypedef struct AVIOContext:
     99        const AVClass *av_class
     100        unsigned char *buffer       #Start of the buffer
     101        int buffer_size             #Maximum buffer size
     102        unsigned char *buf_ptr      #Current position in the buffer
     103        unsigned char *buf_end      #End of the data, may be less than
     104                                    #buffer+buffer_size if the read function returned
     105                                    #less data than requested, e.g. for streams where
     106                                    #no more data has been received yet.
     107        int64_t     pos             #position in the file of the current buffer
     108        int         must_flush      #true if the next seek should flush
     109        int         error           #contains the error code or 0 if no error happened
     110        int         seekable
     111        int64_t     maxsize
     112        int         direct
     113        int64_t     bytes_read
     114
     115    AVIOContext *avio_alloc_context(unsigned char *buffer, int buffer_size, int write_flag,
     116                  void *opaque,
     117                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
     118                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
     119                  int64_t (*seek)(void *opaque, int64_t offset, int whence))
     120
     121
    81122cdef extern from "libavcodec/avcodec.h":
    82123    int FF_THREAD_SLICE     #allow more than one thread per frame
    83124    int FF_THREAD_FRAME     #Decode more than one frame at once
    84125
     126    int FF_PROFILE_H264_CONSTRAINED
     127    int FF_PROFILE_H264_INTRA
     128    int FF_PROFILE_H264_BASELINE
     129    int FF_PROFILE_H264_CONSTRAINED_BASELINE
     130    int FF_PROFILE_H264_MAIN
     131    int FF_PROFILE_H264_EXTENDED
     132    int FF_PROFILE_H264_HIGH
     133    int FF_PROFILE_H264_HIGH_10
     134    int FF_PROFILE_H264_HIGH_10_INTRA
     135    int FF_PROFILE_H264_MULTIVIEW_HIGH
     136    int FF_PROFILE_H264_HIGH_422
     137    int FF_PROFILE_H264_HIGH_422_INTRA
     138    int FF_PROFILE_H264_STEREO_HIGH
     139    int FF_PROFILE_H264_HIGH_444
     140    int FF_PROFILE_H264_HIGH_444_PREDICTIVE
     141    int FF_PROFILE_H264_HIGH_444_INTRA
     142    int FF_PROFILE_H264_CAVLC_444
     143
    85144    int CODEC_FLAG_UNALIGNED
    86145    int CODEC_FLAG_QSCALE
    87146    int CODEC_FLAG_4MV
     
    157216        int den
    158217
    159218    ctypedef struct AVCodecContext:
     219        const AVClass *av_class
    160220        int width
    161221        int height
    162222        AVPixelFormat pix_fmt
     
    174234        AVRational time_base
    175235        unsigned int codec_tag
    176236        int64_t bit_rate
     237        AVSampleFormat sample_fmt
     238        int sample_rate
     239        int channels
     240        int profile
     241        int level
    177242
     243    ctypedef struct AVFormatContext:
     244        const AVClass   *av_class
     245        AVOutputFormat  *oformat
     246        void            *priv_data
     247        AVIOContext     *pb
     248        int             ctx_flags
     249        unsigned int    nb_streams
     250        AVStream        **streams
     251        int64_t         start_time
     252        int64_t         duration
     253        int             bit_rate
     254        unsigned int    packet_size
     255        int             max_delay
     256        int             flags
     257        unsigned int    probesize
     258        int             max_analyze_duration
     259        AVCodecID       video_codec_id
     260        AVCodecID       audio_codec_id
     261        AVCodecID       subtitle_codec_id
     262        unsigned int    max_index_size
     263        unsigned int    max_picture_buffer
     264        unsigned int    nb_chapters
     265        AVDictionary    *metadata
     266        int64_t         start_time_realtime
     267        int             strict_std_compliance
     268        int flush_packets
     269
     270    ctypedef int AVFieldOrder
     271    ctypedef int AVColorRange
     272    ctypedef int AVColorPrimaries
     273    ctypedef int AVColorTransferCharacteristic
     274    ctypedef int AVColorSpace
     275    ctypedef int AVChromaLocation
     276    ctypedef struct AVCodecParameters:
     277        AVCodecID       codec_id
     278        uint32_t        codec_tag
     279        int64_t         bit_rate
     280        int             bits_per_coded_sample
     281        int             bits_per_raw_sample
     282        int             profile
     283        int             level
     284        int             width
     285        int             height
     286        AVFieldOrder    field_order
     287        AVColorRange    color_range
     288        AVColorPrimaries    color_primaries
     289        AVColorTransferCharacteristic color_trc
     290        AVColorSpace        color_space
     291        AVChromaLocation    chroma_location
     292        int             sample_rate
     293        int             frame_size
     294
    178295    AVCodecID AV_CODEC_ID_H264
    179296    AVCodecID AV_CODEC_ID_H265
    180297    AVCodecID AV_CODEC_ID_VP8
     
    181298    AVCodecID AV_CODEC_ID_VP9
    182299    AVCodecID AV_CODEC_ID_MPEG4
    183300
     301    AVCodecID AV_CODEC_ID_AAC
     302
    184303    #init and free:
    185304    void avcodec_register_all()
    186305    AVCodec *avcodec_find_encoder(AVCodecID id)
     
    189308    int avcodec_send_frame(AVCodecContext *avctx,const AVFrame *frame) nogil
    190309    int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) nogil
    191310
     311    int av_write_frame(AVFormatContext *s, AVPacket *pkt)
    192312    AVFrame* av_frame_alloc()
    193313    void av_frame_free(AVFrame **frame)
    194314    int avcodec_close(AVCodecContext *avctx)
     315    void av_frame_unref(AVFrame *frame) nogil
    195316    void av_init_packet(AVPacket *pkt) nogil
    196317    void av_packet_unref(AVPacket *pkt) nogil
    197318
     
    231352    void *av_opt_child_next(void *obj, void *prev)
    232353    int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
    233354    int av_opt_get_int(void *obj, const char *name, int search_flags, int64_t *out_val)
     355    int av_opt_set_dict(void *obj, AVDictionary **options)
    234356
    235357
    236358cdef extern from "libavutil/log.h":
     
    247369        #AVClassCategory (*get_category)(void *ctx)
    248370
    249371
     372cdef extern from "libavformat/avformat.h":
     373    int AVFMTCTX_NOHEADER           #signal that no header is present
     374
     375    int AVFMT_FLAG_GENPTS           #Generate missing pts even if it requires parsing future frames
     376    int AVFMT_FLAG_IGNIDX           #Ignore index
     377    int AVFMT_FLAG_NONBLOCK         #Do not block when reading packets from input
     378    int AVFMT_FLAG_IGNDTS           #Ignore DTS on frames that contain both DTS & PTS
     379    int AVFMT_FLAG_NOFILLIN         #Do not infer any values from other values, just return what is stored in the container
     380    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
     381    int AVFMT_FLAG_NOBUFFER         #Do not buffer frames when possible
     382    int AVFMT_FLAG_CUSTOM_IO        #The caller has supplied a custom AVIOContext, don't avio_close() it
     383    int AVFMT_FLAG_DISCARD_CORRUPT  #Discard frames marked corrupted
     384    int AVFMT_FLAG_FLUSH_PACKETS    #Flush the AVIOContext every packet
     385    int AVFMT_FLAG_BITEXACT
     386    int AVFMT_FLAG_MP4A_LATM        #Enable RTP MP4A-LATM payload
     387    int AVFMT_FLAG_SORT_DTS         #try to interleave outputted packets by dts (using this flag can slow demuxing down)
     388    int AVFMT_FLAG_PRIV_OPT         #Enable use of private options by delaying codec open (this could be made default once all code is converted)
     389    int AVFMT_FLAG_KEEP_SIDE_DATA   #Don't merge side data but keep it separate.
     390    int AVFMT_FLAG_FAST_SEEK        #Enable fast, but inaccurate seeks for some formats
     391
     392    int AVFMT_NOFILE                #Demuxer will use avio_open, no opened file should be provided by the caller
     393    int AVFMT_NEEDNUMBER            #Needs '%d' in filename
     394    int AVFMT_SHOW_IDS              #Show format stream IDs numbers
     395    int AVFMT_RAWPICTURE            #Format wants AVPicture structure for raw picture data. @deprecated Not used anymore
     396    int AVFMT_GLOBALHEADER          #Format wants global header
     397    int AVFMT_NOTIMESTAMPS          #Format does not need / have any timestamps
     398    int AVFMT_GENERIC_INDEX         #Use generic index building code
     399    int AVFMT_TS_DISCONT            #Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps
     400    int AVFMT_VARIABLE_FPS          #Format allows variable fps
     401    int AVFMT_NODIMENSIONS          #Format does not need width/height
     402    int AVFMT_NOSTREAMS             #Format does not require any streams
     403    int AVFMT_NOBINSEARCH           #Format does not allow to fall back on binary search via read_timestamp
     404    int AVFMT_NOGENSEARCH           #Format does not allow to fall back on generic search
     405    int AVFMT_NO_BYTE_SEEK          #Format does not allow seeking by bytes
     406    int AVFMT_ALLOW_FLUSH           #Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function
     407    int AVFMT_TS_NONSTRICT          #Format does not require strictly increasing timestamps, but they must still be monotonic
     408    int AVFMT_TS_NEGATIVE           #Format allows muxing negative timestamps.
     409    int AVFMT_SEEK_TO_PTS           #Seeking is based on PTS
     410
     411    ctypedef struct AVStream:
     412        int         index           #stream index in AVFormatContext
     413        int         id
     414        AVCodecContext *codec
     415        AVRational  time_base
     416        int64_t     start_time
     417        int64_t     duration
     418        int64_t     nb_frames       #number of frames in this stream if known or 0
     419        #AVDiscard   discard         #Selects which packets can be discarded at will and do not need to be demuxed.
     420        AVRational  avg_frame_rate
     421        AVCodecParameters *codecpar
     422
     423    ctypedef struct AVOutputFormat:
     424        const char  *name
     425        const char  *long_name
     426        const char  *mime_type
     427        const char  *extensions
     428        AVCodecID   audio_codec
     429        AVCodecID   video_codec
     430        AVCodecID   subtitle_codec
     431        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...
     432        int         (*query_codec)(AVCodecID id, int std_compliance)
     433
     434    void av_register_all()
     435    AVOutputFormat *av_oformat_next(const AVOutputFormat *f)
     436    int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename)
     437    void avformat_free_context(AVFormatContext *s)
     438
     439    int avcodec_parameters_from_context(AVCodecParameters *par, const AVCodecContext *codec)
     440    AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
     441    int avformat_write_header(AVFormatContext *s, AVDictionary **options)
     442    int av_write_trailer(AVFormatContext *s)
     443    int av_write_frame(AVFormatContext *s, AVPacket *pkt)
     444
     445
     446H264_PROFILE_NAMES = {
     447    FF_PROFILE_H264_CONSTRAINED             : "constrained",
     448    FF_PROFILE_H264_INTRA                   : "intra",
     449    FF_PROFILE_H264_BASELINE                : "baseline",
     450    FF_PROFILE_H264_CONSTRAINED_BASELINE    : "constrained baseline",
     451    FF_PROFILE_H264_MAIN                    : "main",
     452    FF_PROFILE_H264_EXTENDED                : "extended",
     453    FF_PROFILE_H264_HIGH                    : "high",
     454    FF_PROFILE_H264_HIGH_10                 : "high10",
     455    FF_PROFILE_H264_HIGH_10_INTRA           : "high10 intra",
     456    FF_PROFILE_H264_MULTIVIEW_HIGH          : "multiview high",
     457    FF_PROFILE_H264_HIGH_422                : "high422",
     458    FF_PROFILE_H264_HIGH_422_INTRA          : "high422 intra",
     459    FF_PROFILE_H264_STEREO_HIGH             : "stereo high",
     460    FF_PROFILE_H264_HIGH_444                : "high444",
     461    FF_PROFILE_H264_HIGH_444_PREDICTIVE     : "high444 predictive",
     462    FF_PROFILE_H264_HIGH_444_INTRA          : "high444 intra",
     463    FF_PROFILE_H264_CAVLC_444               : "cavlc 444",
     464    }
     465H264_PROFILES = dict((v,k) for k,v in H264_PROFILE_NAMES.items())
     466
    250467AV_OPT_TYPES = {
    251468                AV_OPT_TYPE_FLAGS       : "FLAGS",
    252469                AV_OPT_TYPE_INT         : "INT",
     
    274491             AV_PKT_FLAG_CORRUPT    : "CORRUPT",
    275492             }
    276493
     494AVFMTCTX = {
     495            AVFMTCTX_NOHEADER       : "NOHEADER",
     496            }
     497
    277498CODEC_FLAGS = {
    278499               CODEC_FLAG_UNALIGNED         : "UNALIGNED",
    279500               CODEC_FLAG_QSCALE            : "QSCALE",
     
    298519                CODEC_FLAG2_FAST : "FAST",
    299520                }
    300521
     522FMT_FLAGS = {
     523             AVFMT_FLAG_GENPTS          : "GENPTS",
     524             AVFMT_FLAG_IGNIDX          : "IGNIDX",
     525             AVFMT_FLAG_NONBLOCK        : "NONBLOCK",
     526             AVFMT_FLAG_IGNDTS          : "IGNDTS",
     527             AVFMT_FLAG_NOFILLIN        : "NOFILLIN",
     528             AVFMT_FLAG_NOPARSE         : "NOPARSE",
     529             AVFMT_FLAG_NOBUFFER        : "NOBUFFER",
     530             AVFMT_FLAG_CUSTOM_IO       : "CUSTOM_IO",
     531             AVFMT_FLAG_DISCARD_CORRUPT : "DISCARD_CORRUPT",
     532             AVFMT_FLAG_FLUSH_PACKETS   : "FLUSH_PACKETS",
     533             AVFMT_FLAG_BITEXACT        : "BITEXACT",
     534             AVFMT_FLAG_MP4A_LATM       : "MP4A_LATM",
     535             AVFMT_FLAG_SORT_DTS        : "SORT_DTS",
     536             AVFMT_FLAG_PRIV_OPT        : "PRIV_OPT",
     537             AVFMT_FLAG_KEEP_SIDE_DATA  : "KEEP_SIDE_DATA",
     538             AVFMT_FLAG_FAST_SEEK       : "FAST_SEEK",
     539             }
     540
     541AVFMT = {
     542         AVFMT_NOFILE           : "NOFILE",
     543         AVFMT_NEEDNUMBER       : "NEEDNUMBER",
     544         AVFMT_SHOW_IDS         : "SHOW_IDS",
     545         AVFMT_RAWPICTURE       : "RAWPICTURE",
     546         AVFMT_GLOBALHEADER     : "GLOBALHEADER",
     547         AVFMT_NOTIMESTAMPS     : "NOTIMESTAMPS",
     548         AVFMT_GENERIC_INDEX    : "GENERIC_INDEX",
     549         AVFMT_TS_DISCONT       : "TS_DISCONT",
     550         AVFMT_VARIABLE_FPS     : "VARIABLE_FPS",
     551         AVFMT_NODIMENSIONS     : "NODIMENSIONS",
     552         AVFMT_NOSTREAMS        : "NOSTREAMS",
     553         AVFMT_NOBINSEARCH      : "NOBINSEARCH",
     554         AVFMT_NOGENSEARCH      : "NOGENSEARCH",
     555         AVFMT_NO_BYTE_SEEK     : "NO_BYTE_SEEK",
     556         AVFMT_ALLOW_FLUSH      : "ALLOW_FLUSH",
     557         AVFMT_TS_NONSTRICT     : "TS_NONSTRICT",
     558         AVFMT_TS_NEGATIVE      : "TS_NEGATIVE",
     559         AVFMT_SEEK_TO_PTS      : "SEEK_TO_PTS",
     560         }
     561
     562
    301563CAPS = {
    302564        CODEC_CAP_DRAW_HORIZ_BAND       : "DRAW_HORIZ_BAND",
    303565        CODEC_CAP_DR1                   : "DR1",
     
    356618def flagscsv(flag_dict, value=0):
    357619    return csv([v for k,v in flag_dict.items() if k&value])
    358620
     621
     622def get_muxer_formats():
     623    av_register_all()
     624    cdef AVOutputFormat *fmt = NULL
     625    formats = {}
     626    while True:
     627        fmt = av_oformat_next(fmt)
     628        if fmt==NULL:
     629            break
     630        name = fmt.name
     631        long_name = fmt.long_name
     632        formats[name] = long_name
     633    return formats
     634log("AV Output Formats:")
     635print_nested_dict(get_muxer_formats(), print_fn=log.debug)
     636
     637cdef AVOutputFormat* get_av_output_format(name):
     638    cdef AVOutputFormat *fmt = NULL
     639    while True:
     640        fmt = av_oformat_next(fmt)
     641        if fmt==NULL:
     642            break
     643        if name==fmt.name:
     644            return fmt
     645    return NULL
     646
     647
    359648def get_version():
    360649    return (LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO)
    361650
     
    362651avcodec_register_all()
    363652CODECS = []
    364653if avcodec_find_encoder(AV_CODEC_ID_H264)!=NULL:
    365     CODECS.append("h264")
    366 #if avcodec_find_encoder(AV_CODEC_ID_VP8)!=NULL:
    367 #    CODECS.append("vp8")
     654    CODECS.append("h264+mp4")
     655if avcodec_find_encoder(AV_CODEC_ID_VP8)!=NULL:
     656    CODECS.append("vp8+webm")
    368657#if avcodec_find_encoder(AV_CODEC_ID_VP9)!=NULL:
    369 #    CODECS.append("vp9")
     658#    CODECS.append("vp9+webm")
    370659#if avcodec_find_encoder(AV_CODEC_ID_H265)!=NULL:
    371660#    CODECS.append("h265")
    372661if avcodec_find_encoder(AV_CODEC_ID_MPEG4)!=NULL:
    373     CODECS.append("mpeg4")
     662    CODECS.append("mpeg4+mp4")
    374663log("enc_ffmpeg CODECS=%s", csv(CODECS))
    375664
    376665cdef av_error_str(int errnum):
     
    382671        return bytestostr(err_str[:i])
    383672    return "error %s" % errnum
    384673
    385 DEF EAGAIN = -11
     674DEF DEFAULT_BUF_LEN = 64*1024
    386675
    387676
    388677def init_module():
     
    405694    return  {
    406695             "version"      : get_version(),
    407696             "encodings"    : get_encodings(),
     697             "muxers"       : get_muxer_formats(),
    408698             "buffer_api"   : get_buffer_api_version(),
    409699             "formats"      : f,
    410700             "generation"   : generation.get(),
     
    423713    return ["YUV420P"]
    424714
    425715
     716GEN_TO_ENCODER = weakref.WeakValueDictionary()
     717
     718
     719cdef list_options(void *obj, const AVClass *av_class):
     720    if av_class==NULL:
     721        return
     722    cdef const AVOption *option = <const AVOption*> av_class.option
     723    options = []
     724    while option!=NULL:
     725        oname = option.name
     726        options.append(oname)
     727        option = av_opt_next(obj, option)
     728    log("%s options: %s", av_class.class_name, csv(options))
     729    cdef void *child = NULL
     730    cdef const AVClass *child_class = NULL
     731    while True:
     732        child = av_opt_child_next(obj, child)
     733        if child==NULL:
     734            return
     735        child_class = (<AVClass**> child)[0]
     736        list_options(child, child_class)
     737
     738
     739cdef int write_packet(void *opaque, uint8_t *buf, int buf_size):
     740    global GEN_TO_ENCODER
     741    encoder = GEN_TO_ENCODER.get(<unsigned long> opaque)
     742    #log.warn("write_packet(%#x, %#x, %#x) encoder=%s", <unsigned long> opaque, <unsigned long> buf, buf_size, type(encoder))
     743    if not encoder:
     744        log.error("Error: write_packet called for unregistered encoder %i!", <unsigned long> opaque)
     745        return -1
     746    return encoder.write_packet(<unsigned long> buf, buf_size)
     747
    426748MAX_WIDTH, MAX_HEIGHT = 4096, 4096
    427749def get_spec(encoding, colorspace):
    428750    assert encoding in get_encodings(), "invalid encoding: %s (must be one of %s" % (encoding, get_encodings())
     
    437759    """
    438760        This wraps the AVCodecContext and its configuration,
    439761    """
    440     cdef AVCodecID codec_id
    441     cdef AVCodec *codec
    442     cdef AVCodecContext *codec_ctx
     762    #muxer:
     763    cdef AVFormatContext *muxer_ctx
     764    cdef unsigned char *buffer
     765    cdef object buffers
     766    cdef int64_t offset
     767    cdef object muxer_format
     768    cdef object file
     769    #video:
     770    cdef AVCodec *video_codec
     771    cdef AVStream *video_stream
     772    cdef AVCodecContext *video_ctx
    443773    cdef AVPixelFormat pix_fmt
    444774    cdef object src_format
    445775    cdef AVFrame *av_frame
    446     #this is the actual number of images we have returned
    447776    cdef unsigned long frames
    448777    cdef unsigned int width
    449778    cdef unsigned int height
    450779    cdef object encoding
    451     cdef object file
     780    cdef object profile
     781    #audio:
     782    cdef AVCodec *audio_codec
     783    cdef AVStream *audio_stream
     784    cdef AVCodecContext *audio_ctx
    452785
    453786    cdef object __weakref__
    454787
     
    458791        assert encoding in CODECS
    459792        assert src_format in get_input_colorspaces(encoding), "invalid colorspace: %s" % src_format
    460793        self.encoding = encoding
     794        self.muxer_format = encoding.split("+")[1]  #ie: "mp4"   #"mov", "f4v"
     795        assert self.muxer_format in ("mp4", "webm")
    461796        self.width = width
    462797        self.height = height
    463798        self.src_format = src_format
     
    464799        self.pix_fmt = FORMAT_TO_ENUM.get(src_format, AV_PIX_FMT_NONE)
    465800        if self.pix_fmt==AV_PIX_FMT_NONE:
    466801            raise Exception("invalid pixel format: %s", src_format)
     802        self.buffers = []
    467803
     804        codec = self.encoding.split("+")[0]
    468805        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
     806        cdef AVCodecID video_codec_id
     807        if codec=="h264":
     808            video_codec_id = AV_CODEC_ID_H264
     809        elif codec=="h265":
     810            video_codec_id = AV_CODEC_ID_H265
     811        elif codec=="vp8":
     812            video_codec_id = AV_CODEC_ID_VP8
     813        elif codec=="vp9":
     814            video_codec_id = AV_CODEC_ID_VP9
     815        elif codec=="mpeg4":
     816            video_codec_id = AV_CODEC_ID_MPEG4
    479817        else:
    480818            raise Exception("invalid codec; %s" % self.encoding)
    481         self.codec = avcodec_find_encoder(self.codec_id)
    482         if self.codec==NULL:
     819        self.video_codec = avcodec_find_encoder(video_codec_id)
     820        if self.video_codec==NULL:
    483821            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))
     822        log("%s: \"%s\", codec flags: %s", self.video_codec.name, self.video_codec.long_name, flagscsv(CAPS, self.video_codec.capabilities))
    485823
    486         cdef int b_frames = 0   #int(options.get("b-frames"))
     824        #ie: client side as: "encoding.h264+mpeg4.YUV420P.profile" : "main"
     825        profile = options.get("%s.%s.profile" % (self.encoding, src_format), "main")
    487826        try:
    488             self.init_encoder(b_frames)
     827            self.init_encoder(profile)
    489828        except Exception as e:
    490             log("init_encoder(%i) failed", b_frames, exc_info=True)
     829            log("init_encoder(%s) failed", profile, exc_info=True)
    491830            self.clean()
    492831            raise
    493832        else:
    494833            log("enc_ffmpeg.Encoder.init_context(%s, %s, %s) self=%s", self.width, self.height, self.src_format, self.get_info())
    495834
    496     def init_encoder(self, int b_frames):
     835    def init_encoder(self, profile):
     836        cdef AVDictionary *opts = NULL
     837        cdef AVDictionary *muxer_opts = NULL
     838        global GEN_TO_ENCODER
     839        cdef AVOutputFormat *oformat = get_av_output_format(self.muxer_format)
     840        if oformat==NULL:
     841            raise Exception("libavformat does not support %s" % self.muxer_format)
     842        log("init_encoder() AVOutputFormat(%s)=%#x, flags=%s", self.muxer_format, <unsigned long> oformat, flagscsv(AVFMT, oformat.flags))
     843        if oformat.flags & AVFMT_ALLOW_FLUSH==0:
     844            raise Exception("AVOutputFormat(%s) does not support flushing!" % self.muxer_format)
     845        r = avformat_alloc_output_context2(&self.muxer_ctx, oformat, self.muxer_format, NULL)
     846        if r!=0:
     847            msg = av_error_str(r)
     848            raise Exception("libavformat cannot allocate context: %s" % msg)
     849        log("init_encoder() avformat_alloc_output_context2 returned %i for %s, format context=%#x, flags=%s, ctx_flags=%s", r, self.muxer_format, <unsigned long> self.muxer_ctx,
     850            flagscsv(FMT_FLAGS, self.muxer_ctx.flags), flagscsv(AVFMTCTX, self.muxer_ctx.ctx_flags))
     851        list_options(self.muxer_ctx, self.muxer_ctx.av_class)
     852
     853        cdef int64_t v = 0
     854        movflags = ""
     855        if self.muxer_format=="mp4":
     856            #movflags = "empty_moov+omit_tfhd_offset+frag_keyframe+default_base_moof"
     857            movflags = "empty_moov+frag_keyframe+default_base_moof+faststart"
     858        elif self.muxer_format=="webm":
     859            movflags = "dash+live"
     860        if movflags:
     861            r = av_dict_set(&muxer_opts, "movflags", movflags, 0)
     862            if r!=0:
     863                msg = av_error_str(r)
     864                raise Exception("failed to set %s muxer 'movflags' options '%s': %s" % (self.muxer_format, movflags, msg))
     865
    497866        cdef unsigned long gen = generation.increase()
     867        GEN_TO_ENCODER[gen] = self
     868        self.buffer = <unsigned char*> av_malloc(DEFAULT_BUF_LEN)
     869        if self.buffer==NULL:
     870            raise Exception("failed to allocate %iKB of memory" % (DEFAULT_BUF_LEN//1024))
     871        self.muxer_ctx.pb = avio_alloc_context(self.buffer, DEFAULT_BUF_LEN, 1, <void *> gen, NULL, write_packet, NULL)
     872        if self.muxer_ctx.pb==NULL:
     873            raise Exception("libavformat failed to allocate io context")
     874        log("init_encoder() saving %s stream to bitstream buffer %#x", self.encoding, <unsigned long> self.buffer)
     875        self.muxer_ctx.flush_packets = 1
     876        self.muxer_ctx.bit_rate = 250000
     877        self.muxer_ctx.start_time = 0
     878        #self.muxer_ctx.duration = 999999
     879        self.muxer_ctx.start_time_realtime = int(time.time()*1000)
     880        self.muxer_ctx.strict_std_compliance = 1
    498881
    499         self.codec_ctx = avcodec_alloc_context3(self.codec)
    500         if self.codec_ctx==NULL:
    501             raise Exception("failed to allocate codec context!")
     882        self.video_stream = avformat_new_stream(self.muxer_ctx, NULL)    #self.video_codec
     883        self.video_stream.id = 0
     884        log("init_encoder() video: avformat_new_stream=%#x, nb streams=%i", <unsigned long> self.video_stream, self.muxer_ctx.nb_streams)
    502885
     886        self.video_ctx = avcodec_alloc_context3(self.video_codec)
     887        if self.video_ctx==NULL:
     888            raise Exception("failed to allocate video codec context!")
     889        list_options(self.video_ctx, self.video_ctx.av_class)
     890
     891        cdef int b_frames = 0
    503892        #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
    521         #av_opt_set(c->priv_data, "preset", "slow", 0)
    522         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))
     893        self.video_ctx.framerate.num = 1
     894        self.video_ctx.framerate.den = 25
     895        self.video_ctx.time_base.num = 1
     896        self.video_ctx.time_base.den = 25
     897        self.video_ctx.refcounted_frames = 1
     898        self.video_ctx.max_b_frames = b_frames*1
     899        self.video_ctx.has_b_frames = b_frames
     900        self.video_ctx.delay = 0
     901        self.video_ctx.gop_size = 1
     902        self.video_ctx.width = self.width
     903        self.video_ctx.height = self.height
     904        self.video_ctx.bit_rate = 2500000
     905        self.video_ctx.pix_fmt = self.pix_fmt
     906        self.video_ctx.thread_safe_callbacks = 1
     907        self.video_ctx.thread_type = THREAD_TYPE
     908        self.video_ctx.thread_count = THREAD_COUNT     #0=auto
     909        #if oformat.flags & AVFMT_GLOBALHEADER:
     910        self.video_ctx.flags |= CODEC_FLAG_GLOBAL_HEADER
     911        self.video_ctx.flags2 |= CODEC_FLAG2_FAST   #may cause "no deblock across slices" - which should be fine
     912        if self.encoding.startswith("h264") and profile:
     913            r = av_dict_set(&opts, "vprofile", profile, 0)
     914            log("av_dict_set vprofile=%s returned %i", profile, r)
     915            if r==0:
     916                self.profile = profile
     917            r = av_dict_set(&opts, "tune", "zerolatency", 0)
     918            log("av_dict_set tune=zerolatency returned %i", r)
     919            r = av_dict_set(&opts, "preset","ultrafast", 0)
     920            log("av_dict_set preset=ultrafast returned %i", r)
     921        elif self.encoding.startswith("vp"):
     922            for k,v in {
     923                        "lag-in-frames"     : 0,
     924                        "realtime"          : 1,
     925                        "rc_lookahead"      : 0,
     926                        "error_resilient"   : 0,
     927                        }.items():
     928                r = av_dict_set_int(&opts, k, v, 0)
     929                if r!=0:
     930                    log.error("Error: failed to set video context option '%s' to %i:", k, v)
     931                    log.error(" %s", av_error_str(r))
     932        log("init_encoder() thread-type=%i, thread-count=%i", THREAD_TYPE, THREAD_COUNT)
     933        log("init_encoder() codec flags: %s", flagscsv(CODEC_FLAGS, self.video_ctx.flags))
     934        log("init_encoder() codec flags2: %s", flagscsv(CODEC_FLAGS2, self.video_ctx.flags2))
    525935
    526         r = avcodec_open2(self.codec_ctx, self.codec, NULL)   #NULL, NULL)
     936        r = avcodec_open2(self.video_ctx, self.video_codec, &opts)
     937        av_dict_free(&opts)
    527938        if r!=0:
    528939            raise Exception("could not open %s encoder context: %s" % (self.encoding, av_error_str(r)))
    529940
     941        r = avcodec_parameters_from_context(self.video_stream.codecpar, self.video_ctx)
     942        if r<0:
     943            raise Exception("could not copy video context parameters %#x: %s" % (<unsigned long> self.video_stream.codecpar, av_error_str(r)))
     944
     945        if AUDIO:
     946            self.audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC)
     947            if self.audio_codec==NULL:
     948                raise Exception("cannot find audio codec!")
     949            log("init_encoder() audio_codec=%#x", <unsigned long> self.audio_codec)
     950            self.audio_stream = avformat_new_stream(self.muxer_ctx, NULL)
     951            self.audio_stream.id = 1
     952            log("init_encoder() audio: avformat_new_stream=%#x, nb streams=%i", <unsigned long> self.audio_stream, self.muxer_ctx.nb_streams)
     953            self.audio_ctx = avcodec_alloc_context3(self.audio_codec)
     954            log("init_encoder() audio_context=%#x", <unsigned long> self.audio_ctx)
     955            self.audio_ctx.sample_fmt = AV_SAMPLE_FMT_FLTP
     956            self.audio_ctx.time_base.den = 25
     957            self.audio_ctx.time_base.num = 1
     958            self.audio_ctx.bit_rate = 64000
     959            self.audio_ctx.sample_rate = 44100
     960            self.audio_ctx.channels = 2
     961            #if audio_codec.capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE:
     962            #    pass
     963            #cdef AVDictionary *opts = NULL
     964            #av_dict_set(&opts, "strict", "experimental", 0)
     965            #r = avcodec_open2(audio_ctx, audio_codec, &opts)
     966            #av_dict_free(&opts)
     967            r = avcodec_open2(self.audio_ctx, self.audio_codec, NULL)
     968            if r!=0:
     969                raise Exception("could not open %s encoder context: %s" % (self.encoding, av_error_str(r)))
     970            if r!=0:
     971                raise Exception("could not open %s encoder context: %s" % ("aac", av_error_str(r)))
     972            r = avcodec_parameters_from_context(self.audio_stream.codecpar, self.audio_ctx)
     973            if r<0:
     974                raise Exception("could not copy audio context parameters %#x: %s" % (<unsigned long> self.audio_stream.codecpar, av_error_str(r)))
     975
     976        log("init_encoder() writing %s header", self.muxer_format)
     977        r = avformat_write_header(self.muxer_ctx, &muxer_opts)
     978        av_dict_free(&muxer_opts)
     979        if r!=0:
     980            msg = av_error_str(r)
     981            raise Exception("libavformat failed to write header: %s" % msg)
     982
    530983        self.av_frame = av_frame_alloc()
    531984        if self.av_frame==NULL:
    532985            raise Exception("could not allocate an AVFrame for encoding")
     
    533986        self.frames = 0
    534987
    535988        if SAVE_TO_FILE is not None:
    536             filename = SAVE_TO_FILE+"-"+str(gen)+".%s" % self.encoding
     989            filename = SAVE_TO_FILE+"-"+self.encoding+"-"+str(gen)+".%s" % self.muxer_format
    537990            self.file = open(filename, 'wb')
    538             log.info("saving stream to %s", filename)
     991            log.info("saving %s stream to %s", self.encoding, filename)
    539992
    540993
    541994    def clean(self):
     
    543996            self.clean_encoder()
    544997        except:
    545998            log.error("cleanup failed", exc_info=True)
    546         self.codec = NULL
     999        self.video_codec = NULL
     1000        self.audio_codec = NULL
    5471001        self.pix_fmt = 0
    5481002        self.src_format = ""
    5491003        self.av_frame = NULL                        #should be redundant
     
    5511005        self.width = 0
    5521006        self.height = 0
    5531007        self.encoding = ""
     1008        self.buffers = []
    5541009        f = self.file
    5551010        if f:
    5561011            self.file = None
     
    5621017        if self.av_frame!=NULL:
    5631018            log("clean_encoder() freeing AVFrame: %#x", <unsigned long> self.av_frame)
    5641019            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)
     1020        if self.muxer_ctx!=NULL:
     1021            if self.frames>0:
     1022                log("clean_encoder() writing trailer to stream")
     1023                av_write_trailer(self.muxer_ctx)
     1024                if self.muxer_ctx.pb!=NULL:
     1025                    av_free(self.muxer_ctx.pb)
     1026                    self.muxer_ctx.pb = NULL
     1027            log("clean_encoder() freeing av format context %#x", <unsigned long> self.muxer_ctx)
     1028            avformat_free_context(self.muxer_ctx)
     1029            self.muxer_ctx = NULL
     1030            log("clean_encoder() freeing bitstream buffer %#x", <unsigned long> self.buffer)
     1031            if self.buffer!=NULL:
     1032                av_free(self.buffer)
     1033                self.buffer = NULL
     1034        cdef unsigned long ctx_key          #@DuplicatedSignature
     1035        log("clean_encoder() freeing AVCodecContext: %#x", <unsigned long> self.video_ctx)
     1036        if self.video_ctx!=NULL:
     1037            r = avcodec_close(self.video_ctx)
    5681038            if r!=0:
    569                 log.error("Error: failed to close encoder context %#x", <unsigned long> self.codec_ctx)
     1039                log.error("Error: failed to close video encoder context %#x", <unsigned long> self.video_ctx)
    5701040                log.error(" %s", av_error_str(r))
    571             av_free(self.codec_ctx)
    572             self.codec_ctx = NULL
     1041            av_free(self.video_ctx)
     1042            self.video_ctx = NULL
     1043        if self.audio_ctx!=NULL:
     1044            r = avcodec_close(self.audio_ctx)
     1045            if r!=0:
     1046                log.error("Error: failed to close audio encoder context %#x", <unsigned long> self.audio_ctx)
     1047                log.error(" %s", av_error_str(r))
     1048            av_free(self.audio_ctx)
     1049            self.audio_ctx = NULL
    5731050        log("clean_encoder() done")
    5741051
    5751052    def __repr__(self):                      #@DuplicatedSignature
     
    5811058        info = {
    5821059                "version"   : get_version(),
    5831060                "encoding"  : self.encoding,
     1061                "muxer"     : self.muxer_format,
    5841062                "formats"   : get_input_colorspaces(self.encoding),
    5851063                "type"      : self.get_type(),
    5861064                "frames"    : self.frames,
     
    5871065                "width"     : self.width,
    5881066                "height"    : self.height,
    5891067                }
    590         if self.codec:
    591             info["codec"] = self.codec.name[:]
    592             info["description"] = self.codec.long_name[:]
     1068        if self.video_codec:
     1069            info["video-codec"] = self.video_codec.name[:]
     1070            info["video-description"] = self.video_codec.long_name[:]
     1071        if self.audio_codec:
     1072            info["audio-codec"] = self.audio_codec.name[:]
     1073            info["audio-description"] = self.audio_codec.long_name[:]
    5931074        if self.src_format:
    5941075            info["src_format"] = self.src_format
    5951076        if not self.is_closed():
    596             info["encoder_width"] = self.codec_ctx.width
    597             info["encoder_height"] = self.codec_ctx.height
     1077            info["encoder_width"] = self.video_ctx.width
     1078            info["encoder_height"] = self.video_ctx.height
    5981079        else:
    5991080            info["closed"] = True
    6001081        return info
    6011082
    6021083    def is_closed(self):
    603         return self.codec_ctx==NULL
     1084        return self.video_ctx==NULL
    6041085
    6051086    def __dealloc__(self):                          #@DuplicatedSignature
    6061087        self.clean()
     
    6451126        cdef int ret
    6461127        cdef AVPacket avpkt
    6471128        cdef AVFrame *frame
    648         assert self.codec_ctx!=NULL, "no codec context! (not initialized or already closed)"
    649         assert self.codec!=NULL
     1129        assert self.video_ctx!=NULL, "no codec context! (not initialized or already closed)"
     1130        assert self.video_codec!=NULL, "no video codec!"
    6501131
    6511132        if image:
    6521133            assert image.get_pixel_format()==self.src_format, "invalid input format %s, expected %s" % (image.get_pixel_format, self.src_format)
     
    6721153            self.av_frame.coded_picture_number = self.frames+1
    6731154            self.av_frame.display_picture_number = self.frames+1
    6741155            #if self.frames==0:
    675             #    self.av_frame.pict_type = AV_PICTURE_TYPE_I
     1156            self.av_frame.pict_type = AV_PICTURE_TYPE_I
     1157            #self.av_frame.key_frame = 1
    6761158            #else:
    6771159            #    self.av_frame.pict_type = AV_PICTURE_TYPE_P
    6781160            #self.av_frame.quality = 1
     
    6821164            frame = NULL
    6831165
    6841166        with nogil:
    685             ret = avcodec_send_frame(self.codec_ctx, frame)
     1167            ret = avcodec_send_frame(self.video_ctx, frame)
    6861168        if ret!=0:
    6871169            self.log_av_error(image, ret, options)
    6881170            raise Exception(av_error_str(ret))
     
    6921174        avpkt.data = <uint8_t *> xmemalign(buf_len)
    6931175        avpkt.size = buf_len
    6941176        assert ret==0
    695         bufs = []
    6961177        client_options = {}
    6971178        while ret==0:
    6981179            with nogil:
    699                 ret = avcodec_receive_packet(self.codec_ctx, &avpkt)
    700             if ret==EAGAIN:
    701                 client_options["delayed"] = 1
    702                 log("ffmpeg EAGAIN: delayed picture")
     1180                ret = avcodec_receive_packet(self.video_ctx, &avpkt)
     1181            if ret==-errno.EAGAIN:
     1182                log("ffmpeg avcodec_receive_packet EAGAIN")
    7031183                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)
     1184            if ret!=0:
     1185                if not image:
     1186                    log("avcodec_receive_packet returned error '%s' for flush request", av_error_str(ret))
     1187                else:
     1188                    log("avcodec_receive_packet returned error '%s' for image %s, returning existing buffer", av_error_str(ret), image)
    7061189                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
    7101190            if ret<0:
    7111191                free(avpkt.data)
    7121192                self.log_av_error(image, ret, options)
     
    7201200                free(avpkt.data)
    7211201                self.log_error(image, "packet", options, "av packet is corrupt")
    7221202                raise Exception("av packet is corrupt")
    723             packet_data = avpkt.data[:avpkt.size]
    724             bufs.append(packet_data)
     1203
     1204            avpkt.stream_index = self.video_stream.index
     1205            r = av_write_frame(self.muxer_ctx, &avpkt)
     1206            log("av_write_frame packet returned %i", r)
     1207            if ret<0:
     1208                free(avpkt.data)
     1209                self.log_av_error(image, ret, options)
     1210                raise Exception(av_error_str(ret))
     1211            while True:
     1212                r = av_write_frame(self.muxer_ctx, NULL)
     1213                log("av_write_frame flush returned %i", r)
     1214                if r==1:
     1215                    break
     1216                if ret<0:
     1217                    free(avpkt.data)
     1218                    self.log_av_error(image, ret, options)
     1219                    raise Exception(av_error_str(ret))
    7251220        av_packet_unref(&avpkt)
    7261221        free(avpkt.data)
     1222        if self.frames==0 and self.profile:
     1223            client_options["profile"] = self.profile
     1224            client_options["level"] = "3.0"
     1225        client_options["frame"] = int(self.frames)
     1226        if self.frames==0:
     1227            log("%s client options for first frame: %s", self.encoding, client_options)
    7271228        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)
     1229        data = b"".join(self.buffers)
     1230        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)
     1231        if self.buffers and self.file:
     1232            for x in self.buffers:
     1233                self.file.write(x)
     1234            self.file.flush()
     1235        self.buffers = []
    7321236        return data, client_options
    7331237
    7341238    def flush(self, delayed):
     
    7371241        self.clean()
    7381242        return v
    7391243
     1244    def write_packet(self, unsigned long buf, int buf_size):
     1245        log("write_packet(%#x, %#x)", <unsigned long> buf, buf_size)
     1246        cdef uint8_t *cbuf = <uint8_t*> buf
     1247        buffer = cbuf[:buf_size]
     1248        self.buffers.append(buffer)
     1249        return buf_size
    7401250
     1251
    7411252def selftest(full=False):
    7421253    global CODECS
    7431254    from xpra.codecs.codec_checks import testencoder
     
    7461257        suspend_nonfatal_logging()
    7471258        CODECS = testencoder(encoder, full)
    7481259    finally:
     1260        pass
    7491261        resume_nonfatal_logging()
  • xpra/codecs/loader.py

     
    248248
    249249#note: this is just for defining the order of encodings,
    250250#so we have both core encodings (rgb24/rgb32) and regular encodings (rgb) in here:
    251 PREFERED_ENCODING_ORDER = ["h264", "vp9", "vp8", "mpeg4", "png", "png/P", "png/L", "webp", "rgb", "rgb24", "rgb32", "jpeg", "h265"]
     251PREFERED_ENCODING_ORDER = ["h264", "vp9", "vp8", "mpeg4", "mpeg4+mp4", "h264+mp4", "mpeg4+mp4", "vp8+webm", "vp9+webm", "png", "png/P", "png/L", "webp", "rgb", "rgb24", "rgb32", "jpeg", "h265"]
    252252#encoding order for edges (usually one pixel high or wide):
    253253EDGE_ENCODING_ORDER = ["rgb24", "rgb32", "jpeg", "png", "webp", "png/P", "png/L", "rgb"]
    254254