ffmpeg vs2017录制vp9格式的桌面视频

ffmpeg本身不支持vp8,vp9;若要支持,则需要自己事先编译好vpx库,然后在ffmpeg中显式支持vpx库。

windows下采用mingw编译器编译vpx;本人采取的是MSYS2,并且本人编译vpx和ffmpeg时,编译的都是静态库,故而在编译vpx和ffmpeg的时候,需要指定编译器。

在MSYS2的安装目录下,找到文件,msys2_shell.cmd,打开,在第一行加入vs2017环境变量的调用,如下所示:
在这里插入图片描述

call "D:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvars64.bat"

然后启动msys2,进入到libvpx的解压缩目录,本人采取的版本是libvpx-1.12.0,首先执行./configure --help,可以看到支持的target如下:
在这里插入图片描述
本人是vs2017,64位静态库编译,选择的target是x86_64-win64-vs15,注意,这地方不能选择x86_64-win64-vs17,可以到vs2017中查看版本信息,如下所示,版本中的第一个字段值是15.
在这里插入图片描述
然后执行./configure --target=x86_64-win64-vs15 --enable-static-msvcrt,生成Makefile文件,接着执行make和make install。
编译的成果物目录如下,有Debug和Release
在这里插入图片描述
很遗憾,本人未能找到只编译Release成果物的配置方式,导致编译时间比较长。
make install的时候,只会安装Release的库。

接着是编译配置ffmpeg了,命令如下:

./configure --toolchain=msvc --arch=x86_64 --disable-debug --enable-gpl --enable-libfreetype --enable-libfontconfig --enable-libx264 --extra-cflags=-I/usr/local/x264/include --extra-ldflags='-LIBPATH:/usr/local/x264/lib' --prefix=/home/ffmpeg_x264_vpx_freetype_fontconfig_static --enable-libvpx --enable-encoder=libvpx_vp8 --enable-encoder=libvpx_vp9 --enable-decoder=vp8 --enable-decoder=vp9 --enable-parser=vp8 --enable-parser=vp9

最后本人写了一个例子,录制桌面视频,编码格式是VP9。

在这里插入图片描述

其中CaptureScreen.h的内容如下:

#ifndef _CCAPTURE_SCREEN_HH
#define _CCAPTURE_SCREEN_HH

#include<time.h>
#include <d3d9.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <windows.h>

#include <tchar.h>
#include <winbase.h>
#include <winreg.h>
#include <Strsafe.h>


//
// ---抓屏类----
//
class CCaptureScreen
{
public:
	CCaptureScreen(void);
	~CCaptureScreen(void);

public:
	/*-----------定义外部调用函数-----------*/
	int Init(int&, int&);//初始化
	BYTE* CaptureImage(); //抓取屏幕

private:
	/*-----------定义内部调用函数-----------*/
	void* CaptureScreenFrame(int, int, int, int);//抓屏
	HCURSOR FetchCursorHandle(); //获取鼠标光标

private:
	/*-----------定义私有变量-----------*/
	int m_width;
	int m_height;
	UINT   wLineLen;
	DWORD  dwSize;
	DWORD  wColSize;

	//设备句柄
	HDC hScreenDC;
	HDC hMemDC;
	//图像RGB内存缓存
	PRGBTRIPLE m_hdib;
	//位图头信息结构体
	BITMAPINFO pbi;

	HBITMAP hbm;
	//鼠标光标
	HCURSOR m_hSavedCursor;


};

#endif //--_CCAPTURE_SCREEN_HH

CaptureScreen.cpp的内容如下:

//#include "stdafx.h"
#include "CaptureScreen.h"

CCaptureScreen::CCaptureScreen(void)
{
	m_hdib = NULL;
	m_hSavedCursor = NULL;
	hScreenDC = NULL;
	hMemDC = NULL;
	hbm = NULL;
	m_width = 1920;
	m_height = 1080;
	FetchCursorHandle();
}
//
// 释放资源
//
CCaptureScreen::~CCaptureScreen(void)
{
	DeleteObject(hbm);
	if (m_hdib){

		free(m_hdib);
		m_hdib = NULL;
	}
	if (hScreenDC){

		::ReleaseDC(NULL, hScreenDC);
	}
	if (hMemDC) {

		DeleteDC(hMemDC);
	}
	if (hbm)
	{
		DeleteObject(hbm);
	}
}

