summaryrefslogtreecommitdiff
path: root/modules/h265
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2014-12-26 13:38:05 +0100
committerAlfred E. Heggestad <aeh@db.org>2014-12-26 13:38:05 +0100
commite95e76d8b222c5433005ca6ad53407bcb51b58d7 (patch)
tree7784dbe6666b8a16097625eeb8dd20551ec396f2 /modules/h265
parentf55560474869956b374606973e4633206e9bf0da (diff)
added h265 module
Diffstat (limited to 'modules/h265')
-rw-r--r--modules/h265/README29
-rw-r--r--modules/h265/TODO6
-rw-r--r--modules/h265/decode.c289
-rw-r--r--modules/h265/encode.c253
-rw-r--r--modules/h265/fmt.c159
-rw-r--r--modules/h265/h265.c68
-rw-r--r--modules/h265/h265.h69
-rw-r--r--modules/h265/module.mk12
-rw-r--r--modules/h265/notes75
9 files changed, 960 insertions, 0 deletions
diff --git a/modules/h265/README b/modules/h265/README
new file mode 100644
index 0000000..75967c0
--- /dev/null
+++ b/modules/h265/README
@@ -0,0 +1,29 @@
+README h265 module
+------------------
+
+
+Steps for building and testing:
+
+
+1. build and install x265 from https://github.com/mirror/x265.git
+2. build and install ffmpeg from git://source.ffmpeg.org/ffmpeg.git
+
+ $ ./configure --disable-everything --enable-decoder=hevc \
+ && make -j4 && sudo make install
+
+3. build baresip with H265 module:
+
+ $ cd baresip
+ $ make EXTRA_MODULES=h265
+
+4. add h265.so to $HOME/.baresip/config
+ module h265.so
+
+5. start baresip with the "vidloop" module, to test a local loop
+ from a suitable vidsrc and vidisp module:
+
+ $ ./baresip -evv
+
+
+
+[END]
diff --git a/modules/h265/TODO b/modules/h265/TODO
new file mode 100644
index 0000000..de706c6
--- /dev/null
+++ b/modules/h265/TODO
@@ -0,0 +1,6 @@
+TODO:
+
+done - get encoder working
+done - get decoder working
+ - add timestamp to vidsrc API
+ - add timestamp to vidcodec API
diff --git a/modules/h265/decode.c b/modules/h265/decode.c
new file mode 100644
index 0000000..f05aafc
--- /dev/null
+++ b/modules/h265/decode.c
@@ -0,0 +1,289 @@
+/**
+ * @file h265/decode.c H.265 Decode
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <libavcodec/avcodec.h>
+#include "h265.h"
+
+
+enum {
+ FU_HDR_SIZE = 1
+};
+
+enum {
+ DECODE_MAXSZ = 524288,
+};
+
+
+struct fu {
+ unsigned s:1;
+ unsigned e:1;
+ unsigned type:5;
+};
+
+struct viddec_state {
+ AVCodecContext *ctx;
+ AVFrame *pict;
+ struct mbuf *mb;
+ size_t frag_start;
+ bool frag;
+ uint16_t frag_seq;
+};
+
+
+static void destructor(void *arg)
+{
+ struct viddec_state *vds = arg;
+
+ if (vds->ctx) {
+ avcodec_close(vds->ctx);
+ av_free(vds->ctx);
+ }
+
+ if (vds->pict)
+ av_free(vds->pict);
+
+ mem_deref(vds->mb);
+}
+
+
+int h265_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc,
+ const char *fmtp)
+{
+ struct viddec_state *vds;
+ AVCodec *codec;
+ int err = 0;
+ (void)vc;
+ (void)fmtp;
+
+ if (!vdsp)
+ return EINVAL;
+
+ vds = *vdsp;
+
+ if (vds)
+ return 0;
+
+ codec = avcodec_find_decoder(AV_CODEC_ID_H265);
+ if (!codec) {
+ warning("h265: could not find H265 decoder\n");
+ return ENOSYS;
+ }
+
+ vds = mem_zalloc(sizeof(*vds), destructor);
+ if (!vds)
+ return ENOMEM;
+
+ vds->mb = mbuf_alloc(1024);
+ if (!vds->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ vds->pict = av_frame_alloc();
+ if (!vds->pict) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ vds->ctx = avcodec_alloc_context3(codec);
+ if (!vds->ctx) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ if (avcodec_open2(vds->ctx, codec, NULL) < 0) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ out:
+ if (err)
+ mem_deref(vds);
+ else
+ *vdsp = vds;
+
+ return err;
+}
+
+
+static inline int fu_decode(struct fu *fu, struct mbuf *mb)
+{
+ uint8_t v;
+
+ if (mbuf_get_left(mb) < 1)
+ return EBADMSG;
+
+ v = mbuf_read_u8(mb);
+
+ fu->s = v>>7 & 0x1;
+ fu->e = v>>6 & 0x1;
+ fu->type = v>>0 & 0x3f;
+
+ return 0;
+}
+
+
+static inline int16_t seq_diff(uint16_t x, uint16_t y)
+{
+ return (int16_t)(y - x);
+}
+
+
+static inline void fragment_rewind(struct viddec_state *vds)
+{
+ vds->mb->pos = vds->frag_start;
+ vds->mb->end = vds->frag_start;
+}
+
+
+int h265_decode(struct viddec_state *vds, struct vidframe *frame,
+ bool marker, uint16_t seq, struct mbuf *mb)
+{
+ static const uint8_t nal_seq[3] = {0, 0, 1};
+ int err, ret, got_picture, i;
+ struct h265_nal hdr;
+ AVPacket avpkt;
+
+ if (!vds || !frame || !mb)
+ return EINVAL;
+
+ err = h265_nal_decode(&hdr, mbuf_buf(mb));
+ if (err)
+ return err;
+
+ mbuf_advance(mb, H265_HDR_SIZE);
+
+#if 1
+ debug("h265: decode: %s type=%2d %s\n",
+ h265_is_keyframe(hdr.nal_unit_type) ? "<KEY>" : " ",
+ hdr.nal_unit_type,
+ h265_nalunit_name(hdr.nal_unit_type));
+#endif
+
+
+ if (vds->frag && hdr.nal_unit_type != H265_NAL_FU) {
+ debug("h265: lost fragments; discarding previous NAL\n");
+ fragment_rewind(vds);
+ vds->frag = false;
+ }
+
+ /* handle NAL types */
+ if (0 <= hdr.nal_unit_type && hdr.nal_unit_type <= 40) {
+
+ mb->pos -= H265_HDR_SIZE;
+
+ err = mbuf_write_mem(vds->mb, nal_seq, 3);
+ err |= mbuf_write_mem(vds->mb, mbuf_buf(mb),mbuf_get_left(mb));
+ if (err)
+ goto out;
+ }
+ else if (H265_NAL_FU == hdr.nal_unit_type) {
+
+ struct fu fu;
+
+ err = fu_decode(&fu, mb);
+ if (err)
+ return err;
+
+ if (fu.s) {
+ if (vds->frag) {
+ debug("h265: lost fragments; ignoring NAL\n");
+ fragment_rewind(vds);
+ }
+
+ vds->frag_start = vds->mb->pos;
+ vds->frag = true;
+
+ hdr.nal_unit_type = fu.type;
+
+ err = mbuf_write_mem(vds->mb, nal_seq, 3);
+ err = h265_nal_encode_mbuf(vds->mb, &hdr);
+ if (err)
+ goto out;
+ }
+ else {
+ if (!vds->frag) {
+ debug("h265: ignoring fragment\n");
+ return 0;
+ }
+
+ if (seq_diff(vds->frag_seq, seq) != 1) {
+ debug("h265: lost fragments detected\n");
+ fragment_rewind(vds);
+ vds->frag = false;
+ return 0;
+ }
+ }
+
+ err = mbuf_write_mem(vds->mb, mbuf_buf(mb), mbuf_get_left(mb));
+ if (err)
+ goto out;
+
+ if (fu.e)
+ vds->frag = false;
+
+ vds->frag_seq = seq;
+ }
+ else {
+ warning("h265: unknown NAL type %u\n", hdr.nal_unit_type);
+ return ENOSYS;
+ }
+
+ if (!marker) {
+
+ if (vds->mb->end > DECODE_MAXSZ) {
+ warning("h265: decode buffer size exceeded\n");
+ err = ENOMEM;
+ goto out;
+ }
+
+ return 0;
+ }
+
+ if (vds->frag) {
+ err = EPROTO;
+ goto out;
+ }
+
+ av_init_packet(&avpkt);
+ avpkt.data = vds->mb->buf;
+ avpkt.size = (int)vds->mb->end;
+
+ ret = avcodec_decode_video2(vds->ctx, vds->pict, &got_picture, &avpkt);
+ if (ret < 0) {
+ debug("h265: decode error\n");
+ err = EPROTO;
+ goto out;
+ }
+
+ if (!got_picture) {
+ /* debug("h265: no picture\n"); */
+ goto out;
+ }
+
+ if (vds->pict->format != PIX_FMT_YUV420P) {
+ warning("h265: bad pixel format (%i)\n", vds->pict->format);
+ goto out;
+ }
+
+ for (i=0; i<4; i++) {
+ frame->data[i] = vds->pict->data[i];
+ frame->linesize[i] = vds->pict->linesize[i];
+ }
+
+ frame->size.w = vds->ctx->width;
+ frame->size.h = vds->ctx->height;
+ frame->fmt = VID_FMT_YUV420P;
+
+ out:
+ mbuf_rewind(vds->mb);
+ vds->frag = false;
+
+ return err;
+}
diff --git a/modules/h265/encode.c b/modules/h265/encode.c
new file mode 100644
index 0000000..18685d6
--- /dev/null
+++ b/modules/h265/encode.c
@@ -0,0 +1,253 @@
+/**
+ * @file h265/encode.c H.265 Encode
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <string.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <x265.h>
+#include "h265.h"
+
+
+struct videnc_state {
+ struct vidsz size;
+ x265_param *param;
+ x265_encoder *x265;
+ int64_t pts;
+ unsigned fps;
+ unsigned bitrate;
+ unsigned pktsize;
+};
+
+
+static void destructor(void *arg)
+{
+ struct videnc_state *st = arg;
+
+ if (st->x265)
+ x265_encoder_close(st->x265);
+ if (st->param)
+ x265_param_free(st->param);
+}
+
+
+static int set_params(struct videnc_state *st, unsigned fps, unsigned bitrate)
+{
+ st->param = x265_param_alloc();
+ if (!st->param) {
+ warning("h265: x265_param_alloc failed\n");
+ return ENOMEM;
+ }
+
+ x265_param_default(st->param);
+
+ if (0 != x265_param_apply_profile(st->param, "main")) {
+ warning("h265: x265_param_apply_profile failed\n");
+ return EINVAL;
+ }
+
+ if (0 != x265_param_default_preset(st->param,
+ "ultrafast", "zerolatency")) {
+
+ warning("h265: x265_param_default_preset error\n");
+ return EINVAL;
+ }
+
+ st->param->fpsNum = fps;
+ st->param->fpsDenom = 1;
+
+ /* VPS, SPS and PPS headers should be output with each keyframe */
+ st->param->bRepeatHeaders = 1;
+
+ /* Rate Control */
+ st->param->rc.rateControlMode = X265_RC_CRF;
+ st->param->rc.bitrate = bitrate / 1000;
+ st->param->rc.vbvMaxBitrate = bitrate / 1000;
+ st->param->rc.vbvBufferSize = 2 * bitrate / fps;
+
+ return 0;
+}
+
+
+int h265_encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
+ struct videnc_param *prm, const char *fmtp)
+{
+ struct videnc_state *ves;
+ int err = 0;
+ (void)fmtp;
+
+ if (!vesp || !vc || !prm || prm->pktsize < 3)
+ return EINVAL;
+
+ ves = *vesp;
+
+ if (!ves) {
+
+ ves = mem_zalloc(sizeof(*ves), destructor);
+ if (!ves)
+ return ENOMEM;
+
+ *vesp = ves;
+ }
+ else {
+ if (ves->x265 && (ves->bitrate != prm->bitrate ||
+ ves->pktsize != prm->pktsize ||
+ ves->fps != prm->fps)) {
+
+ x265_encoder_close(ves->x265);
+ ves->x265 = NULL;
+ }
+ }
+
+ ves->bitrate = prm->bitrate;
+ ves->pktsize = prm->pktsize;
+ ves->fps = prm->fps;
+
+ err = set_params(ves, prm->fps, prm->bitrate);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+static int open_encoder(struct videnc_state *st, const struct vidsz *size)
+{
+ if (st->x265) {
+ debug("h265: re-opening encoder\n");
+ x265_encoder_close(st->x265);
+ }
+
+ st->param->sourceWidth = size->w;
+ st->param->sourceHeight = size->h;
+
+ st->x265 = x265_encoder_open(st->param);
+ if (!st->x265) {
+ warning("h265: x265_encoder_open failed\n");
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+
+static inline int packetize(bool marker, const uint8_t *buf, size_t len,
+ size_t maxlen, videnc_packet_h *pkth, void *arg)
+{
+ int err = 0;
+
+ if (len <= maxlen) {
+ err = pkth(marker, NULL, 0, buf, len, arg);
+ }
+ else {
+ struct h265_nal nal;
+ uint8_t fu_hdr[3];
+ const size_t flen = maxlen - sizeof(fu_hdr);
+
+ err = h265_nal_decode(&nal, buf);
+
+ h265_nal_encode(fu_hdr, H265_NAL_FU,
+ nal.nuh_temporal_id_plus1);
+
+ fu_hdr[2] = 1<<7 | nal.nal_unit_type;
+
+ buf+=2;
+ len-=2;
+
+ while (len > flen) {
+ err |= pkth(false, fu_hdr, 3, buf, flen, arg);
+
+ buf += flen;
+ len -= flen;
+ fu_hdr[2] &= ~(1 << 7); /* clear Start bit */
+ }
+
+ fu_hdr[2] |= 1<<6; /* set END bit */
+
+ err |= pkth(marker, fu_hdr, 3, buf, len, arg);
+ }
+
+ return err;
+}
+
+
+int h265_encode(struct videnc_state *st, bool update,
+ const struct vidframe *frame,
+ videnc_packet_h *pkth, void *arg)
+{
+ x265_picture *pic_in = NULL, pic_out;
+ x265_nal *nalv;
+ uint32_t i, nalc = 0;
+ int n, err = 0;
+
+ if (!st || !frame || !pkth || frame->fmt != VID_FMT_YUV420P)
+ return EINVAL;
+
+ if (!st->x265 || !vidsz_cmp(&st->size, &frame->size)) {
+
+ err = open_encoder(st, &frame->size);
+ if (err)
+ return err;
+
+ st->size = frame->size;
+ }
+
+ if (update) {
+ debug("h265: encode: picture update was requested\n");
+ }
+
+ pic_in = x265_picture_alloc();
+ if (!pic_in) {
+ warning("h265: x265_picture_alloc failed\n");
+ return ENOMEM;
+ }
+
+ x265_picture_init(st->param, pic_in);
+
+ pic_in->sliceType = update ? X265_TYPE_IDR : X265_TYPE_AUTO;
+ pic_in->pts = ++st->pts; /* XXX: add PTS to API */
+ pic_in->colorSpace = X265_CSP_I420;
+
+ for (i=0; i<3; i++) {
+ pic_in->planes[i] = frame->data[i];
+ pic_in->stride[i] = frame->linesize[i];
+ }
+
+ /* NOTE: important to get the PTS of the "out" picture */
+ n = x265_encoder_encode(st->x265, &nalv, &nalc, pic_in, &pic_out);
+ if (n <= 0)
+ goto out;
+
+ for (i=0; i<nalc; i++) {
+
+ x265_nal *nal = &nalv[i];
+ uint8_t *p = nal->payload;
+ size_t len = nal->sizeBytes;
+ bool marker;
+
+#if 1
+ debug("h265: encode: %s type=%2d %s\n",
+ h265_is_keyframe(nal->type) ? "<KEY>" : " ",
+ nal->type, h265_nalunit_name(nal->type));
+#endif
+
+ h265_skip_startcode(&p, &len);
+
+ /* XXX: use pic_out.pts */
+
+ marker = (i+1)==nalc; /* last NAL */
+
+ err = packetize(marker, p, len, st->pktsize, pkth, arg);
+ if (err)
+ goto out;
+ }
+
+ out:
+ if (pic_in)
+ x265_picture_free(pic_in);
+
+ return err;
+}
diff --git a/modules/h265/fmt.c b/modules/h265/fmt.c
new file mode 100644
index 0000000..a3acc0b
--- /dev/null
+++ b/modules/h265/fmt.c
@@ -0,0 +1,159 @@
+
+#include <string.h>
+#include <re.h>
+#include <baresip.h>
+#include "h265.h"
+
+
+/*
+1.1.4 NAL Unit Header
+
+ HEVC maintains the NAL unit concept of H.264 with modifications.
+ HEVC uses a two-byte NAL unit header, as shown in Figure 1. The
+ payload of a NAL unit refers to the NAL unit excluding the NAL unit
+ header.
+
+ +---------------+---------------+
+ |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F| Type | LayerId | TID |
+ +-------------+-----------------+
+
+ Figure 1 The structure of HEVC NAL unit header
+*/
+
+
+void h265_nal_encode(uint8_t buf[2], unsigned nal_unit_type,
+ unsigned nuh_temporal_id_plus1)
+{
+ if (!buf)
+ return;
+
+ buf[0] = (nal_unit_type & 0x3f) << 1;
+ buf[1] = nuh_temporal_id_plus1 & 0x07;
+}
+
+
+int h265_nal_encode_mbuf(struct mbuf *mb, const struct h265_nal *nal)
+{
+ uint8_t buf[2];
+
+ h265_nal_encode(buf, nal->nal_unit_type, nal->nuh_temporal_id_plus1);
+
+ return mbuf_write_mem(mb, buf, sizeof(buf));
+}
+
+
+int h265_nal_decode(struct h265_nal *nal, const uint8_t *p)
+{
+ bool forbidden_zero_bit;
+ unsigned nuh_layer_id;
+
+ if (!nal || !p)
+ return EINVAL;
+
+ forbidden_zero_bit = p[0] >> 7;
+ nal->nal_unit_type = (p[0] >> 1) & 0x3f;
+ nuh_layer_id = (p[0]&1)<<5 | p[1] >> 3;
+ nal->nuh_temporal_id_plus1 = p[1] & 0x07;
+
+ if (forbidden_zero_bit) {
+ re_fprintf(stderr, "?!?!?!?! FORBIDDEN !!! ?!?!?!*\n");
+ return EBADMSG;
+ }
+ if (nuh_layer_id != 0) {
+ re_fprintf(stderr, "h265_nal_decode: LayerId MUST be zero\n");
+ return EBADMSG;
+ }
+
+ return 0;
+}
+
+
+void h265_nal_print(const struct h265_nal *nal)
+{
+ re_printf("type=%u(%s), TID=%u\n",
+ nal->nal_unit_type,
+ h265_nalunit_name(nal->nal_unit_type),
+ nal->nuh_temporal_id_plus1);
+}
+
+
+static const uint8_t sc3[3] = {0, 0, 1};
+static const uint8_t sc4[4] = {0, 0, 0, 1};
+
+
+void h265_skip_startcode(uint8_t **p, size_t *n)
+{
+ if (*n < 4)
+ return;
+
+ if (0 == memcmp(*p, sc4, 4)) {
+ (*p) += 4;
+ *n -= 4;
+ }
+ else if (0 == memcmp(*p, sc3, 3)) {
+ (*p) += 3;
+ *n -= 3;
+ }
+}
+
+
+bool h265_have_startcode(const uint8_t *p, size_t len)
+{
+ if (len >= 4 && 0 == memcmp(p, sc4, 4)) return true;
+ if (len >= 3 && 0 == memcmp(p, sc3, 3)) return true;
+
+ return false;
+}
+
+
+bool h265_is_keyframe(enum h265_naltype type)
+{
+ /* between 16 and 21 (inclusive) */
+ switch (type) {
+
+ case H265_NAL_BLA_W_LP:
+ case H265_NAL_BLA_W_RADL:
+ case H265_NAL_BLA_N_LP:
+ case H265_NAL_IDR_W_RADL:
+ case H265_NAL_IDR_N_LP:
+ case H265_NAL_CRA_NUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+const char *h265_nalunit_name(enum h265_naltype type)
+{
+ switch (type) {
+
+ /* VCL class */
+ case H265_NAL_TRAIL_N: return "TRAIL_N";
+ case H265_NAL_TRAIL_R: return "TRAIL_R";
+
+ case H265_NAL_RASL_N: return "RASL_N";
+ case H265_NAL_RASL_R: return "RASL_R";
+
+ case H265_NAL_BLA_W_LP: return "BLA_W_LP";
+ case H265_NAL_BLA_W_RADL: return "BLA_W_RADL";
+ case H265_NAL_BLA_N_LP: return "BLA_N_LP";
+ case H265_NAL_IDR_W_RADL: return "IDR_W_RADL";
+ case H265_NAL_IDR_N_LP: return "IDR_N_LP";
+ case H265_NAL_CRA_NUT: return "CRA_NUT";
+
+ /* non-VCL class */
+ case H265_NAL_VPS_NUT: return "VPS_NUT";
+ case H265_NAL_SPS_NUT: return "SPS_NUT";
+ case H265_NAL_PPS_NUT: return "PPS_NUT";
+ case H265_NAL_PREFIX_SEI_NUT: return "PREFIX_SEI_NUT";
+ case H265_NAL_SUFFIX_SEI_NUT: return "SUFFIX_SEI_NUT";
+
+ /* draft-ietf-payload-rtp-h265 */
+ case H265_NAL_FU: return "H265_NAL_FU";
+ }
+
+ return "???";
+}
diff --git a/modules/h265/h265.c b/modules/h265/h265.c
new file mode 100644
index 0000000..04ad679
--- /dev/null
+++ b/modules/h265/h265.c
@@ -0,0 +1,68 @@
+/**
+ * @file h265.c H.265 Video Codec
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <libavcodec/avcodec.h>
+#include <x265.h>
+#include "h265.h"
+
+
+/**
+ * @defgroup h265 h265
+ *
+ * The H.265 video codec (aka HEVC)
+ *
+ * This is an experimental module adding support for H.265 video codec.
+ * The encoder is using x265 and the decoder is using libavcodec.
+ *
+ *
+ * References:
+ *
+ * draft-ietf-payload-rtp-h265-07
+ * http://x265.org/
+ * https://www.ffmpeg.org/
+ */
+
+
+static struct vidcodec h265 = {
+ .name = "H265",
+ .encupdh = h265_encode_update,
+ .ench = h265_encode,
+ .decupdh = h265_decode_update,
+ .dech = h265_decode,
+};
+
+
+static int module_init(void)
+{
+ info("h265: using x265 %s %s\n",
+ x265_version_str, x265_build_info_str);
+
+ avcodec_register_all();
+
+ vidcodec_register(&h265);
+
+ return 0;
+}
+
+
+static int module_close(void)
+{
+ vidcodec_unregister(&h265);
+
+ x265_cleanup();
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(h265) = {
+ "h265",
+ "vidcodec",
+ module_init,
+ module_close,
+};
diff --git a/modules/h265/h265.h b/modules/h265/h265.h
new file mode 100644
index 0000000..f537ea9
--- /dev/null
+++ b/modules/h265/h265.h
@@ -0,0 +1,69 @@
+/**
+ * @file h265.h H.265 Video Codec -- internal API
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+/*
+ * H.265 format
+ */
+enum {
+ H265_HDR_SIZE = 2
+};
+
+enum h265_naltype {
+ /* VCL class */
+ H265_NAL_TRAIL_N = 0,
+ H265_NAL_TRAIL_R = 1,
+
+ H265_NAL_RASL_N = 8,
+ H265_NAL_RASL_R = 9,
+
+ H265_NAL_BLA_W_LP = 16,
+ H265_NAL_BLA_W_RADL = 17,
+ H265_NAL_BLA_N_LP = 18,
+ H265_NAL_IDR_W_RADL = 19,
+ H265_NAL_IDR_N_LP = 20,
+ H265_NAL_CRA_NUT = 21,
+
+ /* non-VCL class */
+ H265_NAL_VPS_NUT = 32,
+ H265_NAL_SPS_NUT = 33,
+ H265_NAL_PPS_NUT = 34,
+ H265_NAL_PREFIX_SEI_NUT = 39,
+ H265_NAL_SUFFIX_SEI_NUT = 40,
+
+ /* draft-ietf-payload-rtp-h265 */
+ H265_NAL_FU = 49,
+};
+
+struct h265_nal {
+ unsigned nal_unit_type:6; /* NAL unit type (0-40) */
+ unsigned nuh_temporal_id_plus1:3; /* temporal identifier plus 1 */
+};
+
+void h265_nal_encode(uint8_t buf[2], unsigned nal_unit_type,
+ unsigned nuh_temporal_id_plus1);
+int h265_nal_encode_mbuf(struct mbuf *mb, const struct h265_nal *nal);
+int h265_nal_decode(struct h265_nal *nal, const uint8_t *p);
+void h265_nal_print(const struct h265_nal *nal);
+
+bool h265_have_startcode(const uint8_t *p, size_t len);
+void h265_skip_startcode(uint8_t **p, size_t *n);
+bool h265_is_keyframe(enum h265_naltype type);
+const char *h265_nalunit_name(enum h265_naltype type);
+
+
+/* encoder */
+int h265_encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
+ struct videnc_param *prm, const char *fmtp);
+int h265_encode(struct videnc_state *ves, bool update,
+ const struct vidframe *frame,
+ videnc_packet_h *pkth, void *arg);
+
+/* decoder */
+int h265_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc,
+ const char *fmtp);
+int h265_decode(struct viddec_state *vds, struct vidframe *frame,
+ bool marker, uint16_t seq, struct mbuf *mb);
diff --git a/modules/h265/module.mk b/modules/h265/module.mk
new file mode 100644
index 0000000..1600a61
--- /dev/null
+++ b/modules/h265/module.mk
@@ -0,0 +1,12 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+MOD := h265
+$(MOD)_SRCS += h265.c encode.c decode.c fmt.c
+$(MOD)_LFLAGS += -lavcodec -lavutil -lx265
+CFLAGS +=
+
+include mk/mod.mk
diff --git a/modules/h265/notes b/modules/h265/notes
new file mode 100644
index 0000000..0f01c5a
--- /dev/null
+++ b/modules/h265/notes
@@ -0,0 +1,75 @@
+notes:
+-----
+
+
+x265 [info]: HEVC encoder version 1.4-253-g920d714
+x265 [info]: build info [Linux][GCC 4.9.1][64 bit] 8bpp
+x265 [info]: using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX
+x265 [info]: Main profile, Level-2 (Main tier)
+x265 [info]: WPP streams / frame threads / pool : 8 / 2 / 4
+x265 [info]: CTU size / RQT depth inter / intra : 32 / 1 / 1
+x265 [info]: ME / range / subpel / merge : dia / 25 / 0 / 2
+x265 [info]: Keyframe min / max / scenecut : 25 / 250 / 0
+x265 [info]: Lookahead / bframes / badapt : 10 / 4 / 0
+x265 [info]: b-pyramid / weightp / weightb / refs: 1 / 0 / 0 / 1
+x265 [info]: Rate Control / AQ-Strength / CUTree : CRF-28.0 / 0.0 / 0
+x265 [info]: VBV/HRD buffer / max-rate / init : 40960 / 512 / 0.900
+x265 [info]: tools: rd=2 early-skip fast-intra tmvp
+
+
+x265 [info]: frame I: 1, Avg QP:25.40 kb/s: 639.60
+x265 [info]: frame P: 3, Avg QP:30.29 kb/s: 256.93
+x265 [info]: frame B: 12, Avg QP:32.92 kb/s: 58.68
+x265 [info]: global : 16, Avg QP:31.96 kb/s: 132.16
+x265 [info]: consecutive B-frames: 20.0% 0.0% 0.0% 0.0% 80.0%
+
+
+
+
+h265: decode: type=32 VPS_NUT
+h265: decode: type=33 SPS_NUT
+h265: decode: type=34 PPS_NUT
+h265: decode: type=39 PREFIX_SEI_NUT
+h265: decode: type=39 PREFIX_SEI_NUT
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+h265: decode: type=49 H265_NAL_FU
+
+
+
+
+Test-call between 2 peers (A) and (B):
+
+
+(A):
+
+video Transmit: Receive:
+packets: 435 2001
+avg. bitrate: 56.0 416.0 (kbit/s)
+errors: 0 0
+pkt.report: 417 1939
+lost: 0 0
+jitter: 23.3 0.4 (ms)
+
+
+(B):
+
+video Transmit: Receive:
+packets: 2012 435
+avg. bitrate: 416.0 56.0 (kbit/s)
+errors: 0 0
+pkt.report: 1939 417
+lost: 0 0
+jitter: 0.4 23.3 (ms)
+
+
+
+[END]