Skip to content
This repository has been archived by the owner on Jul 2, 2021. It is now read-only.

Commit

Permalink
feat(elements): vpx decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
tarrencev committed Sep 30, 2020
1 parent dcf8d8a commit 33488c9
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 12 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.13

require (
github.com/at-wat/ebml-go v0.11.0
github.com/dialup-inc/ascii v0.0.0-20191031215043-07c3fdeef4e5
github.com/golang/protobuf v1.4.2
github.com/pion/ion-sfu v1.0.19
github.com/pion/rtcp v1.2.4
Expand Down
53 changes: 53 additions & 0 deletions go.sum

Large diffs are not rendered by default.

41 changes: 29 additions & 12 deletions pkg/elements/decoder.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,63 @@
package elements

/*
#cgo pkg-config: --static vpx
#include "vpx/vpx_decoder.h"
int vpx_init_dec(vpx_codec_ctx_t *ctx);
int vpx_decode(vpx_codec_ctx_t *ctx, const char* frame, int frame_len, char* yv12_frame, int yv12_len, int *decoded_len);
int vpx_cleanup_dec(vpx_codec_ctx_t *ctx);
*/
import "C"
import (
"bytes"

avp "github.com/pion/ion-avp/pkg"
"golang.org/x/image/vp8"
"github.com/pion/ion-avp/pkg/elements/vpx"
)

// Decoder instance
type Decoder struct {
Node
decoder *vp8.Decoder
width int
height int
buf []byte
decoder *vpx.Decoder
}

// NewDecoder instance. Decoder takes as input VP8 keyframes
// and decodes it into a YCbCr image.
func NewDecoder() *Decoder {
return &Decoder{
decoder: vp8.NewDecoder(),
decoder: vpx.NewDecoder(),
}
}

func (d *Decoder) Write(sample *avp.Sample) error {
if sample.Type == avp.TypeVP8 {
payload := sample.Payload.([]byte)

d.decoder.Init(bytes.NewReader(payload), len(payload))
// Read VP8 header.
videoKeyframe := (payload[0]&0x1 == 0)

// Decode header
if _, err := d.decoder.DecodeFrameHeader(); err != nil {
return err
if videoKeyframe {
// Keyframe has frame information.
raw := uint(payload[6]) | uint(payload[7])<<8 | uint(payload[8])<<16 | uint(payload[9])<<24
width := int(raw & 0x3FFF)
height := int((raw >> 16) & 0x3FFF)

if d.width != width || d.height != height {
d.width = width
d.height = height
d.buf = make([]byte, width*height*4)
}
}

// Decode Frame
img, err := d.decoder.DecodeFrame()
n, err := d.decoder.Decode(d.buf, payload)
if err != nil {
return err
}

return d.Node.Write(&avp.Sample{
Type: TypeYCbCr,
Payload: img,
Payload: d.buf[:n],
})
}

Expand Down
62 changes: 62 additions & 0 deletions pkg/elements/vpx/decoder.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// https://github.com/dialup-inc/ascii/blob/master/vpx/decoder.c
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define VPX_CODEC_DISABLE_COMPAT 1
#include "vpx/vp8dx.h"
#include "vpx/vpx_decoder.h"

int vpx_init_dec(vpx_codec_ctx_t *ctx) {
vpx_codec_iface_t *interface = vpx_codec_vp8_dx();

// Initialize codec
int flags = 0;
vpx_codec_err_t err = vpx_codec_dec_init(ctx, interface, NULL, flags);
if (err) {
return err;
}

return 0;
}

int vpx_decode(vpx_codec_ctx_t *ctx, const char *frame, int frame_len,
char *yv12_frame, int yv12_cap, int *yv12_len) {
// Decode the frame
vpx_codec_err_t err =
vpx_codec_decode(ctx, (const unsigned char *)frame, frame_len, NULL, 0);
if (err) {
return err;
}

// Write decoded data to yv12_frame
vpx_codec_iter_t iter = NULL;
vpx_image_t *img;

while ((img = vpx_codec_get_frame(ctx, &iter))) {
for (int plane = 0; plane < 3; plane++) {
unsigned char *buf = img->planes[plane];
for (int y = 0; y < (plane ? (img->d_h + 1) >> 1 : img->d_h); y++) {
if (*yv12_len > yv12_cap) {
return VPX_CODEC_MEM_ERROR;
}

int len = (plane ? (img->d_w + 1) >> 1 : img->d_w);
memcpy(yv12_frame + *yv12_len, buf, len);
buf += img->stride[plane];
*yv12_len += len;
}
}
}

return 0;
}

int vpx_cleanup_dec(vpx_codec_ctx_t *ctx) {
vpx_codec_err_t err = vpx_codec_destroy(ctx);
if (err) {
return err;
}
return 0;
}
71 changes: 71 additions & 0 deletions pkg/elements/vpx/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// https://github.com/dialup-inc/ascii/blob/master/vpx/decoder.go
package vpx

/*
#cgo pkg-config: --static vpx
#include "vpx/vpx_decoder.h"
int vpx_init_dec(vpx_codec_ctx_t *ctx);
int vpx_decode(vpx_codec_ctx_t *ctx, const char* frame, int frame_len, char* yv12_frame, int yv12_len, int *decoded_len);
int vpx_cleanup_dec(vpx_codec_ctx_t *ctx);
*/
import "C"
import (
"sync"
"unsafe"
)

type Decoder struct {
mu sync.Mutex
ctx C.vpx_codec_ctx_t
}

func NewDecoder() (*Decoder, error) {
d := &Decoder{}
ret := C.vpx_init_dec(&d.ctx)
if ret != 0 {
return nil, VPXCodecErr(ret)
}
return d, nil
}

func (d *Decoder) Close() error {
d.mu.Lock()
defer d.mu.Unlock()

ret := C.vpx_cleanup_dec(&d.ctx)
if ret != 0 {
return VPXCodecErr(ret)
}
return nil
}

func (d *Decoder) Decode(out, b []byte) (int, error) {
d.mu.Lock()
defer d.mu.Unlock()

if len(b) == 0 {
return 0, nil
}

n, err := d.decode(out, b)
if err != nil {
return 0, err
}
return n, nil
}

func (d *Decoder) decode(out, in []byte) (n int, err error) {
inP := (*C.char)(unsafe.Pointer(&in[0]))
inL := C.int(len(in))

outP := (*C.char)(unsafe.Pointer(&out[0]))
outCap := C.int(cap(out))
outL := (*C.int)(unsafe.Pointer(&n))

ret := C.vpx_decode(&d.ctx, inP, inL, outP, outCap, outL)
if ret != 0 {
return n, VPXCodecErr(ret)
}

return n, nil
}
46 changes: 46 additions & 0 deletions pkg/elements/vpx/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// https://github.com/dialup-inc/ascii/blob/master/vpx/errors.go
package vpx

import "fmt"

type VPXCodecErr int

const (
VPX_CODEC_OK VPXCodecErr = iota
VPX_CODEC_ERROR
VPX_CODEC_MEM_ERROR
VPX_CODEC_ABI_MISMATCH
VPX_CODEC_INCAPABLE
VPX_CODEC_UNSUP_BITSTREAM
VPX_CODEC_UNSUP_FEATURE
VPX_CODEC_CORRUPT_FRAME
VPX_CODEC_INVALID_PARAM
VPX_CODEC_LIST_END
)

func (e VPXCodecErr) Error() string {
switch e {
case VPX_CODEC_OK:
return "Success"
case VPX_CODEC_ERROR:
return "Unspecified internal error"
case VPX_CODEC_MEM_ERROR:
return "Memory allocation error"
case VPX_CODEC_ABI_MISMATCH:
return "ABI version mismatch"
case VPX_CODEC_INCAPABLE:
return "Codec does not implement requested capability"
case VPX_CODEC_UNSUP_BITSTREAM:
return "Bitstream not supported by this decoder"
case VPX_CODEC_UNSUP_FEATURE:
return "Bitstream required feature not supported by this decoder"
case VPX_CODEC_CORRUPT_FRAME:
return "Corrupt frame detected"
case VPX_CODEC_INVALID_PARAM:
return "Invalid parameter"
case VPX_CODEC_LIST_END:
return "End of iterated list"
default:
return fmt.Sprintf("codec error: %d", e)
}
}

0 comments on commit 33488c9

Please # to comment.