int decompress_image(struct vpx_context *ctx, const uint8_t *in, int size, uint8_t *out[3], int outstride[3]) { vpx_image_t *img; int frame_sz = size; vpx_codec_iter_t iter = NULL; const uint8_t *frame = in; int i = 0; if (vpx_codec_decode(&ctx->codec, frame, frame_sz, NULL, 0)) { codec_error(&ctx->codec, "vpx_codec_decode"); return -1; } img = vpx_codec_get_frame(&ctx->codec, &iter); if (img == NULL) { codec_error(&ctx->codec, "vpx_codec_get_frame"); return -1; } for (i = 0; i < 3; i++) { out[i] = img->planes[i]; outstride[i] = img->stride[i]; } ctx->pixfmt = img->fmt; return 0; }
bool WebmExporter::add_frame(const vpx_image* data) { auto result = vpx_codec_encode(&_codec, data, _frame_index++, 1, 0, _settings.quality <= 1 ? VPX_DL_BEST_QUALITY : VPX_DL_GOOD_QUALITY); if (result != VPX_CODEC_OK) { codec_error("couldn't encode frame"); return false; } vpx_codec_iter_t iter = nullptr; const vpx_codec_cx_pkt_t* packet = nullptr; bool found_packet = false; while (packet = vpx_codec_get_cx_data(&_codec, &iter)) { found_packet = true; if (packet->kind != VPX_CODEC_CX_FRAME_PKT) { continue; } auto timestamp_ns = 1000000000 * packet->data.frame.pts / _settings.fps; bool result = _segment.AddFrame((uint8_t*) packet->data.frame.buf, packet->data.frame.sz, _video_track, timestamp_ns, packet->data.frame.flags & VPX_FRAME_IS_KEY); if (!result) { std::cerr << "couldn't add frame" << std::endl; return false; } } return found_packet; };
WebmExporter::~WebmExporter() { if (_img) { vpx_img_free(_img); } // Flush encoder. while (add_frame(nullptr)) ; if (vpx_codec_destroy(&_codec)) { codec_error("failed to destroy codec"); return; } if (!_segment.Finalize()) { std::cerr << "couldn't finalise muxer segment" << std::endl; return; } _writer.Close(); }
struct vpx_context *init_decoder(int width, int height, const char *colorspace) { int flags = 0; int err = 0; vpx_codec_iface_t *codec_iface = NULL; struct vpx_context *ctx = malloc(sizeof(struct vpx_context)); if (ctx == NULL) return NULL; codec_iface = vpx_codec_vp8_dx(); memset(ctx, 0, sizeof(struct vpx_context)); err = vpx_codec_dec_init(&ctx->codec, codec_iface, NULL, flags); if (err) { codec_error(&ctx->codec, "vpx_codec_dec_init"); printf("vpx_codec_dec_init(..) failed with error %d\n", err); free(ctx); return NULL; } ctx->width = width; ctx->height = height; return ctx; }
int compress_image(struct vpx_context *ctx, uint8_t *input[3], int input_stride[3], uint8_t **out, int *outsz) { struct vpx_image image; const vpx_codec_cx_pkt_t *pkt; vpx_codec_iter_t iter = NULL; int frame_cnt = 0; int flags = 0; int i = 0; /* Encoding */ memset(&image, 0, sizeof(struct vpx_image)); image.w = ctx->width; image.h = ctx->height; image.fmt = ctx->pixfmt; image.planes[0] = input[0]; image.planes[1] = input[1]; image.planes[2] = input[2]; image.stride[0] = input_stride[0]; image.stride[1] = input_stride[1]; image.stride[2] = input_stride[2]; image.d_w = ctx->width; image.d_h = ctx->height; image.x_chroma_shift = 0; image.y_chroma_shift = 0; image.bps = 8; i = vpx_codec_encode(&ctx->codec, &image, frame_cnt, 1, flags, VPX_DL_REALTIME); if (i) { codec_error(&ctx->codec, "vpx_codec_encode"); return i; } pkt = vpx_codec_get_cx_data(&ctx->codec, &iter); if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) { return 1; } *out = pkt->data.frame.buf; *outsz = pkt->data.frame.sz; return 0; }
struct vpx_context *init_encoder(int width, int height, const char *colorspace) { vpx_codec_enc_cfg_t cfg; vpx_img_fmt_t vpx_colorspace; struct vpx_context *ctx; vpx_codec_iface_t *codec_iface; vpx_colorspace = get_vpx_colorspace(colorspace); if (vpx_colorspace<0) return NULL; codec_iface = vpx_codec_vp8_cx(); if (vpx_codec_enc_config_default(codec_iface, &cfg, 0)) return NULL; cfg.rc_target_bitrate = width * height * cfg.rc_target_bitrate / cfg.g_w / cfg.g_h; cfg.g_w = width; cfg.g_h = height; cfg.g_error_resilient = 0; cfg.g_lag_in_frames = 0; cfg.rc_dropframe_thresh = 0; //cfg.rc_resize_allowed = 1; ctx = malloc(sizeof(struct vpx_context)); if (ctx == NULL) return NULL; memset(ctx, 0, sizeof(struct vpx_context)); if (vpx_codec_enc_init(&ctx->codec, codec_iface, &cfg, 0)) { codec_error(&ctx->codec, "vpx_codec_enc_init"); free(ctx); return NULL; } ctx->width = width; ctx->height = height; ctx->pixfmt = vpx_colorspace; return ctx; }
WebmExporter::WebmExporter(const exporter_settings& settings) : _success{false}, _settings(settings), _video_track{0}, _img{nullptr}, _frame_index{0} { if (!_writer.Open(settings.path.c_str())) { std::cerr << "couldn't open " << settings.path << " for writing" << std::endl; return; } if (!_segment.Init(&_writer)) { std::cerr << "couldn't initialise muxer segment" << std::endl; return; } _segment.set_mode(mkvmuxer::Segment::kFile); _segment.OutputCues(true); _segment.GetSegmentInfo()->set_writing_app("trance"); _video_track = _segment.AddVideoTrack(settings.width, settings.height, 0); if (!_video_track) { std::cerr << "couldn't add video track" << std::endl; return; } auto video = (mkvmuxer::VideoTrack*) _segment.GetTrackByNumber(_video_track); if (!video) { std::cerr << "couldn't get video track" << std::endl; return; } video->set_frame_rate(settings.fps); _segment.GetCues()->set_output_block_number(true); _segment.CuesTrack(_video_track); // See http://www.webmproject.org/docs/encoder-parameters. vpx_codec_enc_cfg_t cfg; if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &cfg, 0)) { std::cerr << "couldn't get default codec config" << std::endl; return; } auto area = _settings.width * _settings.height; auto bitrate = (1 + 2 * (4 - _settings.quality)) * area / 1024; cfg.g_w = _settings.width; cfg.g_h = _settings.height; cfg.g_timebase.num = 1; cfg.g_timebase.den = _settings.fps; cfg.g_lag_in_frames = 24; cfg.g_threads = settings.threads; cfg.kf_min_dist = 0; cfg.kf_max_dist = 256; cfg.rc_target_bitrate = bitrate; if (vpx_codec_enc_init(&_codec, vpx_codec_vp8_cx(), &cfg, 0)) { codec_error("couldn't initialise encoder"); return; } _img = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, _settings.width, _settings.height, 16); if (!_img) { std::cerr << "couldn't allocate image for encoding" << std::endl; return; } _success = true; }