VP9お試しビルド

うまく行かなかったので下記の方法にて解決
VP9お試しビルド(その2) - fftester06’s blog

refs/heads/master - webm/libvpx - Git at Google

cygwin上で作業

$ ./configure
  enabling vp8_encoder
  enabling vp8_decoder
  enabling vp9_encoder
  enabling vp9_decoder
Configuring for target 'x86-win32-gcc'
  enabling x86
  enabling runtime_cpu_detect
  enabling mmx
  enabling sse
  enabling sse2
  enabling sse3
  enabling ssse3
  enabling sse4_1
  enabling avx
  enabling avx2
  enabling avx512
  using yasm
  enabling postproc
  enabling unit_tests
  enabling webm_io
  enabling libyuv
Creating makefiles for x86-win32-gcc libs
Creating makefiles for x86-win32-gcc examples
Creating makefiles for x86-win32-gcc tools
Creating makefiles for x86-win32-gcc docs
$ make
    [CREATE] vpx_scale_rtcd.h
    [CREATE] vpx_dsp_rtcd.h
    [CREATE] vp8_rtcd.h
    [CREATE] vp9_rtcd.h
    [DEP] test/test_intra_pred_speed.cc.d
・・・・
    [AR] libvpx_g.a
    [STRIP] libvpx.a < libvpx_g.a
    [CREATE] vpx.pc
    [CXX] third_party/googletest/src/src/gtest-all.cc.o
In file included from /home/ss/libvpx/third_party/googletest/src/include/gtest/i                            nternal/gtest-internal.h:40:0,
                 from /home/ss/libvpx/third_party/googletest/src/include/gtest/g                            test.h:59,
                 from third_party/googletest/src/src/gtest-all.cc:38:
/home/ss/libvpx/third_party/googletest/src/include/gtest/internal/gtest-port.h:                             関数 ‘int testing::internal::posix::FileNo(FILE*)’ 内:
/home/ss/libvpx/third_party/googletest/src/include/gtest/internal/gtest-port.h:2                            487:51: エラー: ‘fileno’ was not declared in this scope
 inline int FileNo(FILE* file) { return fileno(file); }
                                                   ^
/home/ss/libvpx/third_party/googletest/src/include/gtest/internal/gtest-port.h:                             関数 ‘char* testing::internal::posix::StrDup(const char*)’ 内:
/home/ss/libvpx/third_party/googletest/src/include/gtest/internal/gtest-port.h:2                            493:57: エラー: ‘strdup’ was not declared in this scope
 inline char* StrDup(const char* src) { return strdup(src); }
                                                         ^
/home/ss/libvpx/third_party/googletest/src/include/gtest/internal/gtest-port.h:                             関数 ‘FILE* testing::internal::posix::FDOpen(int, const char*)’ 内:
/home/ss/libvpx/third_party/googletest/src/include/gtest/internal/gtest-port.h:2                            521:71: エラー: ‘fdopen’ was not declared in this scope
 inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); }
                                                                       ^
In file included from third_party/googletest/src/src/gtest-all.cc:44:0:
/home/ss/libvpx/third_party/googletest/src/src/gtest-port.cc: コンストラクタ ‘te                            sting::internal::CapturedStream::CapturedStream(int)’ 内:
/home/ss/libvpx/third_party/googletest/src/src/gtest-port.cc:1034:50: エラー: ‘m                            kstemp’ was not declared in this scope
     const int captured_fd = mkstemp(name_template);
                                                  ^
make[1]: *** [Makefile:168: third_party/googletest/src/src/gtest-all.cc.o] エラ                            ー 1
make: *** [Makefile:17: .DEFAULT] エラー 2

テストツールでなんかエラーが出てるけど、とりあえずライブラリの生成は成功。

エラーをなくしてビルドを最後まで通してみる。

./configure --disable-tools --disable-unit-tests --disable-docs
→
    [CXX] third_party/libwebm/mkvmuxer/mkvmuxerutil.cc.o