//
// 初始化
//
int CCaptureScreen::Init(int& src_VideoWidth, int& src_VideoHeight)
{
	hScreenDC = ::GetDC(GetDesktopWindow());
	if (hScreenDC == NULL) return 0;

	int m_nMaxxScreen = GetDeviceCaps(hScreenDC, HORZRES);
	int m_nMaxyScreen = GetDeviceCaps(hScreenDC, VERTRES);

	hMemDC = ::CreateCompatibleDC(hScreenDC);
	if (hMemDC == NULL) return 0;

	m_width = m_nMaxxScreen;
	m_height = m_nMaxyScreen;

	if (!m_hdib){
		m_hdib = (PRGBTRIPLE)malloc(m_width * m_height * 3);//24位图像大小
	}
	//位图头信息结构体
	pbi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	pbi.bmiHeader.biWidth = m_width;
	pbi.bmiHeader.biHeight = m_height;
	pbi.bmiHeader.biPlanes = 1;
	pbi.bmiHeader.biBitCount = 24;
	pbi.bmiHeader.biCompression = BI_RGB;

	src_VideoWidth = m_width;
	src_VideoHeight = m_height;

	hbm = CreateCompatibleBitmap(hScreenDC, m_width, m_height);
	SelectObject(hMemDC, hbm);

	wLineLen = ((m_width * 24 + 31) & 0xffffffe0) / 8;
	wColSize = sizeof(RGBQUAD)* ((24 <= 8) ? 1 << 24 : 0);
	dwSize = (DWORD)(UINT)wLineLen * (DWORD)(UINT)m_height;

	return 1;
}

//抓取屏幕数据
BYTE* CCaptureScreen::CaptureImage()
{

	VOID*  alpbi = CaptureScreenFrame(0, 0, m_width, m_height);
	return (BYTE*)(alpbi);
}

void* CCaptureScreen::CaptureScreenFrame(int left, int top, int width, int height)
{

	if (hbm == NULL || hMemDC == NULL || hScreenDC == NULL) return NULL;

	BitBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY);
	/*-------------------------捕获鼠标-------------------------------*/
	{
		POINT xPoint;
		GetCursorPos(&xPoint);
		HCURSOR hcur = FetchCursorHandle();
		xPoint.x -= left;
		xPoint.y -= top;

		ICONINFO iconinfo;
		BOOL ret;
		ret = GetIconInfo(hcur, &iconinfo);
		if (ret){
			xPoint.x -= iconinfo.xHotspot;
			xPoint.y -= iconinfo.yHotspot;

			if (iconinfo.hbmMask) DeleteObject(iconinfo.hbmMask);
			if (iconinfo.hbmColor) DeleteObject(iconinfo.hbmColor);
		}
		/*画鼠标*/
		::DrawIcon(hMemDC, xPoint.x, xPoint.y, hcur);
	}
	//动态分配的内存
	PRGBTRIPLE hdib = m_hdib;
	if (!hdib)
		return hdib;

	GetDIBits(hMemDC, hbm, 0, m_height, hdib, (LPBITMAPINFO)&pbi, DIB_RGB_COLORS);
	return hdib;
}

//
// 获取窗体鼠标光标
//
HCURSOR CCaptureScreen::FetchCursorHandle()
{
	if (m_hSavedCursor == NULL)
	{
		m_hSavedCursor = GetCursor();
	}
	return m_hSavedCursor;
}

FfmpegVideoCaptureWithGdi.cpp的内容如下:

// RecordingScreen.cpp : 定义控制台应用程序的入口点。
//

//#include "stdafx.h"
#include "CaptureScreen.h"
extern "C"
{
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil\time.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/imgutils.h>
#include <libavcodec/avcodec.h>
#include <libavdevice\avdevice.h>

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avfilter.lib")
#pragma comment(lib, "postproc.lib")
#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "swscale.lib")


}
//#include <ipp.h>
#include <chrono>

