/* * Copyright (c) 2012 Yoann Blein * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #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& 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& 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(r); data[i + 1] = static_cast(g); data[i + 2] = static_cast(b); i += 3; } } return rgbImg; } }