static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) { vpx_context_t *context = NULL; int encoding, decoding; encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); decoding = (flags & SWITCH_CODEC_FLAG_DECODE); if (!(encoding || decoding) || ((context = switch_core_alloc(codec->memory_pool, sizeof(*context))) == 0)) { return SWITCH_STATUS_FALSE; } memset(context, 0, sizeof(*context)); context->flags = flags; codec->private_info = context; context->pool = codec->memory_pool; if (codec_settings) { context->codec_settings = *codec_settings; } if (!strcmp(codec->implementation->iananame, "VP9")) { context->is_vp9 = 1; context->encoder_interface = vpx_codec_vp9_cx(); context->decoder_interface = vpx_codec_vp9_dx(); } else { context->encoder_interface = vpx_codec_vp8_cx(); context->decoder_interface = vpx_codec_vp8_dx(); } if (codec->fmtp_in) { codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in); } if (vpx_codec_enc_config_default(context->encoder_interface, &context->config, 0) != VPX_CODEC_OK) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder config Error\n"); return SWITCH_STATUS_FALSE; } context->codec_settings.video.width = 320; context->codec_settings.video.height = 240; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VPX VER:%s VPX_IMAGE_ABI_VERSION:%d VPX_CODEC_ABI_VERSION:%d\n", vpx_codec_version_str(), VPX_IMAGE_ABI_VERSION, VPX_CODEC_ABI_VERSION); return SWITCH_STATUS_SUCCESS; }
nsresult SoftwareWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight) { int decode_threads = 2; //Default to 2 threads for small sizes or VP8 vpx_codec_iface_t* dx = nullptr; switch(mReader->GetVideoCodec()) { case NESTEGG_CODEC_VP8: dx = vpx_codec_vp8_dx(); break; case NESTEGG_CODEC_VP9: dx = vpx_codec_vp9_dx(); if (aWidth >= 2048) { decode_threads = 8; } else if (aWidth >= 1024) { decode_threads = 4; } break; } // Never exceed the number of system cores! decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors()); vpx_codec_dec_cfg_t config; config.threads = decode_threads; config.w = aWidth; config.h = aHeight; if (!dx || vpx_codec_dec_init(&mVPX, dx, &config, 0)) { return NS_ERROR_FAILURE; } return NS_OK; }
static vpx_codec_iface_t *get_codec_interface(unsigned int fourcc) { switch (fourcc) { case VP8_FOURCC: return vpx_codec_vp8_dx(); case VP9_FOURCC: return vpx_codec_vp9_dx(); } return NULL; }
nsresult SoftwareWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight) { vpx_codec_iface_t* dx = nullptr; switch(mReader->GetVideoCodec()) { case NESTEGG_CODEC_VP8: dx = vpx_codec_vp8_dx(); break; case NESTEGG_CODEC_VP9: dx = vpx_codec_vp9_dx(); break; } if (!dx || vpx_codec_dec_init(&mVPX, dx, nullptr, 0)) { return NS_ERROR_FAILURE; } return NS_OK; }
bool SoftwareWebMVideoDecoder::DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) { MOZ_ASSERT(mReader->OnTaskQueue()); // Record number of frames decoded and parsed. Automatically update the // stats counters using the AutoNotifyDecoded stack-based class. AbstractMediaDecoder::AutoNotifyDecoded a(mReader->GetDecoder()); nsAutoRef<NesteggPacketHolder> holder(mReader->NextPacket(WebMReader::VIDEO)); if (!holder) { return false; } nestegg_packet* packet = holder->mPacket; unsigned int track = 0; int r = nestegg_packet_track(packet, &track); if (r == -1) { return false; } unsigned int count = 0; r = nestegg_packet_count(packet, &count); if (r == -1) { return false; } uint64_t tstamp = 0; r = nestegg_packet_tstamp(packet, &tstamp); if (r == -1) { return false; } // The end time of this frame is the start time of the next frame. Fetch // the timestamp of the next packet for this track. If we've reached the // end of the resource, use the file's duration as the end time of this // video frame. uint64_t next_tstamp = 0; nsAutoRef<NesteggPacketHolder> next_holder(mReader->NextPacket(WebMReader::VIDEO)); if (next_holder) { r = nestegg_packet_tstamp(next_holder->mPacket, &next_tstamp); if (r == -1) { return false; } mReader->PushVideoPacket(next_holder.disown()); } else { next_tstamp = tstamp; next_tstamp += tstamp - mReader->GetLastVideoFrameTime(); } mReader->SetLastVideoFrameTime(tstamp); int64_t tstamp_usecs = tstamp / NS_PER_USEC; for (uint32_t i = 0; i < count; ++i) { unsigned char* data; size_t length; r = nestegg_packet_data(packet, i, &data, &length); if (r == -1) { return false; } vpx_codec_stream_info_t si; memset(&si, 0, sizeof(si)); si.sz = sizeof(si); if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP8) { vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si); } else if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP9) { vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si); } if (aKeyframeSkip && (!si.is_kf || tstamp_usecs < aTimeThreshold)) { // Skipping to next keyframe... a.mParsed++; // Assume 1 frame per chunk. a.mDropped++; continue; } if (aKeyframeSkip && si.is_kf) { aKeyframeSkip = false; } if (vpx_codec_decode(&mVPX, data, length, nullptr, 0)) { return false; } // If the timestamp of the video frame is less than // the time threshold required then it is not added // to the video queue and won't be displayed. if (tstamp_usecs < aTimeThreshold) { a.mParsed++; // Assume 1 frame per chunk. a.mDropped++; continue; } vpx_codec_iter_t iter = nullptr; vpx_image_t *img; while ((img = vpx_codec_get_frame(&mVPX, &iter))) { NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format not I420"); // Chroma shifts are rounded down as per the decoding examples in the SDK VideoData::YCbCrBuffer b; b.mPlanes[0].mData = img->planes[0]; b.mPlanes[0].mStride = img->stride[0]; b.mPlanes[0].mHeight = img->d_h; b.mPlanes[0].mWidth = img->d_w; b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0; b.mPlanes[1].mData = img->planes[1]; b.mPlanes[1].mStride = img->stride[1]; b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift; b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift; b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0; b.mPlanes[2].mData = img->planes[2]; b.mPlanes[2].mStride = img->stride[2]; b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift; b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift; b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0; nsIntRect pictureRect = mReader->GetPicture(); IntRect picture = pictureRect; nsIntSize initFrame = mReader->GetInitialFrame(); if (img->d_w != static_cast<uint32_t>(initFrame.width) || img->d_h != static_cast<uint32_t>(initFrame.height)) { // Frame size is different from what the container reports. This is // legal in WebM, and we will preserve the ratio of the crop rectangle // as it was reported relative to the picture size reported by the // container. picture.x = (pictureRect.x * img->d_w) / initFrame.width; picture.y = (pictureRect.y * img->d_h) / initFrame.height; picture.width = (img->d_w * pictureRect.width) / initFrame.width; picture.height = (img->d_h * pictureRect.height) / initFrame.height; } VideoInfo videoInfo = mReader->GetMediaInfo().mVideo; nsRefPtr<VideoData> v = VideoData::Create(videoInfo, mReader->GetDecoder()->GetImageContainer(), holder->mOffset, tstamp_usecs, (next_tstamp / NS_PER_USEC) - tstamp_usecs, b, si.is_kf, -1, picture); if (!v) { return false; } a.mParsed++; a.mDecoded++; NS_ASSERTION(a.mDecoded <= a.mParsed, "Expect only 1 frame per chunk per packet in WebM..."); mReader->VideoQueue().Push(v); } } return true; }