third_party/libwebm/mkvmuxer/mkvmuxerutil.cc: 関数 ‘mkvmuxer::uint64 mkvmuxer::MakeUID(unsigned int*)’ 内:
third_party/libwebm/mkvmuxer/mkvmuxerutil.cc:640:33: エラー: ‘rand_r’ was not declared in this scope
     const int32 nn = rand_r(seed);
                                 ^
make[1]: *** [Makefile:168: third_party/libwebm/mkvmuxer/mkvmuxerutil.cc.o] エラー 1
make: *** [Makefile:17: .DEFAULT] エラー 2

mkvmuxerutil.cc:640
const int32 nn = rand_r(seed); → const int32 nn = rand();

    [CXX] third_party/libwebm/mkvparser/mkvreader.cc.o
third_party/libwebm/mkvparser/mkvreader.cc: メンバ関数 ‘virtual int mkvparser::MkvReader::Read(long long int, long int, unsigned char*)’ 内:
third_party/libwebm/mkvparser/mkvreader.cc:124:54: エラー: ‘fseeko’ was not declared in this scope
   fseeko(m_file, static_cast<off_t>(offset), SEEK_SET);
                                                      ^
make[1]: *** [Makefile:168: third_party/libwebm/mkvparser/mkvreader.cc.o] エラー 1
make: *** [Makefile:17: .DEFAULT] エラー 2

fseekoがないようなのでfseekに書き換え・・・関数の仕様の互換性などは見てません・・・。
まあでもこれでビルドが最後まで通るようになった!

生成されたlibvpx.aをMinGWに持ってきて、simple_encoderをコンパイルしてみる。

