Android NDK动态获取系统版本

让电脑连上安卓手机上,执行

1
2
adb shell
cat /system/build.prop

可看到

1
2
3
4
5
...
ro.build.version.sdk=18
ro.build.version.codename=REL
ro.build.version.release=4.3
...

于是jni中可写如下代码获取系统版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <sys/system_properties.h>
 
static int test(){
    char *key = (char *)"ro.build.version.sdk";
    //char *key = (char *)"ro.build.version.release";
    char value[1024] = {0};
    int ret = __system_property_get(key, value);
    if (ret <= 0 ) {
        DBG(("get prop value failed.\n"));
        return 0;
    }
    DBG(("ro.build.id is [%s]\n", value));
 
    return 0;
}

Android jni基于NdkMediaCodec硬解码mjpeg

NdkMediaCodec需要安卓5.0以上系统才能运行,在Android.mk里加入

1
LOCAL_LDLIBS += -lmediandk

如果是在安卓5.0以下系统中跑,程序会直接崩掉,主函数都不会进,所以要在旧手机中运行,要根据系统版本来判断是不是5.0以上系统,如果是则动态加载libmediandk.so,而不要写死在编译脚本里。

还有,要看手机上有没有解mjpeg的硬解码器,通过以下java代码来查看

1
2
3
4
5
6
7
8
9
10
11
12
int n = MediaCodecList.getCodecCount();
for (int i = 0; i < n; ++i) {
	MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
	String[] supportedTypes = info.getSupportedTypes();
	boolean mime_support = false;
	if(info.isEncoder()){
		continue;
	}
	for (int j = 0; j < supportedTypes.length; ++j) {
		Log.v("euhat", "codec info:" + info.getName()+" supportedTypes:" + supportedTypes[j]);
	}
}

以下是jni硬解码代码,因为没找到支持mjpeg硬解的手机,所以还没实际跑起来过。
但实际测试过程中,新的手机上软解1920x1080的mjpeg输出yuv都很流畅了。

Read more

电子游戏历史好文推荐

电子游戏发展史之百家争鸣的1970年代
https://www.toutiao.com/i6583582769782194702/
https://www.toutiao.com/i6584374365003973128/
https://www.toutiao.com/i6585338411727454733/

NAMCO创始人中村雅哉,这篇文章里又一次提到了乔帮主
https://www.vgtime.com/topic/453341.jhtml
https://www.vgtime.com/topic/559644.jhtml

飞行射击
http://m.appgame.com/archives/376628.html

荒野大镖客此时非彼时
https://www.toutiao.com/a6611296396240749069/

任天堂当年为什么会把塞尔达放给卡普空做?
https://www.toutiao.com/a6607893750234481160/

做出了《双截龙》和“热血”系列,并将自身的影子融入到游戏中
https://www.toutiao.com/a6633393093313823239/

这个由KONAMI辞职员工建立的公司,建立了一个新的游戏王朝
https://www.toutiao.com/i6633764479127519752/

值得关注的号
======
闯关族之家
https://www.toutiao.com/c/user/17238062515/#mid=1606759100795908
1号玩家
https://www.toutiao.com/c/user/77965025598/#mid=1585652639205389

关键词
===
岩谷彻的《吃豆人》
西角友宏的《太空侵略者》
石村繁一的《Galaxian》
远藤雅伸的《铁板阵》
《妖怪道中记》
TENGEN(天元)中岛英行
坂本贺勇的《银河战士》
“游戏音乐圣手”的细野晴臣
田尻智的《口袋妖怪》
上月景正

安卓jni毫秒级打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <string>
 
using namespace std;
 
void ivrLog(const char *format, ...)
{
	va_list argptr;
	char buf[1024 * 2];
 
	va_start(argptr, format);
	vsprintf(buf, format, argptr);
	va_end(argptr);
 
	struct timeval tv;
	int iRet = gettimeofday(&tv, NULL);
	time_t t = tv.tv_sec;
	tm* local = localtime(&t);
	char timeBuf[256];
	strftime(timeBuf, 254, "[%Y-%m-%d %H:%M:%S", local);
	sprintf(timeBuf + strlen(timeBuf), ":%d] ", (int)(tv.tv_usec / 1000));
	string dispStr = timeBuf;
	dispStr += buf;
 
	printf("%s", dispStr.c_str());
 
	// please ensure /sdcard/Test dir existed first
	char *logFileName = (char *)"/sdcard/Test/log.txt";
 
	int fd = open(logFileName, O_CREAT|O_WRONLY|O_APPEND, 0666);
	if (fd != -1)
	{
		write(fd, dispStr.c_str(), dispStr.length());
		close(fd);
	}
}