//signed int Bgr2YuvI420(const BYTE* srcBgr, int image_width, int image_height, BYTE* dstYuvI420)
//{
//	IppStatus ipp_status;
//
//	int srcStep = image_width * 3;
//	int dstYStep = image_width;
//	int dstCbCrStep = image_width;
//	IppiSize roiSize = { image_width, image_height };
//
//	const Ipp8u* pSrc = (Ipp8u*)srcBgr;
//
//	Ipp8u *pDstY = (Ipp8u*)dstYuvI420;
//	Ipp8u *pDstU = (Ipp8u*)&dstYuvI420[image_width * image_height];
//	Ipp8u *pDstV = (Ipp8u*)&dstYuvI420[image_width * image_height * 5 / 4];
//	Ipp8u *pDst[3];
//	pDst[0] = pDstY;
//	pDst[1] = pDstU;
//	pDst[2] = pDstV;
//	int dstStep[3] = { image_width, image_width / 2, image_width / 2 };
//
//	ipp_status = ippiBGRToYCbCr420_8u_C3P3R(pSrc, srcStep, pDst, dstStep, roiSize);
//
//	return ipp_status;
//}


unsigned char clip_value(unsigned char x, unsigned char min_val, unsigned char  max_val) {
	if (x > max_val) {
		return max_val;
	}
	else if (x < min_val) {
		return min_val;
	}
	else {
		return x;
	}
}