C:\Documents and Settings\ss\My Documents\src\imgtrans\VP9\libvpx-refs_heads_mas
ter\examples>gcc simple_decoder.c ..\tools_common.c ..\video_writer.c ..\video_r
eader.c ..\y4minput.c ..\ivfenc.c  ..\ivfdec.c libvpx.a -I..
libvpx.a(vp8_cx_iface.c.o):(.text+0x1af5): undefined reference to `setjmp'
libvpx.a(onyx_if.c.o):(.text+0x2fee): undefined reference to `setjmp'
libvpx.a(vp8_dx_iface.c.o):(.text+0x522): undefined reference to `setjmp'
libvpx.a(vp8_dx_iface.c.o):(.text+0x68b): undefined reference to `setjmp'
libvpx.a(vp8_dx_iface.c.o):(.text+0x7bb): undefined reference to `setjmp'
libvpx.a(onyxd_if.c.o):(.text+0x7a): more undefined references to `setjmp' follo
w
libvpx.a(systemdependent.c.o):(.text+0xf): undefined reference to `sysconf'
collect2.exe: error: ld returned 1 exit status

cygwinでは不要だったsetjmp.hがMinGWでは必要な模様
→vp8_dx_iface.cに追加
同様にunistd.hが必要
→systemdependent.cに追加
...ここまでくるとMinGW用にbashとmakeがほしいような・・・。
しかもビルドがうまく行かないので、Git bash上に環境構築してみることに。

libjpeg-turbo試用

"libjpeg-turbo"JPEGエンコーダ/デコーダlibjpegの、SIMD指令使用版をWindowsで使ってみる。
libjpeg-turbo | Main / libjpeg-turbo
従来のlibjpegよりも2-6倍速いと言う謳い文句。
Windows用バイナリも提供されていて、実際の速度も割とよさそうだったのでとりあえず試用。

計測例など:
UbuntuでOpenCVをlibjpeg-turboつきでビルドする | さかな前線
libjpeg-turboでjpegの変換が速くなる - かみぽわーる
開発メモ その134 OpenCVにlibjpeg-turboをリンクして性能比較 – A certain engineer "COMPLEX"
libjpeg-turboがいいらしい at softelメモ

比較的当たらしめかと思いきや、2010年のブログにも載っているのでそれなりの歴史もありそう。SIMDに対応のみということで、ガチの人向けにはマルチプロセッサ/マルチコア用最適化などもあるのだろうか・・・・?

超シンプルなCサンプル:

#include <stdio.h>
#include "turbojpeg.h"

int main() {
	unsigned char* jpegBuf = NULL, *imgBuf = NULL;
	FILE* jpegFile;
	long size, jpegSize;
	int width, height;
	int inSubsamp, outSubsamp = -1, inColorspace, pixelFormat = TJPF_UNKNOWN;
	int numScalingFactors = 0, outQual, flags=0;
	tjhandle tjInstance = NULL;

	if ((imgBuf = tjLoadImage("test.bmp", &width, 1, &height, &pixelFormat, 0)) == NULL)
      	printf("loading input image: %s\n", tjGetErrorStr2(tjInstance));
	outSubsamp = TJSAMP_444;
	outQual = 50;

    if ((tjInstance = tjInitCompress()) == NULL)
        printf("initializing compressor: %s\n", tjGetErrorStr2(NULL));

	if (tjCompress2(tjInstance, imgBuf, width, 0, height, pixelFormat,
                  &jpegBuf, &jpegSize, outSubsamp, outQual, flags) < 0)
    	printf("compressing image: %s\n",  tjGetErrorStr2(tjInstance));

  	tjDestroy(tjInstance);
  	tjInstance = NULL;

    if ((jpegFile = fopen("turbotest.jpg", "wb")) == NULL)
        printf("opening output file\n");
    if (fwrite(jpegBuf, jpegSize, 1, jpegFile) < 1)
        printf("writing output file");

    fclose(jpegFile);
    jpegFile = NULL;
    return 0;
}

若干のはまりポイントとしては、APIにポインタを渡して中身を入れてもらう変数については、あらかじめNULLをいれておいてわたさないとSIGSEGVを食らうことがある模様。
まあ当然と言えば当然ですね。

リモートデスクトップアプリ試作版

ができました。

エラーチェックもほぼなし、高速化等の処理もほぼは言ってない、昨日動作確認までですが、いかんせんもっさりした動作・・・。

これから高速化について探っていこうかと思います。

ちなみに、クライアントについてはこちらから拝借して、対応するサーバーソフトを書いたような感じです。ただし、画面全体ではなくウィンドウ単位で転送するようにしたなどの都合で、クライアント側も一部修正しています。

 

Brynhildr(ブリュンヒルデ) – リモートデスクトップ ( リモートデスクトップエンジニアのブログ。 )

 

これの、クライアント機能サンプルを使用しました。

いまのとこ生のビットマップをそのまま送っているけど、JPGにしたり、RTSPにのせたりできるのかな・・・?

いろいろチャレンジしてみよう。

 

VJoy(仮想ジョイパッド)のAPIテスト

XPで動くバージョンでAPIのテスト(v1.2)

Headsoft - VJoy Virtual Joystick Driver - Home

ちなみに最新版はこちら

vJoy download | SourceForge.net

SDKを使用すると、付属のDLLを使用してジョイパッドの入力を生成することが可能。
ただ、implicit(暗黙的)なDLL読み込みがうまくいかなかった。

念のためstatic libraryの生成も行う。VJoy.cpp, Vjoy32.dll, VJoy.h, StdAfx.hを同一ディレクトリに配置し以下を実行。
ちなみにdlltoolはmingwデフォルトインストールで導入済み、pexportはもともと標準だったものがオプションになったらしく、以下から入手。
https://sourceforge.net/projects/mingw/files/MinGW/Extension/pexports/pexports-0.47/pexports-0.47-mingw32-bin.tar.xz/download
tar.xzは7zipで解凍可能。

> pexport VJoy32.dll > VJoy32.def
> dlltool --dllname VJoy32.dll --input-def VJoy32.def --output-lib VJoy32.lib
> gcc VJoy.cpp VJoy32.dll
VJoy.cpp: In function 'int main(int, char**)':
VJoy.cpp:15:24: warning: ISO C++ forbids converting a string constant to 'PCHAR'
 {aka 'char*'} [-Wwrite-strings]
  VJoy_Initialize("", "");
                        ^
VJoy.cpp:15:24: warning: ISO C++ forbids converting a string constant to 'PCHAR'
 {aka 'char*'} [-Wwrite-strings]
Temp\ccE9Yzks.o:VJoy.cpp:(.text+0x26): undefined reference to `_imp__VJoy_Initialize@8'
Temp\ccE9Yzks.o:VJoy.cpp:(.text+0x6d): undefined reference to `_imp__VJoy_UpdateJoyState@8'
Temp\ccE9Yzks.o:VJoy.cpp:(.text+0x77): undefined reference to `_imp__VJoy_Shutdown@0'
collect2.exe: error: ld returned 1 exit status

