-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathopenh264_decoder.go
More file actions
184 lines (155 loc) · 4.97 KB
/
openh264_decoder.go
File metadata and controls
184 lines (155 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//go:build cgo && openh264
package codec
/*
#include <wels/codec_api.h>
#include <wels/codec_def.h>
#include <wels/codec_app_def.h>
#include <string.h>
#include <stdlib.h>
// oh264dec wraps an OpenH264 decoder instance.
typedef struct {
ISVCDecoder* dec;
} oh264dec_t;
static int oh264dec_open(oh264dec_t* h) {
long rc = WelsCreateDecoder(&h->dec);
if (rc != 0 || h->dec == NULL) {
return -1;
}
// Suppress verbose OpenH264 logging.
int logLevel = WELS_LOG_QUIET;
(*h->dec)->SetOption(h->dec, DECODER_OPTION_TRACE_LEVEL, &logLevel);
SDecodingParam param;
memset(¶m, 0, sizeof(SDecodingParam));
param.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;
param.eEcActiveIdc = ERROR_CON_SLICE_COPY;
rc = (*h->dec)->Initialize(h->dec, ¶m);
if (rc != 0) {
WelsDestroyDecoder(h->dec);
h->dec = NULL;
return -2;
}
return 0;
}
static void oh264dec_close(oh264dec_t* h) {
if (h->dec) {
(*h->dec)->Uninitialize(h->dec);
WelsDestroyDecoder(h->dec);
h->dec = NULL;
}
}
// oh264dec_decode decodes Annex B data into YUV420 planes.
// On success, dst_y/dst_u/dst_v point to decoder-owned buffers (valid until next call),
// and width/height/y_stride/uv_stride are set.
// Returns 0 on success with frame data, 1 if no output yet, negative on error.
static int oh264dec_decode(oh264dec_t* h,
unsigned char* src, int src_len,
unsigned char** dst_y, unsigned char** dst_u, unsigned char** dst_v,
int* width, int* height, int* y_stride, int* uv_stride) {
unsigned char* ptrs[3] = {NULL, NULL, NULL};
SBufferInfo buf_info;
memset(&buf_info, 0, sizeof(SBufferInfo));
DECODING_STATE state = (*h->dec)->DecodeFrameNoDelay(h->dec, src, src_len, ptrs, &buf_info);
if (state != dsErrorFree && state != dsDataErrorConcealed) {
return -1;
}
if (buf_info.iBufferStatus != 1) {
return 1; // no output frame yet
}
*dst_y = buf_info.pDst[0];
*dst_u = buf_info.pDst[1];
*dst_v = buf_info.pDst[2];
*width = buf_info.UsrData.sSystemBuffer.iWidth;
*height = buf_info.UsrData.sSystemBuffer.iHeight;
*y_stride = buf_info.UsrData.sSystemBuffer.iStride[0];
*uv_stride = buf_info.UsrData.sSystemBuffer.iStride[1];
return 0;
}
*/
import "C"
import (
"errors"
"fmt"
"unsafe"
"github.com/zsiec/switchframe/server/transition"
)
// Compile-time check that OpenH264Decoder implements transition.VideoDecoder.
var _ transition.VideoDecoder = (*OpenH264Decoder)(nil)
// OpenH264Decoder wraps the OpenH264 decoder and implements transition.VideoDecoder.
// It decodes Annex B H.264 bitstream to packed YUV420 planar.
type OpenH264Decoder struct {
handle C.oh264dec_t
closed bool
}
// NewOpenH264Decoder creates a new OpenH264 decoder instance.
func NewOpenH264Decoder() (*OpenH264Decoder, error) {
d := &OpenH264Decoder{}
rc := C.oh264dec_open(&d.handle)
if rc != 0 {
return nil, fmt.Errorf("failed to create OpenH264 decoder: code %d", int(rc))
}
return d, nil
}
// Decode decodes Annex B encoded H.264 data into packed YUV420 planar bytes.
// Returns the YUV buffer, width, height, and any error.
// The decoder output may have padded strides, so this method copies to a
// tightly-packed planar layout (Y: w*h, U: w/2*h/2, V: w/2*h/2).
func (d *OpenH264Decoder) Decode(data []byte) ([]byte, int, int, error) {
if d.closed {
return nil, 0, 0, errors.New("decoder is closed")
}
if len(data) == 0 {
return nil, 0, 0, errors.New("empty input data")
}
var dstY, dstU, dstV *C.uchar
var width, height, yStride, uvStride C.int
rc := C.oh264dec_decode(
&d.handle,
(*C.uchar)(unsafe.Pointer(&data[0])),
C.int(len(data)),
&dstY, &dstU, &dstV,
&width, &height, &yStride, &uvStride,
)
if rc > 0 {
// No output frame yet (buffering). Not an error but no data.
return nil, 0, 0, errors.New("no output frame yet (buffering)")
}
if rc < 0 {
return nil, 0, 0, fmt.Errorf("OpenH264 decode error: code %d", int(rc))
}
w := int(width)
h := int(height)
ys := int(yStride)
uvs := int(uvStride)
if dstY == nil || dstU == nil || dstV == nil {
return nil, 0, 0, errors.New("decoder returned nil plane pointers")
}
// Copy strided decoder output to tightly-packed planar buffer.
ySize := w * h
uvW := w / 2
uvH := h / 2
uvSize := uvW * uvH
out := make([]byte, ySize+2*uvSize)
// Copy Y plane.
srcY := unsafe.Slice((*byte)(unsafe.Pointer(dstY)), ys*h)
for row := 0; row < h; row++ {
copy(out[row*w:(row+1)*w], srcY[row*ys:row*ys+w])
}
// Copy U plane.
srcU := unsafe.Slice((*byte)(unsafe.Pointer(dstU)), uvs*uvH)
for row := 0; row < uvH; row++ {
copy(out[ySize+row*uvW:ySize+(row+1)*uvW], srcU[row*uvs:row*uvs+uvW])
}
// Copy V plane.
srcV := unsafe.Slice((*byte)(unsafe.Pointer(dstV)), uvs*uvH)
for row := 0; row < uvH; row++ {
copy(out[ySize+uvSize+row*uvW:ySize+uvSize+(row+1)*uvW], srcV[row*uvs:row*uvs+uvW])
}
return out, w, h, nil
}
// Close releases the decoder resources. Safe to call multiple times.
func (d *OpenH264Decoder) Close() {
if !d.closed {
C.oh264dec_close(&d.handle)
d.closed = true
}
}