diff --git a/lib/flags.Makefile b/lib/flags.Makefile index f0d2b5a29..7b9708b72 100644 --- a/lib/flags.Makefile +++ b/lib/flags.Makefile @@ -21,6 +21,7 @@ CODEC_SRCS += evs.strhash.c vorbis.c ac3.c atrac.c evrc.c amr.strhash.c pseudo.c CODEC_SRCS += g726.c l16.c u8.c mp3.c CODEC_SRCS += avc.c CODEC_SRCS += vp8.c +CODEC_SRCS += vp9.c endif LDFLAGS += -pie diff --git a/lib/vp9.c b/lib/vp9.c new file mode 100644 index 000000000..b672aadff --- /dev/null +++ b/lib/vp9.c @@ -0,0 +1,230 @@ +#include "codecmod.h" +#include "loglib.h" + + +static void vp9_decode_frame(decoder_t *dec, frame_q *out) { + dec->dec_ts = dec->rtp_ts; + + if (dec->packet_buffer->len) { + ilog(LOG_DEBUG, "Decoding %zu bytes of VP9", dec->packet_buffer->len); + + avc_decoder_input(dec, &STR_GS(dec->packet_buffer), out, false); + } + + g_string_truncate(dec->packet_buffer, 0); +} + + +static int vp9_decoder_input(decoder_t *dec, const str *data, frame_q *out, bool mark) { + if (!data) + goto err; + + if (dec->rtp_ts != dec->dec_ts) + vp9_decode_frame(dec, out); + + str frame = *data; + str b; + + if (str_shift_ret(&frame, 1, &b)) + goto err; + uint8_t iplfbevz = b.s[0]; + + if ((iplfbevz & 0x80)) { // I bit + if (str_shift_ret(&frame, 1, &b)) + goto err; + + uint8_t m_pid = b.s[0]; + if ((m_pid & 0x80)) { // M bit + if (str_shift_ret(&frame, 1, &b)) + goto err; + // XXX 2nd byte PID + } + } + + if ((iplfbevz & 0x20)) { // L bit + if (str_shift_ret(&frame, 1, &b)) + goto err; + //uint8_t tid_u_sid_d = b.s[0]; + + if (!(iplfbevz & 0x10)) { + // non-flexible mode + + if (str_shift_ret(&frame, 1, &b)) + goto err; + //uint8_t tl0picidx = b.s[0]; + } + } + + if ((iplfbevz & 0x50) == 0x50) { // both P and F + if (str_shift_ret(&frame, 1, &b)) + goto err; + uint8_t p_diff_n0 = b.s[0]; + + if ((p_diff_n0 & 0x01)) { + if (str_shift_ret(&frame, 1, &b)) + goto err; + uint8_t p_diff_n1 = b.s[0]; + + if ((p_diff_n1 & 0x01)) { + if (str_shift_ret(&frame, 1, &b)) + goto err; + uint8_t p_diff_n2 = b.s[0]; + + if ((p_diff_n2 & 0x01)) + goto err; // only max 3 allowed + } + } + } + + if ((iplfbevz & 0x02)) { // V bit + if (str_shift_ret(&frame, 1, &b)) + goto err; + uint8_t n_s_yg_RRR = b.s[0]; + + if ((n_s_yg_RRR & 0x10)) { // Y bit + uint8_t n_s = (n_s_yg_RRR >> 5) + 1; + + for (uint8_t i = 0; i < n_s; i++) { + if (str_shift_ret(&frame, 4, &b)) + goto err; + } + } + + if ((n_s_yg_RRR & 0x08)) { // G bit + if (str_shift_ret(&frame, 1, &b)) + goto err; + uint8_t n_g = b.s[0]; + + for (uint8_t i = 0; i < n_g; i++) { + if (str_shift_ret(&frame, 1, &b)) + goto err; + uint8_t tid_u_r_RR = b.s[0]; + + uint8_t r = (tid_u_r_RR >> 2) & 0x3; + for (uint8_t j = 0; j < r; j++) { + if (str_shift_ret(&frame, 1, &b)) + goto err; + //uint8_t p_diff = b.s[0]; + } + } + } + } + + g_string_append_len(dec->packet_buffer, frame.s, frame.len); + + if (mark) + vp9_decode_frame(dec, out); + + return 0; + +err: + ilog(LOG_ERR | LOG_FLAG_LIMIT, "Invalid VP9 packet"); + return -1; +} + + +static bool set_enc_options(encoder_t *enc, const str *codec_opts) { + // defaults + enc->avc.avcctx->gop_size = 10; + + codeclib_set_av_opt_int(enc, "lag-in-frames", 0); + codeclib_set_av_opt_int(enc, "cpu-used", 8); + codeclib_set_av_opt_int(enc, "deadline", 5000); + codeclib_set_av_opt_int(enc, "threads", 8); + + codeclib_key_value_parse(codec_opts, codeclib_avc_video_enc_options, enc); + + return true; +} + + +static const codec_type_t codec_type_vp9 = { + .def_init = avc_def_init, + .decoder_init = avc_video_decoder_init, + .decoder_input = vp9_decoder_input, + .decoder_close = avc_decoder_close, + .encoder_init = avc_encoder_init, + .encoder_input = avc_video_encoder_input, + .encoder_close = avc_encoder_close, +}; + + +static int packetizer_vp9_fn(AVPacket *pkt, str *output, size_t num_bytes, encoder_t *enc, + int64_t *__restrict pts, int64_t *__restrict duration, bool *mark) +{ + size_t out_len = 0; + GString *buf = enc->sample_buffer; + + output->s[0] = 0x80; // iplfbevz + out_len++; + + if (pkt) { + if (pkt->size < 3) + return -1; + + // new frame, move into buffer + g_string_truncate(buf, 0); + g_string_append_len(buf, (const char *) pkt->data, pkt->size); + ilog(LOG_DEBUG, "Packetising %u bytes of VP9", pkt->size); + + enc->packet_pts = pkt->pts; + enc->frame_idx++; + + output->s[0] |= 0x08; // B bit + } + + output->s[1] = 0x80 | ((enc->frame_idx >> 8) & 0x7f); // M + PID + output->s[2] = enc->frame_idx & 0xff; // PID + out_len += 2; + + size_t len = MIN(rtpe_common_config_ptr->video_pkt_size, output->len); + len = MIN(len, buf->len); + + memcpy(output->s + out_len, buf->str, len); + out_len += len; + + *pts = enc->packet_pts; + + g_string_erase(buf, 0, len); + + output->len = out_len; + + *mark = false; + + if (buf->len) + return 1; + + *mark = true; + output->s[0] |= 0x04; // E bit + + return 0; +} + +packetizer_t packetizer_vp9 = { + .init = packetizer_buffered_init, + .fn = packetizer_vp9_fn, + .destroy = packetizer_buffered_destroy, +}; + + +static const codec_def_t vp9 = { + .rtpname = "VP9", + .avcodec_id = AV_CODEC_ID_VP9, + .default_width = 1280, + .default_height = 720, + .avcodec_name_enc = "libvpx-vp9", + .avcodec_name_dec = "libvpx-vp9", + .default_bitrate = 2000000, + .packetizer = &packetizer_vp9, + .media_type = MT_VIDEO, + .codec_type = &codec_type_vp9, + .set_enc_options = set_enc_options, + .format_cmp = format_cmp_ignore, + .default_fmtp = "max-fr:30;max-fs:1800;profile-id=0", +}; + + +__attribute__((constructor)) +static void init(void) { + codeclib_register_codec(&vp9); +} diff --git a/t/auto-daemon-tests-create.pl b/t/auto-daemon-tests-create.pl index 75f597c49..99f45537e 100755 --- a/t/auto-daemon-tests-create.pl +++ b/t/auto-daemon-tests-create.pl @@ -2577,6 +2577,7 @@ c=IN IP4 203.0.113.1 a=rtpmap:96 VP8/90000 a=fmtp:96 max-fr:30;max-fs:1800 a=rtpmap:97 VP9/90000 +a=fmtp:97 max-fr:30;max-fs:1800;profile-id=0 a=sendrecv a=rtcp:PORT SDP