以下で試している明示的リンクではうまくいっているため、DLLファイルそのものには問題はなし。
→namespaceやらなにやらの問題?
ということで、添付のVJoy.cppを明示的リンクに書き換えてテスト。

//main.c
#include <stdio.h>
#include <windows.h>
#include "VJoy.h"
 
int main() {
	HINSTANCE hinst;
	JOYSTICK_STATE joyState[2] = {0};
	int (*fp_VJoy_Initialize)(char*, char*);
	int (*fp_VJoy_UpdateJoyState)(int,  JOYSTICK_STATE*);
	int (*fp_VJoy_Shutdown)();

	int c;
	printf("started.\n");
	if ((hinst = LoadLibrary("VJoy32.dll")) == NULL) {
    	printf("Error LoadLibrary\n");
	}
	fp_VJoy_Initialize = (int (*)(char*, char*))GetProcAddress(hinst, "VJoy_Initialize");
	fp_VJoy_UpdateJoyState = (int (*)(int, JOYSTICK_STATE*))GetProcAddress(hinst, "VJoy_UpdateJoyState");
	fp_VJoy_Shutdown = (int (*)())GetProcAddress(hinst, "VJoy_Shutdown");
 	
	printf("call VJoy_Initialize.\n");
	c = fp_VJoy_Initialize("", "");

	joyState[0].XAxis = 32767;
	joyState[0].YAxis = 32767;
	joyState[0].ZAxis = 32767;
	joyState[0].Buttons = 0xAAAAAAAA;
	joyState[0].POV = (4 << 12) | (4 << 8) | (4 << 4) | 4;

	c = fp_VJoy_UpdateJoyState(0, &joyState[0]);
    
	fp_VJoy_Shutdown();

	FreeLibrary(hinst);
	return 0;
}
> gcc VJoy.cpp -o VJoy.exe
> VJoy.exe
started.
call VJoy_Initialize.

うごいたっぽい。

DirectSoundでマイクからの音声入力をキャプチャして保存する

だけなのに、いいサンプルが少なく苦戦した・・・。

MSオフィシャルのドキュメントも見づらい・・・・。

のでソースを置いて自分用メモ。

#include <dsound.h>
#include <time.h>
#include <stdio.h>
#include <windows.h>
#include <time.h>

#define SAMPLERATE 44100 //Hz
#define CHANNELS 2 // 1:monoral, 2:stereo
#define BITSPERSAMPLE 16 // bits per sample

#pragma comment(lib, "dsound.lib")
#pragma comment(lib, "dxguid.lib")

typedef struct {
	char riffID[4];
	unsigned long fileSize;
	char waveID[4];
} RIFFHeader;

typedef struct {
	char chunkID[4];
	long chunkSize; 
	short wFormatTag;
	unsigned short wChannels;
	unsigned long dwSamplesPerSec;
	unsigned long dwAvgBytesPerSec;
	unsigned short wBlockAlign;
	unsigned short wBitsPerSample;
/* Note: there may be additional fields here, depending upon wFormatTag. */ 
} FormatChunk;

typedef struct {
	char chunkID[4];
	long chunkSize; 
} DataChunk;

BOOL CALLBACK DSEnumProc(LPGUID lpGUID,  
             LPCTSTR lpszDesc, 
             LPCTSTR lpszDrvName,  
             LPVOID lpContext ) 
{ 
	if (lpGUID != NULL) { //  NULL only for "Primary Sound Driver". 
		printf("Driver name: %s, Description:%s\n", lpszDrvName, lpszDesc);
	}
	return TRUE;
} 

