1# This file is part of Xpra.
2# Copyright (C) 2017-2021 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
5
6#cython: wraparound=False
7
8import os
9import time
10import errno
11import weakref
12import platform
13from xpra.log import Logger
14log = Logger("encoder", "ffmpeg")
15
16from xpra.codecs.codec_constants import video_spec
17from xpra.codecs.libav_common.av_log cimport override_logger, restore_logger, av_error_str #@UnresolvedImport pylint: disable=syntax-error
18from xpra.codecs.libav_common.av_log import SilenceAVWarningsContext  # @UnresolvedImport
19from xpra.util import AtomicInteger, csv, print_nested_dict, reverse_dict, envint, envbool, typedict
20from xpra.os_util import bytestostr, strtobytes, LINUX
21from xpra.buffers.membuf cimport memalign
22
23from libc.string cimport memset #pylint: disable=syntax-error
24from libc.stdint cimport uintptr_t, uint8_t, uint16_t, uint32_t, int64_t, uint64_t
25from libc.stdlib cimport free
26
27SAVE_TO_FILE = os.environ.get("XPRA_SAVE_TO_FILE")
28
29THREAD_TYPE = envint("XPRA_FFMPEG_THREAD_TYPE", 2)
30THREAD_COUNT= envint("XPRA_FFMPEG_THREAD_COUNT")
31AUDIO = envbool("XPRA_FFMPEG_MPEG4_AUDIO", False)
32
33
34cdef extern from "Python.h":
35    int PyObject_GetBuffer(object obj, Py_buffer *view, int flags)
36    void PyBuffer_Release(Py_buffer *view)
37    int PyBUF_ANY_CONTIGUOUS
38
39cdef extern from "libavutil/mem.h":
40    void av_free(void *ptr)
41    void *av_malloc(size_t size)
42
43cdef extern from "libavcodec/version.h":
44    int LIBAVCODEC_VERSION_MAJOR
45    int LIBAVCODEC_VERSION_MINOR
46    int LIBAVCODEC_VERSION_MICRO
47
48#why can't we define this inside the avcodec.h section? (beats me)
49ctypedef unsigned int AVCodecID
50ctypedef long AVPixelFormat
51ctypedef long AVSampleFormat
52ctypedef int AVPictureType
53
54
55cdef extern from "libavutil/avutil.h":
56    int AV_PICTURE_TYPE_NONE
57    int AV_PICTURE_TYPE_I
58    int AV_PICTURE_TYPE_P
59    int AV_PICTURE_TYPE_B
60    int AV_PICTURE_TYPE_S
61    int AV_PICTURE_TYPE_SI
62    int AV_PICTURE_TYPE_SP
63    int AV_PICTURE_TYPE_BI
64
65cdef extern from "libavutil/dict.h":
66    int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
67    int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags)
68    void av_dict_free(AVDictionary **m)
69
70cdef extern from "libavutil/pixfmt.h":
71    AVPixelFormat AV_PIX_FMT_NONE
72    AVPixelFormat AV_PIX_FMT_YUV420P
73    AVPixelFormat AV_PIX_FMT_YUV422P
74    AVPixelFormat AV_PIX_FMT_YUV444P
75    AVPixelFormat AV_PIX_FMT_RGB24
76    AVPixelFormat AV_PIX_FMT_0RGB
77    AVPixelFormat AV_PIX_FMT_BGR0
78    AVPixelFormat AV_PIX_FMT_ARGB
79    AVPixelFormat AV_PIX_FMT_BGRA
80    AVPixelFormat AV_PIX_FMT_GBRP
81    AVPixelFormat AV_PIX_FMT_VAAPI
82    AVPixelFormat AV_PIX_FMT_NV12
83
84cdef extern from "libavutil/samplefmt.h":
85    AVSampleFormat AV_SAMPLE_FMT_S16
86    AVSampleFormat AV_SAMPLE_FMT_FLTP
87
88
89cdef extern from "libavutil/frame.h":
90    int av_frame_get_buffer(AVFrame *frame, int align)
91
92cdef extern from "libavutil/buffer.h":
93    ctypedef struct AVBuffer:
94        pass
95    ctypedef struct AVBufferRef:
96        AVBuffer* buffer
97        uint8_t *data
98        int      size
99    AVBufferRef *av_buffer_ref(AVBufferRef *buf)
100    void av_buffer_unref(AVBufferRef **buf)
101
102cdef extern from "libavformat/avio.h":
103    ctypedef int AVIODataMarkerType
104    int AVIO_FLAG_WRITE
105
106    ctypedef struct AVIOContext:
107        const AVClass *av_class
108        unsigned char *buffer       #Start of the buffer
109        int buffer_size             #Maximum buffer size
110        unsigned char *buf_ptr      #Current position in the buffer
111        unsigned char *buf_end      #End of the data, may be less than
112                                    #buffer+buffer_size if the read function returned
113                                    #less data than requested, e.g. for streams where
114                                    #no more data has been received yet.
115        int64_t     pos             #position in the file of the current buffer
116        int         must_flush      #true if the next seek should flush
117        int         error           #contains the error code or 0 if no error happened
118        int         seekable
119        int64_t     maxsize
120        int         direct
121        int64_t     bytes_read
122
123    AVIOContext *avio_alloc_context(unsigned char *buffer, int buffer_size, int write_flag,
124                  void *opaque,
125                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
126                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
127                  int64_t (*seek)(void *opaque, int64_t offset, int whence))
128
129
130cdef extern from "libavcodec/avcodec.h":
131    int FF_THREAD_SLICE     #allow more than one thread per frame
132    int FF_THREAD_FRAME     #Decode more than one frame at once
133
134    int FF_PROFILE_H264_CONSTRAINED
135    int FF_PROFILE_H264_INTRA
136    int FF_PROFILE_H264_BASELINE
137    int FF_PROFILE_H264_CONSTRAINED_BASELINE
138    int FF_PROFILE_H264_MAIN
139    int FF_PROFILE_H264_EXTENDED
140    int FF_PROFILE_H264_HIGH
141    int FF_PROFILE_H264_HIGH_10
142    int FF_PROFILE_H264_HIGH_10_INTRA
143    int FF_PROFILE_H264_MULTIVIEW_HIGH
144    int FF_PROFILE_H264_HIGH_422
145    int FF_PROFILE_H264_HIGH_422_INTRA
146    int FF_PROFILE_H264_STEREO_HIGH
147    int FF_PROFILE_H264_HIGH_444
148    int FF_PROFILE_H264_HIGH_444_PREDICTIVE
149    int FF_PROFILE_H264_HIGH_444_INTRA
150    int FF_PROFILE_H264_CAVLC_444
151
152    int FF_PROFILE_HEVC_MAIN
153    int FF_PROFILE_HEVC_MAIN_10
154    int FF_PROFILE_HEVC_MAIN_STILL_PICTURE
155    int FF_PROFILE_HEVC_REXT
156
157    int FF_PROFILE_MPEG2_422
158    int FF_PROFILE_MPEG2_HIGH
159    int FF_PROFILE_MPEG2_SS
160    int FF_PROFILE_MPEG2_SNR_SCALABLE
161    int FF_PROFILE_MPEG2_MAIN
162    int FF_PROFILE_MPEG2_SIMPLE
163
164    int AV_CODEC_FLAG_UNALIGNED
165    int AV_CODEC_FLAG_QSCALE
166    int AV_CODEC_FLAG_4MV
167    int AV_CODEC_FLAG_OUTPUT_CORRUPT
168    int AV_CODEC_FLAG_QPEL
169    int AV_CODEC_FLAG_PASS1
170    int AV_CODEC_FLAG_PASS2
171    int AV_CODEC_FLAG_GRAY
172    int AV_CODEC_FLAG_PSNR
173    int AV_CODEC_FLAG_TRUNCATED
174    int AV_CODEC_FLAG_INTERLACED_DCT
175    int AV_CODEC_FLAG_GLOBAL_HEADER
176
177    int AV_CODEC_FLAG2_FAST
178
179    int AV_CODEC_CAP_DRAW_HORIZ_BAND
180    int AV_CODEC_CAP_DR1
181    int AV_CODEC_CAP_TRUNCATED
182    int AV_CODEC_CAP_DELAY
183    int AV_CODEC_CAP_SMALL_LAST_FRAME
184    int AV_CODEC_CAP_SUBFRAMES
185    int AV_CODEC_CAP_EXPERIMENTAL
186    int AV_CODEC_CAP_CHANNEL_CONF
187    int AV_CODEC_CAP_FRAME_THREADS
188    int AV_CODEC_CAP_SLICE_THREADS
189    int AV_CODEC_CAP_PARAM_CHANGE
190    int AV_CODEC_CAP_AUTO_THREADS
191    int AV_CODEC_CAP_VARIABLE_FRAME_SIZE
192    int AV_CODEC_CAP_INTRA_ONLY
193    int AV_CODEC_CAP_LOSSLESS
194
195    ctypedef struct AVFrame:
196        uint8_t     **data
197        int         *linesize
198        int         width
199        int         height
200        int         format
201        int         key_frame
202        int64_t     pts
203        int         coded_picture_number
204        int         display_picture_number
205        int         quality
206        void        *opaque
207        AVPictureType pict_type
208        AVDictionary *metadata
209        AVBufferRef *hw_frames_ctx
210    ctypedef struct AVCodec:
211        int         capabilities
212        const char  *name
213        const char  *long_name
214    ctypedef struct AVDictionary:
215        pass
216    int AV_PKT_FLAG_KEY
217    int AV_PKT_FLAG_CORRUPT
218    ctypedef struct AVPacket:
219        int64_t pts
220        int64_t dts
221        uint8_t *data
222        int     size
223        int     stream_index
224        int     flags
225        int64_t duration
226        int64_t pos
227
228    ctypedef struct AVRational:
229        int num
230        int den
231
232    ctypedef int AVMediaType
233    ctypedef struct AVCodecContext:
234        const AVClass *av_class
235        int log_level_offset
236        AVMediaType codec_type
237        AVCodec *codec
238        AVCodecID codec_id
239        unsigned int codec_tag
240        void *priv_data
241        void *internal
242        void *opaque
243        int64_t bit_rate
244        int bit_rate_tolerance
245        int global_quality
246        int compression_level
247        int flags
248        int flags2
249        uint8_t *extradata
250        int extradata_size
251        AVRational time_base
252        int ticks_per_frame
253        int delay
254        int width
255        int height
256        int coded_width
257        int coded_height
258        int gop_size
259        AVPixelFormat pix_fmt
260        #some functions omitted here
261        int max_b_frames
262        float b_quant_factor
263        float b_quant_offset
264        int has_b_frames
265        int i_quant_factor
266        float i_quant_offset
267        float lumi_masking
268        float temporal_cplx_masking
269        float spatial_cplx_masking
270        float p_masking
271        float dark_masking
272        int slice_count
273        int slice_offset
274        AVRational sample_aspect_ratio
275        int me_cmp
276        int me_sub_cmp
277        int mb_cmp
278        int ildct_cmp
279        int dia_size
280        int last_predictor_count
281        int me_pre_cmp
282        int pre_dia_size
283        int me_subpel_quality
284        int me_range
285        int slice_flags
286        int mb_decision
287        uint16_t *intra_matrix
288        uint16_t *inter_matrix
289        int intra_dc_precision
290        int skip_top
291        int skip_bottom
292        int mb_lmin
293        int mb_lmax
294        int bidir_refine
295        int keyint_mint
296        int refs
297        int mv0_threshold
298        #skipped: AVColor*
299        int slices
300        int sample_rate
301        int channels
302        AVSampleFormat sample_fmt
303        int frame_size
304        int frame_number
305        int block_align
306        int cutoff
307        uint64_t channel_layout
308        uint64_t request_channel_layout
309        #skippped: AVAudioServiceType
310        #        AVSampleFormat
311        int refcounted_frames
312        float qcompress
313        float qblur
314        int qmin
315        int qmax
316        int max_qdiff
317        int rc_buffer_size
318        int rc_override_count
319        int64_t rc_max_rate
320        int64_t rc_min_rate
321        float rc_max_available_vbv_use
322        float rc_min_vbv_overflow_use
323        int rc_initial_buffer_occupancy
324        int strict_std_compliance
325        int error_concealment
326        int debug
327        int debug_mv
328        int err_recognition
329        int thread_count
330        int thread_type
331        int active_thread_type
332        #int thread_safe_callbacks
333        int profile
334        int level
335        AVRational framerate
336        AVBufferRef *hw_frames_ctx
337
338    ctypedef struct AVFormatContext:
339        const AVClass   *av_class
340        AVOutputFormat  *oformat
341        void            *priv_data
342        AVIOContext     *pb
343        int             ctx_flags
344        unsigned int    nb_streams
345        AVStream        **streams
346        int64_t         start_time
347        int64_t         duration
348        int             bit_rate
349        unsigned int    packet_size
350        int             max_delay
351        int             flags
352        unsigned int    probesize
353        int             max_analyze_duration
354        AVCodecID       video_codec_id
355        AVCodecID       audio_codec_id
356        AVCodecID       subtitle_codec_id
357        unsigned int    max_index_size
358        unsigned int    max_picture_buffer
359        unsigned int    nb_chapters
360        AVDictionary    *metadata
361        int64_t         start_time_realtime
362        int             strict_std_compliance
363        int flush_packets
364
365    ctypedef int AVFieldOrder
366    ctypedef int AVColorRange
367    ctypedef int AVColorPrimaries
368    ctypedef int AVColorTransferCharacteristic
369    ctypedef int AVColorSpace
370    ctypedef int AVChromaLocation
371    ctypedef struct AVCodecParameters:
372        AVCodecID       codec_id
373        uint32_t        codec_tag
374        int64_t         bit_rate
375        int             bits_per_coded_sample
376        int             bits_per_raw_sample
377        int             profile
378        int             level
379        int             width
380        int             height
381        AVFieldOrder    field_order
382        AVColorRange    color_range
383        AVColorPrimaries    color_primaries
384        AVColorTransferCharacteristic color_trc
385        AVColorSpace        color_space
386        AVChromaLocation    chroma_location
387        int             sample_rate
388        int             frame_size
389
390    AVCodecID AV_CODEC_ID_H264
391    AVCodecID AV_CODEC_ID_H265
392    AVCodecID AV_CODEC_ID_VP8
393    AVCodecID AV_CODEC_ID_VP9
394    AVCodecID AV_CODEC_ID_MPEG4
395    AVCodecID AV_CODEC_ID_MPEG1VIDEO
396    AVCodecID AV_CODEC_ID_MPEG2VIDEO
397
398    AVCodecID AV_CODEC_ID_AAC
399
400    #init and free:
401    AVCodec *avcodec_find_encoder(AVCodecID id)
402    AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)
403    int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
404    int avcodec_send_frame(AVCodecContext *avctx,const AVFrame *frame) nogil
405    int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) nogil
406
407    AVFrame* av_frame_alloc()
408    void av_frame_free(AVFrame **frame)
409    int avcodec_close(AVCodecContext *avctx)
410    void av_frame_unref(AVFrame *frame) nogil
411
412    AVPacket *av_packet_alloc() nogil
413    void av_packet_free(AVPacket **avpkt)
414
415    void avcodec_free_context(AVCodecContext **avctx)
416    const AVCodec *avcodec_find_encoder_by_name(const char *name)
417
418ctypedef int AVOptionType
419cdef extern from "libavutil/opt.h":
420    AVOptionType AV_OPT_TYPE_FLAGS
421    AVOptionType AV_OPT_TYPE_INT
422    AVOptionType AV_OPT_TYPE_INT64
423    AVOptionType AV_OPT_TYPE_DOUBLE
424    AVOptionType AV_OPT_TYPE_FLOAT
425    AVOptionType AV_OPT_TYPE_STRING
426    AVOptionType AV_OPT_TYPE_RATIONAL
427    AVOptionType AV_OPT_TYPE_BINARY         #offset must point to a pointer immediately followed by an int for the length
428    AVOptionType AV_OPT_TYPE_DICT
429    AVOptionType AV_OPT_TYPE_CONST
430    AVOptionType AV_OPT_TYPE_IMAGE_SIZE
431    AVOptionType AV_OPT_TYPE_PIXEL_FMT
432    AVOptionType AV_OPT_TYPE_SAMPLE_FMT
433    AVOptionType AV_OPT_TYPE_VIDEO_RATE
434    AVOptionType AV_OPT_TYPE_DURATION
435    AVOptionType AV_OPT_TYPE_COLOR
436    AVOptionType AV_OPT_TYPE_CHANNEL_LAYOUT
437    AVOptionType AV_OPT_TYPE_BOOL
438
439    int AV_OPT_SEARCH_CHILDREN
440    int AV_OPT_SEARCH_FAKE_OBJ
441
442    ctypedef struct AVOption:
443        const char *name        #short English help text
444        const char *help
445        int offset              #The offset relative to the context structure where the option value is stored. It should be 0 for named constants.
446        AVOptionType type
447        int flags
448        const char *unit
449
450    const AVOption* av_opt_next(void *obj, const AVOption *prev)
451    void *av_opt_child_next(void *obj, void *prev)
452    int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
453    int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
454    int av_opt_get_int(void *obj, const char *name, int search_flags, int64_t *out_val)
455    int av_opt_set_dict(void *obj, AVDictionary **options)
456
457
458cdef extern from "libavutil/log.h":
459    ctypedef struct AVClass:
460        const char  *class_name                 #The name of the class; usually it is the same name as the context structure type to which the AVClass is associated.
461        const char  *(*item_name)(void *ctx)    #A pointer to a function which returns the name of a context instance ctx associated with the class.
462        AVOption    *option                     #a pointer to the first option specified in the class if any or NULL
463        int         version                     #LIBAVUTIL_VERSION with which this structure was created
464        int         log_level_offset_offset     #Offset in the structure where log_level_offset is stored
465        int         parent_log_context_offset   #Offset in the structure where a pointer to the parent context for logging is stored
466        void        *(*child_next)(void *obj, void *prev)  #Return next AVOptions-enabled child or NULL
467        AVClass     *(*child_class_next)(const AVClass *prev) #Return an AVClass corresponding to the next potential AVOptions-enabled child.
468        #AVClassCategory category                #Category used for visualization (like color) This is only set if the category is equal for all objects using this class.
469        #AVClassCategory (*get_category)(void *ctx)
470
471
472cdef extern from "libavformat/avformat.h":
473    int AVFMTCTX_NOHEADER           #signal that no header is present
474
475    int AVFMT_FLAG_GENPTS           #Generate missing pts even if it requires parsing future frames
476    int AVFMT_FLAG_IGNIDX           #Ignore index
477    int AVFMT_FLAG_NONBLOCK         #Do not block when reading packets from input
478    int AVFMT_FLAG_IGNDTS           #Ignore DTS on frames that contain both DTS & PTS
479    int AVFMT_FLAG_NOFILLIN         #Do not infer any values from other values, just return what is stored in the container
480    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
481    int AVFMT_FLAG_NOBUFFER         #Do not buffer frames when possible
482    int AVFMT_FLAG_CUSTOM_IO        #The caller has supplied a custom AVIOContext, don't avio_close() it
483    int AVFMT_FLAG_DISCARD_CORRUPT  #Discard frames marked corrupted
484    int AVFMT_FLAG_FLUSH_PACKETS    #Flush the AVIOContext every packet
485    int AVFMT_FLAG_BITEXACT
486    int AVFMT_FLAG_SORT_DTS         #try to interleave outputted packets by dts (using this flag can slow demuxing down)
487    int AVFMT_FLAG_PRIV_OPT         #Enable use of private options by delaying codec open (this could be made default once all code is converted)
488    int AVFMT_FLAG_FAST_SEEK        #Enable fast, but inaccurate seeks for some formats
489
490    int AVFMT_NOFILE                #Demuxer will use avio_open, no opened file should be provided by the caller
491    int AVFMT_NEEDNUMBER            #Needs '%d' in filename
492    int AVFMT_SHOW_IDS              #Show format stream IDs numbers
493    int AVFMT_GLOBALHEADER          #Format wants global header
494    int AVFMT_NOTIMESTAMPS          #Format does not need / have any timestamps
495    int AVFMT_GENERIC_INDEX         #Use generic index building code
496    int AVFMT_TS_DISCONT            #Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps
497    int AVFMT_VARIABLE_FPS          #Format allows variable fps
498    int AVFMT_NODIMENSIONS          #Format does not need width/height
499    int AVFMT_NOSTREAMS             #Format does not require any streams
500    int AVFMT_NOBINSEARCH           #Format does not allow to fall back on binary search via read_timestamp
501    int AVFMT_NOGENSEARCH           #Format does not allow to fall back on generic search
502    int AVFMT_NO_BYTE_SEEK          #Format does not allow seeking by bytes
503    int AVFMT_ALLOW_FLUSH           #Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function
504    int AVFMT_TS_NONSTRICT          #Format does not require strictly increasing timestamps, but they must still be monotonic
505    int AVFMT_TS_NEGATIVE           #Format allows muxing negative timestamps.
506    int AVFMT_SEEK_TO_PTS           #Seeking is based on PTS
507
508    ctypedef struct AVStream:
509        int         index           #stream index in AVFormatContext
510        int         id
511        AVCodecContext *codec
512        AVRational  time_base
513        int64_t     start_time
514        int64_t     duration
515        int64_t     nb_frames       #number of frames in this stream if known or 0
516        #AVDiscard   discard         #Selects which packets can be discarded at will and do not need to be demuxed.
517        AVRational  avg_frame_rate
518        AVCodecParameters *codecpar
519
520    ctypedef struct AVOutputFormat:
521        const char  *name
522        const char  *long_name
523        const char  *mime_type
524        const char  *extensions
525        AVCodecID   audio_codec
526        AVCodecID   video_codec
527        AVCodecID   subtitle_codec
528        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...
529        int         (*query_codec)(AVCodecID id, int std_compliance)
530
531    const AVOutputFormat *av_muxer_iterate(void **opaque)
532    int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename)
533    void avformat_free_context(AVFormatContext *s)
534
535    int avcodec_parameters_from_context(AVCodecParameters *par, const AVCodecContext *codec)
536    AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
537    int avformat_write_header(AVFormatContext *s, AVDictionary **options)
538    int av_write_trailer(AVFormatContext *s)
539    int av_write_frame(AVFormatContext *s, AVPacket *pkt)
540    int av_frame_make_writable(AVFrame *frame)
541
542
543cdef extern from "libavutil/hwcontext.h":
544    ctypedef int AVHWDeviceType
545    int AV_HWDEVICE_TYPE_NONE
546    int AV_HWDEVICE_TYPE_VDPAU
547    int AV_HWDEVICE_TYPE_CUDA
548    int AV_HWDEVICE_TYPE_VAAPI
549    int AV_HWDEVICE_TYPE_DXVA2
550    int AV_HWDEVICE_TYPE_QSV
551    int AV_HWDEVICE_TYPE_VIDEOTOOLBOX
552    int AV_HWDEVICE_TYPE_D3D11VA
553    int AV_HWDEVICE_TYPE_DRM
554    int AV_HWDEVICE_TYPE_OPENCL
555    int AV_HWDEVICE_TYPE_MEDIACODEC
556
557    ctypedef struct AVHWDeviceContext:
558        const AVClass av_class
559        #AVHWDeviceType type
560
561    ctypedef struct AVHWFramesContext:
562        const AVClass av_class
563        AVBufferRef device_ref
564        AVHWDeviceContext *device_ctx
565        void *hwctx
566        void *user_opaque
567        int initial_pool_size
568        AVPixelFormat format
569        AVPixelFormat sw_format
570        int width
571        int height
572
573    AVBufferRef *av_hwframe_ctx_alloc(AVBufferRef *device_ctx)
574    int av_hwframe_ctx_init(AVBufferRef *ref)
575    int av_hwdevice_ctx_create(AVBufferRef **device_ctx, AVHWDeviceType type,
576                               const char *device, AVDictionary *opts, int flags)
577    int av_hwframe_get_buffer(AVBufferRef *hwframe_ctx, AVFrame *frame, int flags)
578    int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags)
579
580
581H264_PROFILE_NAMES = {
582    FF_PROFILE_H264_CONSTRAINED             : "constrained",
583    FF_PROFILE_H264_INTRA                   : "intra",
584    FF_PROFILE_H264_BASELINE                : "baseline",
585    FF_PROFILE_H264_CONSTRAINED_BASELINE    : "constrained baseline",
586    FF_PROFILE_H264_MAIN                    : "main",
587    FF_PROFILE_H264_EXTENDED                : "extended",
588    FF_PROFILE_H264_HIGH                    : "high",
589    FF_PROFILE_H264_HIGH_10                 : "high10",
590    FF_PROFILE_H264_HIGH_10_INTRA           : "high10 intra",
591    FF_PROFILE_H264_MULTIVIEW_HIGH          : "multiview high",
592    FF_PROFILE_H264_HIGH_422                : "high422",
593    FF_PROFILE_H264_HIGH_422_INTRA          : "high422 intra",
594    FF_PROFILE_H264_STEREO_HIGH             : "stereo high",
595    FF_PROFILE_H264_HIGH_444                : "high444",
596    FF_PROFILE_H264_HIGH_444_PREDICTIVE     : "high444 predictive",
597    FF_PROFILE_H264_HIGH_444_INTRA          : "high444 intra",
598    FF_PROFILE_H264_CAVLC_444               : "cavlc 444",
599    }
600H264_PROFILES = reverse_dict(H264_PROFILE_NAMES)
601
602HEVC_PROFILE_NAMES = {
603    FF_PROFILE_HEVC_MAIN                : "main",
604    FF_PROFILE_HEVC_MAIN_10             : "main10",
605    FF_PROFILE_HEVC_MAIN_STILL_PICTURE  : "picture",
606    FF_PROFILE_HEVC_REXT                : "rext",
607    }
608HEVC_PROFILES = reverse_dict(HEVC_PROFILE_NAMES)
609
610MPEG2_PROFILE_NAMES = {
611    FF_PROFILE_MPEG2_422            : "422",
612    FF_PROFILE_MPEG2_HIGH           : "high",
613    FF_PROFILE_MPEG2_SS             : "ss",
614    FF_PROFILE_MPEG2_SNR_SCALABLE   : "snr-scalable",
615    FF_PROFILE_MPEG2_MAIN           : "main",
616    FF_PROFILE_MPEG2_SIMPLE         : "simple",
617    }
618MPEG2_PROFILES = reverse_dict(MPEG2_PROFILE_NAMES)
619
620PROFILES = {
621    "h264"  : H264_PROFILES,
622    "hevc"  : HEVC_PROFILES,
623    "mpeg2" : MPEG2_PROFILES,
624    }
625
626DEFAULT_PROFILE = {
627    "h264"  : "main",
628    "hevc"  : "main",
629    "mpeg"  : "main",
630    }
631
632
633AV_OPT_TYPES = {
634    AV_OPT_TYPE_FLAGS       : "FLAGS",
635    AV_OPT_TYPE_INT         : "INT",
636    AV_OPT_TYPE_INT64       : "INT64",
637    AV_OPT_TYPE_DOUBLE      : "DOUBLE",
638    AV_OPT_TYPE_FLOAT       : "FLOAT",
639    AV_OPT_TYPE_STRING      : "STRING",
640    AV_OPT_TYPE_RATIONAL    : "RATIONAL",
641    AV_OPT_TYPE_BINARY      : "BINARY",
642    AV_OPT_TYPE_DICT        : "DICT",
643    AV_OPT_TYPE_CONST       : "CONST",
644    AV_OPT_TYPE_IMAGE_SIZE  : "IMAGE_SIZE",
645    AV_OPT_TYPE_PIXEL_FMT   : "PIXEL_FMT",
646    AV_OPT_TYPE_SAMPLE_FMT  : "SAMPLE_FMT",
647    AV_OPT_TYPE_VIDEO_RATE  : "VIDEO_RATE",
648    AV_OPT_TYPE_DURATION    : "DURATION",
649    AV_OPT_TYPE_COLOR       : "COLOR",
650    AV_OPT_TYPE_CHANNEL_LAYOUT : "CHANNEL_LAYOUT",
651    AV_OPT_TYPE_BOOL        : "BOOL",
652    }
653
654
655PKT_FLAGS = {
656    AV_PKT_FLAG_KEY        : "KEY",
657    AV_PKT_FLAG_CORRUPT    : "CORRUPT",
658    }
659
660AVFMTCTX = {
661    AVFMTCTX_NOHEADER       : "NOHEADER",
662    }
663
664CODEC_FLAGS = {
665    AV_CODEC_FLAG_UNALIGNED          : "UNALIGNED",
666    AV_CODEC_FLAG_QSCALE             : "QSCALE",
667    AV_CODEC_FLAG_4MV                : "4MV",
668    AV_CODEC_FLAG_OUTPUT_CORRUPT     : "OUTPUT_CORRUPT",
669    AV_CODEC_FLAG_QPEL               : "QPEL",
670    AV_CODEC_FLAG_PASS1              : "PASS1",
671    AV_CODEC_FLAG_PASS2              : "PASS2",
672    AV_CODEC_FLAG_GRAY               : "GRAY",
673    AV_CODEC_FLAG_PSNR               : "PSNR",
674    AV_CODEC_FLAG_TRUNCATED          : "TRUNCATED",
675    AV_CODEC_FLAG_INTERLACED_DCT     : "INTERLACED_DCT",
676    AV_CODEC_FLAG_GLOBAL_HEADER      : "GLOBAL_HEADER",
677    }
678
679CODEC_FLAGS2 = {
680    AV_CODEC_FLAG2_FAST : "FAST",
681    }
682
683FMT_FLAGS = {
684    AVFMT_FLAG_GENPTS          : "GENPTS",
685    AVFMT_FLAG_IGNIDX          : "IGNIDX",
686    AVFMT_FLAG_NONBLOCK        : "NONBLOCK",
687    AVFMT_FLAG_IGNDTS          : "IGNDTS",
688    AVFMT_FLAG_NOFILLIN        : "NOFILLIN",
689    AVFMT_FLAG_NOPARSE         : "NOPARSE",
690    AVFMT_FLAG_NOBUFFER        : "NOBUFFER",
691    AVFMT_FLAG_CUSTOM_IO       : "CUSTOM_IO",
692    AVFMT_FLAG_DISCARD_CORRUPT : "DISCARD_CORRUPT",
693    AVFMT_FLAG_FLUSH_PACKETS   : "FLUSH_PACKETS",
694    AVFMT_FLAG_BITEXACT        : "BITEXACT",
695    AVFMT_FLAG_SORT_DTS        : "SORT_DTS",
696    AVFMT_FLAG_PRIV_OPT        : "PRIV_OPT",
697    AVFMT_FLAG_FAST_SEEK       : "FAST_SEEK",
698    }
699
700AVFMT = {
701    AVFMT_NOFILE           : "NOFILE",
702    AVFMT_NEEDNUMBER       : "NEEDNUMBER",
703    AVFMT_SHOW_IDS         : "SHOW_IDS",
704    AVFMT_GLOBALHEADER     : "GLOBALHEADER",
705    AVFMT_NOTIMESTAMPS     : "NOTIMESTAMPS",
706    AVFMT_GENERIC_INDEX    : "GENERIC_INDEX",
707    AVFMT_TS_DISCONT       : "TS_DISCONT",
708    AVFMT_VARIABLE_FPS     : "VARIABLE_FPS",
709    AVFMT_NODIMENSIONS     : "NODIMENSIONS",
710    AVFMT_NOSTREAMS        : "NOSTREAMS",
711    AVFMT_NOBINSEARCH      : "NOBINSEARCH",
712    AVFMT_NOGENSEARCH      : "NOGENSEARCH",
713    AVFMT_NO_BYTE_SEEK     : "NO_BYTE_SEEK",
714    AVFMT_ALLOW_FLUSH      : "ALLOW_FLUSH",
715    AVFMT_TS_NONSTRICT     : "TS_NONSTRICT",
716    AVFMT_TS_NEGATIVE      : "TS_NEGATIVE",
717    AVFMT_SEEK_TO_PTS      : "SEEK_TO_PTS",
718    }
719
720
721CAPS = {
722    AV_CODEC_CAP_DRAW_HORIZ_BAND        : "DRAW_HORIZ_BAND",
723    AV_CODEC_CAP_DR1                    : "DR1",
724    AV_CODEC_CAP_TRUNCATED              : "TRUNCATED",
725    AV_CODEC_CAP_DELAY                  : "DELAY",
726    AV_CODEC_CAP_SMALL_LAST_FRAME       : "SMALL_LAST_FRAME",
727    AV_CODEC_CAP_SUBFRAMES              : "SUBFRAMES",
728    AV_CODEC_CAP_EXPERIMENTAL           : "EXPERIMENTAL",
729    AV_CODEC_CAP_CHANNEL_CONF           : "CHANNEL_CONF",
730    AV_CODEC_CAP_FRAME_THREADS          : "FRAME_THREADS",
731    AV_CODEC_CAP_SLICE_THREADS          : "SLICE_THREADS",
732    AV_CODEC_CAP_PARAM_CHANGE           : "PARAM_CHANGE",
733    AV_CODEC_CAP_AUTO_THREADS           : "AUTO_THREADS",
734    AV_CODEC_CAP_VARIABLE_FRAME_SIZE    : "VARIABLE_FRAME_SIZE",
735    AV_CODEC_CAP_INTRA_ONLY             : "INTRA_ONLY",
736    AV_CODEC_CAP_LOSSLESS               : "LOSSLESS",
737    }
738log("CODEC_CAP:")
739print_nested_dict(dict((hex(abs(k)),v) for k,v in CAPS.items()), print_fn=log.debug)
740
741PICTURE_TYPE = {
742    AV_PICTURE_TYPE_NONE    : "NONE",
743    AV_PICTURE_TYPE_I       : "I",
744    AV_PICTURE_TYPE_P       : "P",
745    AV_PICTURE_TYPE_B       : "B",
746    AV_PICTURE_TYPE_S       : "S",
747    AV_PICTURE_TYPE_SI      : "SI",
748    AV_PICTURE_TYPE_SP      : "SP",
749    AV_PICTURE_TYPE_BI      : "BI",
750    }
751log("AV_PICTURE:")
752print_nested_dict(PICTURE_TYPE, print_fn=log.debug)
753
754FORMAT_TO_ENUM = {
755    "YUV420P"   : AV_PIX_FMT_YUV420P,
756    "YUV422P"   : AV_PIX_FMT_YUV422P,
757    "YUV444P"   : AV_PIX_FMT_YUV444P,
758    "RGB"       : AV_PIX_FMT_RGB24,
759    "XRGB"      : AV_PIX_FMT_0RGB,
760    "BGRX"      : AV_PIX_FMT_BGR0,
761    "ARGB"      : AV_PIX_FMT_ARGB,
762    "BGRA"      : AV_PIX_FMT_BGRA,
763    "GBRP"      : AV_PIX_FMT_GBRP,
764    "VAAPI"     : AV_PIX_FMT_VAAPI,
765    "NV12"      : AV_PIX_FMT_NV12,
766    }
767
768CODEC_ID = {
769    "h264"      : AV_CODEC_ID_H264,
770    "h265"      : AV_CODEC_ID_H265,
771    "vp8"       : AV_CODEC_ID_VP8,
772    "vp9"       : AV_CODEC_ID_VP9,
773    "mpeg4"     : AV_CODEC_ID_MPEG4,
774    "mpeg1"     : AV_CODEC_ID_MPEG1VIDEO,
775    "mpeg2"     : AV_CODEC_ID_MPEG2VIDEO,
776    }
777
778COLORSPACES = FORMAT_TO_ENUM.keys()
779ENUM_TO_FORMAT = {}
780for pix_fmt, av_enum in FORMAT_TO_ENUM.items():
781    ENUM_TO_FORMAT[av_enum] = pix_fmt
782log("AV_PIX_FMT:")
783print_nested_dict(ENUM_TO_FORMAT, print_fn=log.debug)
784
785def flagscsv(flag_dict, value=0):
786    return csv([bytestostr(v) for k,v in flag_dict.items() if k&value])
787
788
789def get_muxer_formats():
790    cdef AVOutputFormat *fmt
791    cdef void *opaque = NULL
792    formats = {}
793    while True:
794        fmt = <AVOutputFormat*> av_muxer_iterate(&opaque)
795        if fmt==NULL:
796            break
797        name = bytestostr(fmt.name)
798        long_name = bytestostr(fmt.long_name)
799        formats[name] = bytestostr(long_name)
800    return formats
801log("AV Output Formats:")
802print_nested_dict(get_muxer_formats(), print_fn=log.debug)
803
804cdef AVOutputFormat* get_av_output_format(name):
805    cdef AVOutputFormat *fmt
806    cdef void *opaque = NULL
807    while True:
808        fmt = <AVOutputFormat*> av_muxer_iterate(&opaque)
809        if fmt==NULL:
810            break
811        if bytestostr(name)==bytestostr(fmt.name):
812            return fmt
813    return NULL
814
815
816def get_version():
817    return (LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO)
818
819
820VAAPI = envbool("XPRA_VAAPI", LINUX and (LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR)>=(4, 4) and platform.release()>="5.")
821
822CODECS = ()
823
824DEF DEFAULT_BUF_LEN = 64*1024
825
826
827VAAPI_CODECS = []
828
829ENCODER_NAMES = {
830    "h264"  : b"libx264",
831    "vp8"   : b"libvpx",
832    "vp9"   : b"libvpx",
833    "mpeg1" : b"mpeg1video",
834    "mpeg2" : b"mpeg2video",
835    }
836
837
838def init_module():
839    global CODECS
840    log("enc_ffmpeg.init_module()")
841    override_logger()
842    all_codecs = []
843    for codec_id, codecs in {
844        AV_CODEC_ID_H264    : ("h264+mp4", ), #"h264"
845        #AV_CODEC_ID_VP8     : ("vp8", "vp8+webm"),
846        #AV_CODEC_ID_VP9     : ("vp9", "vp9+webm"),
847        #AV_CODEC_ID_H265    : ("h265"),
848        AV_CODEC_ID_MPEG4   : ("mpeg4+mp4", ),
849        AV_CODEC_ID_MPEG1VIDEO : ("mpeg1", ),
850        AV_CODEC_ID_MPEG2VIDEO : ("mpeg2", )
851        }.items():
852        if avcodec_find_encoder(codec_id):
853            all_codecs += codecs
854        else:
855            log("cannot find encoder %i for %s", codec_id, csv(codecs))
856    log("enc_ffmpeg non vaapi CODECS=%s", csv(all_codecs))
857    if VAAPI and LINUX:
858        with SilenceAVWarningsContext():
859            try:
860                vaapi_codecs = init_vaapi()
861                for c in vaapi_codecs:
862                    if c not in all_codecs:
863                        all_codecs.append(c)
864            except Exception as e:
865                log("no vappi support", exc_info=True)
866                log.warn("Warning: no VAAPI support")
867                log.warn(" %s", e)
868    CODECS = tuple(all_codecs)
869
870VAAPI_ENCODINGS = os.environ.get("XPRA_VAAPI_ENCODINGS", "h264,hevc,mpeg2,vp8,vp9").split(",")
871
872def init_vaapi():
873    global VAAPI_CODECS
874    #can we find a device:
875    cdef AVBufferRef *hw_device_ctx = init_vaapi_device()
876    cdef AVCodecContext *avctx
877    cdef const AVCodec *codec
878    cdef AVFrame *sw_frame
879    cdef AVFrame *hw_frame
880    cdef int WIDTH = 640
881    cdef int HEIGHT = 480
882    for c in VAAPI_ENCODINGS:
883        if c in VAAPI_CODECS:
884            continue
885        name = ("%s_vaapi" % c).encode("latin1")
886        codec = avcodec_find_encoder_by_name(name)
887        log("testing %s_vaapi=%#x", c, <uintptr_t> codec)
888        if not codec:
889            continue
890        #now verify we can use this codec with the device selected:
891        avctx = avcodec_alloc_context3(codec)
892        if avctx==NULL:
893            log.error("Error: failed to allocate codec context")
894            break
895        log("%s_vaapi options:", c)
896        list_options(avctx, avctx.av_class)
897        avctx.width     = WIDTH
898        avctx.height    = HEIGHT
899        avctx.global_quality = 20
900        avctx.framerate.num = 25
901        avctx.framerate.den = 1
902        avctx.time_base.num = 1
903        avctx.time_base.den = 25
904        avctx.sample_aspect_ratio.num = 1
905        avctx.sample_aspect_ratio.den = 1
906        avctx.pix_fmt = AV_PIX_FMT_VAAPI
907        #test encode a frame:
908        try:
909            err = set_hwframe_ctx(avctx, hw_device_ctx, WIDTH, HEIGHT)
910            log("set_hwframe_ctx(%#x, %#x, %i, %i)=%i", <uintptr_t> avctx, <uintptr_t> hw_device_ctx, WIDTH, HEIGHT, err)
911            if err<0:
912                log("failed to set hwframe context")
913                log.warn(" %s", av_error_str(err))
914                continue
915            err = avcodec_open2(avctx, codec, NULL)
916            log("avcodec_open2(%#x, %i, NULL)=%i", <uintptr_t> avctx, <uintptr_t> codec, err)
917            if err<0:
918                log("failed to open video encoder codec")
919                log(" %i: %s", err, av_error_str(err))
920                continue
921            sw_frame = av_frame_alloc()
922            log("av_frame_alloc()=%#x", <uintptr_t> sw_frame)
923            if sw_frame==NULL:
924                log.error("Error: failed to allocate a sw frame")
925                break
926            sw_frame.width  = WIDTH
927            sw_frame.height = HEIGHT
928            sw_frame.format = AV_PIX_FMT_NV12
929            err = av_frame_get_buffer(sw_frame, 32)
930            log("av_frame_get_buffer(%#x, 32)=%i", <uintptr_t> sw_frame, err)
931            if err<0:
932                log.error("Error: failed to allocate sw buffer for a frame")
933                break
934            #TODO: put some real data in the frame:
935            for i in range(min(WIDTH, HEIGHT)//2):
936                (<uint8_t*> sw_frame.data[0])[i**2] = 255
937                (<uint8_t*> sw_frame.data[1])[i**2] = 255
938            #hardware side:
939            hw_frame = av_frame_alloc()
940            log("av_frame_alloc()=%#x", <uintptr_t> hw_frame)
941            if hw_frame==NULL:
942                log.error("Error: failed to allocate a hw frame")
943                break
944            err = av_hwframe_get_buffer(avctx.hw_frames_ctx, hw_frame, 0)
945            log("av_frame_get_buffer(%#x, %#x, 0)=%i", <uintptr_t> avctx.hw_frames_ctx, <uintptr_t> hw_frame, err)
946            if err<0 or hw_frame.hw_frames_ctx==NULL:
947                log.error("Error: failed to allocate a hw buffer")
948                log.error(" %s", av_error_str(err))
949                continue
950            err = av_hwframe_transfer_data(hw_frame, sw_frame, 0)
951            log("av_hwframe_transfer_data(%#x, %#x, 0)=%i", <uintptr_t> hw_frame, <uintptr_t> sw_frame, err)
952            if err<0:
953                log.error("Error: failed to transfer frame data to surface")
954                log.error(" %s", av_error_str(err))
955                continue
956            data = encode_frame(avctx, hw_frame)
957            log("encode_frame(%#x, %#x)=%i buffers", <uintptr_t> avctx, <uintptr_t> hw_frame, len(data))
958            flushed = encode_frame(avctx, NULL)
959            log("encode_frame(%#x, NULL)=%i buffers", <uintptr_t> avctx, len(flushed))
960            if not data and not flushed:
961                log("no data")
962                continue
963            if flushed:
964                data += flushed
965            log("compressed data buffer lengths: %s", csv([len(x) for x in data]))
966        finally:
967            av_frame_free(&hw_frame)
968            av_frame_free(&sw_frame)
969            avcodec_free_context(&avctx)
970        VAAPI_CODECS.append(c)
971    av_buffer_unref(&hw_device_ctx)
972    if VAAPI_CODECS:
973        log.info("found %i vaapi codecs: %s", len(VAAPI_CODECS), csv(VAAPI_CODECS))
974    else:
975        log.info("no vaapi codecs found")
976    return VAAPI_CODECS
977
978cdef AVBufferRef *init_vaapi_device() except NULL:
979    cdef char* device = NULL
980    dev_str = os.environ.get("XPRA_VAAPI_DEVICE")
981    if dev_str:
982        device = dev_str
983    cdef AVDictionary *opts = NULL
984    cdef AVBufferRef *hw_device_ctx = NULL
985    cdef int err = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI,
986                                          device, opts, 0)
987    if err<0:
988        raise Exception("vaapi device context not found")
989    log("init_vaapi_device()=%#x", <uintptr_t> hw_device_ctx)
990    return hw_device_ctx
991
992
993cdef int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int width, int height):
994    cdef AVBufferRef *hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx)
995    if not hw_frames_ref:
996        log.error("Error: faicreate VAAPI frame context")
997        return -1
998    cdef AVHWFramesContext *frames_ctx = <AVHWFramesContext *> hw_frames_ref.data
999    frames_ctx.format    = AV_PIX_FMT_VAAPI
1000    frames_ctx.sw_format = AV_PIX_FMT_NV12
1001    frames_ctx.width     = width
1002    frames_ctx.height    = height
1003    frames_ctx.initial_pool_size = 20
1004    cdef int err = av_hwframe_ctx_init(hw_frames_ref)
1005    if err<0:
1006        log.error("Error: failed to initialize VAAPI frame context")
1007        log.error(" %s", av_error_str(err))
1008        av_buffer_unref(&hw_frames_ref)
1009        return -1
1010    ctx.hw_frames_ctx = av_buffer_ref(hw_frames_ref)
1011    if ctx.hw_frames_ctx == NULL:
1012        log.error("Error: failed to allocate hw frame buffer")
1013        av_buffer_unref(&hw_frames_ref)
1014        return -1
1015    av_buffer_unref(&hw_frames_ref)
1016    return 0
1017
1018cdef encode_frame(AVCodecContext *avctx, AVFrame *frame):
1019    err = avcodec_send_frame(avctx, frame)
1020    if err<0:
1021        log.error("Error: failed to send frame to encoder")
1022        log.error(" %s", av_error_str(err))
1023        return None
1024    cdef AVPacket * pkt = av_packet_alloc()
1025    pkt.data = NULL
1026    pkt.size = 0
1027    data = []
1028    while True:
1029        ret = avcodec_receive_packet(avctx, pkt)
1030        if ret:
1031            break
1032        pkt.stream_index = 0
1033        data.append(pkt.data[:pkt.size])
1034    return data
1035
1036
1037def cleanup_module():
1038    log("enc_ffmpeg.cleanup_module()")
1039    restore_logger()
1040
1041def get_type():
1042    return "ffmpeg"
1043
1044generation = AtomicInteger()
1045def get_info():
1046    global generation
1047    f = {}
1048    for e in get_encodings():
1049        f[e] = get_input_colorspaces(e)
1050    return  {
1051             "version"      : get_version(),
1052             "encodings"    : get_encodings(),
1053             "muxers"       : get_muxer_formats(),
1054             "formats"      : f,
1055             "generation"   : generation.get(),
1056             }
1057
1058def get_encodings():
1059    global CODECS
1060    return CODECS
1061
1062def get_input_colorspaces(encoding):
1063    if encoding in VAAPI_CODECS:
1064        return ["NV12"]
1065    return ["YUV420P"]
1066
1067def get_output_colorspaces(encoding, csc):
1068    if encoding not in CODECS:
1069        return []
1070    return ["YUV420P"]
1071
1072
1073GEN_TO_ENCODER = weakref.WeakValueDictionary()
1074
1075
1076cdef list_options(void *obj, const AVClass *av_class, int skip=1):
1077    if av_class==NULL:
1078        return
1079    cdef const AVOption *option = <const AVOption*> av_class.option
1080    if skip<=0:
1081        options = []
1082        while option:
1083            oname = bytestostr(option.name)
1084            options.append(oname)
1085            option = av_opt_next(obj, option)
1086        log("%s options: %s", bytestostr(av_class.class_name), csv(options))
1087    cdef void *child = NULL
1088    cdef const AVClass *child_class
1089    while True:
1090        child = av_opt_child_next(obj, child)
1091        if child==NULL:
1092            return
1093        child_class = (<AVClass**> child)[0]
1094        list_options(child, child_class, skip-1)
1095
1096
1097cdef int write_packet(void *opaque, uint8_t *buf, int buf_size):
1098    global GEN_TO_ENCODER
1099    encoder = GEN_TO_ENCODER.get(<uintptr_t> opaque)
1100    #log.warn("write_packet(%#x, %#x, %#x) encoder=%s", <uintptr_t> opaque, <uintptr_t> buf, buf_size, type(encoder))
1101    if not encoder:
1102        log.error("Error: write_packet called for unregistered encoder %i!", <uintptr_t> opaque)
1103        return -1
1104    return encoder.write_packet(<uintptr_t> buf, buf_size)
1105
1106MAX_WIDTH, MAX_HEIGHT = 4096, 4096
1107def get_spec(encoding, colorspace):
1108    assert encoding in get_encodings(), "invalid encoding: %s (must be one of %s" % (encoding, get_encodings())
1109    assert colorspace in get_input_colorspaces(encoding), "invalid colorspace: %s (must be one of %s)" % (colorspace, get_input_colorspaces(encoding))
1110    speed = 40
1111    setup_cost = 50
1112    cpu_cost = 100
1113    gpu_cost = 0
1114    size_efficiency = 50
1115    if encoding in VAAPI_CODECS and colorspace=="NV12":
1116        speed = 100
1117        cpu_cost = 10
1118        gpu_cost = 100
1119        size_efficiency = 100
1120    return video_spec(encoding=encoding, input_colorspace=colorspace,
1121                      output_colorspaces=get_output_colorspaces(encoding, colorspace), has_lossless_mode=False,
1122                      codec_class=Encoder, codec_type=get_type(),
1123                      quality=40, speed=speed, size_efficiency=size_efficiency,
1124                      setup_cost=setup_cost, cpu_cost=cpu_cost, gpu_cost=gpu_cost,
1125                      width_mask=0xFFFE, height_mask=0xFFFE, max_w=MAX_WIDTH, max_h=MAX_HEIGHT)
1126
1127
1128cdef class Encoder:
1129    """
1130        This wraps the AVCodecContext and its configuration,
1131    """
1132    #muxer:
1133    cdef AVFormatContext *muxer_ctx
1134    cdef AVDictionary *muxer_opts
1135    cdef unsigned char *buffer
1136    cdef object buffers
1137    cdef int64_t offset
1138    cdef object muxer_format
1139    cdef object file
1140    #video:
1141    cdef const AVCodec *video_codec
1142    cdef AVStream *video_stream
1143    cdef AVCodecContext *video_ctx
1144    cdef AVPixelFormat pix_fmt
1145    cdef object src_format
1146    cdef AVFrame *av_frame
1147    cdef unsigned long frames
1148    cdef unsigned int width
1149    cdef unsigned int height
1150    cdef uint8_t nplanes
1151    cdef object encoding
1152    cdef object profile
1153    cdef uint8_t fast_decode
1154    #audio:
1155    cdef const AVCodec *audio_codec
1156    cdef AVStream *audio_stream
1157    cdef AVCodecContext *audio_ctx
1158    cdef uint8_t ready
1159    cdef uint8_t vaapi
1160    cdef AVBufferRef *hw_device_ctx
1161
1162    cdef object __weakref__
1163
1164    def init_context(self, encoding, unsigned int width, unsigned int height, src_format, options):
1165        global CODECS, generation
1166        assert encoding in CODECS
1167        options = options or typedict()
1168        assert options.get("scaled-width", width)==width, "ffmpeg encoder does not handle scaling"
1169        assert options.get("scaled-height", height)==height, "ffmpeg encoder does not handle scaling"
1170        self.vaapi = encoding in VAAPI_CODECS and src_format=="NV12"
1171        self.fast_decode = options.boolget("%s.fast-decode" % encoding, False)
1172        assert src_format in get_input_colorspaces(encoding), "invalid colorspace: %s" % src_format
1173        if src_format=="YUV420P":
1174            self.nplanes = 3
1175        elif src_format=="NV12":
1176            self.nplanes = 2
1177        else:
1178            raise Exception("unknown source format '%s'" % src_format)
1179        self.encoding = encoding
1180        self.muxer_format = None
1181        if encoding.find("+")>0:
1182            self.muxer_format = encoding.split("+")[1]  #ie: "mpeg4+mp4" -> "mp4"   #"mov", "f4v"
1183            assert self.muxer_format in ("mp4", "webm")
1184        self.width = width
1185        self.height = height
1186        self.src_format = src_format
1187        self.pix_fmt = FORMAT_TO_ENUM.get(src_format, AV_PIX_FMT_NONE)
1188        if self.pix_fmt==AV_PIX_FMT_NONE:
1189            raise Exception("invalid pixel format: %s", src_format)
1190        self.buffers = []
1191
1192        codec = self.encoding.split("+")[0]
1193        log("init_context codec(%s)=%s, src_format=%s, vaapi=%s", encoding, codec, src_format, self.vaapi)
1194        cdef AVCodecID video_codec_id
1195        if self.vaapi:
1196            name = ("%s_vaapi" % encoding).encode("latin1")
1197            self.video_codec = avcodec_find_encoder_by_name(name)
1198            self.hw_device_ctx = init_vaapi_device()
1199        else:
1200            name = ENCODER_NAMES.get(self.encoding)
1201            if name:
1202                log("using encoder name '%s' for '%s'", name, self.encoding)
1203                self.video_codec = avcodec_find_encoder_by_name(name)
1204            else:
1205                name = self.encoding
1206                video_codec_id = CODEC_ID.get(codec, 0) #ie: AV_CODEC_ID_H264
1207                assert video_codec_id!=0, "invalid codec; %s" % self.encoding
1208                self.video_codec = avcodec_find_encoder(video_codec_id)
1209        if self.video_codec==NULL:
1210            raise Exception("codec not found for '%s'!" % bytestostr(name))
1211        if not self.vaapi:
1212            #make sure that we don't end up using vaapi from here
1213            #if we didn't want to use it
1214            #(otherwise it will crash)
1215            video_codec_name = bytestostr(self.video_codec.name)
1216            if video_codec_name.endswith("vaapi"):
1217                raise Exception("codec '%s' would use vaapi" % self.encoding)
1218        log("%s: \"%s\", codec flags: %s",
1219            bytestostr(self.video_codec.name), bytestostr(self.video_codec.long_name),
1220            flagscsv(CAPS, self.video_codec.capabilities))
1221
1222        cdef int quality = options.intget("quality", 50)
1223        cdef int speed = options.intget("speed", 50)
1224        cdef uintptr_t gen = generation.increase()
1225        GEN_TO_ENCODER[gen] = self
1226        try:
1227            if self.muxer_format:
1228                assert not self.vaapi
1229                self.init_muxer(gen)
1230            self.init_encoder(quality, speed, options)
1231            if AUDIO:
1232                self.init_audio()
1233            if self.muxer_format:
1234                self.write_muxer_header()
1235            if SAVE_TO_FILE is not None:
1236                if self.muxer_format:
1237                    filename = SAVE_TO_FILE+"-"+self.encoding+"-"+str(gen)+".%s" % self.muxer_format
1238                else:
1239                    filename = SAVE_TO_FILE+"-"+str(gen)+"."+self.encoding
1240                self.file = open(filename, 'wb')
1241                log.info("saving %s stream to %s", self.encoding, filename)
1242        except Exception:
1243            log("init_encoder(%i, %i, %s) failed", quality, speed, options, exc_info=True)
1244            self.clean()
1245            del GEN_TO_ENCODER[gen]
1246            raise
1247        else:
1248            log("enc_ffmpeg.Encoder.init_context(%s, %s, %s) self=%s", self.width, self.height, self.src_format, self.get_info())
1249        self.ready = 1
1250
1251    def is_ready(self):
1252        return bool(self.ready)
1253
1254    def init_muxer(self, uintptr_t gen):
1255        global GEN_TO_ENCODER
1256        cdef AVOutputFormat *oformat = get_av_output_format(strtobytes(self.muxer_format))
1257        if oformat==NULL:
1258            raise Exception("libavformat does not support %s" % self.muxer_format)
1259        log("init_muxer(%i) AVOutputFormat(%s)=%#x, flags=%s",
1260            gen, self.muxer_format, <uintptr_t> oformat, flagscsv(AVFMT, oformat.flags))
1261        if oformat.flags & AVFMT_ALLOW_FLUSH==0:
1262            raise Exception("AVOutputFormat(%s) does not support flushing!" % self.muxer_format)
1263        r = avformat_alloc_output_context2(&self.muxer_ctx, oformat, strtobytes(self.muxer_format), NULL)
1264        if r!=0:
1265            msg = av_error_str(r)
1266            raise Exception("libavformat cannot allocate context: %s" % msg)
1267        log("init_muxer(%i) avformat_alloc_output_context2 returned %i for %s, format context=%#x, flags=%s, ctx_flags=%s",
1268            gen, r, self.muxer_format, <uintptr_t> self.muxer_ctx,
1269            flagscsv(FMT_FLAGS, self.muxer_ctx.flags), flagscsv(AVFMTCTX, self.muxer_ctx.ctx_flags))
1270        list_options(self.muxer_ctx, self.muxer_ctx.av_class, 0)
1271
1272        movflags = b""
1273        if self.muxer_format=="mp4":
1274            #movflags = "empty_moov+omit_tfhd_offset+frag_keyframe+default_base_moof"
1275            movflags = b"empty_moov+frag_keyframe+default_base_moof+faststart"
1276        elif self.muxer_format=="webm":
1277            movflags = b"dash+live"
1278        if movflags:
1279            r = av_dict_set(&self.muxer_opts, b"movflags", movflags, 0)
1280            if r!=0:
1281                msg = av_error_str(r)
1282                raise Exception("failed to set %s muxer 'movflags' options '%s': %s" % (self.muxer_format, movflags, msg))
1283
1284        self.buffer = <unsigned char*> av_malloc(DEFAULT_BUF_LEN)
1285        if self.buffer==NULL:
1286            raise Exception("failed to allocate %iKB of memory" % (DEFAULT_BUF_LEN//1024))
1287        self.muxer_ctx.pb = avio_alloc_context(self.buffer, DEFAULT_BUF_LEN, 1, <void *> gen, NULL, write_packet, NULL)
1288        if self.muxer_ctx.pb==NULL:
1289            raise Exception("libavformat failed to allocate io context")
1290        log("init_muxer(%i) saving %s stream to bitstream buffer %#x",
1291            gen, self.encoding, <uintptr_t> self.buffer)
1292        self.muxer_ctx.flush_packets = 1
1293        self.muxer_ctx.bit_rate = 250000
1294        self.muxer_ctx.start_time = 0
1295        #self.muxer_ctx.duration = 999999
1296        self.muxer_ctx.start_time_realtime = int(time.time()*1000)
1297        self.muxer_ctx.strict_std_compliance = 1
1298
1299        self.video_stream = avformat_new_stream(self.muxer_ctx, NULL)    #self.video_codec
1300        self.video_stream.id = 0
1301        log("init_muxer(%i) video: avformat_new_stream=%#x, nb streams=%i",
1302            gen, <uintptr_t> self.video_stream, self.muxer_ctx.nb_streams)
1303
1304    def write_muxer_header(self):
1305        log("write_muxer_header() %s header", self.muxer_format)
1306        assert self.muxer_opts
1307        r = avformat_write_header(self.muxer_ctx, &self.muxer_opts)
1308        av_dict_free(&self.muxer_opts)
1309        if r!=0:
1310            msg = av_error_str(r)
1311            raise Exception("libavformat failed to write header: %s" % msg)
1312
1313    def init_encoder(self, int quality, int speed, options):
1314        log("init_encoder(%i, %i, %s)", quality, speed, options)
1315        self.video_ctx = avcodec_alloc_context3(self.video_codec)
1316        if self.video_ctx==NULL:
1317            raise Exception("failed to allocate video codec context!")
1318        list_options(self.video_ctx, self.video_ctx.av_class)
1319        cdef int b_frames = 0
1320        #we need a framerate.. make one up:
1321        self.video_ctx.global_quality = 20
1322        self.video_ctx.framerate.num = 25
1323        self.video_ctx.framerate.den = 1
1324        self.video_ctx.time_base.num = 1
1325        self.video_ctx.time_base.den = 25
1326        self.video_ctx.sample_aspect_ratio.num = 1
1327        self.video_ctx.sample_aspect_ratio.den = 1
1328        #self.video_ctx.refcounted_frames = 1
1329        self.video_ctx.max_b_frames = b_frames*1
1330        self.video_ctx.has_b_frames = b_frames
1331        self.video_ctx.delay = 0
1332        self.video_ctx.gop_size = 10
1333        self.video_ctx.width = self.width
1334        self.video_ctx.height = self.height
1335        self.video_ctx.bit_rate = max(200000, self.width*self.height*4) #4 bits per pixel
1336        #self.video_ctx.thread_safe_callbacks = 1
1337
1338        cdef AVDictionary *opts = NULL
1339        cdef int r
1340        if self.encoding.startswith("h") or self.encoding=="mpeg2":
1341            #these formats all have 'profile' and 'level' attributes: hevc, h264, mpeg2
1342            #ie: the client can specify the encoding option:
1343            # "encoding.h264+mpeg4.YUV420P.profile" : "main"
1344            # (the html5 client does)
1345            # lookup YUV420P because the client doesn't need to know about NV12,
1346            # which will be decoded into YUV420P..
1347            default_profile = DEFAULT_PROFILE.get(self.encoding)
1348            profile = options.strget("%s.YUV420P.profile" % self.encoding, default_profile)
1349            profile = options.strget("%s.%s.profile" % (self.encoding, self.src_format), profile)
1350            log("init_encoder() profile=%s", profile)
1351            if profile:
1352                if self.vaapi:
1353                    global PROFILES
1354                    profiles = PROFILES.get(self.encoding, {})
1355                    v = profiles.get(profile, None)
1356                    if v is not None:
1357                        r = av_dict_set_int(&opts, b"profile", v, 0)
1358                        if r==0:
1359                            self.profile = profile
1360                    else:
1361                        log.warn("unknown profile '%s', options for %s: %s", profile, self.encoding, csv(profiles.keys()) or "none")
1362                else:
1363                    av_dict_set(&opts, b"profile", strtobytes(profile), 0)
1364                    self.profile = profile
1365
1366            level = 0
1367            level_str = options.strget("%s.%s.level" % (self.encoding, self.src_format), "")
1368            if level_str:
1369                try:
1370                    level = int(float(level_str)*10)    #ie: "2.1" -> 21
1371                except ValueError:
1372                    pass
1373            if profile and profile.find("baseline")>=0:
1374                level = min(21, level)
1375            log("init_encoder() level=%s", level)
1376            if level>0:
1377                r = av_dict_set_int(&opts, b"level", level, 0)
1378                if r!=0:
1379                    raise Exception("failed to set level=%i", level)
1380
1381        if self.vaapi:
1382            self.video_ctx.pix_fmt = AV_PIX_FMT_VAAPI
1383            r = set_hwframe_ctx(self.video_ctx, self.hw_device_ctx, self.width, self.height)
1384            if r<0:
1385                raise Exception("failed to set hwframe context")
1386            if self.encoding=="h264":
1387                #reach highest quality (compression_level=0) for quality>=91:
1388                self.video_ctx.compression_level = max(0, min(7, 7-quality/13))
1389                log("init_encoder() compression_level=%s", self.video_ctx.compression_level)
1390        else:
1391            self.video_ctx.pix_fmt = self.pix_fmt
1392            if self.encoding not in ("mpeg1", "mpeg2"):
1393                self.video_ctx.thread_type = THREAD_TYPE
1394                self.video_ctx.thread_count = THREAD_COUNT     #0=auto
1395                self.video_ctx.flags |= AV_CODEC_FLAG_GLOBAL_HEADER
1396                self.video_ctx.flags2 |= AV_CODEC_FLAG2_FAST   #may cause "no deblock across slices" - which should be fine
1397                log("init_encoder() thread-type=%i, thread-count=%i", THREAD_TYPE, THREAD_COUNT)
1398                log("init_encoder() codec flags: %s", flagscsv(CODEC_FLAGS, self.video_ctx.flags))
1399                log("init_encoder() codec flags2: %s", flagscsv(CODEC_FLAGS2, self.video_ctx.flags2))
1400            if self.encoding.startswith("h264") or self.encoding.find("mpeg4")>=0:
1401                #x264 options:
1402                tunes = [b"zerolatency"]
1403                if self.fast_decode:
1404                    tunes.append(b"fastdecode")
1405                r = av_dict_set(&opts, b"tune", b"+".join(tunes), 0)
1406                log("av_dict_set tune=%s returned %i", tunes, r)
1407                r = av_dict_set(&opts, b"preset", b"ultrafast", 0)
1408                log("av_dict_set preset=ultrafast returned %i", r)
1409            if self.encoding.startswith("vp"):
1410                for k,v in {
1411                        "lag-in-frames"     : 0,
1412                        "realtime"          : 1,
1413                        "rc_lookahead"      : 0,
1414                        "error_resilient"   : 0,
1415                        }.items():
1416                    r = av_dict_set_int(&opts, strtobytes(k), v, 0)
1417                    if r!=0:
1418                        log.error("Error: failed to set video context option '%s' to %i:", k, v)
1419                        log.error(" %s", av_error_str(r))
1420
1421        r = avcodec_open2(self.video_ctx, self.video_codec, &opts)
1422        av_dict_free(&opts)
1423        if r!=0:
1424            raise Exception("could not open %s encoder context: %s" % (self.encoding, av_error_str(r)))
1425        log("init_encoder() avcodec_open2 success")
1426
1427        if self.video_stream:
1428            assert not self.vaapi
1429            r = avcodec_parameters_from_context(self.video_stream.codecpar, self.video_ctx)
1430            if r<0:
1431                raise Exception("could not copy video context parameters %#x: %s" % (<uintptr_t> self.video_stream.codecpar, av_error_str(r)))
1432
1433        self.av_frame = av_frame_alloc()
1434        if self.av_frame==NULL:
1435            raise Exception("could not allocate an AVFrame for encoding")
1436        self.frames = 0
1437
1438    def init_audio(self):
1439        self.audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC)
1440        if self.audio_codec==NULL:
1441            raise Exception("cannot find audio codec!")
1442        log("init_audio() audio_codec=%#x", <uintptr_t> self.audio_codec)
1443        self.audio_stream = avformat_new_stream(self.muxer_ctx, NULL)
1444        self.audio_stream.id = 1
1445        log("init_audio() audio: avformat_new_stream=%#x, nb streams=%i", <uintptr_t> self.audio_stream, self.muxer_ctx.nb_streams)
1446        self.audio_ctx = avcodec_alloc_context3(self.audio_codec)
1447        log("init_audio() audio_context=%#x", <uintptr_t> self.audio_ctx)
1448        self.audio_ctx.sample_fmt = AV_SAMPLE_FMT_FLTP
1449        self.audio_ctx.time_base.den = 25
1450        self.audio_ctx.time_base.num = 1
1451        self.audio_ctx.bit_rate = 64000
1452        self.audio_ctx.sample_rate = 44100
1453        self.audio_ctx.channels = 2
1454        #if audio_codec.capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE:
1455        #    pass
1456        #cdef AVDictionary *opts = NULL
1457        #av_dict_set(&opts, "strict", "experimental", 0)
1458        #r = avcodec_open2(audio_ctx, audio_codec, &opts)
1459        #av_dict_free(&opts)
1460        r = avcodec_open2(self.audio_ctx, self.audio_codec, NULL)
1461        if r!=0:
1462            raise Exception("could not open %s encoder context: %s" % (self.encoding, av_error_str(r)))
1463        if r!=0:
1464            raise Exception("could not open %s encoder context: %s" % ("aac", av_error_str(r)))
1465        r = avcodec_parameters_from_context(self.audio_stream.codecpar, self.audio_ctx)
1466        if r<0:
1467            raise Exception("could not copy audio context parameters %#x: %s" % (<uintptr_t> self.audio_stream.codecpar, av_error_str(r)))
1468
1469
1470    def clean(self):
1471        try:
1472            self.clean_encoder()
1473        except:
1474            log.error("cleanup failed", exc_info=True)
1475        self.video_codec = NULL
1476        self.audio_codec = NULL
1477        self.pix_fmt = 0
1478        self.src_format = ""
1479        self.av_frame = NULL                        #should be redundant
1480        self.frames = 0
1481        self.width = 0
1482        self.height = 0
1483        self.encoding = ""
1484        self.buffers = []
1485        f = self.file
1486        if f:
1487            self.file = None
1488            f.close()
1489
1490    def clean_encoder(self):
1491        cdef int r
1492        log("%s.clean_encoder()", self)
1493        if self.av_frame:
1494            log("clean_encoder() freeing AVFrame: %#x", <uintptr_t> self.av_frame)
1495            av_frame_free(&self.av_frame)
1496        if self.muxer_ctx:
1497            if self.frames>0:
1498                log("clean_encoder() writing trailer to stream")
1499                av_write_trailer(self.muxer_ctx)
1500                if self.muxer_ctx.pb:
1501                    av_free(self.muxer_ctx.pb)
1502                    self.muxer_ctx.pb = NULL
1503            log("clean_encoder() freeing av format context %#x", <uintptr_t> self.muxer_ctx)
1504            avformat_free_context(self.muxer_ctx)
1505            self.muxer_ctx = NULL
1506            log("clean_encoder() freeing bitstream buffer %#x", <uintptr_t> self.buffer)
1507            if self.buffer:
1508                av_free(self.buffer)
1509                self.buffer = NULL
1510        log("clean_encoder() freeing AVCodecContext: %#x", <uintptr_t> self.video_ctx)
1511        if self.video_ctx:
1512            r = avcodec_close(self.video_ctx)
1513            if r!=0:
1514                log.error("Error: failed to close video encoder context %#x", <uintptr_t> self.video_ctx)
1515                log.error(" %s", av_error_str(r))
1516            av_free(self.video_ctx)
1517            self.video_ctx = NULL
1518        if self.audio_ctx:
1519            r = avcodec_close(self.audio_ctx)
1520            if r!=0:
1521                log.error("Error: failed to close audio encoder context %#x", <uintptr_t> self.audio_ctx)
1522                log.error(" %s", av_error_str(r))
1523            av_free(self.audio_ctx)
1524            self.audio_ctx = NULL
1525        if self.hw_device_ctx:
1526            av_buffer_unref(&self.hw_device_ctx)
1527        log("clean_encoder() done")
1528
1529    def __repr__(self):
1530        if self.is_closed():
1531            return "enc_ffmpeg.Encoder(*closed*)"
1532        return "enc_ffmpeg.Encoder(%s)" % self.get_info()
1533
1534    def get_info(self) -> dict:
1535        info = {
1536                "version"   : get_version(),
1537                "encoding"  : self.encoding,
1538                "formats"   : get_input_colorspaces(self.encoding),
1539                "type"      : self.get_type(),
1540                "frames"    : int(self.frames),
1541                "width"     : self.width,
1542                "height"    : self.height,
1543                }
1544        if self.muxer_format:
1545            info["muxer"] = self.muxer_format
1546        if self.video_codec:
1547            info["video-codec"] = self.video_codec.name[:]
1548            info["video-description"] = self.video_codec.long_name[:]
1549        if self.audio_codec:
1550            info["audio-codec"] = self.audio_codec.name[:]
1551            info["audio-description"] = self.audio_codec.long_name[:]
1552        if self.src_format:
1553            info["src_format"] = self.src_format
1554        if not self.is_closed():
1555            info["encoder_width"] = self.video_ctx.width
1556            info["encoder_height"] = self.video_ctx.height
1557        else:
1558            info["closed"] = True
1559        return info
1560
1561    def is_closed(self):
1562        return self.video_ctx==NULL
1563
1564    def __dealloc__(self):
1565        self.clean()
1566
1567    def get_width(self):
1568        return self.width
1569
1570    def get_height(self):
1571        return self.height
1572
1573    def get_src_format(self):
1574        return self.src_format
1575
1576    def get_encoding(self):
1577        return self.encoding
1578
1579    def get_type(self):
1580        return "ffmpeg"
1581
1582    def get_delayed_frames(self):
1583        return 0
1584
1585    def log_av_error(self, image, err_no, options=None):
1586        msg = av_error_str(err_no)
1587        self.log_error(image, msg, options, "error %i" % err_no)
1588
1589    def log_error(self, image, err, options=None, error_type="error"):
1590        log.error("Error: ffmpeg %s encoding %s:", error_type, self.encoding)
1591        log.error(" '%s'", err)
1592        log.error(" on image %s", image)
1593        log.error(" frame number %i", self.frames)
1594        if options:
1595            log.error(" options=%s", options)
1596        log.error(" encoder state:")
1597        for k,v in self.get_info().items():
1598            log.error("  %s = %s", k, v)
1599
1600    def compress_image(self, image=None, options=None):
1601        cdef int ret, i
1602        cdef AVFrame *frame
1603        cdef AVFrame *hw_frame = NULL
1604        cdef Py_buffer py_buf[4]
1605        assert self.video_ctx, "no codec context! (not initialized or already closed)"
1606        assert self.video_codec, "no video codec!"
1607
1608        for i in range(4):
1609            memset(&py_buf[i], 0, sizeof(Py_buffer))
1610
1611        def release_buffers():
1612            for i in range(4):
1613                if py_buf[i].buf:
1614                    PyBuffer_Release(&py_buf[i])
1615
1616        if image:
1617            assert image.get_pixel_format()==self.src_format, "invalid input format %s, expected %s" % (image.get_pixel_format, self.src_format)
1618            assert image.get_width()==self.width and image.get_height()==self.height
1619
1620            pixels = image.get_pixels()
1621            istrides = image.get_rowstride()
1622            assert len(pixels)==self.nplanes, "image pixels does not have %i planes! (found %s)" % (self.nplanes, len(pixels))
1623            assert len(istrides)==self.nplanes, "image strides does not have %i values! (found %s)" % (self.nplanes, len(istrides))
1624            #populate the avframe:
1625            ret = av_frame_make_writable(self.av_frame)
1626            if not ret!=0:
1627                raise Exception(av_error_str(ret))
1628            for i in range(4):
1629                if i<self.nplanes:
1630                    if PyObject_GetBuffer(pixels[i], &py_buf[i], PyBUF_ANY_CONTIGUOUS):
1631                        raise Exception("failed to read pixel data from %s" % type(pixels[i]))
1632                    #log("plane %s: %i bytes (%ix%i stride=%i)", ["Y", "U", "V"][i], buf_len, self.width, self.height, istrides[i])
1633                    self.av_frame.data[i] = <uint8_t *> py_buf[i].buf
1634                    self.av_frame.linesize[i] = istrides[i]
1635                else:
1636                    self.av_frame.data[i] = NULL
1637            self.av_frame.width = self.width
1638            self.av_frame.height = self.height
1639            self.av_frame.format = self.pix_fmt
1640            if self.encoding not in ("mpeg1", "mpeg2"):
1641                self.av_frame.pts = self.frames+1
1642                self.av_frame.coded_picture_number = self.frames+1
1643                self.av_frame.display_picture_number = self.frames+1
1644                #if self.frames==0:
1645                self.av_frame.pict_type = AV_PICTURE_TYPE_I
1646            #self.av_frame.key_frame = 1
1647            #else:
1648            #    self.av_frame.pict_type = AV_PICTURE_TYPE_P
1649            #self.av_frame.quality = 1
1650            frame = self.av_frame
1651        else:
1652            assert options and options.get("flush"), "no image and no flush flag"
1653            frame = NULL
1654
1655        if self.vaapi and frame:
1656            #copy to hardware:
1657            hw_frame = av_frame_alloc()
1658            log("av_frame_alloc()=%#x", <uintptr_t> hw_frame)
1659            if hw_frame==NULL:
1660                release_buffers()
1661                log.error("Error: failed to allocate a hw frame")
1662                return None
1663            ret = av_hwframe_get_buffer(self.video_ctx.hw_frames_ctx, hw_frame, 0)
1664            log("av_frame_get_buffer(%#x, %#x, 0)=%i", <uintptr_t> self.video_ctx.hw_frames_ctx, <uintptr_t> hw_frame, ret)
1665            if ret<0 or hw_frame.hw_frames_ctx==NULL:
1666                release_buffers()
1667                log.error("Error: failed to allocate a hw buffer")
1668                log.error(" %s", av_error_str(ret))
1669                return None
1670            ret = av_hwframe_transfer_data(hw_frame, frame, 0)
1671            log("av_hwframe_transfer_data(%#x, %#x, 0)=%i", <uintptr_t> hw_frame, <uintptr_t> frame, ret)
1672            if ret<0:
1673                release_buffers()
1674                log.error("Error: failed to transfer frame data to surface")
1675                log.error(" %s", av_error_str(ret))
1676                return None
1677            frame = hw_frame
1678        log("compress_image%s avcodec_send_frame frame=%#x", (image, options), <uintptr_t> frame)
1679        with nogil:
1680            ret = avcodec_send_frame(self.video_ctx, frame)
1681        release_buffers()
1682        if ret!=0:
1683            self.log_av_error(image, ret, options)
1684            raise Exception("%i: %s" % (ret, av_error_str(ret)))
1685
1686        buf_len = 1024+self.width*self.height
1687        cdef AVPacket *avpkt = av_packet_alloc()
1688        avpkt.data = <uint8_t *> memalign(buf_len)
1689        avpkt.size = buf_len
1690        assert ret==0
1691        try:
1692            while ret==0:
1693                log("compress_image%s avcodec_receive_packet avpacket=%#x", (image, options), <uintptr_t> &avpkt)
1694                with nogil:
1695                    ret = avcodec_receive_packet(self.video_ctx, avpkt)
1696                log("avcodec_receive_packet(..)=%i", ret)
1697                if ret==-errno.EAGAIN:
1698                    log("ffmpeg avcodec_receive_packet EAGAIN")
1699                    break
1700                if ret!=0:
1701                    if not image:
1702                        log("avcodec_receive_packet returned error '%s' for flush request", av_error_str(ret))
1703                    else:
1704                        log("avcodec_receive_packet returned error '%s' for image %s, returning existing buffer", av_error_str(ret), image)
1705                    break
1706                if ret<0:
1707                    self.log_av_error(image, ret, options)
1708                    raise Exception(av_error_str(ret))
1709                if ret>0:
1710                    self.log_av_error(image, ret, options, "no stream")
1711                    raise Exception("no stream")
1712                log("avcodec_receive_packet returned %#x bytes of data, flags: %s", avpkt.size, flagscsv(PKT_FLAGS, avpkt.flags))
1713                if avpkt.flags & AV_PKT_FLAG_CORRUPT:
1714                    self.log_error(image, "packet", options, "av packet is corrupt")
1715                    raise Exception("av packet is corrupt")
1716
1717                if self.muxer_format:
1718                    #give the frame to the muxer:
1719                    #(the muxer will append to self.buffers)
1720                    avpkt.stream_index = self.video_stream.index
1721                    r = av_write_frame(self.muxer_ctx, avpkt)
1722                    log("av_write_frame packet returned %i", r)
1723                    if ret<0:
1724                        free(avpkt.data)
1725                        self.log_av_error(image, ret, options)
1726                        raise Exception(av_error_str(ret))
1727                    #flush muxer:
1728                    while True:
1729                        r = av_write_frame(self.muxer_ctx, NULL)
1730                        log("av_write_frame flush returned %i", r)
1731                        if r==1:
1732                            break
1733                        if ret<0:
1734                            self.log_av_error(image, ret, options)
1735                            raise Exception(av_error_str(ret))
1736                else:
1737                    #process frame data without a muxer:
1738                    self.buffers.append(avpkt.data[:avpkt.size])
1739        finally:
1740            free(avpkt.data)
1741            av_packet_free(&avpkt)
1742            if hw_frame:
1743                av_frame_free(&hw_frame)
1744
1745        #NV12 also uses YUV420P,
1746        #only with a different pixel layout
1747        #which is irrelevant to the client
1748        client_options = {
1749            "csc" : "YUV420P",
1750            }
1751        if self.frames==0 and self.profile:
1752            client_options["profile"] = self.profile
1753            client_options["level"] = "3.0"
1754        data = b"".join(self.buffers)
1755        if self.buffers and self.file:
1756            for x in self.buffers:
1757                self.file.write(x)
1758            self.file.flush()
1759        if self.encoding in ("mpeg1", "mpeg2"):
1760            #always one frame buffered
1761            client_options["delayed"] = 1
1762        if data:
1763            client_options["frame"] = int(self.frames)
1764            if self.frames==0:
1765                log("%s client options for first frame: %s", self.encoding, client_options)
1766            self.frames += 1
1767        log("compress_image(%s) %5i bytes (%i buffers) for %4s with client options: %s", image, len(data), len(self.buffers), self.encoding, client_options)
1768        self.buffers = []
1769        return data, client_options
1770
1771    def flush(self, delayed):
1772        v = self.compress_image(options={"flush" : True})
1773        #ffmpeg context cannot be re-used after a flush..
1774        self.clean()
1775        return v
1776
1777    def write_packet(self, uintptr_t buf, int buf_size):
1778        log("write_packet(%#x, %#x)", <uintptr_t> buf, buf_size)
1779        cdef uint8_t *cbuf = <uint8_t*> buf
1780        buffer = cbuf[:buf_size]
1781        self.buffers.append(buffer)
1782        return buf_size
1783
1784
1785def selftest(full=False):
1786    global CODECS
1787    from xpra.codecs.codec_checks import testencoder
1788    from xpra.codecs.enc_ffmpeg import encoder
1789    with SilenceAVWarningsContext():
1790        CODECS = testencoder(encoder, full)
1791