Android jni fopen返回NULL而errno为13

除了要在AndroidManifest.xml加

1
2
3
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

关键是看传给fopen或open的文件路径是否“正确”,比如传入“/storage/6236-6439/down/log.txt”之类的路径会返回NULL,errno为EACCES。
比如传入“/sdcard/Test/log.txt”之类的路径则一切正常没任何问题。

如果还是没看到文件被创建,那就该好好看看该jni库是否被正确加载了。

recv 10060

请检查socket连接时有没有设置超时。

Linux可复入的互斥量pthread_mutex_t

互斥量可重入(可复入)的概念是:同一个线程,进入一互斥量后,不出此互斥量而再次进入此互斥量,线程不会死等。

pthread_mutex_init调用时第二个参数传入NULL,即为默认的mutex初始化方式。请记住,这种默认的初始化的方式,生成的是不可重入的互斥量,即同线程重复进入会死等(或叫死锁)。这与Windows上InitializeCriticalSection初始化恰相反。

要pthread_mutex_t可重入,需要使用PTHREAD_MUTEX_RECURSIVE_NP参数,具体见本文后所附源码。

本人已抽象出跨平台的互斥量源码库,其中的互斥量都是可重入的,在Linux和Windows上都可以统一调用,先说使用举例。

定义:

1
WhMutex mutex_;

初始化:

1
whMutexInit(&mutex_);

反初始化:

1
whMutexFini(&mutex_);

当要把一段代码设为临界段时:

1
2
3
4
5
{
	WhMutexGuard guard(&mutex_);
	// your critical section code here.
	int i;
}

以下是抽象互斥量库源码(直接复制过去就可编译):

Read more

Mame自定义按钮和组合键即FC游戏里的单发连发及侍魂里的大刀

先明确一下概念,一个是物理键,比如键盘上的键,一个是功能键,比如子弹键。

1,有时候我们想一个物理键对应多个功能键,比如在《侍魂2》里,我们想很容易地砍出大刀,就是这种需求。

在mame64.exe里,我们可以按TAB键,在“Input (general)”或“Input (this Machine)”子菜单中,给一个功能键指定多个物理键,那么就能做出一个物理键触发多个功能键,满足了这种需求。

在mamep.exe里,我们同样按TAB键,顶层菜单中会出现“自定义按钮”的选项,点进去,在某个自定义按钮条目上,同时按数字键1、2、3、4中的若干个,会提示此自定义按钮对应了哪几个功能键。然后,在顶层菜单的输入设置里,会找到4个自定义按钮的物理键指定条目,这样设置下来,同样可以砍出大刀。

2,有时候我们想多个物理键对应一个功能键,比如一个物理键对应FC游戏里的单发A键,另一个物理键对应FC游戏里的连发A键。

对于这个需求,mame64.exe是做不到的,因为mame64.exe的连射功能(Autofire Settings)只指定给功能键,这样无论指定多少个物理键给这个指定了连射的功能键,都不能在键盘上,一键单发子弹,另一键连发子弹。

但mamep.exe可行,因为mamep.exe的“连射设置”是指定给自定义按钮的,自定义按钮是介乎物理键与功能键中间的一层。方法如下:

我们以“P1 按钮 1”为子弹键为例来说明。按TAB键,在顶层菜单中,进入mamep.exe的“连射设置”,将“P1 按钮 1”条目设为关闭,将“P1 自定义按钮 1”条目设为打开。

回到顶层菜单,进入“输入设置(一般)”,再进入“玩家 1 控制”,将“P1 按钮 1”设为比如键盘上的U键,将“P1 自定义按钮 1”设为比如键盘上的N键。

再次回到顶层菜单,进入“自定义按钮”,在“P1 自定义按钮 1”条目上,点击一下键盘上的数字键1,使此条目右边只有一个圈起来的1字。

好,设置完成,这时按TAB键关闭菜单,开始游戏,就会发现,键盘上U键是单发子弹,键盘上的N键是连发子弹。完美!

Read more

Mame游戏里的作弊

将下载的包cheat.7z放在mame64.exe的同层目录中,再运行mamepgui.exe(M+GUI)启动游戏,加载的若是mame64.exe,按一下TAB键,顶层菜单中会看到“Cheat”的设置,点进去,同时可看到在“Cheat”菜单里还有“连发设置”也叫“连射设置”的功能。

注意这里是mame64.exe(mame32.exe/mame.exe)而非mamep.exe,关于两者的区别见:

Read more