int writeWaveFile(char* copiedBuffer, long copiedLength) {

	RIFFHeader riffHeader
		= {{'R','I','F','F'},
		sizeof(FormatChunk) + sizeof(DataChunk) + 4 + copiedLength,
		{'W','A','V','E'}};

	FormatChunk formatChunk
		= {{'f','m','t',' '}, 16, 1, CHANNELS, SAMPLERATE,
		SAMPLERATE*CHANNELS*BITSPERSAMPLE/8, CHANNELS*BITSPERSAMPLE/8, BITSPERSAMPLE};

	DataChunk dataChunk = {{'d','a','t','a'}, copiedLength};

	// ファイル出力
	HANDLE hFile = CreateFile("test1.wav" , GENERIC_WRITE , 0 , NULL ,
		CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		MessageBox(NULL , TEXT("ファイルが開けません") , NULL , MB_OK);
		return -1;
	}

	DWORD dwWriteSize;
	WriteFile(hFile, (void*)&riffHeader, sizeof(RIFFHeader), &dwWriteSize, NULL);
	WriteFile(hFile, (void*)&formatChunk, sizeof(FormatChunk), &dwWriteSize, NULL);
	WriteFile(hFile, (void*)&dataChunk, sizeof(DataChunk), &dwWriteSize, NULL);
	WriteFile(hFile, copiedBuffer, copiedLength, &dwWriteSize, NULL);
	CloseHandle(hFile);
	printf("%d bytes written.\n", dwWriteSize);

	return 0;
}

//int _tmain(int argc, char* argv[])
int main(int argc, char* argv[])
{
	LPDIRECTSOUNDCAPTURE captureDevice = NULL;//DirectSoundCaptureDeviceオブジェクト
	LPDIRECTSOUNDCAPTUREBUFFER captureBuffer = NULL;//DirectSoundCaptureBufferオブジェクト

	WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, CHANNELS, SAMPLERATE,
		SAMPLERATE*CHANNELS*BITSPERSAMPLE/8, CHANNELS*BITSPERSAMPLE/8, BITSPERSAMPLE, 0};
	// 単純なPCMのWAVEデータを定義
	// wFormatTag、Waveのフォーマット
	// nChannels モノラル1 ステレオ2(データセットの種類)
	// nSamplesPerSec 1秒あたりのサンプル数
	// mAvgBytesPerSec、1秒あたりのバイト数。nSamplesPerSec*nBlockAlign。
	// nBlockAlign 1サンプルのバイト数。nChannels×wBitsPerSample÷8 8・・・8ビット=1バイト
	// wBitsPerSample 1サンプルあたりのビット数。8か16
	// cbSize 常に0

	DSCBUFFERDESC bufferDescriber
		= {sizeof(DSCBUFFERDESC), 0, wfx.nAvgBytesPerSec*1, 0, &wfx, 0, NULL};
	// DirectSound Capture Buffer DESC キャプチャ バッファを記述する構造体
	// dwSize この構造体のサイズ(=sizeof(DSCBUFFERDESC))
	// dwFlags デバイス付加能力の指定フラグ(未使用につき0)
	// dwBufferBytes バッファサイズ(byte)
	// dwReserved 予約領域(=0)
	// lpwfxFormat キャプチャフォーマットをWAVEFORMATX構造体で指定
	// dwFXCount エフェクトを使用しない場合は0
	// lpDSCFXDesc ハードウェアサポートのエフェクト指定

	DWORD readablePos, capturedPos, readBufferPos, lockLength, capturedLength, wrappedCapturedLength;
	DWORD copiedLength = 0;
	void *capturedData = NULL, *wrappedCapturedData = NULL;
	char *copiedBuffer;
	int recordDurationSec = 3; //録音時間(秒)
	HRESULT Hret;
	time_t start, end;

	CoInitialize(NULL);
	DirectSoundCaptureCreate8( NULL, &captureDevice, NULL );

	// サウンドデバイスが複数ある場合に使用
	// DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc, (VOID*)NULL);

	readBufferPos = 0;
	copiedBuffer = (char*)malloc(
		wfx.nAvgBytesPerSec * wfx.nChannels * wfx.wBitsPerSample / 8 * recordDurationSec * 2); 
	captureDevice->CreateCaptureBuffer(&bufferDescriber,&captureBuffer,NULL);
	start = time(NULL);
	captureBuffer->Start(DSCBSTART_LOOPING);
	Sleep(100); // キャプチャが少し進んでからデータ取得開始
	
	while(1)
	{
		captureBuffer->GetCurrentPosition(&capturedPos, &readablePos);
		if  ( readablePos > readBufferPos ) lockLength = readablePos - readBufferPos;
		else lockLength = bufferDescriber.dwBufferBytes - readBufferPos + readablePos;

		// printf("Lock startRead:%d, readable:%d, locklen:%d, captured:%d\n",
		//	readBufferPos, readablePos, lockLength, capturedPos);
		Hret = captureBuffer->Lock(readBufferPos, lockLength,
			&capturedData, &capturedLength,
			&wrappedCapturedData, &wrappedCapturedLength,
			NULL);
		if( Hret != DS_OK ) {
			printf("Lock error:%x\n", Hret);
		} else {
			// printf("buffer read, buf1:%d, buf2:%d\n", capturedLength, wrappedCapturedLength);
		}

		if (capturedData != NULL) {
			memcpy(copiedBuffer+copiedLength, capturedData, capturedLength);
			copiedLength += capturedLength;
			readBufferPos += capturedLength;
			if (readBufferPos >= bufferDescriber.dwBufferBytes)
				readBufferPos = 0;
		}

		if (wrappedCapturedData != NULL) { // Ring buffer wrapped
			memcpy(copiedBuffer+copiedLength, wrappedCapturedData, wrappedCapturedLength);
			copiedLength += wrappedCapturedLength;
			readBufferPos = wrappedCapturedLength;
		}

		Hret = captureBuffer->Unlock( capturedData, capturedLength,
			wrappedCapturedData, wrappedCapturedLength);

		end = time(NULL);
		if((end-start) > recordDurationSec ){
			break;
		}
		Sleep(100);
	}
	printf("%d bytes recorded.\n", copiedLength);
	writeWaveFile(copiedBuffer, copiedLength);
	captureBuffer->Stop();
	free(copiedBuffer);
	CoUninitialize();

	return 0;
}

