summaryrefslogtreecommitdiffstats
blob: 90f5101ffd2c25e167e1398b5f10f0d0d36fe0c6 (plain)
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
/*
 * Copyright (c) 2012 Yoann Blein
 * Licensed under the simplified BSD license.
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 */

#include <Swiften/ScreenSharing/VP8Decoder.h>

#include <Swiften/ScreenSharing/Image.h>
#include <Swiften/Base/Log.h>
#include "vpx/vp8dx.h"

namespace Swift {

inline int clamp8(int v)
{
	return std::min(std::max(v, 0), 255);
}

VP8Decoder::VP8Decoder()
	: VideoDecoder(),
	  codecInterface(vpx_codec_vp8_dx()), codecFlags(0), refFrame(0)
{
	SWIFT_LOG(debug) << "VP8 Decoder:" << vpx_codec_iface_name(codecInterface) << std::endl;

	updateCodecConfig();
}

VP8Decoder::~VP8Decoder()
{
	vpx_codec_destroy(&codecContext);
	if (refFrame) {
		vpx_img_free(&refFrame->img);
		delete refFrame;
	}
}

void VP8Decoder::updateCodecConfig()
{
	// TODO: vpx_codec_flags_t flags = VPX_CODEC_USE_INPUT_FRAGMENTS;
	vpx_codec_err_t err = vpx_codec_dec_init(&codecContext, codecInterface, NULL, codecFlags);
	if (err) {
		SWIFT_LOG(debug) << "VP8 Decoder: Failed to initialize decoder, " << vpx_codec_err_to_string(err) << std::endl;
		// TODO: exception ?
	}
}

void VP8Decoder::decodeFrame(const std::vector<uint8_t>& frame, int pictureID)
{
	// Decode the frame
	vpx_codec_err_t err = vpx_codec_decode(&codecContext, &frame[0], frame.size(), NULL, 0);
	if (err) {
		SWIFT_LOG(debug) << "VP8 Decoder: Failed to decode frame, " << vpx_codec_err_to_string(err) << std::endl;
		onNewFrameDecoded(pictureID, false);
		return;
	} else {
		onNewFrameDecoded(pictureID, true);
	}

	vpx_codec_iter_t iter = NULL;
	vpx_image_t* decodedImg;
	while ((decodedImg = vpx_codec_get_frame(&codecContext, &iter))) {
		Image rgbImg = convertYV12toRGB(decodedImg);
		onNewImageAvailable(rgbImg);
		// Restore ref from last save if corrupted, else save the ref
		int corrupted = 0;
		vpx_codec_control(&codecContext, VP8D_GET_FRAME_CORRUPTED, &corrupted);
		if (corrupted) {
			vpx_codec_control(&codecContext, VP8_SET_REFERENCE, refFrame);
		} else {
			// Re-alloc the image ref if the format has changed
			if (refFrame && (decodedImg->d_w != refFrame->img.d_w
							 || decodedImg->d_h != refFrame->img.d_h
							 || decodedImg->fmt != refFrame->img.fmt)) {
				vpx_img_free(&refFrame->img);
				delete refFrame;
				refFrame = 0;
			}
			if (!refFrame) {
				refFrame = new vpx_ref_frame_t;
				refFrame->frame_type = VP8_LAST_FRAME;
				unsigned int align = 1;
				if (decodedImg->d_w % 32 == 0)
					align = 32;
				vpx_img_alloc(&refFrame->img, decodedImg->fmt, decodedImg->d_w, decodedImg->d_h, align);
			}
			vpx_codec_control(&codecContext, VP8_COPY_REFERENCE, refFrame);
		}
	}
}

Image VP8Decoder::convertYV12toRGB(const vpx_image_t* img)
{
	Image rgbImg(img->d_w, img->d_h);
	std::vector<uint8_t>& data = rgbImg.data;

	uint8_t *yPlane = img->planes[VPX_PLANE_Y];
	uint8_t *uPlane = img->planes[VPX_PLANE_U];
	uint8_t *vPlane = img->planes[VPX_PLANE_V];

	int i = 0;
	for (unsigned int imgY = 0; imgY < img->d_h; imgY++) {
		for (unsigned int imgX = 0; imgX < img->d_w; imgX++) {
			int y = yPlane[imgY * img->stride[VPX_PLANE_Y] + imgX];
			int u = uPlane[(imgY / 2) * img->stride[VPX_PLANE_U] + (imgX / 2)];
			int v = vPlane[(imgY / 2) * img->stride[VPX_PLANE_V] + (imgX / 2)];

			int c = y - 16;
			int d = (u - 128);
			int e = (v - 128);

			// TODO: adjust colors ?

			int r = clamp8((298 * c           + 409 * e + 128) >> 8);
			int g = clamp8((298 * c - 100 * d - 208 * e + 128) >> 8);
			int b = clamp8((298 * c + 516 * d           + 128) >> 8);

			// TODO: cast instead of clamp8

			data[i + 0] = static_cast<uint8_t>(r);
			data[i + 1] = static_cast<uint8_t>(g);
			data[i + 2] = static_cast<uint8_t>(b);

			i += 3;
		}
	}
	return rgbImg;
}

}