/* * Copyright (c) 2012 Yoann Blein * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include "vpx/vp8cx.h" namespace Swift { VP8Encoder::VP8Encoder(VP8RTPPacketizer* packetizer, unsigned int width, unsigned int height) : VideoEncoder(), packetizer(packetizer), codecInterface(vpx_codec_vp8_cx()), imageBuffer(0), codecFlags(0), frameFlags(0), frameNumber(0) { SWIFT_LOG(debug) << "VP8 Encoder:" << vpx_codec_iface_name(codecInterface) << std::endl; // Populate encoder configuration vpx_codec_err_t err = vpx_codec_enc_config_default(codecInterface, &codecConfig, 0); if (err) { SWIFT_LOG(error) << "VP8 Encoder: Failed to get config, " << vpx_codec_err_to_string(err) << std::endl; // TODO: exception } // Update the default configuration with our settings codecConfig.rc_target_bitrate = width * height * codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h; // Sample config on VP8 doc //codecConfig.rc_target_bitrate = 90000; // ? codecConfig.g_w = width; codecConfig.g_h = height; //codecConfig.g_threads = ?; // codecFlags = VPX_CODEC_USE_OUTPUT_PARTITION; // TODO: adapt packetization and decoder updateCodecConfig(); // TODO: Handle and re-throw exceptions } VP8Encoder::~VP8Encoder() { vpx_codec_destroy(&codecContext); vpx_img_free(imageBuffer); } void VP8Encoder::updateCodecConfig() { if (imageBuffer) vpx_img_free(imageBuffer); vpx_codec_err_t err = vpx_codec_enc_init(&codecContext, codecInterface, &codecConfig, codecFlags); if (err) { SWIFT_LOG(error) << "VP8 Encoder: Failed to initialize encoder, " << vpx_codec_err_to_string(err) << std::endl; // TODO: exception return; } imageBuffer = vpx_img_alloc(0, VPX_IMG_FMT_YV12, codecConfig.g_w, codecConfig.g_h, 1); if (!imageBuffer) { SWIFT_LOG(error) << "VP8 Encoder: Failed to allocate image" << std::endl; // TODO: exception } } void VP8Encoder::encodeImage(const Image& frame) { if (!convertRGB24toYV12inBuffer(frame)) { SWIFT_LOG(error) << "VP8 Encoder: Failed to convert frame: Image buffer not initialized" << std::endl; // TODO: exception ? return; } vpx_codec_err_t err = vpx_codec_encode(&codecContext, imageBuffer, frameNumber, 1, frameFlags, VPX_DL_REALTIME); if (err) { SWIFT_LOG(error) << "VP8 Encoder: Failed to encode frame, " << vpx_codec_err_to_string(err) << std::endl; // TODO: exception ? return; } const vpx_codec_cx_pkt_t* pkt; vpx_codec_iter_t iter = NULL; while ((pkt = vpx_codec_get_cx_data(&codecContext, &iter))) { switch (pkt->kind) { case VPX_CODEC_CX_FRAME_PKT: packetizer->packetizeFrame(pkt); break; default: break; } } ++frameNumber; } bool VP8Encoder::convertRGB24toYV12inBuffer(const Image& frame) { if (!imageBuffer) return false; uint8_t* yPlane = imageBuffer->planes[VPX_PLANE_Y]; uint8_t* uPlane = imageBuffer->planes[VPX_PLANE_U]; uint8_t* vPlane = imageBuffer->planes[VPX_PLANE_V]; unsigned int width = frame.width; unsigned int height = frame.height; unsigned int len = width * height; const std::vector& data = frame.data; for (unsigned int i = 0; i < len; ++i) { const uint8_t* p = &data[3 * i]; yPlane[i] = ((66 * p[0] + 129 * p[1] + 25 * p[2] + 128) >> 8) + 16; } for (unsigned int line = 0; line < height / 2; ++line) { for (unsigned int col = 0; col < width / 2; ++col) { int pos = 2 * (line * width + col); const uint8_t* p1 = &data[3 * (pos)]; const uint8_t* p2 = &data[3 * (pos + 1)]; const uint8_t* p3 = &data[3 * (pos + width)]; const uint8_t* p4 = &data[3 * (pos + width + 1)]; int r = (p1[0] + p2[0] + p3[0] + p4[0]) * 0.25; int g = (p1[1] + p2[1] + p3[1] + p4[1]) * 0.25; int b = (p1[2] + p2[2] + p3[2] + p4[2]) * 0.25; uPlane[line * imageBuffer->stride[VPX_PLANE_U] + col] = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; vPlane[line * imageBuffer->stride[VPX_PLANE_V] + col] = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; } } return true; } }