画面転送アプリ事始め

WindowsUDPベースの快適な画面転送ソフト作り始めるための雑多メモ

 

Win32API、チュートリアル

標準 Windows API

高速な画面キャプチャの方法について

c++ 保存 スクリーンキャプチャ - Windowsでの画面キャプチャの最も速い方法 - CODE Q&A 問題解決

c++ capture screenshot - Fastest method of screen capturing on Windows - CODE Q&A Solved(上記の英語原文)

Various methods for capturing the screen - CodeProject

ハードウェアエンコーディングをOFFにした方が早い?

 

Tkinter PhotoImageの参照の寿命について

PhotoImageでボタンの画像を作成しているコードにて、べた書きからクラス形式に変更したところ、画像が表示されなくなった。
どうもこういうことらしい。
Python 2.7 - [tkinter]canvasの写真を更新したい|teratail

import tkinter as tk

class MyWidget(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        frame1 = tk.Frame(self)
        imageicon = tk.PhotoImage(file='button.png')
        button = tk.Button(frame1, image=imageicon)
        button.pack()
        frame1.pack()

root = MyWidget()
root.mainloop()

→画像が表示されず、ボタンも反応しない

import tkinter as tk

class MyWidget(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        frame1 = tk.Frame(self)
        self.imageicon = tk.PhotoImage(file='button.png')
        button = tk.Button(frame1, image=self.imageicon)
        button.pack()
        frame1.pack()

root = MyWidget()
root.mainloop()

imageiconの参照をselfで持つことによって、rootが死なない限り破棄されなくなった模様
純粋なオブジェクト指向じゃない?ようなので、こういう仕様は恐ろしい・・・構文エラーでも実行時エラーでもなく、単に期待した動作をしない。
将来的にはリリース前にテストを通すかもしれないけど、これはスルーするかもしれない・・・。