//RGB to YUV420
bool RGB24_TO_YUV420(unsigned char *RgbBuf, int w, int h, unsigned char *yuvBuf)
{
	unsigned char*ptrY, *ptrU, *ptrV, *ptrRGB;
	memset(yuvBuf, 0, w*h * 3 / 2);
	ptrY = yuvBuf;
	ptrU = yuvBuf + w * h;
	ptrV = ptrU + (w*h * 1 / 4);
	unsigned char y, u, v, r, g, b;
	for (int j = h - 1; j >= 0; j--) {
		ptrRGB = RgbBuf + w * j * 3;
		for (int i = 0; i < w; i++) {

			b = *(ptrRGB++);
			g = *(ptrRGB++);
			r = *(ptrRGB++);


			y = (unsigned char)((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
			u = (unsigned char)((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
			v = (unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
			*(ptrY++) = clip_value(y, 0, 255);
			if (j % 2 == 0 && i % 2 == 0) {
				*(ptrU++) = clip_value(u, 0, 255);
			}
			else {
				if (i % 2 == 0) {
					*(ptrV++) = clip_value(v, 0, 255);
				}
			}
		}
	}
	return true;
}

extern const AVOutputFormat ff_mp4_muxer;

DWORD WINAPI ScreenCapThreadProc(LPVOID lpParam)
{
	CCaptureScreen* ccs = new CCaptureScreen();
	int width = 0;
	int height = 0;

	ccs->Init(width, height);

	AVFormatContext* avFormCtx_Out;
	AVCodecContext*  avCodecCtx_Out;
	AVCodec*  avCodec;
	AVStream* avStream;
	AVFrame* frame;
	AVPacket* packet;

	int frameRate = 10;
	int ret = 0;
	const char* filename = "out.mp4";

	/*
	AVOutputFormat ff_mp4_muxer = {
	.name = "mp4",
	.long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"),
	.mime_type = "video/mp4",
	.extensions = "mp4",
	.priv_data_size = sizeof(MOVMuxContext),
	.audio_codec = AV_CODEC_ID_AAC,
	.video_codec = CONFIG_LIBX264_ENCODER ?
						 AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
	.init = mov_init,
	.write_header = mov_write_header,
	.write_packet = mov_write_packet,
	.write_trailer = mov_write_trailer,
	.deinit = mov_free,
	.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
	.codec_tag = mp4_codec_tags_list,
	.check_bitstream = mov_check_bitstream,
	.priv_class = &mp4_muxer_class,
	};
	*/
	/*AVOutputFormat ff_mp4_muxer;
	ff_mp4_muxer.video_codec = AV_CODEC_ID_VP9;
	ff_mp4_muxer.audio_codec = AV_CODEC_ID_AAC;
	ff_mp4_muxer.name = "mp4";
	ff_mp4_muxer.long_name = "MP4 (MPEG-4 Part 14)";
	ff_mp4_muxer.extensions = "mp4";*/


	ret = avformat_alloc_output_context2(&avFormCtx_Out, NULL, NULL, filename);
	if (ret < 0)
	{
		printf("Init avformat object is faild! \n");
		return 0;
	}
	//AVOutputFormat * poformat = (AVOutputFormat *)avFormCtx_Out->oformat;
	//poformat->video_codec = AV_CODEC_ID_VP9;

	//avCodec = (AVCodec *)avcodec_find_encoder(avFormCtx_Out->oformat->video_codec);
	avCodec = (AVCodec *)avcodec_find_encoder(AV_CODEC_ID_VP9);
	if (!avCodec)
	{
		printf("Init avCodec object is faild! \n");
		return 0;
	}

	avCodecCtx_Out = avcodec_alloc_context3(avCodec);
	if (!avCodecCtx_Out)
	{
		printf("Init avCodecCtx_Out object is faild! \n");
		return 0;
	}
	avStream = avformat_new_stream(avFormCtx_Out, avCodec);
	if (!avStream)
	{
		printf("Init avStream object is faild! \n");
		return 0;
	}

	avCodecCtx_Out->flags |= AV_CODEC_FLAG_QSCALE;
	avCodecCtx_Out->bit_rate = 4000000;
	avCodecCtx_Out->rc_min_rate = 4000000;
	avCodecCtx_Out->rc_max_rate = 4000000;
	avCodecCtx_Out->bit_rate_tolerance = 4000000;
	avCodecCtx_Out->time_base.den = frameRate;
	avCodecCtx_Out->time_base.num = 1;

	avCodecCtx_Out->width = width;
	avCodecCtx_Out->height = height;
	//pH264Encoder->pCodecCtx->frame_number = 1;
	avCodecCtx_Out->gop_size = 12;
	avCodecCtx_Out->max_b_frames = 0;
	avCodecCtx_Out->thread_count = 4;
	avCodecCtx_Out->pix_fmt = AV_PIX_FMT_YUV420P;
	avCodecCtx_Out->codec_id = AV_CODEC_ID_VP9;
	avCodecCtx_Out->codec_type = AVMEDIA_TYPE_VIDEO;

	av_opt_set(avCodecCtx_Out->priv_data, "b-pyramid", "none", 0);
	av_opt_set(avCodecCtx_Out->priv_data, "preset", "superfast", 0);
	//av_opt_set(avCodecCtx_Out->priv_data, "tune", "zerolatency", 0);

	if (avFormCtx_Out->oformat->flags & AVFMT_GLOBALHEADER)
		avCodecCtx_Out->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

	ret = avcodec_open2(avCodecCtx_Out, avCodec, NULL);
	if (ret < 0)
	{
		printf("Open avcodec is faild! \n");
		return 0;
	}

	avcodec_parameters_from_context(avStream->codecpar, avCodecCtx_Out);
	if (!(avFormCtx_Out->oformat->flags & AVFMT_NOFILE))
	{
		ret = avio_open(&avFormCtx_Out->pb, filename, AVIO_FLAG_WRITE);
		if (ret < 0)
		{
			printf("Open file is faild! \n");
			return 0;
		}
	}
	ret = avformat_write_header(avFormCtx_Out, NULL);
	if (ret < 0)
	{
		printf("write header is faild! \n");
		return 0;
	}

	frame = av_frame_alloc();
	if (!frame)
	{
		printf("Init frame is faild! \n");
		return 0;
	}
	frame->format = AV_PIX_FMT_YUV420P;
	frame->width = width;
	frame->height = height;

	LONG64 frameSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 1920, 1080, 1);
	BYTE* outbuffer = new BYTE[frameSize];

	ret = av_image_fill_arrays(frame->data,
		frame->linesize,
		outbuffer,
		AV_PIX_FMT_YUV420P,
		1920,
		1080, 1);

	if (ret < 0)
	{
		printf("av_image_fill_arrays is faild! \n");
		return 0;
	}
	packet = av_packet_alloc();
	//av_init_packet(packet);
	if (!packet)
	{
		printf("packet is faild! \n");
		return 0;
	}

	int frameNumber = 0;
	int got_packet = 0;

	DWORD dwBeginTime = ::GetTickCount();
	for (;;)
	{
		BYTE* frameimage = ccs->CaptureImage();

		RGB24_TO_YUV420(frameimage, width, height, outbuffer);

		DWORD dwCurrentTime2 = ::GetTickCount();
		DWORD dwLastTime = dwCurrentTime2 - dwBeginTime;

		frame->pkt_dts = frame->pts = av_rescale_q_rnd(frameNumber, avCodecCtx_Out->time_base, avStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
		frame->pkt_duration = 0;
		frame->pkt_pos = -1;
		

		//frame->pkt_dts = frame->pts = av_rescale_q_rnd(frameNumber, avCodecCtx_Out->time_base, avStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
		//frame->pkt_duration = 0;
		//frame->pkt_pos = -1;

		ret = avcodec_send_frame(avCodecCtx_Out, frame);
		if (ret < 0)
			continue;

		ret = avcodec_receive_packet(avCodecCtx_Out, packet);
		if (ret < 0)
			continue;

		static DWORD dwInitTime = ::GetTickCount();
		if (packet->size > 0)
		{
			//av_packet_rescale_ts(packet, avCodecCtx_Out->time_base, avStream->time_base);
			av_write_frame(avFormCtx_Out, packet);
			frameNumber++;
			printf("录入第%d帧....\n", frameNumber);
		}

		DWORD dwCurrentTime = ::GetTickCount();
		if (dwCurrentTime - dwInitTime > 60 * 1000)
		{
			break;
		}

		int dwPassedMillSeconds = dwCurrentTime - dwBeginTime;
		int dwDiff = frameNumber * 100 - dwPassedMillSeconds;
		if (dwDiff > 0)
		{
			Sleep(dwDiff);
		}
	}
	av_write_trailer(avFormCtx_Out);
	avformat_free_context(avFormCtx_Out);
	avcodec_close(avCodecCtx_Out);
	avcodec_free_context(&avCodecCtx_Out);
	av_free(avCodec);
	av_packet_free(&packet);
	av_frame_free(&frame);
	return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
	//av_register_all();
	//avformat_network_init();
	avdevice_register_all();


	HANDLE hThread = CreateThread(NULL, 0, ScreenCapThreadProc, 0, 0, NULL);
	WaitForSingleObject(hThread, INFINITE);
	return 0;
}






最后出来的是VP9格式的视频。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
世界上最快的VP9视频解码器 As before , I was very excited when Google released VP9 – for one, because I was one of the people involved in creating it back when I worked for Google (I no longer do). How good is it, and how much better can it be? To evaluate that question, Clément Bœsch and I set out to write a VP9 decoder from scratch for FFmpeg. The goals never changed from the original ffvp8 situation (community-developed, fast, free from the beginning). We also wanted to answer new questions: how does a well-written decoder compare, speed-wise, with a well-written decoder for other codecs? TLDR (see rest of post for details): as a codec, VP9 is quite impressive – it beats x264 in many cases. However, the encoder is slow, very slow. At higher speed settings, the quality gain melts away. This seems to be similar to what people report about HEVC (using e.g. x265 as an encoder). single-threaded decoding speed of libvpx isn’t great. FFvp9 beats it by 25-50% on a variety of machines. FFvp9 is somewhat slower than ffvp8, and somewhat faster than ffh264 decoding speed (for files encoded to matching SSIM scores). Multi-threading performance in libvpx is deplorable, it gains virtually nothing from its loopfilter-mt algorithm. FFvp9 multi-threading gains nearly as much as ffh264/ffvp8 multithreading, but there’s a cap (material-, settings- and resolution-dependent, we found it to be around 3 threads in one of our clips although it’s typically higher) after which further threads don’t cause any more gain. The codec itself To start, we did some tests on the encoder itself. The direct goal here was to identify bitrates at which encodings would give matching SSIM-scores so we could do same-quality decoder performance measurements. However, as such, it also allows us to compare encoder performance in itself. We used settings very close to recommended settings forVP8,VP9andx264, optimized for SSIM as a metric. As source clips, we chose Sintel (1920×1080 CGI content, source ), a 2-minute clip from Tears of Steel (1920×800 cinematic content, source ), and a 3-minute clip from Enter the Void (1920×818 high-grain/noise content,screenshot). For each, we encoded at various bitrates and plotted effective bitrate versus SSIM . sintel_ssimtos_ssimetv_ssim You’ll notice that in most cases, VP9 can indeed beat x264, but, there’s some big caveats: VP9 encoding (using libvpx) is horrendously slow – like, 50x slower than VP8/x264 encoding. This means that encoding a 3-minute 1080p clip takes several days on a high-end machine. Higher –cpu-used=X parameters make the quality gains melt away. libvpx’ VP9 encodes miss the target bitrates by a long shot (100% off) for the ETV clip, possibly because of our use of –aq-mode=1. libvpx tends to slowly crumble at higher bitrates for hard content – again, look at the ETV clip, where x264 shows some serious mature killer instinct at the high bitrate end of things. Overall, these results are promising, although the lack-of-speed is a serious issue. Decoder performance For decoding performance measurements, we chose (Sintel)500 (VP9), 1200 (VP8) and 700 (x264) kbps (SSIM=19.8); Tears of Steel4.0 (VP9), 7.9 (VP8) and 6.3 (x264) mbps (SSIM=19.2); and Enter the Void 9.7 (VP9), 16.6 (VP8) and 10.7 (x264) mbps (SSIM=16.2). We used FFmpeg to decode each of these files, either using the built-in decoder (to compare between codecs), or using libvpx-vp9 (to compare ffvp9 versus libvpx). Decoding time was measured in seconds using “time ffmpeg -threads 1 [-c:v libvpx-vp9] -i $file -f null -v 0 -nostats – 2>&1 | grep user”, with this FFmpeg and this libvpx revision (downloaded on Feb 20th, 2014). sintel_archs tos_archsetv_archs A few notes on ffvp9 vs. libvpx-vp9 performance: ffvp9 beats libvpx consistently by 25-50%. In practice, this means that typical middle- to high-end hardware will be able to playback 4K content using ffvp9, but not using libvpx. Low-end hardware will struggle to playback even 720p content using libvpx (but do so fine using ffvp9). on Haswell, the difference is significantly smaller than on sandybridge, likely because libvpx has some AVX2 optimizations (e.g. for MC and loop filtering), whereas ffvp9 doesn’t have that yet; this means this difference might grow over time as ffvp9 gets AVX2 optimizations also. on the Atom, the differences are significantly smaller than on other systems; the reason for this is likely that we haven’t done any significant work on Atom-performance yet. Atom has unusually large latencies between GPRs and XMM registers, which means you need to take special care in ordering your instructions to prevent unnecessary halts – we haven’t done anything in that area yet (for ffvp9). Some users may find that ffvp9 is a lot slower than advertised on 32bit; this is correct, most of our SIMD only works on 64bit machines. If you have 32bit software, port it to 64bit. Can’t port it? Ditch it. Nobody owns 32bit x86 hardware anymore these days. So how does VP9 decoding performance compare to that of other codecs? There’s basically two ways to measure this: same-bitrate (e.g. a 500kbps VP8 file vs. a 500kbps VP9 file, where the VP9 file likely looks much better), or same-quality (e.g. a VP8 file with SSIM=19.2 vs. a VP9 file with SSIM=19.2, where the VP9 file likely has a much lower bitrate). We did same-quality measurements, and found: ffvp9 tends to beat ffh264 by a tiny bit (10%), except on Atom (which is likely because ffh264 has received more Atom-specific attention than ffvp9). ffvp9 tends to be quite a bit slower than ffvp8 (15%), although the massive bitrate differences in Enter the Void actually makes it win for that clip (by about 15%, except on Atom). Given that Google promised VP9 would be no more than 40% more complex than VP8, it seems they kept that promise. we did some same-bitrate comparisons, and found that x264 and ffvp9 are essentially identical in that scenario (with x264 having slightly lower SSIM scores); vp8 tends to be about 50% faster, but looks significantly worse. Multithreading One of the killer-features in FFmpeg is frame-level multithreading, which allows multiple cores to decode different video frames in parallel. Libvpx also supports multithreading. So which is better?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值