加入usb-camera

Signed-off-by: donghongyu <donghongyu@zhidaoauto.com>
This commit is contained in:
donghongyu
2022-02-09 18:57:27 +08:00
parent 986c3d46f5
commit 3f64bb093a
70 changed files with 10326 additions and 642 deletions

View File

@@ -25,6 +25,10 @@ android {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
buildTypes {
@@ -38,6 +42,18 @@ android {
sourceCompatibility 1.8
targetCompatibility 1.8
}
repositories {
flatDir {
dirs 'libs'
}
}
sourceSets{
main{
jniLibs.srcDir(['libs'])
}
}
}
dependencies {
@@ -45,21 +61,12 @@ dependencies {
implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler
implementation(name: 'common-4.1.1', ext: 'aar')
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogoutils
implementation rootProject.ext.dependencies.mogomapapi
implementation rootProject.ext.dependencies.mogocommons
implementation rootProject.ext.dependencies.mogomapapi
implementation rootProject.ext.dependencies.mogo_core_data
} else {
implementation project(':foudations:mogo-utils')
implementation project(':libraries:mogo-map-api')
implementation project(':foudations:mogo-commons')
implementation project(':services:mogo-service-api')
implementation project(':core:mogo-core-data')
}
}

View File

@@ -1,3 +1,3 @@
GROUP=com.mogo.map
POM_ARTIFACT_ID=map-autonavi
GROUP=com.mogo.camera
POM_ARTIFACT_ID=map-usbcamera
VERSION_CODE=1

Binary file not shown.

View File

@@ -1,13 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.map.impl.automap">
package="com.mogo.usbcamera">
<application>
<receiver
android:name=".navi.AutoNaviReceiver"
android:exported="true">
<intent-filter>
<action android:name="AUTONAVI_STANDARD_BROADCAST_SEND" />
</intent-filter>
</receiver>
</application>
</manifest>

Binary file not shown.

View File

@@ -1,229 +0,0 @@
package com.mogo.map.impl.automap.navi;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.location.Location;
import com.mogo.eagle.core.data.map.MogoLatLng;
import com.mogo.map.navi.IMogoCarLocationChangedListener2;
import com.mogo.map.navi.IMogoNavi;
import com.mogo.map.navi.MogoCalculatePath;
import com.mogo.map.navi.MogoNaviConfig;
import com.mogo.map.navi.OnCalculatePathItemClickInteraction;
import com.mogo.utils.logger.Logger;
import java.util.List;
/**
* @author congtaowang
* @since 2020/6/2
* <p>
* 使用高德车机版导航
*/
public class AutoNaviClient implements IMogoNavi {
private static final String TAG = "NaviClient";
public static final String ACTION_AUTO_MAP = "AUTONAVI_STANDARD_BROADCAST_RECV";
public static final String KEY_TYPE = "KEY_TYPE";
public static final String SOURCE_APP = "SOURCE_APP";
public static final String LAT = "LAT";
public static final String LON = "LON";
public static final String ENTRY_LAT = "ENTRY_LAT";
public static final String ENTRY_LON = "ENTRY_LON";
public static final String DEV = "DEV"; // (int)是否偏移(0:lat 和 lon 是已经加密后的,不需要国测加密; 1:需要国测加密)
/**
* (必填)(int)导航方式
* =1(避免收费)
* =2(多策略算路)
* =3 (不走高速)
* =4(躲避拥堵)
* =5(不走高速且避免收费)
* =6(不走高速且躲避拥堵)
* =7(躲避收费且躲避拥堵)
* =8(不走高速躲避收费和拥堵)
* =20 (高速优先)
* =24(高速优先且躲避拥堵)
* =-1(地图内部设置默认规则)
*/
public static final String STYLE = "STYLE";
private static volatile AutoNaviClient sInstance;
private final Context mContext;
private AutoNaviClient( Context context ) {
mContext = context.getApplicationContext();
}
public static AutoNaviClient getInstance( Context context ) {
if ( sInstance == null ) {
synchronized ( AutoNaviClient.class ) {
if ( sInstance == null ) {
sInstance = new AutoNaviClient( context );
}
}
}
return sInstance;
}
public synchronized void release() {
sInstance = null;
}
@Override
public void naviTo( MogoLatLng endPoint ) {
Intent intent = new Intent();
intent.putExtra( KEY_TYPE, 10038 );
intent.putExtra( LAT, endPoint.lat );
intent.putExtra( LON, endPoint.lon );
// intent.putExtra( ENTRY_LAT, endPoint.lat );
// intent.putExtra( ENTRY_LON, endPoint.lon );
intent.putExtra( DEV, 0 );
intent.putExtra( STYLE, -1 );
intent.putExtra( SOURCE_APP, "Third App" );
sendByIntent( intent );
}
@Override
public void naviTo( MogoLatLng endPoint, MogoNaviConfig config ) {
naviTo( endPoint );
}
@Override
public void naviTo( MogoLatLng endPoint, List< MogoLatLng > wayPoints ) {
naviTo( endPoint );
}
@Override
public void naviTo( MogoLatLng endPoint, List< MogoLatLng > wayPoints, MogoNaviConfig config ) {
naviTo( endPoint );
}
@Override
public void reCalculateRoute( MogoNaviConfig config ) {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public void stopNavi() {
Intent intent = new Intent();
intent.putExtra( "KEY_TYPE", 10010 );
sendByIntent( intent );
}
@Override
public void startNavi( boolean isRealNavi ) {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
private void sendByIntent( Intent intent ) {
intent.setAction( ACTION_AUTO_MAP );
intent.addFlags( Intent.FLAG_INCLUDE_STOPPED_PACKAGES );
mContext.sendBroadcast( intent );
}
@Override
public boolean isNaviing() {
return MapState.getInstance().isNaving();
}
@Override
public List< MogoCalculatePath > getCalculatedStrategies() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
return null;
}
@Override
public List< MogoLatLng > getCalculatedPathPos() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
return null;
}
@Override
public OnCalculatePathItemClickInteraction getItemClickInteraction() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
return null;
}
@Override
public void setLineClickInteraction( OnCalculatePathItemClickInteraction itemClickInteraction ) {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public void clearCalculatePaths() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public void setCalculatePathDisplayBounds( Rect bounds ) {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public MogoNaviConfig getNaviConfig() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
return MogoMapApi.getApiBuilder().getNavi( mContext ).getNaviConfig();
}
@Override
public boolean setBroadcastMode( int mode ) {
Logger.w( TAG, "高德车机导航,不支持此设置" );
return false;
}
@Override
public List< MogoLatLng > getNaviPathCoordinates() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
return null;
}
@Override
public MogoLatLng getCarLocation() {
return MogoMapApi.getApiBuilder().getNavi( mContext ).getCarLocation();
}
@Override
public Location getCarLocation2() {
return MogoMapApi.getApiBuilder().getNavi( mContext ).getCarLocation2();
}
@Override
public void registerCarLocationChangedListener( IMogoCarLocationChangedListener2 listener ) {
//do not impl
}
@Override
public void startAimlessMode() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public void stopAimlessMode() {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public void setAimlessModeStatus( boolean open ) {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public void displayOverview( Rect bounds ) {
Logger.w( TAG, "高德车机导航,不支持此设置" );
}
@Override
public void setUseExtraGPSData( boolean use ) {
MogoMapApi.getApiBuilder().getNavi( mContext ).setUseExtraGPSData( use );
}
@Override
public void setExtraGPSData( double lon, double lat, float speed, float accuracy, float bearing, long timestamp ) {
MogoMapApi.getApiBuilder().getNavi( mContext ).setExtraGPSData( lon, lat, speed, accuracy, bearing, timestamp );
}
}

View File

@@ -1,159 +0,0 @@
package com.mogo.map.impl.automap.navi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.text.TextUtils;
import com.mogo.commons.storage.SpStorage;
import com.mogo.map.navi.MogoNaviInfo;
import com.mogo.map.navi.MogoNaviListenerHandler;
import com.mogo.map.navi.MogoTraffic;
import com.mogo.utils.logger.Logger;
/**
* @author congtaowang
* @since 2020/6/2
* <p>
* 高德公版地图透出信息广播接收者
*/
public class AutoNaviReceiver extends BroadcastReceiver {
private static final String TAG = "AutoNaviReceiver";
public static final String ACTION_AUTONAVI_SEND = "AUTONAVI_STANDARD_BROADCAST_SEND";
private static AutoNaviReceiver autoNaviReceiver;
private static boolean sRegisterFlag = false;
private static MogoNaviInfo sNaviInfo;
private static MogoTraffic sTraffic;
public static void register( Context context ) {
IntentFilter filter = new IntentFilter();
filter.addAction( ACTION_AUTONAVI_SEND );
autoNaviReceiver = new AutoNaviReceiver();
context.registerReceiver( autoNaviReceiver, filter );
sRegisterFlag = true;
}
public static void unregister( Context context ) {
if ( autoNaviReceiver != null && sRegisterFlag ) {
try {
context.unregisterReceiver( autoNaviReceiver );
} catch ( Exception e ) {
Logger.e( TAG, e, "error. " );
}
}
}
@Override
public void onReceive( Context context, Intent intent ) {
String action = intent.getAction();
if ( !TextUtils.equals( ACTION_AUTONAVI_SEND, action ) ) {
return;
}
int keyType = intent.getIntExtra( "KEY_TYPE", 0 );
switch ( keyType ) {
case 10001:
handleAutoNaviInfo( context, intent );
break;
case 10019:
int state = intent.getIntExtra( "EXTRA_STATE", -1 );
handleMapStatusChanged( state );
break;
case 10056:
String json = intent.getStringExtra( "EXTRA_ROAD_INFO" );
SpStorage.setNavigationTarget( json );
Logger.d( TAG, json );
break;
}
}
/**
* 在导航/巡航/模拟导航中auto主动将变化的引导信息发送给第三方系统
*
* @param intent
*/
private void handleAutoNaviInfo( Context context, Intent intent ) {
int type = intent.getIntExtra( GuideInfoExtraKey.TYPE, -1 );
int cameraSpeed = intent.getIntExtra( GuideInfoExtraKey.CAMERA_SPEED, 0 );
int cameraDisc = intent.getIntExtra( GuideInfoExtraKey.CAMERA_DIST, 0 );
int cameraType = intent.getIntExtra( GuideInfoExtraKey.CAMERA_TYPE, 0 );
if ( type == 0 || type == 1 ) {
if ( !MapState.getInstance().isNaving()
&& MogoNaviListenerHandler.getInstance().hasDelegateListener() ) {
MapState.getInstance().setNaving( true );
MogoNaviListenerHandler.getInstance().onStartNavi();
}
if ( sNaviInfo == null ) {
sNaviInfo = new MogoNaviInfo();
}
sNaviInfo.setCurrentLimitSpeed( cameraSpeed );
sNaviInfo.setCurrentRoadName( intent.getStringExtra( GuideInfoExtraKey.CUR_ROAD_NAME ) );
sNaviInfo.setCurrentSpeed( intent.getIntExtra( GuideInfoExtraKey.CUR_SPEED, 0 ) );
sNaviInfo.setCurStepRetainDistance( intent.getIntExtra( GuideInfoExtraKey.SEG_REMAIN_DIS, 0 ) );
sNaviInfo.setCurStepRetainTime( intent.getIntExtra( GuideInfoExtraKey.SEG_REMAIN_TIME, 0 ) );
sNaviInfo.setIconResId( MogoMapApi.getApiBuilder().getResIdByIconType( context, intent.getIntExtra( GuideInfoExtraKey.NEW_ICON, 0 ) ) );
sNaviInfo.setNextRoadName( intent.getStringExtra( GuideInfoExtraKey.NEXT_ROAD_NAME ) );
sNaviInfo.setPathRetainDistance( intent.getIntExtra( GuideInfoExtraKey.ROUTE_REMAIN_DIS, 0 ) );
sNaviInfo.setPathRetainTime( intent.getIntExtra( GuideInfoExtraKey.ROUTE_REMAIN_TIME, 0 ) );
MogoNaviListenerHandler.getInstance().onNaviInfoUpdate( sNaviInfo );
}
if ( sTraffic == null ) {
sTraffic = new MogoTraffic( MapState.getInstance().isAimless() ? MogoTraffic.TYPE_AIM : MogoTraffic.TYPE_NAVI );
}
sTraffic.setFromType( MapState.getInstance().isAimless() ? MogoTraffic.TYPE_AIM : MogoTraffic.TYPE_NAVI );
sTraffic.setDistance( cameraDisc );
sTraffic.setSpeedLimit( cameraSpeed );
sTraffic.setTrafficType( cameraType );
MogoNaviListenerHandler.getInstance().onUpdateTraffic2( sTraffic );
}
/**
* 当导航发生状态变更时,将相应的状态通知给系统。
*
* @param state
*/
private void handleMapStatusChanged( int state ) {
if ( state == -1 ) {
return;
}
switch ( state ) {
case MapStateValue.START_NAVI:
case MapStateValue.START_EMULATOR_NAVI:
if ( MapState.getInstance().isNaving() ) {
Logger.w( TAG, "naving..." );
return;
}
MapState.getInstance().setNaving( true );
MogoNaviListenerHandler.getInstance().onStartNavi();
break;
case MapStateValue.STOP_NAVI:
case MapStateValue.STOP_EMULATOR_NAVI:
case MapStateValue.APP_START: // 语音退出导航感觉是杀掉了高德APP了
if ( MapState.getInstance().isNaving() ) {
MapState.getInstance().setNaving( false );
MogoNaviListenerHandler.getInstance().onStopNavi();
}
break;
case MapStateValue.START_AIMLESS_NAVI:
MapState.getInstance().setAimless( true );
break;
case MapStateValue.STOP_AIMLESS_NAVI:
MapState.getInstance().setAimless( false );
break;
case MapStateValue.EXIT_APP:
break;
case MapStateValue.DESTINATION:
MogoNaviListenerHandler.getInstance().onArriveDestination();
break;
}
}
}

View File

@@ -1,129 +0,0 @@
package com.mogo.map.impl.automap.navi;
//引导信息对应的KEY值机器描述
public class GuideInfoExtraKey {
/**
* 导航类型对应的值为int类型
* 0:GPS导航
* 1:模拟导航
* 2:巡航
*/
public static final String TYPE = "TYPE";
/**
* 当前道路名称对应的值为String类型
*/
public static final String CUR_ROAD_NAME = "CUR_ROAD_NAME";
/**
* 下一道路名对应的值为String类型
*/
public static final String NEXT_ROAD_NAME = "NEXT_ROAD_NAME";
/**
* 电子眼限速度对应的值为int类型无限速则为0单位:公里/小时
*/
public static final String CAMERA_SPEED = "CAMERA_SPEED";
/**
* 导航转向图标对应的值为int类型
*/
public static final String ICON = "ICON";
/**
* 导航最新的转向图标对应的值为int类型
*/
public static final String NEW_ICON = "NEW_ICON";
/**
* 路径剩余距离对应的值为int类型单位:米
*/
public static final String ROUTE_REMAIN_DIS = "ROUTE_REMAIN_DIS";
/**
* 路径剩余时间对应的值为int类型单位:秒
*/
public static final String ROUTE_REMAIN_TIME = "ROUTE_REMAIN_TIME";
/**
* 当前导航段剩余距离对应的值为int类型单位:米
*/
public static final String SEG_REMAIN_DIS = "SEG_REMAIN_DIS";
/**
* 当前导航段剩余时间对应的值为int类型单位:秒
*/
public static final String SEG_REMAIN_TIME = "SEG_REMAIN_TIME";
/**
* 路径总距离对应的值为int类型单位:米
*/
public static final String ROUTE_ALL_DIS = "ROUTE_ALL_DIS";
/**
* 路径总时间对应的值为int类型单位:秒
*/
public static final String ROUTE_ALL_TIME = "ROUTE_ALL_TIME";
/**
* 当前车速对应的值为int类型单位:公里/小时
*/
public static final String CUR_SPEED = "CUR_SPEED";
/**
* 当前道路类型对应的值为int类型
* 0:高速公路
* 1:国道
* 2:省道
* 3:县道
* 4:乡公路
* 5:县乡村内部道路
* 6:主要大街、城市快速道 * 7:主要道路
* 8:次要道路
* 9:普通道路
* 10:非导航道路
*/
public static final String ROAD_TYPE = "ROAD_TYPE";
/**
* 路径剩余时间对应的值为String类型单位:天/小时/分钟 比如:1天2小时 21小时30分
* 钟(只用于长安)
*/
public static final String ROUTE_REMAIN_TIME_STRING = "ROUTE_REMAIN_TIME_S TRING";
/**
* 下下个路名名称对应的值为String类型
*/
public static final String NEXT_NEXT_ROAD_NAME = "NEXT_NEXT_ROAD_NAME";
/**
* 下下个路口转向图标对应的值为int类型
*/
public static final String NEXT_NEXT_TURN_ICON = "NEXT_NEXT_TURN_ICON";
/**
* 距离下下个路口剩余距离对应的值为int类型单位:米
*/
public static final String NEXT_SEG_REMAIN_DIS = "NEXT_SEG_REMAIN_DIS";
/**
* 距离下下个路口剩余时间对应的值为int类型单位:秒
*/
public static final String NEXT_SEG_REMAIN_TIME = "NEXT_SEG_REMAIN_TIME";
/**
* 距离最近的电子眼距离对应的值为int类型单位:米
*/
public static final String CAMERA_DIST = "CAMERA_DIST";
/**
* 电子眼类型对应的值为int类型
* 0 测速摄像头
* 1为监控摄像头
* 2为闯红灯拍照
* 3为违章拍照
* 4为公交专用道摄像头
* 5为应急车道摄像头
* 6为非机动车道拍照
*/
public static final String CAMERA_TYPE = "CAMERA_TYPE";
}

View File

@@ -1,50 +0,0 @@
package com.mogo.map.impl.automap.navi;
public
/**
* @author congtaowang
* @since 2020/6/3
*
* 高德地图状态
*/
class MapState {
private static volatile MapState sInstance;
private MapState() {
}
public static MapState getInstance() {
if ( sInstance == null ) {
synchronized ( MapState.class ) {
if ( sInstance == null ) {
sInstance = new MapState();
}
}
}
return sInstance;
}
public synchronized void release() {
sInstance = null;
}
private boolean isNaving = false;
private boolean isAimless = false;
public boolean isNaving() {
return isNaving;
}
public void setNaving( boolean naving ) {
isNaving = naving;
}
public boolean isAimless() {
return isAimless;
}
public void setAimless( boolean aimless ) {
isAimless = aimless;
}
}

View File

@@ -1,28 +0,0 @@
package com.mogo.map.impl.automap.navi;
/**
* @author congtaowang
* @since 2020/6/3
* <p>
* 高德公版地图状态值
*/
public interface MapStateValue {
int START_NAVI = 8;
int STOP_NAVI = 9;
int START_EMULATOR_NAVI = 10;
int STOP_EMULATOR_NAVI = 12;
int START_AIMLESS_NAVI = 24;
int STOP_AIMLESS_NAVI = 25;
int EXIT_APP = 45;
int DESTINATION = 39;
int APP_START = 0;
}

View File

@@ -1,26 +0,0 @@
package com.mogo.map.impl.automap.navi;
import com.alibaba.android.arouter.launcher.ARouter;
import com.mogo.map.IMogoMapApiBuilder;
/**
* @author congtaowang
* @since 2020/12/10
* <p>
* 描述
*/
public class MogoMapApi {
private static IMogoMapApiBuilder sApiBuilder;
public static IMogoMapApiBuilder getApiBuilder() {
if (sApiBuilder == null) {
synchronized (AutoNaviClient.class) {
if (sApiBuilder == null) {
sApiBuilder = ARouter.getInstance().navigation(IMogoMapApiBuilder.class);
}
}
}
return sApiBuilder;
}
}

View File

@@ -0,0 +1,371 @@
package com.mogo.usbcamera;
import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.usb.UsbDevice;
import android.os.Environment;
import org.easydarwin.sw.TxtOverlay;
import com.serenegiant.usb.DeviceFilter;
import com.serenegiant.usb.Size;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.common.AbstractUVCCameraHandler;
import com.serenegiant.usb.common.UVCCameraHandler;
import com.serenegiant.usb.encoder.RecordParams;
import com.serenegiant.usb.widget.CameraViewInterface;
import java.io.File;
import java.util.List;
import java.util.Objects;
/** UVCCamera Helper class
*
* Created by jiangdongguo on 2017/9/30.
*/
public class UVCCameraHelper {
public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator;
public static final String SUFFIX_JPEG = ".jpg";
public static final String SUFFIX_MP4 = ".mp4";
private static final String TAG = "UVCCameraHelper";
private int previewWidth = 640;
private int previewHeight = 480;
public static final int FRAME_FORMAT_YUYV = UVCCamera.FRAME_FORMAT_YUYV;
// Default using MJPEG
// if your device is connected,but have no images
// please try to change it to FRAME_FORMAT_YUYV
public static final int FRAME_FORMAT_MJPEG = UVCCamera.FRAME_FORMAT_MJPEG;
public static final int MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS;
public static final int MODE_CONTRAST = UVCCamera.PU_CONTRAST;
private int mFrameFormat = FRAME_FORMAT_MJPEG;
private static UVCCameraHelper mCameraHelper;
// USB Manager
private USBMonitor mUSBMonitor;
// Camera Handler
private UVCCameraHandler mCameraHandler;
private USBMonitor.UsbControlBlock mCtrlBlock;
private Activity mActivity;
private CameraViewInterface mCamView;
private UVCCameraHelper() {
}
public static UVCCameraHelper getInstance() {
if (mCameraHelper == null) {
mCameraHelper = new UVCCameraHelper();
}
return mCameraHelper;
}
public void closeCamera() {
if (mCameraHandler != null) {
mCameraHandler.close();
}
}
public interface OnMyDevConnectListener {
void onAttachDev(UsbDevice device);
void onDettachDev(UsbDevice device);
void onConnectDev(UsbDevice device, boolean isConnected);
void onDisConnectDev(UsbDevice device);
}
public void initUSBMonitor(Activity activity, CameraViewInterface cameraView, final OnMyDevConnectListener listener) {
this.mActivity = activity;
this.mCamView = cameraView;
mUSBMonitor = new USBMonitor(activity.getApplicationContext(), new USBMonitor.OnDeviceConnectListener() {
// called by checking usb device
// do request device permission
@Override
public void onAttach(UsbDevice device) {
if (listener != null) {
listener.onAttachDev(device);
}
}
// called by taking out usb device
// do close camera
@Override
public void onDettach(UsbDevice device) {
if (listener != null) {
listener.onDettachDev(device);
}
}
// called by connect to usb camera
// do open camera,start previewing
@Override
public void onConnect(final UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
mCtrlBlock = ctrlBlock;
openCamera(ctrlBlock);
new Thread(new Runnable() {
@Override
public void run() {
// wait for camera created
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// start previewing
startPreview(mCamView);
}
}).start();
if(listener != null) {
listener.onConnectDev(device,true);
}
}
// called by disconnect to usb camera
// do nothing
@Override
public void onDisconnect(UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock) {
if (listener != null) {
listener.onDisConnectDev(device);
}
}
@Override
public void onCancel(UsbDevice device) {
}
});
createUVCCamera();
}
public void createUVCCamera() {
if (mCamView == null) {
throw new NullPointerException("CameraViewInterface cannot be null!");
}
// release resources for initializing camera handler
if (mCameraHandler != null) {
mCameraHandler.release();
mCameraHandler = null;
}
// initialize camera handler
mCamView.setAspectRatio(previewWidth / (float)previewHeight);
mCameraHandler = UVCCameraHandler.createHandler(mActivity, mCamView, 2,
previewWidth, previewHeight, mFrameFormat);
}
public void updateResolution(int width, int height) {
if (previewWidth == width && previewHeight == height) {
return;
}
this.previewWidth = width;
this.previewHeight = height;
if (mCameraHandler != null) {
mCameraHandler.release();
mCameraHandler = null;
}
mCamView.setAspectRatio(previewWidth / (float)previewHeight);
mCameraHandler = UVCCameraHandler.createHandler(mActivity,mCamView, 2,
previewWidth, previewHeight, mFrameFormat);
openCamera(mCtrlBlock);
new Thread(new Runnable() {
@Override
public void run() {
// wait for camera created
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// start previewing
startPreview(mCamView);
}
}).start();
}
public void registerUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.register();
}
}
public void unregisterUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.unregister();
}
}
public boolean checkSupportFlag(final int flag) {
return mCameraHandler != null && mCameraHandler.checkSupportFlag(flag);
}
public int getModelValue(final int flag) {
return mCameraHandler != null ? mCameraHandler.getValue(flag) : 0;
}
public int setModelValue(final int flag, final int value) {
return mCameraHandler != null ? mCameraHandler.setValue(flag, value) : 0;
}
public int resetModelValue(final int flag) {
return mCameraHandler != null ? mCameraHandler.resetValue(flag) : 0;
}
public void requestPermission(int index) {
List<UsbDevice> devList = getUsbDeviceList();
if (devList == null || devList.size() == 0) {
return;
}
int count = devList.size();
if (index >= count)
new IllegalArgumentException("index illegal,should be < devList.size()");
if (mUSBMonitor != null) {
mUSBMonitor.requestPermission(getUsbDeviceList().get(index));
}
}
public int getUsbDeviceCount() {
List<UsbDevice> devList = getUsbDeviceList();
if (devList == null || devList.size() == 0) {
return 0;
}
return devList.size();
}
public List<UsbDevice> getUsbDeviceList() {
List<DeviceFilter> deviceFilters = DeviceFilter
.getDeviceFilters(mActivity.getApplicationContext(), R.xml.device_filter);
if (mUSBMonitor == null || deviceFilters == null)
// throw new NullPointerException("mUSBMonitor ="+mUSBMonitor+"deviceFilters=;"+deviceFilters);
return null;
// matching all of filter devices
return mUSBMonitor.getDeviceList(deviceFilters);
}
public void capturePicture(String savePath,AbstractUVCCameraHandler.OnCaptureListener listener) {
if (mCameraHandler != null && mCameraHandler.isOpened()) {
File file = new File(savePath);
if(! Objects.requireNonNull(file.getParentFile()).exists()) {
file.getParentFile().mkdirs();
}
mCameraHandler.captureStill(savePath,listener);
}
}
public void startPusher(AbstractUVCCameraHandler.OnEncodeResultListener listener) {
if (mCameraHandler != null && !isPushing()) {
mCameraHandler.startRecording(null, listener);
}
}
public void startPusher(RecordParams params, AbstractUVCCameraHandler.OnEncodeResultListener listener) {
if (mCameraHandler != null && !isPushing()) {
if(params.isSupportOverlay()) {
TxtOverlay.install(mActivity.getApplicationContext());
}
mCameraHandler.startRecording(params, listener);
}
}
public void stopPusher() {
if (mCameraHandler != null && isPushing()) {
mCameraHandler.stopRecording();
}
}
public boolean isPushing() {
if (mCameraHandler != null) {
return mCameraHandler.isRecording();
}
return false;
}
public boolean isCameraOpened() {
if (mCameraHandler != null) {
return mCameraHandler.isOpened();
}
return false;
}
public void release() {
if (mCameraHandler != null) {
mCameraHandler.release();
mCameraHandler = null;
}
if (mUSBMonitor != null) {
mUSBMonitor.destroy();
mUSBMonitor = null;
}
}
public USBMonitor getUSBMonitor() {
return mUSBMonitor;
}
public void setOnPreviewFrameListener(AbstractUVCCameraHandler.OnPreViewResultListener listener) {
if(mCameraHandler != null) {
mCameraHandler.setOnPreViewResultListener(listener);
}
}
private void openCamera(USBMonitor.UsbControlBlock ctrlBlock) {
if (mCameraHandler != null) {
mCameraHandler.open(ctrlBlock);
}
}
public void startPreview(CameraViewInterface cameraView) {
SurfaceTexture st = cameraView.getSurfaceTexture();
if (mCameraHandler != null) {
mCameraHandler.startPreview(st);
}
}
public void stopPreview() {
if (mCameraHandler != null) {
mCameraHandler.stopPreview();
}
}
public void startCameraFoucs() {
if (mCameraHandler != null) {
mCameraHandler.startCameraFoucs();
}
}
public List<Size> getSupportedPreviewSizes() {
if (mCameraHandler == null) {
return null;
}
return mCameraHandler.getSupportedPreviewSizes();
}
public void setDefaultPreviewSize(int defaultWidth,int defaultHeight) {
if(mUSBMonitor != null) {
throw new IllegalStateException("setDefaultPreviewSize should be call before initMonitor");
}
this.previewWidth = defaultWidth;
this.previewHeight = defaultHeight;
}
public void setDefaultFrameFormat(int format) {
if(mUSBMonitor != null) {
throw new IllegalStateException("setDefaultFrameFormat should be call before initMonitor");
}
this.mFrameFormat = format;
}
public int getPreviewWidth() {
return previewWidth;
}
public int getPreviewHeight() {
return previewHeight;
}
}

View File

@@ -0,0 +1,61 @@
package com.mogo.usbcamera.utils;
import android.os.Environment;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
*
* Created by jiangdongguo on 2017/10/18.
*/
public class FileUtils {
private static BufferedOutputStream outputStream;
public static String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator;
public static void createfile(String path){
File file = new File(path);
if(file.exists()){
file.delete();
}
try {
outputStream = new BufferedOutputStream(new FileOutputStream(file));
} catch (Exception e){
e.printStackTrace();
}
}
public static void releaseFile(){
try {
if(outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void putFileStream(byte[] data,int offset,int length){
if(outputStream != null) {
try {
outputStream.write(data,offset,length);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void putFileStream(byte[] data){
if(outputStream != null) {
try {
outputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,245 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckedTextView;
import android.widget.Spinner;
import com.mogo.usbcamera.R;
import java.util.ArrayList;
import java.util.List;
public class CameraDialog extends DialogFragment {
private static final String TAG = CameraDialog.class.getSimpleName();
public interface CameraDialogParent {
public USBMonitor getUSBMonitor();
public void onDialogResult(boolean canceled);
}
/**
* Helper method
*
* @param parent FragmentActivity
* @return
*/
public static CameraDialog showDialog(final Activity parent/* add parameters here if you need */) {
CameraDialog dialog = newInstance(/* add parameters here if you need */);
try {
dialog.show(parent.getFragmentManager(), TAG);
} catch (final IllegalStateException e) {
dialog = null;
}
return dialog;
}
public static CameraDialog newInstance(/* add parameters here if you need */) {
final CameraDialog dialog = new CameraDialog();
final Bundle args = new Bundle();
// add parameters here if you need
dialog.setArguments(args);
return dialog;
}
protected USBMonitor mUSBMonitor;
private Spinner mSpinner;
private DeviceListAdapter mDeviceListAdapter;
public CameraDialog(/* no arguments */) {
// Fragment need default constructor
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
if (mUSBMonitor == null) {
try {
mUSBMonitor = ((CameraDialogParent) activity).getUSBMonitor();
} catch (final NullPointerException | ClassCastException e) {
e.printStackTrace();
}
}
if (mUSBMonitor == null) {
throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
savedInstanceState = getArguments();
}
}
@Override
public void onSaveInstanceState(final Bundle saveInstanceState) {
final Bundle args = getArguments();
if (args != null) {
saveInstanceState.putAll(args);
}
super.onSaveInstanceState(saveInstanceState);
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(initView());
builder.setTitle(R.string.select);
builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
builder.setNegativeButton(android.R.string.cancel, mOnDialogClickListener);
builder.setNeutralButton(R.string.refresh, null);
final Dialog dialog = builder.create();
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
return dialog;
}
/**
* create view that this fragment shows
*
* @return
*/
private final View initView() {
final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
mSpinner = (Spinner) rootView.findViewById(R.id.spinner1);
final View empty = rootView.findViewById(android.R.id.empty);
mSpinner.setEmptyView(empty);
return rootView;
}
@Override
public void onResume() {
super.onResume();
updateDevices();
final Button button = (Button) getDialog().findViewById(android.R.id.button3);
if (button != null) {
button.setOnClickListener(mOnClickListener);
}
}
private final OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onClick(final View v) {
switch (v.getId()) {
case android.R.id.button3:
updateDevices();
break;
}
}
};
private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
final Object item = mSpinner.getSelectedItem();
if (item instanceof UsbDevice) {
mUSBMonitor.requestPermission((UsbDevice) item);
((CameraDialogParent) getActivity()).onDialogResult(false);
}
break;
case DialogInterface.BUTTON_NEGATIVE:
((CameraDialogParent) getActivity()).onDialogResult(true);
break;
}
}
};
@Override
public void onCancel(final DialogInterface dialog) {
((CameraDialogParent) getActivity()).onDialogResult(true);
super.onCancel(dialog);
}
public void updateDevices() {
// mUSBMonitor.dumpDevices();
final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
mSpinner.setAdapter(mDeviceListAdapter);
}
private static final class DeviceListAdapter extends BaseAdapter {
private final LayoutInflater mInflater;
private final List<UsbDevice> mList;
public DeviceListAdapter(final Context context, final List<UsbDevice> list) {
mInflater = LayoutInflater.from(context);
mList = list != null ? list : new ArrayList<UsbDevice>();
}
@Override
public int getCount() {
return mList.size();
}
@Override
public UsbDevice getItem(final int position) {
if ((position >= 0) && (position < mList.size())) {
return mList.get(position);
} else {
return null;
}
}
@Override
public long getItemId(final int position) {
return position;
}
@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
}
if (convertView instanceof CheckedTextView) {
final UsbDevice device = getItem(position);
((CheckedTextView) convertView).setText(
String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
}
return convertView;
}
}
}

View File

@@ -0,0 +1,527 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.text.TextUtils;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public final class DeviceFilter {
private static final String TAG = "DeviceFilter";
// USB Vendor ID (or -1 for unspecified)
public final int mVendorId;
// USB Product ID (or -1 for unspecified)
public final int mProductId;
// USB device or interface class (or -1 for unspecified)
public final int mClass;
// USB device subclass (or -1 for unspecified)
public final int mSubclass;
// USB device protocol (or -1 for unspecified)
public final int mProtocol;
// USB device manufacturer name string (or null for unspecified)
public final String mManufacturerName;
// USB device product name string (or null for unspecified)
public final String mProductName;
// USB device serial number string (or null for unspecified)
public final String mSerialNumber;
// set true if specific device(s) should exclude
public final boolean isExclude;
public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
final int protocol, final String manufacturer, final String product, final String serialNum) {
this(vid, pid, clasz, subclass, protocol, manufacturer, product, serialNum, false);
}
public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
final int protocol, final String manufacturer, final String product, final String serialNum, final boolean isExclude) {
mVendorId = vid;
mProductId = pid;
mClass = clasz;
mSubclass = subclass;
mProtocol = protocol;
mManufacturerName = manufacturer;
mProductName = product;
mSerialNumber = serialNum;
this.isExclude = isExclude;
/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
}
public DeviceFilter(final UsbDevice device) {
this(device, false);
}
public DeviceFilter(final UsbDevice device, final boolean isExclude) {
mVendorId = device.getVendorId();
mProductId = device.getProductId();
mClass = device.getDeviceClass();
mSubclass = device.getDeviceSubclass();
mProtocol = device.getDeviceProtocol();
mManufacturerName = null; // device.getManufacturerName();
mProductName = null; // device.getProductName();
mSerialNumber = null; // device.getSerialNumber();
this.isExclude = isExclude;
/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
}
/**
* 指定したxmlリソースからDeviceFilterリストを生成する
* @param context
* @param deviceFilterXmlId
* @return
*/
public static List<DeviceFilter> getDeviceFilters(final Context context, final int deviceFilterXmlId) {
final XmlPullParser parser = context.getResources().getXml(deviceFilterXmlId);
final List<DeviceFilter> deviceFilters = new ArrayList<DeviceFilter>();
try {
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
final DeviceFilter deviceFilter = readEntryOne(context, parser);
if (deviceFilter != null) {
deviceFilters.add(deviceFilter);
}
}
eventType = parser.next();
}
} catch (final XmlPullParserException e) {
Log.d(TAG, "XmlPullParserException", e);
} catch (final IOException e) {
Log.d(TAG, "IOException", e);
}
return Collections.unmodifiableList(deviceFilters);
}
/**
* read as integer values with default value from xml(w/o exception throws)
* resource integer id is also resolved into integer
* @param parser
* @param namespace
* @param name
* @param defaultValue
* @return
*/
private static final int getAttributeInteger(final Context context, final XmlPullParser parser, final String namespace, final String name, final int defaultValue) {
int result = defaultValue;
try {
String v = parser.getAttributeValue(namespace, name);
if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
final String r = v.substring(1);
final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
if (resId > 0) {
result = context.getResources().getInteger(resId);
}
} else {
int radix = 10;
if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
(v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
// allow hex values starting with 0x or 0X
radix = 16;
v = v.substring(2);
}
result = Integer.parseInt(v, radix);
}
} catch (final NotFoundException e) {
result = defaultValue;
} catch (final NumberFormatException e) {
result = defaultValue;
} catch (final NullPointerException e) {
result = defaultValue;
}
return result;
}
/**
* read as boolean values with default value from xml(w/o exception throws)
* resource boolean id is also resolved into boolean
* if the value is zero, return false, if the value is non-zero integer, return true
* @param context
* @param parser
* @param namespace
* @param name
* @param defaultValue
* @return
*/
private static final boolean getAttributeBoolean(final Context context, final XmlPullParser parser, final String namespace, final String name, final boolean defaultValue) {
boolean result = defaultValue;
try {
String v = parser.getAttributeValue(namespace, name);
if ("TRUE".equalsIgnoreCase(v)) {
result = true;
} else if ("FALSE".equalsIgnoreCase(v)) {
result = false;
} else if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
final String r = v.substring(1);
final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
if (resId > 0) {
result = context.getResources().getBoolean(resId);
}
} else {
int radix = 10;
if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
(v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
// allow hex values starting with 0x or 0X
radix = 16;
v = v.substring(2);
}
final int val = Integer.parseInt(v, radix);
result = val != 0;
}
} catch (final NotFoundException e) {
result = defaultValue;
} catch (final NumberFormatException e) {
result = defaultValue;
} catch (final NullPointerException e) {
result = defaultValue;
}
return result;
}
/**
* read as String attribute with default value from xml(w/o exception throws)
* resource string id is also resolved into string
* @param parser
* @param namespace
* @param name
* @param defaultValue
* @return
*/
private static final String getAttributeString(final Context context, final XmlPullParser parser, final String namespace, final String name, final String defaultValue) {
String result = defaultValue;
try {
result = parser.getAttributeValue(namespace, name);
if (result == null)
result = defaultValue;
if (!TextUtils.isEmpty(result) && result.startsWith("@")) {
final String r = result.substring(1);
final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
if (resId > 0)
result = context.getResources().getString(resId);
}
} catch (final NotFoundException e) {
result = defaultValue;
} catch (final NumberFormatException e) {
result = defaultValue;
} catch (final NullPointerException e) {
result = defaultValue;
}
return result;
}
public static DeviceFilter readEntryOne(final Context context, final XmlPullParser parser)
throws XmlPullParserException, IOException {
int vendorId = -1;
int productId = -1;
int deviceClass = -1;
int deviceSubclass = -1;
int deviceProtocol = -1;
boolean exclude = false;
String manufacturerName = null;
String productName = null;
String serialNumber = null;
boolean hasValue = false;
String tag;
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
if (!TextUtils.isEmpty(tag) && (tag.equalsIgnoreCase("usb-device"))) {
if (eventType == XmlPullParser.START_TAG) {
hasValue = true;
vendorId = getAttributeInteger(context, parser, null, "vendor-id", -1);
if (vendorId == -1) {
vendorId = getAttributeInteger(context, parser, null, "vendorId", -1);
if (vendorId == -1)
vendorId = getAttributeInteger(context, parser, null, "venderId", -1);
}
productId = getAttributeInteger(context, parser, null, "product-id", -1);
if (productId == -1)
productId = getAttributeInteger(context, parser, null, "productId", -1);
deviceClass = getAttributeInteger(context, parser, null, "class", -1);
deviceSubclass = getAttributeInteger(context, parser, null, "subclass", -1);
deviceProtocol = getAttributeInteger(context, parser, null, "protocol", -1);
manufacturerName = getAttributeString(context, parser, null, "manufacturer-name", null);
if (TextUtils.isEmpty(manufacturerName))
manufacturerName = getAttributeString(context, parser, null, "manufacture", null);
productName = getAttributeString(context, parser, null, "product-name", null);
if (TextUtils.isEmpty(productName))
productName = getAttributeString(context, parser, null, "product", null);
serialNumber = getAttributeString(context, parser, null, "serial-number", null);
if (TextUtils.isEmpty(serialNumber))
serialNumber = getAttributeString(context, parser, null, "serial", null);
exclude = getAttributeBoolean(context, parser, null, "exclude", false);
} else if (eventType == XmlPullParser.END_TAG) {
if (hasValue) {
return new DeviceFilter(vendorId, productId, deviceClass,
deviceSubclass, deviceProtocol, manufacturerName, productName,
serialNumber, exclude);
}
}
}
eventType = parser.next();
}
return null;
}
/* public void write(XmlSerializer serializer) throws IOException {
serializer.startTag(null, "usb-device");
if (mVendorId != -1) {
serializer
.attribute(null, "vendor-id", Integer.toString(mVendorId));
}
if (mProductId != -1) {
serializer.attribute(null, "product-id",
Integer.toString(mProductId));
}
if (mClass != -1) {
serializer.attribute(null, "class", Integer.toString(mClass));
}
if (mSubclass != -1) {
serializer.attribute(null, "subclass", Integer.toString(mSubclass));
}
if (mProtocol != -1) {
serializer.attribute(null, "protocol", Integer.toString(mProtocol));
}
if (mManufacturerName != null) {
serializer.attribute(null, "manufacturer-name", mManufacturerName);
}
if (mProductName != null) {
serializer.attribute(null, "product-name", mProductName);
}
if (mSerialNumber != null) {
serializer.attribute(null, "serial-number", mSerialNumber);
}
serializer.attribute(null, "serial-number", Boolean.toString(isExclude));
serializer.endTag(null, "usb-device");
} */
/**
* 指定したクラス・サブクラス・プロトコルがこのDeviceFilterとマッチするかどうかを返す
* mExcludeフラグは別途#isExcludeか自前でチェックすること
* @param clasz
* @param subclass
* @param protocol
* @return
*/
private boolean matches(final int clasz, final int subclass, final int protocol) {
return ((mClass == -1 || clasz == mClass)
&& (mSubclass == -1 || subclass == mSubclass) && (mProtocol == -1 || protocol == mProtocol));
}
/**
* 指定したUsbDeviceがこのDeviceFilterにマッチするかどうかを返す
* mExcludeフラグは別途#isExcludeか自前でチェックすること
* @param device
* @return
*/
public boolean matches(final UsbDevice device) {
if (mVendorId != -1 && device.getVendorId() != mVendorId) {
return false;
}
if (mProductId != -1 && device.getProductId() != mProductId) {
return false;
}
/* if (mManufacturerName != null && device.getManufacturerName() == null)
return false;
if (mProductName != null && device.getProductName() == null)
return false;
if (mSerialNumber != null && device.getSerialNumber() == null)
return false;
if (mManufacturerName != null && device.getManufacturerName() != null
&& !mManufacturerName.equals(device.getManufacturerName()))
return false;
if (mProductName != null && device.getProductName() != null
&& !mProductName.equals(device.getProductName()))
return false;
if (mSerialNumber != null && device.getSerialNumber() != null
&& !mSerialNumber.equals(device.getSerialNumber()))
return false; */
// check device class/subclass/protocol
if (matches(device.getDeviceClass(), device.getDeviceSubclass(), device.getDeviceProtocol())) {
return true;
}
// if device doesn't match, check the interfaces
final int count = device.getInterfaceCount();
for (int i = 0; i < count; i++) {
final UsbInterface intf = device.getInterface(i);
if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), intf.getInterfaceProtocol())) {
return true;
}
}
return false;
}
/**
* このDeviceFilterに一致してかつmExcludeがtrueならtrueを返す
* @param device
* @return
*/
public boolean isExclude(final UsbDevice device) {
return isExclude && matches(device);
}
/**
* これって要らんかも, equalsでできる気が
* @param f
* @return
*/
public boolean matches(final DeviceFilter f) {
if (isExclude != f.isExclude) {
return false;
}
if (mVendorId != -1 && f.mVendorId != mVendorId) {
return false;
}
if (mProductId != -1 && f.mProductId != mProductId) {
return false;
}
if (f.mManufacturerName != null && mManufacturerName == null) {
return false;
}
if (f.mProductName != null && mProductName == null) {
return false;
}
if (f.mSerialNumber != null && mSerialNumber == null) {
return false;
}
if (mManufacturerName != null && f.mManufacturerName != null
&& !mManufacturerName.equals(f.mManufacturerName)) {
return false;
}
if (mProductName != null && f.mProductName != null
&& !mProductName.equals(f.mProductName)) {
return false;
}
if (mSerialNumber != null && f.mSerialNumber != null
&& !mSerialNumber.equals(f.mSerialNumber)) {
return false;
}
// check device class/subclass/protocol
return matches(f.mClass, f.mSubclass, f.mProtocol);
}
@Override
public boolean equals(final Object obj) {
// can't compare if we have wildcard strings
if (mVendorId == -1 || mProductId == -1 || mClass == -1
|| mSubclass == -1 || mProtocol == -1) {
return false;
}
if (obj instanceof DeviceFilter) {
final DeviceFilter filter = (DeviceFilter) obj;
if (filter.mVendorId != mVendorId
|| filter.mProductId != mProductId
|| filter.mClass != mClass || filter.mSubclass != mSubclass
|| filter.mProtocol != mProtocol) {
return false;
}
if ((filter.mManufacturerName != null && mManufacturerName == null)
|| (filter.mManufacturerName == null && mManufacturerName != null)
|| (filter.mProductName != null && mProductName == null)
|| (filter.mProductName == null && mProductName != null)
|| (filter.mSerialNumber != null && mSerialNumber == null)
|| (filter.mSerialNumber == null && mSerialNumber != null)) {
return false;
}
if ((filter.mManufacturerName != null && mManufacturerName != null && !mManufacturerName
.equals(filter.mManufacturerName))
|| (filter.mProductName != null && mProductName != null && !mProductName
.equals(filter.mProductName))
|| (filter.mSerialNumber != null && mSerialNumber != null && !mSerialNumber
.equals(filter.mSerialNumber))) {
return false;
}
return (filter.isExclude != isExclude);
}
if (obj instanceof UsbDevice) {
final UsbDevice device = (UsbDevice) obj;
if (isExclude
|| (device.getVendorId() != mVendorId)
|| (device.getProductId() != mProductId)
|| (device.getDeviceClass() != mClass)
|| (device.getDeviceSubclass() != mSubclass)
|| (device.getDeviceProtocol() != mProtocol) ) {
return false;
}
/* if ((mManufacturerName != null && device.getManufacturerName() == null)
|| (mManufacturerName == null && device
.getManufacturerName() != null)
|| (mProductName != null && device.getProductName() == null)
|| (mProductName == null && device.getProductName() != null)
|| (mSerialNumber != null && device.getSerialNumber() == null)
|| (mSerialNumber == null && device.getSerialNumber() != null)) {
return (false);
} */
/* if ((device.getManufacturerName() != null && !mManufacturerName
.equals(device.getManufacturerName()))
|| (device.getProductName() != null && !mProductName
.equals(device.getProductName()))
|| (device.getSerialNumber() != null && !mSerialNumber
.equals(device.getSerialNumber()))) {
return (false);
} */
return true;
}
return false;
}
@Override
public int hashCode() {
return (((mVendorId << 16) | mProductId) ^ ((mClass << 16)
| (mSubclass << 8) | mProtocol));
}
@Override
public String toString() {
return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId="
+ mProductId + ",mClass=" + mClass + ",mSubclass=" + mSubclass
+ ",mProtocol=" + mProtocol
+ ",mManufacturerName=" + mManufacturerName
+ ",mProductName=" + mProductName
+ ",mSerialNumber=" + mSerialNumber
+ ",isExclude=" + isExclude
+ "]";
}
}

View File

@@ -0,0 +1,5 @@
package com.serenegiant.usb;
public interface IButtonCallback {
void onButton(int button, int state);
}

View File

@@ -0,0 +1,46 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb;
import java.nio.ByteBuffer;
/**
* Callback interface for UVCCamera class
* If you need frame data as ByteBuffer, you can use this callback interface with UVCCamera#setFrameCallback
*/
public interface IFrameCallback {
/**
* This method is called from native library via JNI on the same thread as UVCCamera#startCapture.
* You can use both UVCCamera#startCapture and #setFrameCallback
* but it is better to use either for better performance.
* You can also pass pixel format type to UVCCamera#setFrameCallback for this method.
* Some frames may drops if this method takes a time.
* When you use some color format like NV21, this library never execute color space conversion,
* just execute pixel format conversion. If you want to get same result as on screen, please try to
* consider to get images via texture(SurfaceTexture) and read pixel buffer from it using OpenGL|ES2/3
* instead of using IFrameCallback(this way is much efficient in most case than using IFrameCallback).
* @param frame this is direct ByteBuffer from JNI layer and you should handle it's byte order and limitation.
*/
public void onFrame(ByteBuffer frame);
}

View File

@@ -0,0 +1,7 @@
package com.serenegiant.usb;
import java.nio.ByteBuffer;
public interface IStatusCallback {
void onStatus(int statusClass, int event, int selector, int statusAttribute, ByteBuffer data);
}

View File

@@ -0,0 +1,302 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Locale;
public class Size implements Parcelable {
//
/**
* native側のuvc_raw_format_tの値, こっちは主にlibuvc用
* 9999 is still image
*/
public int type;
/**
* native側のraw_frame_tの値, androusb用,
* libuvcは対応していない
*/
public int frame_type;
public int index;
public int width;
public int height;
public int frameIntervalType;
public int frameIntervalIndex;
public int[] intervals;
// ここ以下はframeIntervalTypeとintervalsから#updateFrameRateで計算する
public float[] fps;
private String frameRates;
/**
* コンストラクタ
* @param _type native側のraw_format_tの値, ただし9999は静止画
* @param _frame_type native側のraw_frame_tの値
* @param _index
* @param _width
* @param _height
*/
public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height) {
type = _type;
frame_type = _frame_type;
index = _index;
width = _width;
height = _height;
frameIntervalType = -1;
frameIntervalIndex = 0;
intervals = null;
updateFrameRate();
}
/**
* コンストラクタ
* @param _type native側のraw_format_tの値, ただし9999は静止画
* @param _frame_type native側のraw_frame_tの値
* @param _index
* @param _width
* @param _height
* @param _min_intervals
* @param _max_intervals
*/
public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int _min_intervals, final int _max_intervals, final int _step) {
type = _type;
frame_type = _frame_type;
index = _index;
width = _width;
height = _height;
frameIntervalType = 0;
frameIntervalIndex = 0;
intervals = new int[3];
intervals[0] = _min_intervals;
intervals[1] = _max_intervals;
intervals[2] = _step;
updateFrameRate();
}
/**
* コンストラクタ
* @param _type native側のraw_format_tの値, ただし9999は静止画
* @param _frame_type native側のraw_frame_tの値
* @param _index
* @param _width
* @param _height
* @param _intervals
*/
public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int[] _intervals) {
type = _type;
frame_type = _frame_type;
index = _index;
width = _width;
height = _height;
final int n = _intervals != null ? _intervals.length : -1;
if (n > 0) {
frameIntervalType = n;
intervals = new int[n];
System.arraycopy(_intervals, 0, intervals, 0, n);
} else {
frameIntervalType = -1;
intervals = null;
}
frameIntervalIndex = 0;
updateFrameRate();
}
/**
* コピーコンストラクタ
* @param other
*/
public Size(final Size other) {
type = other.type;
frame_type = other.frame_type;
index = other.index;
width = other.width;
height = other.height;
frameIntervalType = other.frameIntervalType;
frameIntervalIndex = other.frameIntervalIndex;
final int n = other.intervals != null ? other.intervals.length : -1;
if (n > 0) {
intervals = new int[n];
System.arraycopy(other.intervals, 0, intervals, 0, n);
} else {
intervals = null;
}
updateFrameRate();
}
private Size(final Parcel source) {
// 読み取り順はwriteToParcelでの書き込み順と同じでないとダメ
type = source.readInt();
frame_type = source.readInt();
index = source.readInt();
width = source.readInt();
height = source.readInt();
frameIntervalType = source.readInt();
frameIntervalIndex = source.readInt();
if (frameIntervalType >= 0) {
if (frameIntervalType > 0) {
intervals = new int[frameIntervalType];
} else {
intervals = new int[3];
}
source.readIntArray(intervals);
} else {
intervals = null;
}
updateFrameRate();
}
public Size set(final Size other) {
if (other != null) {
type = other.type;
frame_type = other.frame_type;
index = other.index;
width = other.width;
height = other.height;
frameIntervalType = other.frameIntervalType;
frameIntervalIndex = other.frameIntervalIndex;
final int n = other.intervals != null ? other.intervals.length : -1;
if (n > 0) {
intervals = new int[n];
System.arraycopy(other.intervals, 0, intervals, 0, n);
} else {
intervals = null;
}
updateFrameRate();
}
return this;
}
public float getCurrentFrameRate() throws IllegalStateException {
final int n = fps != null ? fps.length : 0;
if ((frameIntervalIndex >= 0) && (frameIntervalIndex < n)) {
return fps[frameIntervalIndex];
}
throw new IllegalStateException("unknown frame rate or not ready");
}
public void setCurrentFrameRate(final float frameRate) {
// 一番近いのを選ぶ
int index = -1;
final int n = fps != null ? fps.length : 0;
for (int i = 0; i < n; i++) {
if (fps[i] <= frameRate) {
index = i;
break;
}
}
frameIntervalIndex = index;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeInt(type);
dest.writeInt(frame_type);
dest.writeInt(index);
dest.writeInt(width);
dest.writeInt(height);
dest.writeInt(frameIntervalType);
dest.writeInt(frameIntervalIndex);
if (intervals != null) {
dest.writeIntArray(intervals);
}
}
public void updateFrameRate() {
final int n = frameIntervalType;
if (n > 0) {
fps = new float[n];
for (int i = 0; i < n; i++) {
final float _fps = fps[i] = 10000000.0f / intervals[i];
}
} else if (n == 0) {
try {
final int min = Math.min(intervals[0], intervals[1]);
final int max = Math.max(intervals[0], intervals[1]);
final int step = intervals[2];
if (step > 0) {
int m = 0;
for (int i = min; i <= max; i+= step) { m++; }
fps = new float[m];
m = 0;
for (int i = min; i <= max; i+= step) {
final float _fps = fps[m++] = 10000000.0f / i;
}
} else {
final float max_fps = 10000000.0f / min;
int m = 0;
for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) { m++; }
fps = new float[m];
m = 0;
for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) {
this.fps[m++] = fps;
}
}
} catch (final Exception e) {
// ignore, なんでかminとmaxが0になってるんちゃうかな
fps = null;
}
}
final int m = fps != null ? fps.length : 0;
final StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < m; i++) {
sb.append(String.format(Locale.US, "%4.1f", fps[i]));
if (i < m-1) {
sb.append(",");
}
}
sb.append("]");
frameRates = sb.toString();
if (frameIntervalIndex > m) {
frameIntervalIndex = 0;
}
}
@Override
public String toString() {
float frame_rate = 0.0f;
try {
frame_rate = getCurrentFrameRate();
} catch (final Exception e) {
}
return String.format(Locale.US, "Size(%dx%d@%4.1f,type:%d,frame:%d,index:%d,%s)", width, height, frame_rate, type, frame_type, index, frameRates);
}
public static final Creator<Size> CREATOR = new Creator<Size>() {
@Override
public Size createFromParcel(final Parcel source) {
return new Size(source);
}
@Override
public Size[] newArray(final int size) {
return new Size[size];
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,859 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb;
import android.util.SparseArray;
public class USBVendorId {
private static final SparseArray<String> IDS = new SparseArray<String>();
public static String vendorName(final int vendor_id) {
return IDS.get(vendor_id);
}
static {
IDS.put(10006, "YUEN DA ELECTRONIC PRODUCTS FACTORY");
IDS.put(10013, "Gionee Communication Equipment Co., Ltd. ShenZhen");
IDS.put(10022, "Universal Electronics Inc. (dba: TVIEW)");
IDS.put(1003, "Atmel Corporation");
IDS.put(1006, "Mitsumi");
IDS.put(1008, "HP Inc.");
IDS.put(10112, "M31 Technology Corp.");
IDS.put(10113, "Liteconn Co., Ltd.");
IDS.put(10121, "Suzhou WEIJU Electronics Technology Co., Ltd.");
IDS.put(10144, "Mondokey Limited");
IDS.put(10149, "Advantest Corporation");
IDS.put(10150, "iRobot Corporation");
IDS.put(1020, "Elitegroup Computer Systems");
IDS.put(1021, "Xilinx Inc.");
IDS.put(10226, "Sibridge Tech.");
IDS.put(1026, "ALi Corporation");
IDS.put(1027, "Future Technology Devices International Limited");
IDS.put(10275, "Dongguan Jiumutong Industry Co., Ltd.");
IDS.put(10289, "Power Integrations");
IDS.put(10291, "Oculus VR, Inc.");
IDS.put(10300, "HIGH TEK HARNESS ENTERPRISE CO., LTD.");
IDS.put(10316, "Full in Hope Co., Ltd.");
IDS.put(1032, "Quanta Computer Inc.");
IDS.put(10329, "Viconn Technology (HK) Co., Ltd.");
IDS.put(1033, "NEC Corporation");
IDS.put(1035, "Weltrend Semiconductor");
IDS.put(1037, "VIA Technologies, Inc.");
IDS.put(10374, "Seeed Technology Co., Ltd.");
IDS.put(10375, "Specwerkz");
IDS.put(1038, "MCCI Corporation");
IDS.put(10398, "Esselte Leitz GmbH & Co. KG");
IDS.put(10406, "E-SEEK Inc.");
IDS.put(1041, "BUFFALO INC.");
IDS.put(10423, "Pleora Technologies Inc.");
IDS.put(10431, "Vitetech Int'l Co., Ltd.");
IDS.put(1044, "Giga-Byte Technology Co., Ltd.");
IDS.put(10446, "Changzhou Shi Wujin Miqi East Electronic Co., Ltd.");
IDS.put(10457, "Shenzhen Ourconn Technology Co., Ltd.");
IDS.put(10458, "G.SKILL Int'l Enterprice Co., Ltd.");
IDS.put(1046, "Nuvoton Technology Corp.");
IDS.put(10466, "Surplus Electronic Technology Co., Ltd.");
IDS.put(10470, "BIAMP SYSTEMS");
IDS.put(10509, "IBCONN Technologies (Shenzhen) Co., Ltd.");
IDS.put(10510, "Fugoo Inc.");
IDS.put(10519, "Pan Xin Precision Electronics Co., Ltd.");
IDS.put(10530, "Dongguan Digi-in Digital Technology Co., Ltd.");
IDS.put(1054, "Creative Labs");
IDS.put(10540, "GENUSION, Inc.");
IDS.put(10544, "Ineda Systems Inc.");
IDS.put(10545, "Jolla Ltd.");
IDS.put(10546, "Peraso Technologies, Inc.");
IDS.put(10549, "Nanjing Magewell Electronics Co., Ltd.");
IDS.put(10560, "Shenzhen Yiwanda Electronics Co., Ltd.");
IDS.put(1057, "Nokia Corporation");
IDS.put(10575, "Dollar Connection Ltd.");
IDS.put(10595, "BIO-key International, Inc.");
IDS.put(1060, "Microchip-SMSC");
IDS.put(10603, "Xacti Corporation");
IDS.put(10615, "Shenzhen Zowee Technology Co., Ltd.");
IDS.put(10643, "ADPlaus Technology Limited");
IDS.put(10646, "Unwired Technology");
IDS.put(1065, "Cirrus Logic Inc.");
IDS.put(10657, "Union Electric Plug & Connector Corp.");
IDS.put(10674, "Canova Tech");
IDS.put(10685, "Silicon Works");
IDS.put(10695, "HANRICO ANFU ELECTRONICS CO., LTD.");
IDS.put(10700, "Kodak Alaris");
IDS.put(10702, "JGR Optics Inc.");
IDS.put(10703, "Richtek Technology Corporation");
IDS.put(10705, "Binatone Electronics Int. Ltd.");
IDS.put(1071, "Molex Inc.");
IDS.put(10715, "Shenzhen iBoard Technology Co., Ltd.");
IDS.put(10719, "SMIT(HK) Limited");
IDS.put(1072, "Fujitsu Component Limited");
IDS.put(10725, "Dongguan Kechenda Electronic Technology Co., Ltd.");
IDS.put(10726, "Fengshun Peiying Electro-Acoustic Co., Ltd.");
IDS.put(10744, "MD ELEKTRONIK GmbH");
IDS.put(10749, "Bad Elf, LLC");
IDS.put(10770, "Vreo Limited");
IDS.put(10772, "Kanex");
IDS.put(10781, "Oxford Nanopore Technologies");
IDS.put(10782, "Obsidian Technology");
IDS.put(10783, "Lucent Trans Electronics Co., Ltd.");
IDS.put(10784, "GUOGUANG GROUP CO., LTD.");
IDS.put(10788, "CNPLUS");
IDS.put(10789, "Fourstar Group");
IDS.put(10790, "Tragant International Co., Ltd.");
IDS.put(10791, "DongGuan LianGang Optoelectronic Technology Co., Ltd.");
IDS.put(10797, "Atrust Computer Corp.");
IDS.put(10798, "VIA Alliance Semiconductor Co., Ltd.");
IDS.put(10799, "BSUN Electronics Co., Ltd.");
IDS.put(1080, "Advanced Micro Devices");
IDS.put(10807, "RTD Embedded Technologies, Inc.");
IDS.put(10816, "Shenzhen Choseal Industrial Co., Ltd.");
IDS.put(10817, "Canyon Semiconductor");
IDS.put(10818, "Spectra7 Microsystems Corp.");
IDS.put(10821, "Meizu Technology Co., Ltd.");
IDS.put(10822, "Hubei Yingtong Telecommunication Cable Inc.");
IDS.put(10829, "Wilder Technologies");
IDS.put(10837, "Diodes Inc.");
IDS.put(10846, "DuPont");
IDS.put(1085, "Lexmark International Inc.");
IDS.put(10852, "Zhejiang Songcheng Electronics Co., Ltd.");
IDS.put(10859, "VSN Mobil");
IDS.put(10875, "Bellwether Electronic Corp.");
IDS.put(10878, "VAIO Corporation");
IDS.put(10879, "Perixx Computer GmbH");
IDS.put(10885, "HANK ELECTRONICS CO., LTD");
IDS.put(10892, "Sonnet Technologies, Inc.");
IDS.put(10893, "Keysight Technologies Inc.");
IDS.put(10895, "Manutronics Vietnam Joint Stock Company");
IDS.put(10900, "G2 Touch Co., Ltd.");
IDS.put(10902, "Micromax Informatics Ltd");
IDS.put(10910, "SEIKO SOLUTIONS Inc.");
IDS.put(10912, "Casco Products Corp.");
IDS.put(10922, "Virtium Technology, Inc.");
IDS.put(10923, "Field and Company LLC, dba Leef USA");
IDS.put(10928, "GM Global Technology Operations LLC");
IDS.put(10931, "Key Asic Inc.");
IDS.put(10943, "Revolabs, Inc.");
IDS.put(10945, "Lattice Semiconductor Corp");
IDS.put(10947, "Foshan Nanhai Saga Audio Equipment Co., Ltd.");
IDS.put(10957, "Silergy Corp.");
IDS.put(10963, "Shenzhen Hali-Power Industrial Co., Ltd.");
IDS.put(10971, "I-PEX (Dai-ichi Seiko)");
IDS.put(10973, "SEE-PLUS INDUSTRIAL LTD.");
IDS.put(10990, "Adapt-IP Company");
IDS.put(10997, "Libratone A/S");
IDS.put(10999, "Shenzhen Hazens Automotive Electronics (SZ) Co., Ltd.");
IDS.put(11000, "Jiangsu Toppower Automotive Electronics Co., Ltd.");
IDS.put(11001, "Drapho Electronics Technology Co., Ltd.");
IDS.put(1102, "Alps Electric Co., Ltd.");
IDS.put(11022, "Le Shi Zhi Xin Electronic Technology (Tian Jin) Limited");
IDS.put(11024, "Cardiac Insight, Inc.");
IDS.put(11028, "EverPro Technologies Company, Ltd.");
IDS.put(11029, "Rosenberger Hochfrequenztechnik");
IDS.put(11035, "Dongguan City Sanji Electronics Co., Ltd.");
IDS.put(11037, "Lintes Technology Co., Ltd.");
IDS.put(11039, "KinnexA, Inc.");
IDS.put(11042, "Metra Electronics Corp.");
IDS.put(11044, "KeepKey, LLC");
IDS.put(11047, "FluxData Incorporated");
IDS.put(1105, "Texas Instruments");
IDS.put(11061, "Assem Technology Co., Ltd.");
IDS.put(11062, "Dongguan City Jianghan Electronics Co., Ltd.");
IDS.put(11063, "Huizhou Desay SV Automotive Co., Ltd.");
IDS.put(11064, "Ningbo Rixing Electronics Co., Ltd.");
IDS.put(11069, "GuangDong YuanFeng Automotive Electroics Co., Ltd.");
IDS.put(11080, "Sounding Audio Industrial Limited");
IDS.put(11082, "Yueqing Huaxin Electronic Co., Ltd.");
IDS.put(11098, "Universal Audio, Inc.");
IDS.put(11111, "Lifesize, Inc.");
IDS.put(11123, "Pioneer DJ Corporation");
IDS.put(11124, "Embedded Intelligence, Inc.");
IDS.put(11125, "New Matter");
IDS.put(11126, "Shanghai Wingtech Electronic Technology Co., Ltd.");
IDS.put(11127, "Epiphan Systems Inc.");
IDS.put(11130, "Spin Master Far East Ltd.");
IDS.put(11131, "Gigaset Digital Technology (Shenzhen) Co., Ltd.");
IDS.put(11132, "Noveltek Semiconductor Corp.");
IDS.put(11139, "Silicon Line GmbH");
IDS.put(11140, "Ever Win International Corp.");
IDS.put(11144, "Socionext Inc.");
IDS.put(11145, "Ugreen Group Limited");
IDS.put(11146, "Shanghai Pateo Electronic Equipment Mfg. Co., Ltd.");
IDS.put(1115, "Renesas Electronics Corp.");
IDS.put(11154, "i-BLADES, Inc.");
IDS.put(11155, "Altia Systems Inc.");
IDS.put(11156, "ShenZhen Baoyuanda Electronics Co., Ltd.");
IDS.put(11157, "iST - Integrated Service Technology Inc.");
IDS.put(11158, "HYUNDAI MOBIS Co., Ltd.");
IDS.put(11161, "360fly, Inc.");
IDS.put(11162, "HUIZHOU CHENG SHUO HARDWARE PLASTIC CO., LTD.");
IDS.put(11163, "Zhongshan Aute Electronics Technology Co., Ltd.");
IDS.put(11164, "Guangdong King Link Industrial Co., Ltd.");
IDS.put(11167, "Scietera Technologies, Inc.");
IDS.put(11168, "InVue Security Products");
IDS.put(11169, "I-Sheng Electric Wire & Cable Co., Ltd.");
IDS.put(11170, "China Daheng Group Inc Beijing Image Vision Tech Branch");
IDS.put(11171, "Shenzhen FeiTianXia Technology Ltd.");
IDS.put(11172, "Shenzhen HengJia New Energy Auto Part Co., Ltd.");
IDS.put(11175, "77 Elektronika Kft.");
IDS.put(11176, "YUDU EASON ELECTRONIC CO., LTD.");
IDS.put(1118, "Microsoft Corporation");
IDS.put(11181, "XIN JI (SHENZHEN) COMPUTER PARTS CO., LTD.");
IDS.put(11189, "Silk ID Systems");
IDS.put(11190, "3D Imaging & Simulations Corp. (3DISC)");
IDS.put(11191, "Dongguan ChengXiang Industrial Co., Ltd.");
IDS.put(11192, "OCC (Zhuhai) Electronic Co., Ltd.");
IDS.put(11194, "Sinseader Electronic Co., Ltd.");
IDS.put(11195, "DONGGUAN YELLOWKNIFE Industrial Co., Ltd.");
IDS.put(11197, "RF Creations Ltd.");
IDS.put(11198, "Chengyi Semiconductors (Shanghai) Co., Ltd.");
IDS.put(11199, "Shenzhen Shinning Electronic Co., Ltd.");
IDS.put(11200, "Shenzhen WFD Electronics Co., Ltd.");
IDS.put(11201, "Dongguan Sino Syncs Industrial Co., Ltd.");
IDS.put(11202, "JNTC Co., Ltd.");
IDS.put(11208, "DONGGUAN POLIXIN ELECTRIC CO., LTD.");
IDS.put(11209, "Tama Electric (Suzhou) Co., Ltd.");
IDS.put(1121, "Primax Electronics");
IDS.put(11210, "Exvision, Inc.");
IDS.put(11216, "mophie, LLC");
IDS.put(11219, "Dongguan ULT-unite electronic technology co., LTD");
IDS.put(11220, "JL Audio, Inc.");
IDS.put(11221, "Cable Matters Inc.");
IDS.put(11222, "CoroWare, Inc.");
IDS.put(11229, "Charm Sciences Inc.");
IDS.put(1123, "EATON");
IDS.put(11230, "Pickering Interfaces Limited");
IDS.put(11231, "Hangzhou Hikvision Digital Technology Co., Ltd.");
IDS.put(11232, "FULLINK ELECTRONICS TECHNOLOGY (SZ) LTD");
IDS.put(11233, "AutoChips Inc.");
IDS.put(11234, "Electric Connector Technology Co., Ltd.");
IDS.put(11237, "LELTEK");
IDS.put(11238, "Dongguan KaiWin Electronics Co., Ltd.");
IDS.put(11239, "BEFS Co., Ltd.");
IDS.put(11240, "Archisite, Inc.");
IDS.put(11241, "Magneti Marelli S.p.A Electr BL");
IDS.put(11246, "Ventev Mobile");
IDS.put(11247, "Quanta Storage Inc.");
IDS.put(11248, "Tech-Top Technology Limited");
IDS.put(11253, "Shenzhen YOOBAO Technology Co., Ltd.");
IDS.put(11254, "Shenzhen Sinotek Technology Co., Ltd.");
IDS.put(11255, "KEYW");
IDS.put(11256, "Visual Land Inc.");
IDS.put(11264, "MEEM SL Ltd");
IDS.put(11265, "Dongguan Arin Electronics Technology Co., Ltd.");
IDS.put(11266, "DongGuan City JianNuo Electronics Co., Ltd.");
IDS.put(11268, "Shenzhen XOX Electronics Co., Ltd.");
IDS.put(11269, "Protop International Inc.");
IDS.put(11270, "Microsemi Semiconductor (US) Inc.");
IDS.put(11271, "Webcloak LLC");
IDS.put(11272, "INVECAS INC.");
IDS.put(11274, "ATANS Technology Inc.");
IDS.put(11275, "Triple Win Precision Technology Co., Ltd.");
IDS.put(11276, "IC Realtech");
IDS.put(11277, "Embrava Pty Ltd");
IDS.put(1128, "Wieson Technologies Co., Ltd.");
IDS.put(11280, "Sinotronics Co., Ltd.");
IDS.put(11281, "ALLBEST ELECTRONICS TECHNOLOGY CO., LTD.");
IDS.put(11282, "Shenzhen Xin Kai Feng Electronics Factory");
IDS.put(11283, "MOST WELL Technology Corp.");
IDS.put(11284, "Buffalo Memory Co., Ltd.");
IDS.put(11285, "Xentris Wireless");
IDS.put(11286, "Priferential Accessories Ltd");
IDS.put(11289, "Sunlike Technology Co., Ltd.");
IDS.put(11290, "Young Fast Optoelectronics Co., Ltd.");
IDS.put(11291, "ISAW Camera Inc");
IDS.put(11298, "Qanba USA, LLC");
IDS.put(11299, "Super Micro Computer Inc.");
IDS.put(11302, "Micromax International Corporation");
IDS.put(11304, "Granite River Labs Japan Ltd.");
IDS.put(11305, "Coagent Enterprise Limited");
IDS.put(11306, "LEIA Inc.");
IDS.put(11309, "Shenzhen Ebull Technology Limited");
IDS.put(1131, "American Megatrends");
IDS.put(11310, "Hualun Technology Co., Ltd.");
IDS.put(11311, "Sensel, Inc.");
IDS.put(11319, "Shenzhen Adition Audio Science & Technology Co., Ltd.");
IDS.put(11320, "Goldenconn Electronics Technology (Suzhou) Co., Ltd.");
IDS.put(11321, "JIB Electronics Technology Co., Ltd.");
IDS.put(11322, "Changzhou Shinco Automotive Electronics Co., Ltd.");
IDS.put(11323, "Shenzhen Hangsheng Electronics Corp., Ltd.");
IDS.put(11324, "Beartooth Radio, Inc.");
IDS.put(11325, "Audience, A Knowles Company");
IDS.put(11327, "Nextbit Systems, Inc.");
IDS.put(11328, "Leadtrend");
IDS.put(11329, "Adaptertek Technology Co., Ltd.");
IDS.put(1133, "Logitech Inc.");
IDS.put(11330, "Feature Integration Technology Inc.");
IDS.put(11331, "Avegant Corporation");
IDS.put(11335, "Chunghsin International Electronics Co., Ltd.");
IDS.put(11336, "Delphi Electrical Centers (Shanghai) Co., Ltd.");
IDS.put(11341, "VVETEK DOO");
IDS.put(11347, "Huizhou Foryou General Electronics Co., Ltd.");
IDS.put(11348, "LifeWatch Technologies Ltd.");
IDS.put(11349, "Magicleap");
IDS.put(11355, "Dongguan City Shenglan Electronics Co., LTD.");
IDS.put(11356, "Neusoft Corporation");
IDS.put(11357, "SIP Simya Electronics Technology Co., Ltd.");
IDS.put(11358, "GNSD Automotive Co., Ltd.");
IDS.put(11359, "YOODS Co., Ltd.");
IDS.put(11360, "Sirin Mobile Technologies AG");
IDS.put(11361, "Jadmam Corporation dba: Boytone");
IDS.put(11373, "Gibson Innovations");
IDS.put(11374, "Shen Zhen Xian Shuo Technology Co. LTD");
IDS.put(11375, "PST Eletronica LTDA");
IDS.put(11376, "PERI, Inc.");
IDS.put(11377, "Bozhou BoTong Information Technology Co., Ltd.");
IDS.put(11383, "Profindustry GmbH");
IDS.put(11384, "BRAGI GmbH");
IDS.put(11385, "WAWGD, Inc. (DBA: Foresight Sports)");
IDS.put(11390, "Dongguan Allpass Electronic Co., Ltd.");
IDS.put(11391, "SHENZHEN D-VITEC INDUSTRIAL CO., LTD.");
IDS.put(11392, "motomobile AG");
IDS.put(11393, "Indie Semiconductor");
IDS.put(11397, "Audientes");
IDS.put(11403, "Huizhou Dehong Technology Co., Ltd.");
IDS.put(11404, "PowerCenter Technology Limited");
IDS.put(11405, "Mizco International, Inc.");
IDS.put(11408, "I. AM. PLUS, LLC");
IDS.put(11409, "Corigine, Inc.");
IDS.put(11410, "Ningbo Yinzhou Shengke Electronics Co., Ltd.");
IDS.put(11417, "Prusa Research s.r.o.");
IDS.put(11423, "e-Smart Systems Pvt. Ltd.");
IDS.put(11424, "Leagtech Jiangxi Electronic Co., Ltd.");
IDS.put(11425, "Dongguan Yujia Electronics Technology Co., Ltd.");
IDS.put(11426, "GuangZhou MingPing Electronics Technology");
IDS.put(11427, "DJI Technology Co., Ltd.");
IDS.put(11428, "Shenzhen Alex Technology Co., Ltd.");
IDS.put(11433, "JITS TECHNOLOGY CO., LIMITED");
IDS.put(11434, "LIVV Brand llc");
IDS.put(11444, "Ava Enterprises, Inc. dba: Boss Audio Systems");
IDS.put(11448, "Shenzhen Sydixon Electronic Technology Co., Ltd.");
IDS.put(11449, "On-Bright Electronics (Shanghai) Co., Ltd.");
IDS.put(11450, "Dongguan Puxu Industrial Co., Ltd.");
IDS.put(11451, "Shenzhen Soling Indusrtial Co., Ltd.");
IDS.put(11453, "EGGCYTE, INC.");
IDS.put(11455, "Donggguan Yuhua Electronic Co., Ltd.");
IDS.put(11456, "Hangzhou Zero Zero Technology Co., Ltd.");
IDS.put(11462, "Prodigy Technovations Pvt Ltd");
IDS.put(11463, "EmergiTech, Inc");
IDS.put(11464, "Hewlett Packard Enterprise");
IDS.put(11465, "Monolithic Power Systems Inc.");
IDS.put(11467, "USB Memory Direct");
IDS.put(11468, "Silicon Mitus Inc.");
IDS.put(11472, "Technics Global Electronics & JCE Co., Ltd.");
IDS.put(11478, "Immersive Media");
IDS.put(11479, "Cosemi Technologies Inc.");
IDS.put(11481, "Cambrionix Ltd");
IDS.put(11482, "CXUN Co. Ltd.");
IDS.put(11483, "China Tsp Inc");
IDS.put(11490, "Yanfeng Visteon (Chongqing) Automotive Electronics Co");
IDS.put(11491, "Alcorlink Corp.");
IDS.put(11492, "ISBC Ltd.");
IDS.put(11493, "InX8 Inc dba: AKiTiO");
IDS.put(11494, "SDAN Tecchnology Co., Ltd.");
IDS.put(11495, "Lemobile Information Technology (Beijing) Co., Ltd.");
IDS.put(11496, "GongGuan HWX Electronic Technology Co., Ltd.");
IDS.put(11497, "Suzhu Jingshi Electronic Technology Co., Ltd.");
IDS.put(11498, "Zhong Shan City Richsound Electronic Industrial Ltd.");
IDS.put(11499, "Dongguang Kangbang Electronics Co., Ltd.");
IDS.put(1151, "Plantronics, Inc.");
IDS.put(1154, "Kyocera Corporation");
IDS.put(1155, "STMicroelectronics");
IDS.put(1161, "Foxconn / Hon Hai");
IDS.put(1165, "ITE Tech Inc.");
IDS.put(1177, "Yamaha Corporation");
IDS.put(1188, "Hitachi, Ltd.");
IDS.put(1191, "Visioneer");
IDS.put(1193, "Canon Inc.");
IDS.put(1200, "Nikon Corporation");
IDS.put(1201, "Pan International");
IDS.put(1204, "Cypress Semiconductor");
IDS.put(1205, "ROHM Co., Ltd.");
IDS.put(1207, "Compal Electronics, Inc.");
IDS.put(1208, "Seiko Epson Corp.");
IDS.put(1211, "I-O Data Device, Inc.");
IDS.put(1221, "Fujitsu Ltd.");
IDS.put(1227, "FUJIFILM Corporation");
IDS.put(1238, "Mentor Graphics");
IDS.put(1240, "Microchip Technology Inc.");
IDS.put(1241, "Holtek Semiconductor, Inc.");
IDS.put(1242, "Panasonic Corporation");
IDS.put(1245, "Sharp Corporation");
IDS.put(1250, "Exar Corporation");
IDS.put(1254, "Identiv, Inc.");
IDS.put(1256, "Samsung Electronics Co., Ltd.");
IDS.put(1260, "Tokyo Electron Device Limited");
IDS.put(1266, "Chicony Electronics Co., Ltd.");
IDS.put(1271, "Newnex Technology Corp.");
IDS.put(1273, "Brother Industries, Ltd.");
IDS.put(1276, "SUNPLUS TECHNOLOGY CO., LTD.");
IDS.put(1278, "PFU Limited");
IDS.put(1281, "Fujikura/DDK");
IDS.put(1282, "Acer, Inc.");
IDS.put(1287, "Hosiden Corporation");
IDS.put(1293, "Belkin International, Inc.");
IDS.put(1300, "FCI Electronics");
IDS.put(1302, "Longwell Electronics/Longwell Company");
IDS.put(1305, "Star Micronics Co., LTD");
IDS.put(1309, "American Power Conversion");
IDS.put(1314, "ACON, Advanced-Connectek, Inc.");
IDS.put(1343, "Synopsys, Inc.");
IDS.put(1356, "Sony Corporation");
IDS.put(1360, "Fuji Xerox Co., Ltd.");
IDS.put(1367, "ATEN International Co. Ltd.");
IDS.put(1369, "Cadence Design Systems, Inc.");
IDS.put(1386, "WACOM Co., Ltd.");
IDS.put(1389, "EIZO Corporation");
IDS.put(1390, "Elecom Co., Ltd.");
IDS.put(1394, "Conexant Systems, Inc.");
IDS.put(1398, "BAFO/Quality Computer Accessories");
IDS.put(1403, "Y-E Data, Inc.");
IDS.put(1404, "AVM GmbH");
IDS.put(1410, "Roland Corporation");
IDS.put(1412, "RATOC Systems, Inc.");
IDS.put(1419, "Infineon Technologies");
IDS.put(1423, "Alcor Micro, Corp.");
IDS.put(1424, "OMRON Corporation");
IDS.put(1447, "Bose Corporation");
IDS.put(1449, "OmniVision Technologies, Inc.");
IDS.put(1452, "Apple");
IDS.put(1453, "Y.C. Cable U.S.A., Inc");
IDS.put(14627, "National Instruments");
IDS.put(1470, "Tyco Electronics Corp., a TE Connectivity Ltd. company");
IDS.put(1473, "MegaChips Corporation");
IDS.put(1478, "Qualcomm, Inc");
IDS.put(1480, "Foxlink/Cheng Uei Precision Industry Co., Ltd.");
IDS.put(1482, "Ricoh Company Ltd.");
IDS.put(1498, "Microtek International Inc.");
IDS.put(1504, "Symbol Technologies");
IDS.put(1507, "Genesys Logic, Inc.");
IDS.put(1509, "Fuji Electric Co., Ltd.");
IDS.put(1525, "Unixtar Technology Inc.");
IDS.put(1529, "Datalogic ADC");
IDS.put(1535, "LeCroy Corporation");
IDS.put(1539, "Novatek Microelectronics Corp.");
IDS.put(1545, "SMK Manufacturing Inc.");
IDS.put(1551, "Joinsoon Electronics Mfg. Co., Ltd.");
IDS.put(1555, "TransAct Technologies Incorporated");
IDS.put(1561, "Seiko Instruments Inc.");
IDS.put(1582, "JPC/MAIN SUPER Inc.");
IDS.put(1583, "Sin Sheng Terminal & Machine Inc.");
IDS.put(1593, "Chrontel, Inc.");
IDS.put(1611, "Analog Devices, Inc. Development Tools");
IDS.put(1612, "Ji-Haw Industrial Co., Ltd");
IDS.put(1614, "Suyin Corporation");
IDS.put(1621, "Space Shuttle Hi-Tech Co.,Ltd.");
IDS.put(1622, "Glory Mark Electronic Ltd.");
IDS.put(1623, "Tekcon Electronics Corp.");
IDS.put(1624, "Sigma Designs, Inc.");
IDS.put(1631, "Good Way Technology Co., Ltd. & GWC technology Inc");
IDS.put(1632, "TSAY-E (BVI) International Inc.");
IDS.put(1633, "Hamamatsu Photonics K.K.");
IDS.put(1642, "Total Technologies, Ltd.");
IDS.put(1659, "Prolific Technology, Inc.");
IDS.put(16700, "Dell Inc.");
IDS.put(1680, "Golden Bridge Electech Inc.");
IDS.put(1689, "Tektronix, Inc.");
IDS.put(1690, "Askey Computer Corporation");
IDS.put(1709, "Greatland Electronics Taiwan Ltd.");
IDS.put(1710, "Eurofins Digital Testing Belgium");
IDS.put(1720, "Pixela Corporation");
IDS.put(1724, "Oki Data Corporation");
IDS.put(1727, "Leoco Corporation");
IDS.put(1732, "Bizlink Technology, Inc.");
IDS.put(1736, "SIIG, Inc.");
IDS.put(1747, "Mitsubishi Electric Corporation");
IDS.put(1758, "Heisei Technology Co., Ltd.");
IDS.put(1802, "Oki Electric Industry Co., Ltd.");
IDS.put(1805, "Comoss Electronic Co., Ltd.");
IDS.put(1809, "Magic Control Technology Corp.");
IDS.put(1816, "Imation Corp.");
IDS.put(1838, "Sunix Co., Ltd.");
IDS.put(1846, "Lorom Industrial Co., Ltd.");
IDS.put(1848, "Mad Catz, Inc.");
IDS.put(1899, "HID Global GmbH");
IDS.put(1901, "Denso Corporation");
IDS.put(1913, "Fairchild Semiconductor");
IDS.put(1921, "SanDisk Corporation");
IDS.put(1937, "Copartner Technology Corporation");
IDS.put(1954, "National Technical Systems");
IDS.put(1971, "Plustek, Inc.");
IDS.put(1972, "OLYMPUS CORPORATION");
IDS.put(1975, "TIME Interconnect Ltd.");
IDS.put(1994, "AVerMedia Technologies, Inc.");
IDS.put(1999, "Casio Computer Co., Ltd.");
IDS.put(2015, "David Electronics Company, Ltd.");
IDS.put(2039, "Century Corporation");
IDS.put(2058, "Evermuch Technology Co., Ltd.");
IDS.put(2101, "Action Star Enterprise Co., Ltd.");
IDS.put(2112, "Argosy Research Inc.");
IDS.put(2122, "Wipro Limited");
IDS.put(2159, "MEC IMEX INC/HPT");
IDS.put(2205, "Icron Technologies Corporation");
IDS.put(2247, "TAI TWUN ENTERPRISE CO., LTD.");
IDS.put(2276, "Pioneer Corporation");
IDS.put(2278, "Gemalto SA");
IDS.put(2310, "FARADAY Technology Corp.");
IDS.put(2313, "Audio-Technica Corp.");
IDS.put(2316, "Silicon Motion, Inc. - Taiwan");
IDS.put(2334, "Garmin International");
IDS.put(2352, "Toshiba Corporation");
IDS.put(2362, "Pixart Imaging, Inc.");
IDS.put(2363, "Plextor LLC");
IDS.put(2366, "J.S.T. Mfg. Co., Ltd.");
IDS.put(2385, "Kingston Technology Company");
IDS.put(2389, "NVIDIA");
IDS.put(2395, "Medialogic Corporation");
IDS.put(2397, "Polycom, Inc.");
IDS.put(2468, "Contech Research, Inc.");
IDS.put(2472, "Lin Shiung Enterprise Co., Ltd.");
IDS.put(2475, "Japan Cash Machine Co., Ltd.");
IDS.put(2498, "NISCA Corporation");
IDS.put(2511, "Electronics Testing Center, Taiwan");
IDS.put(2522, "A-FOUR TECH CO., LTD.");
IDS.put(2555, "Altera");
IDS.put(2578, "Cambridge Silicon Radio Ltd.");
IDS.put(2583, "HOYA Corporation");
IDS.put(2631, "Hirose Electric Co., Ltd.");
IDS.put(2636, "COMPUTEX Co., Ltd.");
IDS.put(2640, "Mimaki Engineering Co., Ltd.");
IDS.put(2652, "Broadcom Corp.");
IDS.put(2667, "Green House Co., Ltd.");
IDS.put(2702, "Japan Aviation Electronics Industry Ltd. (JAE)");
IDS.put(2727, "Wincor Nixdorf GmbH & Co KG");
IDS.put(2733, "Rohde & Schwarz GmbH & Co. KG");
IDS.put(2787, "Allion Labs, Inc.");
IDS.put(2821, "ASUSTek Computer Inc.");
IDS.put(2849, "Yokogawa Electric Corporation");
IDS.put(2851, "Pan-Asia Electronics Co., Ltd.");
IDS.put(2894, "Musical Electronics Ltd.");
IDS.put(2907, "Anritsu Corporation");
IDS.put(2922, "Maxim Integrated Products");
IDS.put(2965, "ASIX Electronics Corporation");
IDS.put(2967, "O2Micro, Inc.");
IDS.put(3010, "Seagate Technology LLC");
IDS.put(3034, "Realtek Semiconductor Corp.");
IDS.put(3035, "Ericsson AB");
IDS.put(3044, "Elka International Ltd.");
IDS.put(3056, "Pace Micro Technology PLC");
IDS.put(3108, "Taiyo Yuden Co., Ltd.");
IDS.put(3129, "Aeroflex");
IDS.put(3132, "Radius Co., Ltd.");
IDS.put(3141, "Sonix Technology Co., Ltd.");
IDS.put(3158, "Billion Bright (HK) Corporation Limited");
IDS.put(3161, "Dong Guan Shinko Wire Co., Ltd.");
IDS.put(3170, "Chant Sincere Co., Ltd");
IDS.put(3190, "Solid State System Co., Ltd.");
IDS.put(3209, "Honda Tsushin Kogyo Co., Ltd");
IDS.put(3245, "Motorola Solutions");
IDS.put(3255, "Singatron Enterprise Co. Ltd.");
IDS.put(3268, "emsys Embedded Systems GmbH");
IDS.put(32902, "Intel Corporation");
IDS.put(3294, "Z-Com INC.");
IDS.put(3313, "e-CONN ELECTRONIC CO., LTD.");
IDS.put(3314, "ENE Technology Inc.");
IDS.put(3351, "NALTEC, Inc.");
IDS.put(3402, "NF Corporation");
IDS.put(3403, "Grape Systems Inc.");
IDS.put(3409, "Volex (Asia) Pte Ltd");
IDS.put(3425, "MEILU ELECTRONICS (SHENZHEN) CO., LTD.");
IDS.put(3441, "Hirakawa Hewtech Corp.");
IDS.put(3452, "Taiwan Line Tek Electronic Co., Ltd.");
IDS.put(3463, "Dolby Laboratories Inc.");
IDS.put(3468, "C-MEDIA ELECTRONICS INC.");
IDS.put(3472, "Sure-Fire Electrical Corporation");
IDS.put(3495, "IOGEAR, Inc.");
IDS.put(3504, "Micro-Star International Co., Ltd.");
IDS.put(3537, "Contek Electronics Co., Ltd.");
IDS.put(3540, "Custom Engineering SPA");
IDS.put(3641, "Smart Modular Technologies, Inc.");
IDS.put(3658, "Shenzhen Bao Hing Electric Wire & Cable Mfr. Co.");
IDS.put(3673, "Bourns, Inc.");
IDS.put(3690, "Megawin Technology Co., Ltd.");
IDS.put(3698, "Hsi-Chin Electronics Co., Ltd.");
IDS.put(3714, "Ching Tai Electric Wire & Cable Co., Ltd.");
IDS.put(3724, "Well Force Electronic Co., Ltd");
IDS.put(3725, "MediaTek Inc.");
IDS.put(3728, "CRU");
IDS.put(3744, "Ours Technology Inc.");
IDS.put(3762, "Y-S ELECTRONIC CO., LTD.");
IDS.put(3778, "Sweetray Industrial Ltd.");
IDS.put(3779, "Axell Corporation");
IDS.put(3782, "InnoVISION Multimedia Limited");
IDS.put(3790, "TaiSol Electronics Co., Ltd.");
IDS.put(3812, "Sunrich Technology (H.K.) Ltd.");
IDS.put(3868, "Funai Electric Co., Ltd.");
IDS.put(3873, "IOI Technology Corporation");
IDS.put(3890, "YFC-BonEagle Electric Co., Ltd.");
IDS.put(3896, "Nien-Yi Industrial Corp.");
IDS.put(3916, "WORLDWIDE CABLE OPTO CORP.");
IDS.put(3923, "Taiyo Cable (Dongguan) Co. Ltd.");
IDS.put(3924, "Kawai Musical Instruments Mfg. Co., Ltd.");
IDS.put(3936, "GuangZhou Chief Tech Electronic Technology Co. Ltd.");
IDS.put(3944, "UQUEST, LTD.");
IDS.put(3991, "CviLux Corporation");
IDS.put(4003, "Chief Land Electronic Co., Ltd.");
IDS.put(4046, "Sony Mobile Communications");
IDS.put(4087, "CHI SHING COMPUTER ACCESSORIES CO., LTD.");
IDS.put(4096, "Speed Tech Corp.");
IDS.put(4100, "LG Electronics Inc.");
IDS.put(4101, "Apacer Technology Inc.");
IDS.put(4134, "Newly Corporation");
IDS.put(4168, "Targus Group International");
IDS.put(4172, "AMCO TEC International Inc.");
IDS.put(4183, "ON Semiconductor");
IDS.put(4184, "Western Digital Technologies, Inc.");
IDS.put(4227, "CANON ELECTRONICS INC.");
IDS.put(4235, "Grand-tek Technology Co., Ltd.");
IDS.put(4236, "Robert Bosch GmbH");
IDS.put(4238, "Lotes Co., Ltd.");
IDS.put(4266, "Cables To Go");
IDS.put(4267, "Universal Global Scientific Industrial Co., Ltd.");
IDS.put(4292, "Silicon Laboratories, Inc.");
IDS.put(4301, "Kycon Inc.");
IDS.put(4362, "Moxa Inc.");
IDS.put(4370, "Golden Bright (Sichuan) Electronic Technology Co Ltd");
IDS.put(4382, "VSO ELECTRONICS CO., LTD.");
IDS.put(4398, "Master Hill Electric Wire and Cable Co., Ltd.");
IDS.put(4477, "Santa Electronic Inc.");
IDS.put(4505, "Sierra Wireless Inc.");
IDS.put(4522, "GlobalMedia Group, LLC");
IDS.put(4528, "ATECH FLASH TECHNOLOGY");
IDS.put(4643, "SKYCABLE ENTERPRISE CO., LTD.");
IDS.put(4703, "ADATA Technology Co., Ltd.");
IDS.put(4716, "Aristocrat Technologies");
IDS.put(4717, "Bel Stewart");
IDS.put(4742, "MARVELL SEMICONDUCTOR, INC.");
IDS.put(4756, "RISO KAGAKU CORP.");
IDS.put(4792, "Zhejiang Xinya Electronic Technology Co., Ltd.");
IDS.put(4817, "Huawei Technologies Co., Ltd.");
IDS.put(4823, "Better Holdings (HK) Limited");
IDS.put(4907, "Konica Minolta, Inc.");
IDS.put(4925, "Jasco Products Company");
IDS.put(4989, "Pericom Semiconductor Corp.");
IDS.put(5008, "TomTom International B.V.");
IDS.put(5075, "AzureWave Technologies, Inc.");
IDS.put(5117, "Initio Corporation");
IDS.put(5118, "Phison Electronics Corp.");
IDS.put(5134, "Telechips, Inc.");
IDS.put(5145, "ABILITY ENTERPRISE CO., LTD.");
IDS.put(5148, "Leviton Manufacturing");
IDS.put(5271, "Panstrong Company Ltd.");
IDS.put(5293, "CTK Corporation");
IDS.put(5296, "StarTech.com Ltd.");
IDS.put(5376, "Ellisys");
IDS.put(5404, "VeriSilicon Holdings Co., Ltd.");
IDS.put(5421, "JMicron Technology Corp.");
IDS.put(5422, "HLDS (Hitachi-LG Data Storage, Inc.)");
IDS.put(5440, "Phihong Technology Co., Ltd.");
IDS.put(5451, "PNY Technologies Inc.");
IDS.put(5453, "Rapid Conn, Connect County Holdings Bhd");
IDS.put(5454, "D & M Holdings, Inc.");
IDS.put(5480, "Sunf Pu Technology Co., Ltd");
IDS.put(5488, "ALLTOP TECHNOLOGY CO., LTD.");
IDS.put(5510, "Palconn Technology Co., Ltd.");
IDS.put(5528, "Kunshan Guoji Electronics Co., Ltd.");
IDS.put(5546, "DongGuan Ya Lian Electronics Co., Ltd.");
IDS.put(5645, "Samtec");
IDS.put(5694, "HongLin Electronics Co., Ltd.");
IDS.put(5753, "Total Phase");
IDS.put(5766, "ZOOM Corporation");
IDS.put(5836, "silex technology, Inc.");
IDS.put(5946, "F. Hoffmann-La Roche AG");
IDS.put(5960, "MQP Electronics Ltd.");
IDS.put(5964, "ASMedia Technology Inc.");
IDS.put(5998, "UD electronic corp.");
IDS.put(6001, "Shenzhen Alex Connector Co., Ltd.");
IDS.put(6002, "System Level Solutions, Inc.");
IDS.put(6018, "Spreadtrum Hong Kong Limited");
IDS.put(6024, "ShenZhen Litkconn Technology Co., Ltd.");
IDS.put(6053, "Advanced Connection Technology Inc.");
IDS.put(6095, "Hip Hing Cable & Plug Mfy. Ltd.");
IDS.put(6121, "DisplayLink (UK) Ltd.");
IDS.put(6127, "Lenovo");
IDS.put(6133, "K.K. Rocky");
IDS.put(6160, "Wanshih Electronic Co., Ltd.");
IDS.put(6185, "Dongguan YuQiu Electronics Co., Ltd.");
IDS.put(6193, "Gwo Jinn Industries Co., Ltd.");
IDS.put(6297, "Linkiss Co., Ltd.");
IDS.put(6353, "Google Inc.");
IDS.put(6394, "Kuang Ying Computer Equipment Co., Ltd.");
IDS.put(6421, "Nordic Semiconductor ASA");
IDS.put(6448, "Shenzhen Xianhe Technology Co., Ltd.");
IDS.put(6449, "Ningbo Broad Telecommunication Co., Ltd.");
IDS.put(6470, "Irisguard UK Ltd");
IDS.put(6473, "Lab126");
IDS.put(6481, "Hyperstone GmbH");
IDS.put(6487, "BIOS Corporation");
IDS.put(6626, "Solomon Systech Limited");
IDS.put(6639, "Pak Heng Technology (Shenzhen) Co., Ltd.");
IDS.put(6655, "Best Buy China Ltd.");
IDS.put(6666, "USB-IF non-workshop");
IDS.put(6709, "Artesyn Technologies Inc.");
IDS.put(6720, "TERMINUS TECHNOLOGY INC.");
IDS.put(6766, "Global Unichip Corp.");
IDS.put(6786, "Proconn Technology Co., Ltd.");
IDS.put(6794, "Simula Technology Inc.");
IDS.put(6795, "SGS Taiwan Ltd.");
IDS.put(6830, "Johnson Component & Equipments Co., Ltd.");
IDS.put(6834, "Allied Vision Technologies GmbH");
IDS.put(6859, "Salcomp Plc");
IDS.put(6865, "Desan Wire Co., Ltd.");
IDS.put(6944, "MStar Semiconductor, Inc.");
IDS.put(6984, "Plastron Precision Co., Ltd.");
IDS.put(7013, "The Hong Kong Standards and Testing Centre Ltd.");
IDS.put(7048, "ShenMing Electron (Dong Guan) Co., Ltd.");
IDS.put(7086, "Vuzix Corporation");
IDS.put(7108, "Ford Motor Co.");
IDS.put(7118, "Contac Cable Industrial Limited");
IDS.put(7119, "Sunplus Innovation Technology Inc.");
IDS.put(7120, "Hangzhou Riyue Electronics Co., Ltd.");
IDS.put(7158, "Orient Semiconductor Electronics, Ltd.");
IDS.put(7207, "SHENZHEN DNS INDUSTRIES CO., LTD.");
IDS.put(7217, "LS Mtron Ltd.");
IDS.put(7229, "NONIN MEDICAL INC.");
IDS.put(7275, "Philips & Lite-ON Digital Solutions Corporation");
IDS.put(7310, "ASTRON INTERNATIONAL CORP.");
IDS.put(7320, "ALPINE ELECTRONICS, INC.");
IDS.put(7347, "Aces Electronics Co., Ltd.");
IDS.put(7348, "OPEX CORPORATION");
IDS.put(7390, "Telecommunications Technology Association (TTA)");
IDS.put(7434, "Visteon Corporation");
IDS.put(7465, "Horng Tong Enterprise Co., Ltd.");
IDS.put(7501, "Pegatron Corporation");
IDS.put(7516, "Fresco Logic Inc.");
IDS.put(7529, "Walta Electronic Co., Ltd.");
IDS.put(7543, "Yueqing Changling Electronic Instrument Corp., Ltd.");
IDS.put(7584, "Parade Technologies, Inc.");
IDS.put(7647, "L&T Technology Services");
IDS.put(7649, "Actions Microelectronics Co., Ltd.");
IDS.put(7666, "China Telecommunication Technology Labs - Terminals");
IDS.put(7668, "SHEN ZHEN FORMAN PRECISION INDUSTRY CO., LTD.");
IDS.put(7682, "GLOBEMASTER TECHNOLOGIES CO., LTD.");
IDS.put(7696, "Point Grey Research Inc.");
IDS.put(7751, "HUNG TA H.T.ENTERPRISE CO., LTD.");
IDS.put(7758, "Etron Technology, Inc.");
IDS.put(7795, "COMLINK ELECTRONICS CO., LTD.");
IDS.put(7818, "HIBEST Electronic (DongGuan) Co., Ltd.");
IDS.put(7825, "Other World Computing");
IDS.put(7863, "WIN WIN PRECISION INDUSTRIAL CO., LTD.");
IDS.put(7879, "Gefen Inc.");
IDS.put(7881, "MOSER BAER INDIA LIMITED");
IDS.put(7898, "AIRTIES WIRELESS NETWORKS");
IDS.put(7956, "Astoria Networks GmbH");
IDS.put(7969, "Scosche Industries");
IDS.put(7976, "Cal-Comp Electronics & Communications");
IDS.put(7977, "Analogix Semiconductor, Inc.");
IDS.put(7989, "Amphenol ShouhMin Industry (ShenZhen) Co., Ltd");
IDS.put(7996, "Chang Yang Electronics Company Ltd.");
IDS.put(8073, "Dongguan Goldconn Electronics Co., Ltd.");
IDS.put(8074, "Morning Star Industrial Co., Ltd.");
IDS.put(8117, "Unify Software and Solutions GmbH & Co. KG");
IDS.put(8137, "NXP Semiconductors");
IDS.put(8181, "Changzhou Wujin BEST Electronic Cables Co., Ltd.");
IDS.put(8205, "Belkin Electronic (Changzhou) Co., Ltd.");
IDS.put(8220, "Freeport Resources Enterprises Corp.");
IDS.put(8222, "Qingdao Haier Telecom Co., Ltd.");
IDS.put(8284, "Shenzhen Tronixin Electronics Co., Ltd.");
IDS.put(8294, "Unicorn Electronics Components Co., Ltd.");
IDS.put(8334, "Luxshare-ICT");
IDS.put(8341, "CE LINK LIMITED");
IDS.put(8342, "Microconn Electronic Co., Ltd.");
IDS.put(8367, "Shenzhen CARVE Electronics Co., Ltd.");
IDS.put(8382, "BURY GmbH & Co. KG");
IDS.put(8384, "FENGHUA KINGSUN CO., LTD.");
IDS.put(8386, "Sumitomo Electric Ind., Ltd., Optical Comm. R&D Lab");
IDS.put(8439, "XIMEA s.r.o.");
IDS.put(8457, "VIA Labs, Inc.");
IDS.put(8492, "Shenzhen Linoya Electronic Co., Ltd.");
IDS.put(8494, "Amphenol AssembleTech (Xiamen) Co., Ltd.");
IDS.put(8524, "Y Soft Corporation");
IDS.put(8550, "JVC KENWOOD Corporation");
IDS.put(8564, "Transcend Information, Inc.");
IDS.put(8566, "TMC/Allion Test Labs");
IDS.put(8613, "Genesis Technology USA, Inc.");
IDS.put(8627, "Dongguan Teconn Electronics Technology Co., Ltd.");
IDS.put(8644, "Netcom Technology (HK) Limited");
IDS.put(8659, "Compupack Technology Co., Ltd.");
IDS.put(8667, "G-Max Technology Co., Ltd.");
IDS.put(8679, "Sagemcom Broadband SAS");
IDS.put(8695, "Wuerth-Elektronik eiSos GmbH & Co. KG");
IDS.put(8707, "Shin Shin Co., Ltd.");
IDS.put(8709, "3eYamaichi Electronics Co., Ltd.");
IDS.put(8710, "Wiretek International Investment Ltd.");
IDS.put(8711, "Fuzhou Rockchip Electronics Co., Ltd.");
IDS.put(8752, "Plugable Technologies");
IDS.put(8756, "T-CONN PRECISION CORPORATION");
IDS.put(8831, "Granite River Labs");
IDS.put(8842, "Hotron Precision Electronic Ind. Corp.");
IDS.put(8875, "Trigence Semiconductor, Inc.");
IDS.put(8888, "Motorola Mobility Inc.");
IDS.put(8904, "Karming Electronic (Shenzhen) Co., Ltd.");
IDS.put(8981, "Avery Design Systems, Inc.");
IDS.put(8993, "iKingdom Corp. (d.b.a. iConnectivity)");
IDS.put(9051, "KangXiang Electronic Co., Ltd.");
IDS.put(9068, "ZheJiang Chunsheng Electronics Co., Ltd.");
IDS.put(9130, "DOK (HK) Trading Limited");
IDS.put(9132, "Marunix Electron Limited");
IDS.put(9165, "Avconn Precise Connector Co., Ltd.");
IDS.put(9184, "BitifEye Digital Test Solutions GmbH");
IDS.put(9205, "Speed Conn Co., Ltd.");
IDS.put(9222, "INSIDE Secure");
IDS.put(9292, "Minebea Co., Ltd.");
IDS.put(9299, "BAANTO");
IDS.put(9338, "Suzhou Jutze Technologies Co., Ltd");
IDS.put(9355, "DONGGUAN SYNCONN PRECISION INDUSTRY CO. LTD.");
IDS.put(9382, "Shenzhen Pangngai Industrial Co., Ltd.");
IDS.put(9422, "Shenzhen Deren Electronic Co., Ltd.");
IDS.put(9424, "Smith Micro Software, Inc.");
IDS.put(9453, "ZEN FACTORY GROUP (ASIA) LTD.");
IDS.put(9481, "Chain-In Electronic Co., Ltd.");
IDS.put(9514, "SUZHOU KELI TECHNOLOGY DEVELOPMENT CO., LTD.");
IDS.put(9515, "TOP Exactitude Industry (ShenZhen) Co., Ltd.");
IDS.put(9525, "ShenZhen Hogend Precision Technology Co., Ltd.");
IDS.put(9527, "Norel Systems Ltd.");
IDS.put(9556, "ASSA ABLOY AB");
IDS.put(9575, "DongGuan LongTao Electronic Co., Ltd.");
IDS.put(9577, "DongGuan City MingJi Electronics Co., Ltd.");
IDS.put(9589, "Weida Hi-Tech Co., Ltd.");
IDS.put(9593, "Dongguan Wisechamp Electronic Co., Ltd.");
IDS.put(9613, "Sequans Communications");
IDS.put(9636, "ALGOLTEK, INC.");
IDS.put(9651, "DongGuan Elinke Industrial Co., Ltd.");
IDS.put(9679, "Corning Optical Communications LLC");
IDS.put(9714, "Dongguan Jinyue Electronics Co., Ltd.");
IDS.put(9723, "RICOH IMAGING COMPANY, LTD.");
IDS.put(9742, "DongGuan HYX Industrial Co., Ltd.");
IDS.put(9753, "Advanced Silicon SA");
IDS.put(9756, "EISST Limited");
IDS.put(9771, "YTOP Electronics Technical (Kunshan) Co., Ltd.");
IDS.put(9841, "Innovative Logic");
IDS.put(9842, "GoPro");
IDS.put(9846, "Basler AG");
IDS.put(9851, "Palpilot International Corp.");
IDS.put(9896, "UNIREX CORPORATION");
IDS.put(9917, "Integral Memory Plc.");
IDS.put(9973, "Morning Star Digital Connector Co., Ltd.");
IDS.put(9984, "MITACHI CO., LTD.");
IDS.put(9999, "HGST, a Western Digital Company");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.common;
import android.app.Activity;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.widget.CameraViewInterface;
public class UVCCameraHandler extends AbstractUVCCameraHandler {
/**
* create UVCCameraHandler, use MediaVideoEncoder, try MJPEG, default bandwidth
* @param parent
* @param cameraView
* @param width
* @param height
* @return
*/
public static final UVCCameraHandler createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int width, final int height) {
return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
}
/**
* create UVCCameraHandler, use MediaVideoEncoder, try MJPEG
* @param parent
* @param cameraView
* @param width
* @param height
* @param bandwidthFactor
* @return
*/
public static final UVCCameraHandler createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int width, final int height, final float bandwidthFactor) {
return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, bandwidthFactor);
}
/**
* create UVCCameraHandler, try MJPEG, default bandwidth
* @param parent
* @param cameraView
* @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
* @param width
* @param height
* @return
*/
public static final UVCCameraHandler createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int encoderType, final int width, final int height) {
return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
}
/**
* create UVCCameraHandler, default bandwidth
* @param parent
* @param cameraView
* @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
* @param width
* @param height
* @param format either UVCCamera.FRAME_FORMAT_YUYV(0) or UVCCamera.FRAME_FORMAT_MJPEG(1)
* @return
*/
public static final UVCCameraHandler createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int encoderType, final int width, final int height, final int format) {
return createHandler(parent, cameraView, encoderType, width, height, format, UVCCamera.DEFAULT_BANDWIDTH);
}
/**
* create UVCCameraHandler
* @param parent
* @param cameraView
* @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
* @param width
* @param height
* @param format either UVCCamera.FRAME_FORMAT_YUYV(0) or UVCCamera.FRAME_FORMAT_MJPEG(1)
* @param bandwidthFactor
* @return
*/
public static final UVCCameraHandler createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int encoderType, final int width, final int height, final int format, final float bandwidthFactor) {
final CameraThread thread = new CameraThread(UVCCameraHandler.class, parent, cameraView, encoderType, width, height, format, bandwidthFactor);
thread.start();
return (UVCCameraHandler)thread.getHandler();
}
protected UVCCameraHandler(final CameraThread thread) {
super(thread);
}
@Override
public void startPreview(final Object surface) {
super.startPreview(surface);
}
// @Override
// public void captureStill() {
// super.captureStill();
// }
@Override
public void captureStill(final String path,OnCaptureListener listener) {
super.captureStill(path,listener);
}
@Override
public void startCameraFoucs() {
super.startCameraFoucs();
}
}

View File

@@ -0,0 +1,186 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.common;
import android.app.Activity;
import android.view.Surface;
import com.serenegiant.glutils.RendererHolder;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.widget.CameraViewInterface;
import java.io.FileNotFoundException;
public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
/**
* create UVCCameraHandlerMultiSurface, use MediaVideoEncoder, try MJPEG, default bandwidth
* @param parent
* @param cameraView
* @param width
* @param height
* @return
*/
public static final UVCCameraHandlerMultiSurface createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int width, final int height) {
return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
}
/**
* create UVCCameraHandlerMultiSurface, use MediaVideoEncoder, try MJPEG
* @param parent
* @param cameraView
* @param width
* @param height
* @param bandwidthFactor
* @return
*/
public static final UVCCameraHandlerMultiSurface createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int width, final int height, final float bandwidthFactor) {
return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, bandwidthFactor);
}
/**
* create UVCCameraHandlerMultiSurface, try MJPEG, default bandwidth
* @param parent
* @param cameraView
* @param encoderType
* @param width
* @param height
* @return
*/
public static final UVCCameraHandlerMultiSurface createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int encoderType, final int width, final int height) {
return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
}
/**
* create UVCCameraHandlerMultiSurface, default bandwidth
* @param parent
* @param cameraView
* @param encoderType
* @param width
* @param height
* @param format
* @return
*/
public static final UVCCameraHandlerMultiSurface createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int encoderType, final int width, final int height, final int format) {
return createHandler(parent, cameraView, encoderType, width, height, format, UVCCamera.DEFAULT_BANDWIDTH);
}
/**
* create UVCCameraHandlerMultiSurface
* @param parent
* @param cameraView
* @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
* @param width
* @param height
* @param format either UVCCamera.FRAME_FORMAT_YUYV(0) or UVCCamera.FRAME_FORMAT_MJPEG(1)
* @param bandwidthFactor
* @return
*/
public static final UVCCameraHandlerMultiSurface createHandler(
final Activity parent, final CameraViewInterface cameraView,
final int encoderType, final int width, final int height, final int format, final float bandwidthFactor) {
final CameraThread thread = new CameraThread(UVCCameraHandlerMultiSurface.class, parent, cameraView, encoderType, width, height, format, bandwidthFactor);
thread.start();
return (UVCCameraHandlerMultiSurface)thread.getHandler();
}
private RendererHolder mRendererHolder;
protected UVCCameraHandlerMultiSurface(final CameraThread thread) {
super(thread);
mRendererHolder = new RendererHolder(thread.getWidth(), thread.getHeight(), null);
}
public synchronized void release() {
if (mRendererHolder != null) {
mRendererHolder.release();
mRendererHolder = null;
}
super.release();
}
public synchronized void resize(final int width, final int height) {
super.resize(width, height);
if (mRendererHolder != null) {
mRendererHolder.resize(width, height);
}
}
public synchronized void startPreview() {
checkReleased();
if (mRendererHolder != null) {
super.startPreview(mRendererHolder.getSurface());
} else {
throw new IllegalStateException();
}
}
public synchronized void addSurface(final int surfaceId, final Surface surface, final boolean isRecordable) {
checkReleased();
mRendererHolder.addSurface(surfaceId, surface, isRecordable);
}
public synchronized void removeSurface(final int surfaceId) {
if (mRendererHolder != null) {
mRendererHolder.removeSurface(surfaceId);
}
}
// @Override
// public void captureStill() {
// checkReleased();
// super.captureStill();
// }
@Override
public void captureStill(final String path,OnCaptureListener listener) {
checkReleased();
post(new Runnable() {
@Override
public void run() {
synchronized (UVCCameraHandlerMultiSurface.this) {
if (mRendererHolder != null) {
try {
mRendererHolder.captureStill(path);
updateMedia(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
});
}
}

View File

@@ -0,0 +1,27 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder;
public interface IAudioEncoder {
}

View File

@@ -0,0 +1,28 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder;
public interface IVideoEncoder {
public boolean frameAvailableSoon();
}

View File

@@ -0,0 +1,234 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class MediaAudioEncoder extends MediaEncoder implements IAudioEncoder {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "MediaAudioEncoder";
private static final String MIME_TYPE = "audio/mp4a-latm";
public static final int SAMPLE_RATE = 44100; // 44.1[KHz] is only setting guaranteed to be available on all devices.
private static final int BIT_RATE = 64000;
public static final int SAMPLES_PER_FRAME = 1024; // AAC, bytes/frame/channel
public static final int FRAMES_PER_BUFFER = 25; // AAC, frame/buffer/sec
private AudioThread mAudioThread = null;
public MediaAudioEncoder(final MediaMuxerWrapper muxer, final MediaEncoderListener listener) {
super(muxer, listener);
}
@Override
protected void prepare() throws IOException {
if (DEBUG) Log.v(TAG, "prepare:");
mTrackIndex = -1;
mMuxerStarted = mIsEOS = false;
// prepare MediaCodec for AAC encoding of audio data from inernal mic.
final MediaCodecInfo audioCodecInfo = selectAudioCodec(MIME_TYPE);
if (audioCodecInfo == null) {
Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
return;
}
if (DEBUG) Log.i(TAG, "selected codec: " + audioCodecInfo.getName());
final MediaFormat audioFormat = MediaFormat.createAudioFormat(MIME_TYPE, SAMPLE_RATE, 1);
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_IN_MONO);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
// audioFormat.setLong(MediaFormat.KEY_MAX_INPUT_SIZE, inputFile.length());
// audioFormat.setLong(MediaFormat.KEY_DURATION, (long)durationInMs );
if (DEBUG) Log.i(TAG, "format: " + audioFormat);
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
if (DEBUG) Log.i(TAG, "prepare finishing");
if (mListener != null) {
try {
mListener.onPrepared(this);
} catch (final Exception e) {
Log.e(TAG, "prepare:", e);
}
}
}
@Override
protected void startRecording() {
super.startRecording();
// create and execute audio capturing thread using internal mic
if (mAudioThread == null) {
mAudioThread = new AudioThread();
mAudioThread.start();
}
}
@Override
protected void release() {
mAudioThread = null;
super.release();
}
private static final int[] AUDIO_SOURCES = new int[] {
MediaRecorder.AudioSource.DEFAULT,
MediaRecorder.AudioSource.MIC,
MediaRecorder.AudioSource.CAMCORDER,
};
/**
* Thread to capture audio data from internal mic as uncompressed 16bit PCM data
* and write them to the MediaCodec encoder
*/
private class AudioThread extends Thread {
@Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); // THREAD_PRIORITY_URGENT_AUDIO
int cnt = 0;
final int min_buffer_size = AudioRecord.getMinBufferSize(
SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
int buffer_size = SAMPLES_PER_FRAME * FRAMES_PER_BUFFER;
if (buffer_size < min_buffer_size)
buffer_size = ((min_buffer_size / SAMPLES_PER_FRAME) + 1) * SAMPLES_PER_FRAME * 2;
final ByteBuffer buf = ByteBuffer.allocateDirect(SAMPLES_PER_FRAME).order(ByteOrder.nativeOrder());
AudioRecord audioRecord = null;
for (final int src: AUDIO_SOURCES) {
try {
audioRecord = new AudioRecord(src,
SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, buffer_size);
if (audioRecord != null) {
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
audioRecord.release();
audioRecord = null;
}
}
} catch (final Exception e) {
audioRecord = null;
}
if (audioRecord != null) {
break;
}
}
if (audioRecord != null) {
try {
if (mIsCapturing) {
if (DEBUG) Log.v(TAG, "AudioThread:start audio recording");
int readBytes;
audioRecord.startRecording();
try {
for ( ; mIsCapturing && !mRequestStop && !mIsEOS ; ) {
// read audio data from internal mic
buf.clear();
try {
readBytes = audioRecord.read(buf, SAMPLES_PER_FRAME);
} catch (final Exception e) {
break;
}
if (readBytes > 0) {
// set audio data to encoder
buf.position(readBytes);
buf.flip();
encode(buf, readBytes, getPTSUs());
frameAvailableSoon();
cnt++;
}
}
if (cnt > 0) {
frameAvailableSoon();
}
} finally {
audioRecord.stop();
}
}
} catch (final Exception e) {
Log.e(TAG, "AudioThread#run", e);
} finally {
audioRecord.release();
}
}
if (cnt == 0) {
for (int i = 0; mIsCapturing && (i < 5); i++) {
buf.position(SAMPLES_PER_FRAME);
buf.flip();
try {
encode(buf, SAMPLES_PER_FRAME, getPTSUs());
frameAvailableSoon();
} catch (final Exception e) {
break;
}
synchronized(this) {
try {
wait(50);
} catch (final InterruptedException e) {
}
}
}
}
if (DEBUG) Log.v(TAG, "AudioThread:finished");
}
}
/**
* select the first codec that match a specific MIME type
* @param mimeType
* @return
*/
private static final MediaCodecInfo selectAudioCodec(final String mimeType) {
if (DEBUG) Log.v(TAG, "selectAudioCodec:");
MediaCodecInfo result = null;
// get the list of available codecs
final int numCodecs = MediaCodecList.getCodecCount();
LOOP: for (int i = 0; i < numCodecs; i++) {
final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
if (!codecInfo.isEncoder()) { // skipp decoder
continue;
}
final String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (DEBUG) Log.i(TAG, "supportedType:" + codecInfo.getName() + ",MIME=" + types[j]);
if (types[j].equalsIgnoreCase(mimeType)) {
if (result == null) {
result = codecInfo;
break LOOP;
}
}
}
}
return result;
}
}

View File

@@ -0,0 +1,569 @@
package com.serenegiant.usb.encoder;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import com.jiangdg.usbcamera.utils.FileUtils;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
public abstract class MediaEncoder implements Runnable {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "MediaEncoder";
public static final int TYPE_AUDIO = 0; // 音频数据
public static final int TYPE_VIDEO = 1; // 视频数据
protected static final int TIMEOUT_USEC = 10000; // 10毫秒
protected static final int MSG_FRAME_AVAILABLE = 1;
protected static final int MSG_STOP_RECORDING = 9;
private long lastPush;
private long millisPerframe;
private boolean isExit;
public interface MediaEncoderListener {
void onPrepared(MediaEncoder encoder);
void onStopped(MediaEncoder encoder);
// 音频或视频流type=0为音频type=1为视频
void onEncodeResult(byte[] data, int offset,
int length, long timestamp, int type);
}
protected final Object mSync = new Object();
/**
* Flag that indicate this encoder is capturing now.
*/
protected volatile boolean mIsCapturing;
/**
* Flag that indicate the frame data will be available soon.
*/
private int mRequestDrain;
/**
* Flag to request stop capturing
*/
protected volatile boolean mRequestStop;
/**
* Flag that indicate encoder received EOS(End Of Stream)
*/
protected boolean mIsEOS;
/**
* Flag the indicate the muxer is running
*/
protected boolean mMuxerStarted;
/**
* Track Number
*/
protected int mTrackIndex;
/**
* MediaCodec instance for encoding
*/
protected MediaCodec mMediaCodec; // API >= 16(Android4.1.2)
/**
* Weak refarence of MediaMuxerWarapper instance
*/
protected final WeakReference<MediaMuxerWrapper> mWeakMuxer;
/**
* BufferInfo instance for dequeuing
*/
private MediaCodec.BufferInfo mBufferInfo; // API >= 16(Android4.1.2)
protected final MediaEncoderListener mListener;
/**
* There are 13 supported frequencies by ADTS.
**/
public static final int[] AUDIO_SAMPLING_RATES = { 96000, // 0
88200, // 1
64000, // 2
48000, // 3
44100, // 4
32000, // 5
24000, // 6
22050, // 7
16000, // 8
12000, // 9
11025, // 10
8000, // 11
7350, // 12
-1, // 13
-1, // 14
-1, // 15
};
public MediaEncoder(final MediaMuxerWrapper muxer, final MediaEncoderListener listener) {
if (listener == null) throw new NullPointerException("MediaEncoderListener is null");
if (muxer == null) throw new NullPointerException("MediaMuxerWrapper is null");
mWeakMuxer = new WeakReference<MediaMuxerWrapper>(muxer);
muxer.addEncoder(this);
mListener = listener;
synchronized (mSync) {
// create BufferInfo here for effectiveness(to reduce GC)
mBufferInfo = new MediaCodec.BufferInfo();
// wait for starting thread
new Thread(this, getClass().getSimpleName()).start();
try {
mSync.wait();
} catch (final InterruptedException e) {
}
}
}
public String getOutputPath() {
final MediaMuxerWrapper muxer = mWeakMuxer.get();
return muxer != null ? muxer.getOutputPath() : null;
}
/**
* the method to indicate frame data is soon available or already available
* @return return true if encoder is ready to encod.
*/
public boolean frameAvailableSoon() {
// if (DEBUG) Log.v(TAG, "frameAvailableSoon");
synchronized (mSync) {
if (!mIsCapturing || mRequestStop) {
return false;
}
mRequestDrain++;
mSync.notifyAll();
}
return true;
}
/**
* encoding loop on private thread
*/
@Override
public void run() {
// android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
synchronized (mSync) {
mRequestStop = false;
mRequestDrain = 0;
mSync.notify();
}
final boolean isRunning = true;
boolean localRequestStop;
boolean localRequestDrain;
boolean localIsNotExit;
// 创建h264
FileUtils.createfile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/test222.h264");
while (isRunning) {
synchronized (mSync) {
localRequestStop = mRequestStop;
localRequestDrain = (mRequestDrain > 0);
if (localRequestDrain)
mRequestDrain--;
}
if (localRequestStop) {
drain();
// request stop recording
signalEndOfInputStream();
// process output data again for EOS signale
drain();
// release all related objects
release();
break;
}
if (localRequestDrain) {
drain();
} else {
synchronized (mSync) {
try {
mSync.wait();
} catch (final InterruptedException e) {
break;
}
}
}
} // end of while
synchronized (mSync) {
mRequestStop = true;
mIsCapturing = false;
}
FileUtils.releaseFile();
}
/*
* prepareing method for each sub class
* this method should be implemented in sub class, so set this as abstract method
* @throws IOException
*/
/*package*/ abstract void prepare() throws IOException;
/*package*/ void startRecording() {
if (DEBUG) Log.v(TAG, "startRecording");
synchronized (mSync) {
mIsCapturing = true;
mRequestStop = false;
isExit = false;
mSync.notifyAll();
}
}
/**
* the method to request stop encoding
*/
/*package*/ void stopRecording() {
if (DEBUG) Log.v(TAG, "stopRecording");
synchronized (mSync) {
if (!mIsCapturing || mRequestStop) {
return;
}
mRequestStop = true; // for rejecting newer frame
isExit = true;
mSync.notifyAll();
// We can not know when the encoding and writing finish.
// so we return immediately after request to avoid delay of caller thread
}
}
//********************************************************************************
//********************************************************************************
/**
* Release all releated objects
*/
protected void release() {
if (DEBUG) Log.d(TAG, "release:");
try {
mListener.onStopped(this);
} catch (final Exception e) {
Log.e(TAG, "failed onStopped", e);
}
mIsCapturing = false;
if (mMediaCodec != null) {
try {
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
} catch (final Exception e) {
Log.e(TAG, "failed releasing MediaCodec", e);
}
}
if (mMuxerStarted) {
final MediaMuxerWrapper muxer = mWeakMuxer.get();
if (muxer != null) {
try {
muxer.stop();
} catch (final Exception e) {
Log.e(TAG, "failed stopping muxer", e);
}
}
}
mBufferInfo = null;
}
protected void signalEndOfInputStream() {
if (DEBUG) Log.d(TAG, "sending EOS to encoder");
// signalEndOfInputStream is only avairable for video encoding with surface
// and equivalent sending a empty buffer with BUFFER_FLAG_END_OF_STREAM flag.
// mMediaCodec.signalEndOfInputStream(); // API >= 18
encode((byte[])null, 0, getPTSUs());
}
/**
* Method to set byte array to the MediaCodec encoder
* @param buffer
* @param length length of byte array, zero means EOS.
* @param presentationTimeUs
*/
@SuppressWarnings("deprecation")
protected void encode(final byte[] buffer, final int length, final long presentationTimeUs) {
if (!mIsCapturing) return;
int ix = 0, sz;
final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
while (mIsCapturing && ix < length) {
final int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufferIndex >= 0) {
final ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
sz = inputBuffer.remaining();
sz = (ix + sz < length) ? sz : length - ix;
if (sz > 0 && (buffer != null)) {
inputBuffer.put(buffer, ix, sz);
}
ix += sz;
// if (DEBUG) Log.v(TAG, "encode:queueInputBuffer");
if (length <= 0) {
// send EOS
mIsEOS = true;
if (DEBUG) Log.i(TAG, "send BUFFER_FLAG_END_OF_STREAM");
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, 0,
presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
break;
} else {
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, sz,
presentationTimeUs, 0);
}
} else if (inputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
// wait for MediaCodec encoder is ready to encode
// nothing to do here because MediaCodec#dequeueInputBuffer(TIMEOUT_USEC)
// will wait for maximum TIMEOUT_USEC(10msec) on each call
}
}
}
protected void encode(ByteBuffer yuvBuffer,int len){
if (!mIsCapturing) return;
try {
if (lastPush == 0) {
lastPush = System.currentTimeMillis();
}
long time = System.currentTimeMillis() - lastPush;
if (time >= 0) {
time = millisPerframe - time;
if (time > 0)
Thread.sleep(time / 2);
}
final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
int bufferIndex = -1;
try{
bufferIndex = mMediaCodec.dequeueInputBuffer(0);
}catch (IllegalStateException e){
e.printStackTrace();
}
if (bufferIndex >= 0) {
ByteBuffer mBuffer;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBuffer = mMediaCodec.getInputBuffer(bufferIndex);
} else {
mBuffer = inputBuffers[bufferIndex];
}
byte[] yuvData = new byte[yuvBuffer.capacity()];
yuvBuffer.get(yuvData);
mBuffer.clear();
mBuffer.put(yuvData);
mBuffer.clear();
mMediaCodec.queueInputBuffer(bufferIndex, 0, yuvData.length, System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME);
}
lastPush = System.currentTimeMillis();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
/**
* Method to set ByteBuffer to the MediaCodec encoder
* @param buffer null means EOS
* @param presentationTimeUs
*/
@SuppressWarnings("deprecation")
protected void encode(final ByteBuffer buffer, final int length, final long presentationTimeUs) {
// if (DEBUG) Log.v(TAG, "encode:buffer=" + buffer);
if (!mIsCapturing) return;
int ix = 0, sz;
final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
while (mIsCapturing && ix < length) {
final int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufferIndex >= 0) {
final ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
sz = inputBuffer.remaining();
sz = (ix + sz < length) ? sz : length - ix;
if (sz > 0 && (buffer != null)) {
buffer.position(ix + sz);
buffer.flip();
inputBuffer.put(buffer);
}
ix += sz;
// if (DEBUG) Log.v(TAG, "encode:queueInputBuffer");
if (length <= 0) {
// send EOS
mIsEOS = true;
if (DEBUG) Log.i(TAG, "send BUFFER_FLAG_END_OF_STREAM");
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, 0,
presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
break;
} else {
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, sz,
presentationTimeUs, 0);
}
} else if (inputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
// wait for MediaCodec encoder is ready to encode
// nothing to do here because MediaCodec#dequeueInputBuffer(TIMEOUT_USEC)
// will wait for maximum TIMEOUT_USEC(10msec) on each call
}
}
}
ByteBuffer mBuffer = ByteBuffer.allocate(10240);
/**
* drain encoded data and write them to muxer
*/
@SuppressWarnings("deprecation")
protected void drain() {
if (mMediaCodec == null)
return;
ByteBuffer[] encoderOutputBuffers = mMediaCodec.getOutputBuffers();
int encoderStatus, count = 0;
final MediaMuxerWrapper muxer = mWeakMuxer.get();
if (muxer == null) {
Log.w(TAG, "muxer is unexpectedly null");
return;
}
byte[] mPpsSps = new byte[0];
byte[] h264 = new byte[640 * 480];
while (mIsCapturing) {
encoderStatus = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// 等待 TIMEOUT_USEC x 5 = 50毫秒
// 如果还没有数据,终止循环
if (!mIsEOS) {
if (++count > 5)
break;
}
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
encoderOutputBuffers = mMediaCodec.getOutputBuffers();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
if (mMuxerStarted) {
throw new RuntimeException("format changed twice");
}
final MediaFormat format = mMediaCodec.getOutputFormat();
mTrackIndex = muxer.addTrack(format);
mMuxerStarted = true;
if (!muxer.start()) {
synchronized (muxer) {
while (!muxer.isStarted())
try {
muxer.wait(100);
} catch (final InterruptedException e) {
break;
}
}
}
} else if (encoderStatus < 0) {
if (DEBUG) Log.w(TAG, "drain:unexpected result from encoder#dequeueOutputBuffer: " + encoderStatus);
} else {
ByteBuffer encodedData;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
encodedData = mMediaCodec.getOutputBuffer(encoderStatus);
} else {
encodedData = encoderOutputBuffers[encoderStatus];
}
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
// final ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
if (encodedData == null) {
throw new RuntimeException("encoderOutputBuffer " + encoderStatus + " was null");
}
// BUFFER_FLAG_CODEC_CONFIG标志
// BufferInfo清零
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
if (DEBUG) Log.d(TAG, "drain:BUFFER_FLAG_CODEC_CONFIG");
mBufferInfo.size = 0;
}
// BUFFER_FLAG_END_OF_STREAM标志
// 流结束,终止循环
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mMuxerStarted = mIsCapturing = false;
break;
}
// 有效编码数据流
if (mBufferInfo.size != 0) {
count = 0;
if (!mMuxerStarted) {
throw new RuntimeException("drain:muxer hasn't started");
}
// 写入音频流或视频流到混合器
mBufferInfo.presentationTimeUs = getPTSUs();
muxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo);
prevOutputPTSUs = mBufferInfo.presentationTimeUs;
// 推流获取h.264数据流
// mTrackIndex=0 视频mTrackIndex=1 音频
if(mTrackIndex == 0) {
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
boolean sync = false;
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// sps
sync = (mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
if (!sync) {
byte[] temp = new byte[mBufferInfo.size];
encodedData.get(temp);
mPpsSps = temp;
mMediaCodec.releaseOutputBuffer(encoderStatus, false);
continue;
} else {
mPpsSps = new byte[0];
}
}
sync |= (mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
int len = mPpsSps.length + mBufferInfo.size;
if (len > h264.length) {
h264 = new byte[len];
}
if (sync) {
System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length);
encodedData.get(h264, mPpsSps.length, mBufferInfo.size);
if(mListener != null) {
mListener.onEncodeResult(h264, 0,mPpsSps.length + mBufferInfo.size,
mBufferInfo.presentationTimeUs / 1000,TYPE_VIDEO);
}
// 保存数据流到文件
FileUtils.putFileStream(h264, 0,mPpsSps.length + mBufferInfo.size);
} else {
encodedData.get(h264, 0, mBufferInfo.size);
if(mListener != null) {
mListener.onEncodeResult(h264, 0,mBufferInfo.size,
mBufferInfo.presentationTimeUs / 1000,TYPE_VIDEO);
}
FileUtils.putFileStream(h264, 0,mBufferInfo.size);
}
} else if(mTrackIndex == 1){
mBuffer.clear();
encodedData.get(mBuffer.array(), 7, mBufferInfo.size);
encodedData.clear();
mBuffer.position(7 + mBufferInfo.size);
addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7);
mBuffer.flip();
if(mListener != null){
mListener.onEncodeResult(mBuffer.array(),0, mBufferInfo.size + 7,
mBufferInfo.presentationTimeUs / 1000,TYPE_AUDIO);
}
}
}
// 释放输出缓存,将其还给编码器
mMediaCodec.releaseOutputBuffer(encoderStatus, false);
}
}
}
private void addADTStoPacket(byte[] packet, int packetLen) {
packet[0] = (byte) 0xFF;
packet[1] = (byte) 0xF1;
packet[2] = (byte) (((2 - 1) << 6) + (getSamplingRateIndex() << 2) + (1 >> 2));
packet[3] = (byte) (((1 & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
packet[6] = (byte) 0xFC;
}
private int getSamplingRateIndex(){
int mSamplingRateIndex = -1;
for (int i=0;i < AUDIO_SAMPLING_RATES.length; i++) {
if (AUDIO_SAMPLING_RATES[i] == MediaAudioEncoder.SAMPLE_RATE) {
mSamplingRateIndex = i;
break;
}
}
return mSamplingRateIndex;
}
private long prevOutputPTSUs = 0;
protected long getPTSUs() {
long result = System.nanoTime() / 1000L;
if (result < prevOutputPTSUs)
result = (prevOutputPTSUs - result) + result;
return result;
}
}

View File

@@ -0,0 +1,191 @@
package com.serenegiant.usb.encoder;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;
import java.util.Locale;
public class MediaMuxerWrapper {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "MediaMuxerWrapper";
private static final String DIR_NAME = "USBCameraTest";
private static final SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
private String mOutputPath;
private final MediaMuxer mMediaMuxer; // API >= 18
private int mEncoderCount, mStatredCount;
private boolean mIsStarted;
private MediaEncoder mVideoEncoder, mAudioEncoder;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public MediaMuxerWrapper(String path) throws IOException {
try {
// 保存到自定义路径还是手机默认Movies路径
if (TextUtils.isEmpty(path))
mOutputPath = getCaptureFile(Environment.DIRECTORY_MOVIES, ".mp4").toString();
mOutputPath = path;
} catch (final NullPointerException e) {
throw new RuntimeException("This app has no permission of writing external storage");
}
mMediaMuxer = new MediaMuxer(mOutputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mEncoderCount = mStatredCount = 0;
mIsStarted = false;
}
public String getOutputPath() {
return mOutputPath;
}
public void prepare() throws IOException {
if (mVideoEncoder != null)
mVideoEncoder.prepare();
if (mAudioEncoder != null)
mAudioEncoder.prepare();
}
public void startRecording() {
if (mVideoEncoder != null)
mVideoEncoder.startRecording();
if (mAudioEncoder != null)
mAudioEncoder.startRecording();
}
public void stopRecording() {
if (mVideoEncoder != null)
mVideoEncoder.stopRecording();
mVideoEncoder = null;
if (mAudioEncoder != null)
mAudioEncoder.stopRecording();
mAudioEncoder = null;
}
public synchronized boolean isStarted() {
return mIsStarted;
}
//**********************************************************************
//**********************************************************************
/**
* assign encoder to this calss. this is called from encoder.
* @param encoder instance of MediaVideoEncoder or MediaAudioEncoder
*/
/*package*/ void addEncoder(final MediaEncoder encoder) {
if (encoder instanceof MediaVideoEncoder) {
if (mVideoEncoder != null)
throw new IllegalArgumentException("Video encoder already added.");
mVideoEncoder = encoder;
} else if (encoder instanceof MediaSurfaceEncoder) {
if (mVideoEncoder != null)
throw new IllegalArgumentException("Video encoder already added.");
mVideoEncoder = encoder;
} else if (encoder instanceof MediaVideoBufferEncoder) {
if (mVideoEncoder != null)
throw new IllegalArgumentException("Video encoder already added.");
mVideoEncoder = encoder;
} else if (encoder instanceof MediaAudioEncoder) {
if (mAudioEncoder != null)
throw new IllegalArgumentException("Video encoder already added.");
mAudioEncoder = encoder;
} else
throw new IllegalArgumentException("unsupported encoder");
mEncoderCount = (mVideoEncoder != null ? 1 : 0) + (mAudioEncoder != null ? 1 : 0);
}
/**
* request start recording from encoder
* @return true when muxer is ready to write
*/
/*package*/ synchronized boolean start() {
if (DEBUG) Log.v(TAG, "start:");
mStatredCount++;
if ((mEncoderCount > 0) && (mStatredCount == mEncoderCount)) {
mMediaMuxer.start();
mIsStarted = true;
notifyAll();
if (DEBUG) Log.v(TAG, "MediaMuxer started:");
}
return mIsStarted;
}
/**
* request stop recording from encoder when encoder received EOS
*/
/*package*/ synchronized void stop() {
if (DEBUG) Log.v(TAG, "stop:mStatredCount=" + mStatredCount);
mStatredCount--;
if ((mEncoderCount > 0) && (mStatredCount <= 0)) {
try {
mMediaMuxer.stop();
} catch (final Exception e) {
Log.w(TAG, e);
}
mIsStarted = false;
if (DEBUG) Log.v(TAG, "MediaMuxer stopped:");
}
}
/**
* assign encoder to muxer
* @param format
* @return minus value indicate error
*/
/*package*/ synchronized int addTrack(final MediaFormat format) {
if (mIsStarted)
throw new IllegalStateException("muxer already started");
final int trackIx = mMediaMuxer.addTrack(format);
if (DEBUG) Log.i(TAG, "addTrack:trackNum=" + mEncoderCount + ",trackIx=" + trackIx + ",format=" + format);
return trackIx;
}
/**
* write encoded data to muxer
* @param trackIndex
* @param byteBuf
* @param bufferInfo
*/
/*package*/ synchronized void writeSampleData(final int trackIndex, final ByteBuffer byteBuf, final MediaCodec.BufferInfo bufferInfo) {
if (mStatredCount > 0)
mMediaMuxer.writeSampleData(trackIndex, byteBuf, bufferInfo);
}
//**********************************************************************
//**********************************************************************
/**
* generate output file
* @param type Environment.DIRECTORY_MOVIES / Environment.DIRECTORY_DCIM etc.
* @param ext .mp4(.m4a for audio) or .png
* @return return null when this app has no writing permission to external storage.
*/
public static final File getCaptureFile(final String type, final String ext) {
final File dir = new File(Environment.getExternalStoragePublicDirectory(type), DIR_NAME);
Log.d(TAG, "path=" + dir.toString());
dir.mkdirs();
if (dir.canWrite()) {
return new File(dir, getDateTimeString() + ext);
}
return null;
}
/**
* get current date and time as String
* @return
*/
private static final String getDateTimeString() {
final GregorianCalendar now = new GregorianCalendar();
return mDateTimeFormat.format(now.getTime());
}
}

View File

@@ -0,0 +1,196 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.util.Log;
import android.view.Surface;
import java.io.IOException;
public class MediaSurfaceEncoder extends MediaEncoder implements IVideoEncoder {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "MediaSurfaceEncoder";
private static final String MIME_TYPE = "video/avc";
// parameters for recording
private final int mWidth, mHeight;
private static final int FRAME_RATE = 15;
private static final float BPP = 0.50f;
private Surface mSurface;
public MediaSurfaceEncoder(final MediaMuxerWrapper muxer, final int width, final int height, final MediaEncoderListener listener) {
super(muxer, listener);
if (DEBUG) Log.i(TAG, "MediaVideoEncoder: ");
mWidth = width;
mHeight = height;
}
/**
* Returns the encoder's input surface.
*/
public Surface getInputSurface() {
return mSurface;
}
@Override
protected void prepare() throws IOException {
if (DEBUG) Log.i(TAG, "prepare: ");
mTrackIndex = -1;
mMuxerStarted = mIsEOS = false;
final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
if (videoCodecInfo == null) {
Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
return;
}
if (DEBUG) Log.i(TAG, "selected codec: " + videoCodecInfo.getName());
final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // API >= 18
format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
if (DEBUG) Log.i(TAG, "format: " + format);
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
// get Surface for encoder input
// this method only can call between #configure and #start
mSurface = mMediaCodec.createInputSurface(); // API >= 18
mMediaCodec.start();
if (DEBUG) Log.i(TAG, "prepare finishing");
if (mListener != null) {
try {
mListener.onPrepared(this);
} catch (final Exception e) {
Log.e(TAG, "prepare:", e);
}
}
}
@Override
protected void release() {
if (DEBUG) Log.i(TAG, "release:");
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
super.release();
}
private int calcBitRate() {
final int bitrate = (int)(BPP * FRAME_RATE * mWidth * mHeight);
Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
return bitrate;
}
/**
* select the first codec that match a specific MIME type
* @param mimeType
* @return null if no codec matched
*/
protected static final MediaCodecInfo selectVideoCodec(final String mimeType) {
if (DEBUG) Log.v(TAG, "selectVideoCodec:");
// get the list of available codecs
final int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) {
final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
if (!codecInfo.isEncoder()) { // skipp decoder
continue;
}
// select first codec that match a specific MIME type and color format
final String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
if (DEBUG) Log.i(TAG, "codec:" + codecInfo.getName() + ",MIME=" + types[j]);
final int format = selectColorFormat(codecInfo, mimeType);
if (format > 0) {
return codecInfo;
}
}
}
}
return null;
}
/**
* select color format available on specific codec and we can use.
* @return 0 if no colorFormat is matched
*/
protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
if (DEBUG) Log.i(TAG, "selectColorFormat: ");
int result = 0;
final MediaCodecInfo.CodecCapabilities caps;
try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
caps = codecInfo.getCapabilitiesForType(mimeType);
} finally {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
}
int colorFormat;
for (int i = 0; i < caps.colorFormats.length; i++) {
colorFormat = caps.colorFormats[i];
if (isRecognizedVideoFormat(colorFormat)) {
if (result == 0)
result = colorFormat;
break;
}
}
if (result == 0)
Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
return result;
}
/**
* color formats that we can use in this class
*/
protected static int[] recognizedFormats;
static {
recognizedFormats = new int[] {
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
// MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
};
}
private static final boolean isRecognizedVideoFormat(final int colorFormat) {
if (DEBUG) Log.i(TAG, "isRecognizedVideoFormat:colorFormat=" + colorFormat);
final int n = recognizedFormats != null ? recognizedFormats.length : 0;
for (int i = 0; i < n; i++) {
if (recognizedFormats[i] == colorFormat) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,184 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
public class MediaVideoBufferEncoder extends MediaEncoder implements IVideoEncoder {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "MediaVideoBufferEncoder";
private static final String MIME_TYPE = "video/avc";
private static final int FRAME_RATE = 15;
private static final float BPP = 0.50f;
private final int mWidth, mHeight;
protected int mColorFormat;
public MediaVideoBufferEncoder(final MediaMuxerWrapper muxer, final int width, final int height, final MediaEncoderListener listener) {
super(muxer, listener);
if (DEBUG) Log.i(TAG, "MediaVideoEncoder: ");
mWidth = width;
mHeight = height;
}
public void encode(final ByteBuffer buffer) {
synchronized (mSync) {
if (!mIsCapturing || mRequestStop) return;
}
// encode(buffer, buffer.capacity(), getPTSUs());
encode(buffer, buffer.capacity());
}
@Override
protected void prepare() throws IOException {
if (DEBUG) Log.i(TAG, "prepare: ");
mTrackIndex = -1;
mMuxerStarted = mIsEOS = false;
final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
if (videoCodecInfo == null) {
Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
return;
}
if (DEBUG) Log.i(TAG, "selected codec: " + videoCodecInfo.getName());
final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mColorFormat);
format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
if (DEBUG) Log.i(TAG, "format: " + format);
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
Bundle params = new Bundle();
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mMediaCodec.setParameters(params);
}
if (DEBUG) Log.i(TAG, "prepare finishing");
if (mListener != null) {
try {
mListener.onPrepared(this);
} catch (final Exception e) {
Log.e(TAG, "prepare:", e);
}
}
}
private int calcBitRate() {
final int bitrate = (int)(BPP * FRAME_RATE * mWidth * mHeight);
Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
return bitrate;
}
// 选择第一个与制定MIME类型匹配的编码器
@SuppressWarnings("deprecation")
protected final MediaCodecInfo selectVideoCodec(final String mimeType) {
if (DEBUG) Log.v(TAG, "selectVideoCodec:");
// get the list of available codecs
final int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) {
final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
if (!codecInfo.isEncoder()) { // skipp decoder
continue;
}
// select first codec that match a specific MIME type and color format
final String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
if (DEBUG) Log.i(TAG, "codec:" + codecInfo.getName() + ",MIME=" + types[j]);
final int format = selectColorFormat(codecInfo, mimeType);
if (format > 0) {
mColorFormat = format;
return codecInfo;
}
}
}
}
return null;
}
// 选择编码器支持的格式
protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
if (DEBUG) Log.i(TAG, "selectColorFormat: ");
int result = 0;
final MediaCodecInfo.CodecCapabilities caps;
try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
caps = codecInfo.getCapabilitiesForType(mimeType);
} finally {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
}
int colorFormat;
for (int i = 0; i < caps.colorFormats.length; i++) {
colorFormat = caps.colorFormats[i];
if (isRecognizedViewoFormat(colorFormat)) {
if (result == 0)
result = colorFormat;
break;
}
}
if (result == 0)
Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
return result;
}
// YUV颜色格式
protected static int[] recognizedFormats;
static {
recognizedFormats = new int[] {
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
// MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
};
}
private static final boolean isRecognizedViewoFormat(final int colorFormat) {
if (DEBUG) Log.i(TAG, "isRecognizedViewoFormat:colorFormat=" + colorFormat);
final int n = recognizedFormats != null ? recognizedFormats.length : 0;
for (int i = 0; i < n; i++) {
if (recognizedFormats[i] == colorFormat) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,231 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
import com.serenegiant.glutils.EGLBase;
import com.serenegiant.glutils.RenderHandler;
import java.io.IOException;
/**
* Encode texture images as H.264 video
* using MediaCodec.
* This class render texture images into recording surface
* camera from MediaCodec encoder using Open GL|ES
*/
public class MediaVideoEncoder extends MediaEncoder implements IVideoEncoder {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "MediaVideoEncoder";
private static final String MIME_TYPE = "video/avc";
// parameters for recording
private final int mWidth, mHeight;
private static final int FRAME_RATE = 15;
private static final float BPP = 0.50f;
private RenderHandler mRenderHandler;
private Surface mSurface;
public MediaVideoEncoder(final MediaMuxerWrapper muxer, final int width, final int height, final MediaEncoderListener listener) {
super(muxer, listener);
if (DEBUG) Log.i(TAG, "MediaVideoEncoder: ");
mRenderHandler = RenderHandler.createHandler(TAG);
mWidth = width;
mHeight = height;
}
public boolean frameAvailableSoon(final float[] tex_matrix) {
boolean result;
if (result = super.frameAvailableSoon())
mRenderHandler.draw(tex_matrix);
return result;
}
/**
* This method does not work correctly on this class,
* use #frameAvailableSoon(final float[]) instead
* @return
*/
@Override
public boolean frameAvailableSoon() {
boolean result;
if (result = super.frameAvailableSoon())
mRenderHandler.draw(null);
return result;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void prepare() throws IOException {
if (DEBUG) Log.i(TAG, "prepare: ");
mTrackIndex = -1;
mMuxerStarted = mIsEOS = false;
final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
if (videoCodecInfo == null) {
Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
return;
}
if (DEBUG) Log.i(TAG, "selected codec: " + videoCodecInfo.getName());
final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // API >= 18
format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
if (DEBUG) Log.i(TAG, "format: " + format);
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
// get Surface for encoder input
// this method only can call between #configure and #start
mSurface = mMediaCodec.createInputSurface(); // API >= 18
mMediaCodec.start();
if (DEBUG) Log.i(TAG, "prepare finishing");
if (mListener != null) {
try {
mListener.onPrepared(this);
} catch (final Exception e) {
Log.e(TAG, "prepare:", e);
}
}
}
public void setEglContext(final EGLBase.IContext sharedContext, final int tex_id) {
mRenderHandler.setEglContext(sharedContext, tex_id, mSurface, true);
}
@Override
protected void release() {
if (DEBUG) Log.i(TAG, "release:");
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
if (mRenderHandler != null) {
mRenderHandler.release();
mRenderHandler = null;
}
super.release();
}
private int calcBitRate() {
final int bitrate = (int)(BPP * FRAME_RATE * mWidth * mHeight);
Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
return bitrate;
}
/**
* select the first codec that match a specific MIME type
* @param mimeType
* @return null if no codec matched
*/
protected static final MediaCodecInfo selectVideoCodec(final String mimeType) {
if (DEBUG) Log.v(TAG, "selectVideoCodec:");
// get the list of available codecs
final int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) {
final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
if (!codecInfo.isEncoder()) { // skipp decoder
continue;
}
// select first codec that match a specific MIME type and color format
final String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
if (DEBUG) Log.i(TAG, "codec:" + codecInfo.getName() + ",MIME=" + types[j]);
final int format = selectColorFormat(codecInfo, mimeType);
if (format > 0) {
return codecInfo;
}
}
}
}
return null;
}
/**
* select color format available on specific codec and we can use.
* @return 0 if no colorFormat is matched
*/
protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
if (DEBUG) Log.i(TAG, "selectColorFormat: ");
int result = 0;
final MediaCodecInfo.CodecCapabilities caps;
try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
caps = codecInfo.getCapabilitiesForType(mimeType);
} finally {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
}
int colorFormat;
for (int i = 0; i < caps.colorFormats.length; i++) {
colorFormat = caps.colorFormats[i];
if (isRecognizedVideoFormat(colorFormat)) {
if (result == 0)
result = colorFormat;
break;
}
}
if (result == 0)
Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
return result;
}
/**
* color formats that we can use in this class
*/
protected static int[] recognizedFormats;
static {
recognizedFormats = new int[] {
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
// MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
};
}
private static final boolean isRecognizedVideoFormat(final int colorFormat) {
if (DEBUG) Log.i(TAG, "isRecognizedVideoFormat:colorFormat=" + colorFormat);
final int n = recognizedFormats != null ? recognizedFormats.length : 0;
for (int i = 0; i < n; i++) {
if (recognizedFormats[i] == colorFormat) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,57 @@
package com.serenegiant.usb.encoder;
/** 录制参数
*
* Created by jiangdongguo on 2017/10/19.
*/
public class RecordParams {
private String recordPath;
private int recordDuration;
private boolean voiceClose;
private boolean isAutoSave;
private boolean isSupportOverlay;
public RecordParams() {
}
public boolean isSupportOverlay() {
return isSupportOverlay;
}
public void setSupportOverlay(boolean supportOverlay) {
isSupportOverlay = supportOverlay;
}
public boolean isVoiceClose() {
return voiceClose;
}
public void setVoiceClose(boolean voiceClose) {
this.voiceClose = voiceClose;
}
public String getRecordPath() {
return recordPath;
}
public void setRecordPath(String recordPath) {
this.recordPath = recordPath;
}
public int getRecordDuration() {
return recordDuration;
}
public void setRecordDuration(int recordDuration) {
this.recordDuration = recordDuration;
}
public boolean isAutoSave() {
return isAutoSave;
}
public void setAutoSave(boolean autoSave) {
isAutoSave = autoSave;
}
}

View File

@@ -0,0 +1,385 @@
package com.serenegiant.usb.encoder.biz;
import android.annotation.TargetApi;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
/**将PCM编码为AAC
*
* Created by jianddongguo on 2017/7/21.
*/
public class AACEncodeConsumer extends Thread{
private static final boolean DEBUG = false;
private static final String TAG = "TMPU";
private static final String MIME_TYPE = "audio/mp4a-latm";
private static final long TIMES_OUT = 1000;
private static final int SAMPLE_RATE = 8000; // 采样率
private static final int BIT_RATE = 16000; // 比特率
private static final int BUFFER_SIZE = 1920; // 最小缓存
private int outChannel = 1;
private int bitRateForLame = 32;
private int qaulityDegree = 7;
private int bufferSizeInBytes;
private AudioRecord mAudioRecord; // 音频采集
private MediaCodec mAudioEncoder; // 音频编码
private OnAACEncodeResultListener listener;
private int mSamplingRateIndex = 0;//ADTS
private boolean isEncoderStart = false;
private boolean isRecMp3 = false;
private boolean isExit = false;
private long prevPresentationTimes = 0;
private WeakReference<Mp4MediaMuxer> mMuxerRef;
private MediaFormat newFormat;
private static final int[] AUDIO_SOURCES = new int[] {
MediaRecorder.AudioSource.DEFAULT,
MediaRecorder.AudioSource.MIC,
MediaRecorder.AudioSource.CAMCORDER,
};
/**
* There are 13 supported frequencies by ADTS.
**/
public static final int[] AUDIO_SAMPLING_RATES = { 96000, // 0
88200, // 1
64000, // 2
48000, // 3
44100, // 4
32000, // 5
24000, // 6
22050, // 7
16000, // 8
12000, // 9
11025, // 10
8000, // 11
7350, // 12
-1, // 13
-1, // 14
-1, // 15
};
private FileOutputStream fops;
// 编码流结果回调接口
public interface OnAACEncodeResultListener{
void onEncodeResult(byte[] data, int offset,
int length, long timestamp);
}
public AACEncodeConsumer(){
for (int i=0;i < AUDIO_SAMPLING_RATES.length; i++) {
if (AUDIO_SAMPLING_RATES[i] == SAMPLE_RATE) {
mSamplingRateIndex = i;
break;
}
}
}
public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener){
this.listener = listener;
}
public void exit(){
isExit = true;
}
public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer){
this.mMuxerRef = new WeakReference<>(mMuxer);
Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null && newFormat != null) {
muxer.addTrack(newFormat, false);
}
}
@Override
public void run() {
// 开启音频采集、编码
if(! isEncoderStart){
initAudioRecord();
initMediaCodec();
}
// 初始化音频文件参数
byte[] mp3Buffer = new byte[1024];
// 这里有问题,当本地录制结束后,没有写入
while(! isExit){
byte[] audioBuffer = new byte[2048];
// 采集音频
int readBytes = mAudioRecord.read(audioBuffer,0,BUFFER_SIZE);
if(DEBUG)
Log.i(TAG,"采集音频readBytes = "+readBytes);
// 编码音频
if(readBytes > 0){
encodeBytes(audioBuffer,readBytes);
}
}
// 停止音频采集、编码
stopMediaCodec();
stopAudioRecord();
}
@TargetApi(21)
private void encodeBytes(byte[] audioBuf, int readBytes) {
ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
ByteBuffer[] outputBuffers = mAudioEncoder.getOutputBuffers();
//返回编码器的一个输入缓存区句柄,-1表示当前没有可用的输入缓存区
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(TIMES_OUT);
if(inputBufferIndex >= 0){
// 绑定一个被空的、可写的输入缓存区inputBuffer到客户端
ByteBuffer inputBuffer = null;
if(!isLollipop()){
inputBuffer = inputBuffers[inputBufferIndex];
}else{
inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex);
}
// 向输入缓存区写入有效原始数据,并提交到编码器中进行编码处理
if(audioBuf==null || readBytes<=0){
mAudioEncoder.queueInputBuffer(inputBufferIndex,0,0,getPTSUs(),MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}else{
inputBuffer.clear();
inputBuffer.put(audioBuf);
mAudioEncoder.queueInputBuffer(inputBufferIndex,0,readBytes,getPTSUs(),0);
}
}
// 返回一个输出缓存区句柄,当为-1时表示当前没有可用的输出缓存区
// mBufferInfo参数包含被编码好的数据timesOut参数为超时等待的时间
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = -1;
do{
outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo,TIMES_OUT);
if(outputBufferIndex == MediaCodec. INFO_TRY_AGAIN_LATER){
if(DEBUG)
Log.i(TAG,"获得编码器输出缓存区超时");
}else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
// 如果API小于21APP需要重新绑定编码器的输入缓存区
// 如果API大于21则无需处理INFO_OUTPUT_BUFFERS_CHANGED
if(!isLollipop()){
outputBuffers = mAudioEncoder.getOutputBuffers();
}
}else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
// 编码器输出缓存区格式改变,通常在存储数据之前且只会改变一次
// 这里设置混合器视频轨道,如果音频已经添加则启动混合器(保证音视频同步)
if(DEBUG)
Log.i(TAG,"编码器输出缓存区格式改变,添加视频轨道到混合器");
synchronized (AACEncodeConsumer.this) {
newFormat = mAudioEncoder.getOutputFormat();
if(mMuxerRef != null){
Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null) {
muxer.addTrack(newFormat, false);
}
}
}
}else{
// 当flag属性置为BUFFER_FLAG_CODEC_CONFIG后说明输出缓存区的数据已经被消费了
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0){
if(DEBUG)
Log.i(TAG,"编码数据被消费BufferInfo的size属性置0");
mBufferInfo.size = 0;
}
// 数据流结束标志,结束本次循环
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
if(DEBUG)
Log.i(TAG,"数据流结束,退出循环");
break;
}
// 获取一个只读的输出缓存区inputBuffer ,它包含被编码好的数据
ByteBuffer mBuffer = ByteBuffer.allocate(10240);
ByteBuffer outputBuffer = null;
if(!isLollipop()){
outputBuffer = outputBuffers[outputBufferIndex];
}else{
outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
}
if(mBufferInfo.size != 0){
// 获取输出缓存区失败,抛出异常
if(outputBuffer == null){
throw new RuntimeException("encodecOutputBuffer"+outputBufferIndex+"was null");
}
// 添加视频流到混合器
if(mMuxerRef != null){
Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null) {
muxer.pumpStream(outputBuffer, mBufferInfo, false);
}
}
// AAC流添加ADTS头缓存到mBuffer
mBuffer.clear();
outputBuffer.get(mBuffer.array(), 7, mBufferInfo.size);
outputBuffer.clear();
mBuffer.position(7 + mBufferInfo.size);
addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7);
mBuffer.flip();
// 将AAC回调给MainModelImpl进行push
if(listener != null){
Log.i(TAG,"----->得到aac数据流<-----");
listener.onEncodeResult(mBuffer.array(),0, mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000);
}
}
// 处理结束,释放输出缓存区资源
mAudioEncoder.releaseOutputBuffer(outputBufferIndex,false);
}
}while (outputBufferIndex >= 0);
}
private void initAudioRecord(){
if(DEBUG)
Log.d(TAG,"AACEncodeConsumer-->开始采集音频");
// 设置进程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
for (final int src: AUDIO_SOURCES) {
try {
mAudioRecord = new AudioRecord(src,
SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
if (mAudioRecord != null) {
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
mAudioRecord.release();
mAudioRecord = null;
}
}
} catch (final Exception e) {
mAudioRecord = null;
}
if (mAudioRecord != null) {
break;
}
}
mAudioRecord.startRecording();
}
private void initMediaCodec(){
if(DEBUG)
Log.d(TAG,"AACEncodeConsumer-->开始编码音频");
MediaCodecInfo mCodecInfo = selectSupportCodec(MIME_TYPE);
if(mCodecInfo == null){
Log.e(TAG,"编码器不支持"+MIME_TYPE+"类型");
return;
}
try{
mAudioEncoder = MediaCodec.createByCodecName(mCodecInfo.getName());
}catch(IOException e){
Log.e(TAG,"创建编码器失败"+e.getMessage());
e.printStackTrace();
}
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, BUFFER_SIZE);
mAudioEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mAudioEncoder.start();
isEncoderStart = true;
}
private void stopAudioRecord() {
if(DEBUG)
Log.d(TAG,"AACEncodeConsumer-->停止采集音频");
if(mAudioRecord != null){
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
private void stopMediaCodec() {
if(DEBUG)
Log.d(TAG,"AACEncodeConsumer-->停止编码音频");
if(mAudioEncoder != null){
mAudioEncoder.stop();
mAudioEncoder.release();
mAudioEncoder = null;
}
isEncoderStart = false;
}
// API>=21
private boolean isLollipop(){
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
// API<=19
private boolean isKITKAT(){
return Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
}
private long getPTSUs(){
long result = System.nanoTime()/1000;
if(result < prevPresentationTimes){
result = (prevPresentationTimes - result ) + result;
}
return result;
}
/**
* 遍历所有编解码器返回第一个与指定MIME类型匹配的编码器
* 判断是否有支持指定mime类型的编码器
* */
private MediaCodecInfo selectSupportCodec(String mimeType){
int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
// 判断是否为编码器,否则直接进入下一次循环
if (!codecInfo.isEncoder()) {
continue;
}
// 如果是编码器判断是否支持Mime类型
String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
return codecInfo;
}
}
}
return null;
}
private void addADTStoPacket(byte[] packet, int packetLen) {
packet[0] = (byte) 0xFF;
packet[1] = (byte) 0xF1;
packet[2] = (byte) (((2 - 1) << 6) + (mSamplingRateIndex << 2) + (1 >> 2));
packet[3] = (byte) (((1 & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
packet[6] = (byte) 0xFC;
}
private short[] transferByte2Short(byte[] data,int readBytes){
// byte[] 转 short[],数组长度缩减一半
int shortLen = readBytes / 2;
// 将byte[]数组装如ByteBuffer缓冲区
ByteBuffer byteBuffer = ByteBuffer.wrap(data, 0, readBytes);
// 将ByteBuffer转成小端并获取shortBuffer
ShortBuffer shortBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] shortData = new short[shortLen];
shortBuffer.get(shortData, 0, shortLen);
return shortData;
}
}

View File

@@ -0,0 +1,461 @@
package com.serenegiant.usb.encoder.biz;
import android.annotation.SuppressLint;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
/**
* 对YUV视频流进行编码
* Created by jiangdongguo on 2017/5/6.
*/
@SuppressWarnings("deprecation")
public class H264EncodeConsumer extends Thread {
private static final boolean DEBUG = false;
private static final String TAG = "H264EncodeConsumer";
private static final String MIME_TYPE = "video/avc";
// 间隔1s插入一帧关键帧
private static final int FRAME_INTERVAL = 1;
// 绑定编码器缓存区超时时间为10s
private static final int TIMES_OUT = 10000;
// 硬编码器
private MediaCodec mMediaCodec;
private int mColorFormat;
private boolean isExit = false;
private boolean isEncoderStart = false;
private MediaFormat mFormat;
private static String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test2.h264";
private BufferedOutputStream outputStream;
final int millisPerframe = 1000 / 20;
long lastPush = 0;
private OnH264EncodeResultListener listener;
private int mWidth;
private int mHeight;
private MediaFormat newFormat;
private WeakReference<Mp4MediaMuxer> mMuxerRef;
private boolean isAddKeyFrame = false;
public interface OnH264EncodeResultListener {
void onEncodeResult(byte[] data, int offset,
int length, long timestamp);
}
public void setOnH264EncodeResultListener(OnH264EncodeResultListener listener) {
this.listener = listener;
}
public H264EncodeConsumer(int width, int height) {
this.mWidth = width;
this.mHeight = height;
}
public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer) {
this.mMuxerRef = new WeakReference<>(mMuxer);
Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null && newFormat != null) {
muxer.addTrack(newFormat, true);
}
}
private ByteBuffer[] inputBuffers;
private ByteBuffer[] outputBuffers;
public void setRawYuv(byte[] yuvData, int width, int height) {
if (!isEncoderStart)
return;
if (mWidth != width || mHeight != height) {
mWidth = width;
mHeight = height;
return;
}
try {
if (lastPush == 0) {
lastPush = System.currentTimeMillis();
}
long time = System.currentTimeMillis() - lastPush;
if (time >= 0) {
time = millisPerframe - time;
if (time > 0)
Thread.sleep(time / 2);
}
// 将数据写入编码器
feedMediaCodecData(nv12ToNV21(yuvData, mWidth, mHeight));
if (time > 0)
Thread.sleep(time / 2);
lastPush = System.currentTimeMillis();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
private void feedMediaCodecData(byte[] data) {
if (!isEncoderStart)
return;
int bufferIndex = -1;
try {
bufferIndex = mMediaCodec.dequeueInputBuffer(0);
} catch (IllegalStateException e) {
e.printStackTrace();
}
if (bufferIndex >= 0) {
ByteBuffer buffer;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
buffer = mMediaCodec.getInputBuffer(bufferIndex);
} else {
buffer = inputBuffers[bufferIndex];
}
buffer.clear();
buffer.put(data);
buffer.clear();
mMediaCodec.queueInputBuffer(bufferIndex, 0, data.length, System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME);
}
}
public void exit() {
isExit = true;
}
@SuppressLint("WrongConstant")
@Override
public void run() {
if (!isEncoderStart) {
startMediaCodec();
}
// 休眠200ms等待音频线程开启
// 否则视频第一秒会卡住
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// 如果编码器没有启动或者没有图像数据,线程阻塞等待
while (!isExit) {
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = 0;
byte[] mPpsSps = new byte[0];
byte[] h264 = new byte[mWidth * mHeight];
do {
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
// not expected for an encoder
outputBuffers = mMediaCodec.getOutputBuffers();
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
synchronized (H264EncodeConsumer.this) {
newFormat = mMediaCodec.getOutputFormat();
if (mMuxerRef != null) {
Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null) {
muxer.addTrack(newFormat, true);
}
}
}
} else if (outputBufferIndex < 0) {
// let's ignore it
} else {
ByteBuffer outputBuffer;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
} else {
outputBuffer = outputBuffers[outputBufferIndex];
}
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
boolean sync = false;
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// sps
sync = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
if (!sync) {
byte[] temp = new byte[bufferInfo.size];
outputBuffer.get(temp);
mPpsSps = temp;
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
continue;
} else {
mPpsSps = new byte[0];
}
}
sync |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
int len = mPpsSps.length + bufferInfo.size;
if (len > h264.length) {
h264 = new byte[len];
}
if (sync) {
System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length);
outputBuffer.get(h264, mPpsSps.length, bufferInfo.size);
if (listener != null) {
listener.onEncodeResult(h264, 0, mPpsSps.length + bufferInfo.size, bufferInfo.presentationTimeUs / 1000);
}
// 添加视频流到混合器
if (mMuxerRef != null) {
Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null) {
muxer.pumpStream(outputBuffer, bufferInfo, true);
}
isAddKeyFrame = true;
}
if (DEBUG)
Log.i(TAG, "关键帧 h264.length = " + h264.length + ";mPpsSps.length=" + mPpsSps.length
+ " bufferInfo.size = " + bufferInfo.size);
} else {
outputBuffer.get(h264, 0, bufferInfo.size);
if (listener != null) {
listener.onEncodeResult(h264, 0, bufferInfo.size, bufferInfo.presentationTimeUs / 1000);
}
// 添加视频流到混合器
if (isAddKeyFrame && mMuxerRef != null) {
Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null) {
muxer.pumpStream(outputBuffer, bufferInfo, true);
}
}
if (DEBUG)
Log.i(TAG, "普通帧 h264.length = " + h264.length + " bufferInfo.size = " + bufferInfo.size);
}
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
}
} while (!isExit && isEncoderStart);
}
stopMediaCodec();
}
private void startMediaCodec() {
final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
if (videoCodecInfo == null) {
Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
return;
}
final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mColorFormat);
format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
try {
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
} catch (IOException e) {
e.printStackTrace();
}
mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
isEncoderStart = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + 1) {
inputBuffers = outputBuffers = null;
} else {
inputBuffers = mMediaCodec.getInputBuffers();
outputBuffers = mMediaCodec.getOutputBuffers();
}
Bundle params = new Bundle();
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mMediaCodec.setParameters(params);
}
}
private void stopMediaCodec() {
isEncoderStart = false;
if (mMediaCodec != null) {
mMediaCodec.stop();
mMediaCodec.release();
Log.d(TAG, "关闭视频编码器");
}
}
private static final int FRAME_RATE = 15;
private static final float BPP = 0.50f;
private int calcBitRate() {
final int bitrate = (int) (BPP * FRAME_RATE * mWidth * mHeight);
Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
return bitrate;
}
/**
* select the first codec that match a specific MIME type
*
* @param mimeType
* @return null if no codec matched
*/
@SuppressWarnings("deprecation")
protected final MediaCodecInfo selectVideoCodec(final String mimeType) {
// get the list of available codecs
final int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) {
final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
if (!codecInfo.isEncoder()) { // skipp decoder
continue;
}
// select first codec that match a specific MIME type and color format
final String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
final int format = selectColorFormat(codecInfo, mimeType);
if (format > 0) {
mColorFormat = format;
return codecInfo;
}
}
}
}
return null;
}
/**
* select color format available on specific codec and we can use.
*
* @return 0 if no colorFormat is matched
*/
protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
int result = 0;
final MediaCodecInfo.CodecCapabilities caps;
try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
caps = codecInfo.getCapabilitiesForType(mimeType);
} finally {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
}
int colorFormat;
for (int i = 0; i < caps.colorFormats.length; i++) {
colorFormat = caps.colorFormats[i];
if (isRecognizedViewoFormat(colorFormat)) {
if (result == 0)
result = colorFormat;
break;
}
}
if (result == 0)
Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
return result;
}
/**
* color formats that we can use in this class
*/
protected static int[] recognizedFormats;
static {
recognizedFormats = new int[]{
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar,
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
// MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
};
}
private static final boolean isRecognizedViewoFormat(final int colorFormat) {
final int n = recognizedFormats != null ? recognizedFormats.length : 0;
for (int i = 0; i < n; i++) {
if (recognizedFormats[i] == colorFormat) {
return true;
}
}
return false;
}
private byte[] nv21ToI420(byte[] data, int width, int height) {
byte[] ret = new byte[width * height * 3 / 2];
int total = width * height;
ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量
ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量
ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量
// NV21 YYYYYYYY VUVU
bufferY.put(data, 0, total);
for (int i = total; i < data.length; i += 2) {
bufferV.put(data[i]);
bufferU.put(data[i + 1]);
}
return ret;
}
private byte[] nv12ToI420(byte[] data, int width, int height) {
byte[] ret = new byte[width * height * 3 / 2];
int total = width * height;
ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量
ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量
ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量
// NV12 YYYYYYYY UVUV
bufferY.put(data, 0, total);
for (int i = total; i < data.length; i += 2) {
bufferU.put(data[i]);
bufferV.put(data[i + 1]);
}
return ret;
}
private byte[] nv12ToNv21(byte[] data, int width, int height) {
byte[] ret = new byte[width * height * 3 / 2];
int total = width * height;
ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total); // I420的Y分量
ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4); // I420的U分量
ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4); // I420的V分量
// NV12 YYYYYYYY UVUV
bufferY.put(data, 0, total);
for (int i = total; i < data.length; i += 2) {
bufferU.put(data[i]);
bufferV.put(data[i + 1]);
}
return ret;
}
// YYYYYYYY UVUV(nv21)--> YYYYYYYY VUVU(nv12)
private byte[] nv21ToNV12(byte[] nv21, int width, int height) {
byte[] ret = new byte[width * height * 3 /2];
int framesize = width * height;
int i = 0, j = 0;
// 拷贝Y分量
System.arraycopy(nv21, 0,ret , 0, framesize);
// 拷贝UV分量
for (j = framesize; j < nv21.length; j += 2) {
ret[j+1] = nv21[j+1];
ret[j] = nv21[j];
}
return ret;
}
// YYYYYYYY UVUV(nv12)--> YYYYYYYY VUVU(nv21)
private byte[] nv12ToNV21(byte[] nv12, int width, int height) {
byte[] ret = new byte[width * height * 3 /2];
int framesize = width * height;
int i = 0, j = 0;
// 拷贝Y分量
System.arraycopy(nv12, 0,ret , 0, framesize);
// 拷贝UV分量
for (j = framesize; j < nv12.length; j += 2) {
ret[j] = nv12[j+1];
ret[j+1] = nv12[j];
}
return ret;
}
}

View File

@@ -0,0 +1,158 @@
package com.serenegiant.usb.encoder.biz;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Build;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
/**Mp4封装混合器
*
* Created by jianddongguo on 2017/7/28.
*/
public class Mp4MediaMuxer {
private static final boolean VERBOSE = false;
private static final String TAG = Mp4MediaMuxer.class.getSimpleName();
private String mFilePath;
private MediaMuxer mMuxer;
private long durationMillis;
private int index = 0;
private int mVideoTrackIndex = -1;
private int mAudioTrackIndex = -1;
private long mBeginMillis;
private MediaFormat mVideoFormat;
private MediaFormat mAudioFormat;
private boolean isVoiceClose;
// 文件路径;文件时长
public Mp4MediaMuxer(String path, long durationMillis,boolean isVoiceClose) {
String mFilePath;
this.isVoiceClose = isVoiceClose;
this.durationMillis = durationMillis;
if(durationMillis != 0) {
mFilePath = path + "-" + index++ + ".mp4";
}else{
mFilePath = path+".mp4";
}
Object mux = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mux = new MediaMuxer(mFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
mMuxer = (MediaMuxer) mux;
}
}
public synchronized void addTrack(MediaFormat format, boolean isVideo) {
// now that we have the Magic Goodies, start the muxer
if ((!isVoiceClose && mAudioTrackIndex != -1) && mVideoTrackIndex != -1)
throw new RuntimeException("already add all tracks");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
int track = mMuxer.addTrack(format);
if (VERBOSE)
Log.i(TAG, String.format("addTrack %s result %d", isVideo ? "video" : "audio", track));
if (isVideo) {
mVideoFormat = format;
mVideoTrackIndex = track;
// 当音频轨道添加
// 或者开启静音就start
if (isVoiceClose || mAudioTrackIndex != -1) {
if (VERBOSE)
Log.i(TAG, "both audio and video added,and muxer is started");
mMuxer.start();
mBeginMillis = System.currentTimeMillis();
}
} else {
mAudioFormat = format;
mAudioTrackIndex = track;
if (mVideoTrackIndex != -1) {
mMuxer.start();
mBeginMillis = System.currentTimeMillis();
}
}
}
}
public synchronized void pumpStream(ByteBuffer outputBuffer, MediaCodec.BufferInfo bufferInfo, boolean isVideo) {
if ((!isVoiceClose && mAudioTrackIndex == -1) || mVideoTrackIndex == -1) {
// Log.i(TAG, String.format("pumpStream [%s] but muxer is not start.ignore..", isVideo ? "video" : "audio"));
return;
}
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// The codec config data was pulled out and fed to the muxer when we got
// the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
} else if (bufferInfo.size != 0) {
if (isVideo && mVideoTrackIndex == -1) {
throw new RuntimeException("muxer hasn't started");
}
// adjust the ByteBuffer values to match BufferInfo (not needed?)
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mMuxer.writeSampleData(isVideo ? mVideoTrackIndex : mAudioTrackIndex, outputBuffer, bufferInfo);
}
// if (VERBOSE)
// Log.d(TAG, String.format("sent %s [" + bufferInfo.size + "] with timestamp:[%d] to muxer", isVideo ? "video" : "audio", bufferInfo.presentationTimeUs / 1000));
}
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
// if (VERBOSE)
// Log.i(TAG, "BUFFER_FLAG_END_OF_STREAM received");
}
if (durationMillis!=0 && System.currentTimeMillis() - mBeginMillis >= durationMillis) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
// if (VERBOSE)
// Log.i(TAG, String.format("record file reach expiration.create new file:" + index));
mMuxer.stop();
mMuxer.release();
mMuxer = null;
mVideoTrackIndex = mAudioTrackIndex = -1;
try {
mMuxer = new MediaMuxer(mFilePath + "-" + ++index + ".mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
addTrack(mVideoFormat, true);
addTrack(mAudioFormat, false);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public synchronized void release() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if (mMuxer != null) {
//(!isVoiceClose&&mAudioTrackIndex != -1)
if (mVideoTrackIndex != -1) {
if (VERBOSE)
Log.i(TAG, String.format("muxer is started. now it will be stoped."));
try {
mMuxer.stop();
mMuxer.release();
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
if (System.currentTimeMillis() - mBeginMillis <= 1500){
new File(mFilePath + "-" + index + ".mp4").delete();
}
mAudioTrackIndex = mVideoTrackIndex = -1;
}else{
if (VERBOSE)
Log.i(TAG, String.format("muxer is failed to be stoped."));
}
}
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
import com.serenegiant.widget.IAspectRatioView;
/**
* change the view size with keeping the specified aspect ratio.
* if you set this view with in a FrameLayout and set property "android:layout_gravity="center",
* you can show this view in the center of screen and keep the aspect ratio of content
* XXX it is better that can set the aspect ratio as xml property
*/
public class AspectRatioTextureView extends TextureView // API >= 14
implements IAspectRatioView {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "AbstractCameraView";
private double mRequestedAspect = -1.0;
private CameraViewInterface.Callback mCallback;
public AspectRatioTextureView(final Context context) {
this(context, null, 0);
}
public AspectRatioTextureView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public AspectRatioTextureView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
// 设置屏幕宽高比
@Override
public void setAspectRatio(final double aspectRatio) {
if (aspectRatio < 0) {
throw new IllegalArgumentException();
}
if (mRequestedAspect != aspectRatio) {
mRequestedAspect = aspectRatio;
requestLayout();
}
}
@Override
public void setAspectRatio(final int width, final int height) {
setAspectRatio(width / (double)height);
}
@Override
public double getAspectRatio() {
return mRequestedAspect;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mRequestedAspect > 0) {
int initialWidth = MeasureSpec.getSize(widthMeasureSpec);
int initialHeight = MeasureSpec.getSize(heightMeasureSpec);
final int horizPadding = getPaddingLeft() + getPaddingRight();
final int vertPadding = getPaddingTop() + getPaddingBottom();
initialWidth -= horizPadding;
initialHeight -= vertPadding;
final double viewAspectRatio = (double)initialWidth / initialHeight;
final double aspectDiff = mRequestedAspect / viewAspectRatio - 1;
if (Math.abs(aspectDiff) > 0.01) {
if (aspectDiff > 0) {
// width priority decision
initialHeight = (int) (initialWidth / mRequestedAspect);
} else {
// height priority decision
initialWidth = (int) (initialHeight * mRequestedAspect);
}
initialWidth += horizPadding;
initialHeight += vertPadding;
widthMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

View File

@@ -0,0 +1,47 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.widget;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import com.serenegiant.usb.encoder.IVideoEncoder;
import com.serenegiant.widget.IAspectRatioView;
public interface CameraViewInterface extends IAspectRatioView {
public interface Callback {
public void onSurfaceCreated(CameraViewInterface view, Surface surface);
public void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height);
public void onSurfaceDestroy(CameraViewInterface view, Surface surface);
}
public void onPause();
public void onResume();
public void setCallback(Callback callback);
public SurfaceTexture getSurfaceTexture();
public Surface getSurface();
public boolean hasSurface();
public void setVideoEncoder(final IVideoEncoder encoder);
public Bitmap captureStillImage(int width,int height);
}

View File

@@ -0,0 +1,598 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import com.serenegiant.glutils.EGLBase;
import com.serenegiant.glutils.GLDrawer2D;
import com.serenegiant.glutils.es1.GLHelper;
import com.serenegiant.usb.encoder.IVideoEncoder;
import com.serenegiant.usb.encoder.MediaEncoder;
import com.serenegiant.usb.encoder.MediaVideoEncoder;
import com.serenegiant.utils.FpsCounter;
/**
* change the view size with keeping the specified aspect ratio.
* if you set this view with in a FrameLayout and set property "android:layout_gravity="center",
* you can show this view in the center of screen and keep the aspect ratio of content
* XXX it is better that can set the aspect ratio as xml property
*/
public class UVCCameraTextureView extends AspectRatioTextureView // API >= 14
implements TextureView.SurfaceTextureListener, CameraViewInterface {
private static final boolean DEBUG = true; // TODO set false on release
private static final String TAG = "UVCCameraTextureView";
private boolean mHasSurface;
private RenderHandler mRenderHandler;
private final Object mCaptureSync = new Object();
private Bitmap mTempBitmap;
private boolean mReqesutCaptureStillImage;
private Callback mCallback;
// Camera分辨率宽度
/** for calculation of frame rate */
private final FpsCounter mFpsCounter = new FpsCounter();
public UVCCameraTextureView(final Context context) {
this(context, null, 0);
}
public UVCCameraTextureView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public UVCCameraTextureView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
setSurfaceTextureListener(this);
}
@Override
public void onResume() {
if (DEBUG) Log.v(TAG, "onResume:");
if (mHasSurface) {
mRenderHandler = RenderHandler.createHandler(mFpsCounter, super.getSurfaceTexture(), getWidth(), getHeight());
}
}
@Override
public void onPause() {
if (DEBUG) Log.v(TAG, "onPause:");
if (mRenderHandler != null) {
mRenderHandler.release();
mRenderHandler = null;
}
if (mTempBitmap != null) {
mTempBitmap.recycle();
mTempBitmap = null;
}
}
@Override
public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) {
if (DEBUG) Log.i(TAG, "onSurfaceTextureAvailable:" + surface);
if (mRenderHandler == null) {
mRenderHandler = RenderHandler.createHandler(mFpsCounter, surface, width, height);
} else {
mRenderHandler.resize(width, height);
}
mHasSurface = true;
if (mCallback != null) {
mCallback.onSurfaceCreated(this, getSurface());
}
}
@Override
public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) {
if (DEBUG) Log.i(TAG, "onSurfaceTextureSizeChanged:" + surface);
if (mRenderHandler != null) {
mRenderHandler.resize(width, height);
}
if (mCallback != null) {
mCallback.onSurfaceChanged(this, getSurface(), width, height);
}
}
@Override
public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) {
if (DEBUG) Log.i(TAG, "onSurfaceTextureDestroyed:" + surface);
if (mRenderHandler != null) {
mRenderHandler.release();
mRenderHandler = null;
}
mHasSurface = false;
if (mCallback != null) {
mCallback.onSurfaceDestroy(this, getSurface());
}
if (mPreviewSurface != null) {
mPreviewSurface.release();
mPreviewSurface = null;
}
return true;
}
@Override
public void onSurfaceTextureUpdated(final SurfaceTexture surface) {
synchronized (mCaptureSync) {
if (mReqesutCaptureStillImage) {
mReqesutCaptureStillImage = false;
if (mTempBitmap == null)
mTempBitmap = getBitmap();
else
getBitmap(mTempBitmap);
mCaptureSync.notifyAll();
}
}
}
@Override
public boolean hasSurface() {
return mHasSurface;
}
/**
* capture preview image as a bitmap
* this method blocks current thread until bitmap is ready
* if you call this method at almost same time from different thread,
* the returned bitmap will be changed while you are processing the bitmap
* (because we return same instance of bitmap on each call for memory saving)
* if you need to call this method from multiple thread,
* you should change this method(copy and return)
*/
@Override
public Bitmap captureStillImage(int width,int height) {
synchronized (mCaptureSync) {
mReqesutCaptureStillImage = true;
try {
mCaptureSync.wait();
} catch (final InterruptedException e) {
}
return mTempBitmap;
}
}
@Override
public SurfaceTexture getSurfaceTexture() {
return mRenderHandler != null ? mRenderHandler.getPreviewTexture() : null;
}
private Surface mPreviewSurface;
@Override
public Surface getSurface() {
if (DEBUG) Log.v(TAG, "getSurface:hasSurface=" + mHasSurface);
if (mPreviewSurface == null) {
final SurfaceTexture st = getSurfaceTexture();
if (st != null) {
mPreviewSurface = new Surface(st);
}
}
return mPreviewSurface;
}
@Override
public void setVideoEncoder(final IVideoEncoder encoder) {
if (mRenderHandler != null)
mRenderHandler.setVideoEncoder(encoder);
}
@Override
public void setCallback(final Callback callback) {
mCallback = callback;
}
public void resetFps() {
mFpsCounter.reset();
}
/** update frame rate of image processing */
public void updateFps() {
mFpsCounter.update();
}
/**
* get current frame rate of image processing
* @return
*/
public float getFps() {
return mFpsCounter.getFps();
}
/**
* get total frame rate from start
* @return
*/
public float getTotalFps() {
return mFpsCounter.getTotalFps();
}
/**
* render camera frames on this view on a private thread
* @author saki
*
*/
private static final class RenderHandler extends Handler
implements SurfaceTexture.OnFrameAvailableListener {
private static final int MSG_REQUEST_RENDER = 1;
private static final int MSG_SET_ENCODER = 2;
private static final int MSG_CREATE_SURFACE = 3;
private static final int MSG_RESIZE = 4;
private static final int MSG_TERMINATE = 9;
private RenderThread mThread;
private boolean mIsActive = true;
private final FpsCounter mFpsCounter;
public static final RenderHandler createHandler(final FpsCounter counter,
final SurfaceTexture surface, final int width, final int height) {
final RenderThread thread = new RenderThread(counter, surface, width, height);
thread.start();
return thread.getHandler();
}
private RenderHandler(final FpsCounter counter, final RenderThread thread) {
mThread = thread;
mFpsCounter = counter;
}
public final void setVideoEncoder(final IVideoEncoder encoder) {
if (DEBUG) Log.v(TAG, "setVideoEncoder:");
if (mIsActive)
sendMessage(obtainMessage(MSG_SET_ENCODER, encoder));
}
public final SurfaceTexture getPreviewTexture() {
if (DEBUG) Log.v(TAG, "getPreviewTexture:");
if (mIsActive) {
synchronized (mThread.mSync) {
sendEmptyMessage(MSG_CREATE_SURFACE);
try {
mThread.mSync.wait();
} catch (final InterruptedException e) {
}
return mThread.mPreviewSurface;
}
} else {
return null;
}
}
public void resize(final int width, final int height) {
if (DEBUG) Log.v(TAG, "resize:");
if (mIsActive) {
synchronized (mThread.mSync) {
sendMessage(obtainMessage(MSG_RESIZE, width, height));
try {
mThread.mSync.wait();
} catch (final InterruptedException e) {
}
}
}
}
public final void release() {
if (DEBUG) Log.v(TAG, "release:");
if (mIsActive) {
mIsActive = false;
removeMessages(MSG_REQUEST_RENDER);
removeMessages(MSG_SET_ENCODER);
sendEmptyMessage(MSG_TERMINATE);
}
}
@Override
public final void onFrameAvailable(final SurfaceTexture surfaceTexture) {
if (mIsActive) {
mFpsCounter.count();
sendEmptyMessage(MSG_REQUEST_RENDER);
}
}
@Override
public final void handleMessage(final Message msg) {
if (mThread == null) return;
switch (msg.what) {
case MSG_REQUEST_RENDER:
mThread.onDrawFrame();
break;
case MSG_SET_ENCODER:
mThread.setEncoder((MediaEncoder)msg.obj);
break;
case MSG_CREATE_SURFACE:
mThread.updatePreviewSurface();
break;
case MSG_RESIZE:
mThread.resize(msg.arg1, msg.arg2);
break;
case MSG_TERMINATE:
Looper.myLooper().quit();
mThread = null;
break;
default:
super.handleMessage(msg);
}
}
private static final class RenderThread extends Thread {
private final Object mSync = new Object();
private final SurfaceTexture mSurface;
private RenderHandler mHandler;
private EGLBase mEgl;
/** IEglSurface instance related to this TextureView */
private EGLBase.IEglSurface mEglSurface;
private GLDrawer2D mDrawer;
private int mTexId = -1;
/** SurfaceTexture instance to receive video images */
private SurfaceTexture mPreviewSurface;
private final float[] mStMatrix = new float[16];
private MediaEncoder mEncoder;
private int mViewWidth, mViewHeight;
private final FpsCounter mFpsCounter;
/**
* constructor
* @param surface: drawing surface came from TexureView
*/
public RenderThread(final FpsCounter fpsCounter, final SurfaceTexture surface, final int width, final int height) {
mFpsCounter = fpsCounter;
mSurface = surface;
mViewWidth = width;
mViewHeight = height;
setName("RenderThread");
}
public final RenderHandler getHandler() {
if (DEBUG) Log.v(TAG, "RenderThread#getHandler:");
synchronized (mSync) {
// create rendering thread
if (mHandler == null)
try {
mSync.wait();
} catch (final InterruptedException e) {
}
}
return mHandler;
}
public void resize(final int width, final int height) {
if (((width > 0) && (width != mViewWidth)) || ((height > 0) && (height != mViewHeight))) {
mViewWidth = width;
mViewHeight = height;
updatePreviewSurface();
} else {
synchronized (mSync) {
mSync.notifyAll();
}
}
}
public final void updatePreviewSurface() {
if (DEBUG) Log.i(TAG, "RenderThread#updatePreviewSurface:");
synchronized (mSync) {
if (mPreviewSurface != null) {
if (DEBUG) Log.d(TAG, "updatePreviewSurface:release mPreviewSurface");
mPreviewSurface.setOnFrameAvailableListener(null);
mPreviewSurface.release();
mPreviewSurface = null;
}
mEglSurface.makeCurrent();
if (mTexId >= 0) {
mDrawer.deleteTex(mTexId);
}
// create texture and SurfaceTexture for input from camera
mTexId = mDrawer.initTex();
if (DEBUG) Log.v(TAG, "updatePreviewSurface:tex_id=" + mTexId);
mPreviewSurface = new SurfaceTexture(mTexId);
mPreviewSurface.setDefaultBufferSize(mViewWidth, mViewHeight);
mPreviewSurface.setOnFrameAvailableListener(mHandler);
// notify to caller thread that previewSurface is ready
mSync.notifyAll();
}
}
public final void setEncoder(final MediaEncoder encoder) {
if (DEBUG) Log.v(TAG, "RenderThread#setEncoder:encoder=" + encoder);
if (encoder != null && (encoder instanceof MediaVideoEncoder)) {
((MediaVideoEncoder)encoder).setEglContext(mEglSurface.getContext(), mTexId);
}
mEncoder = encoder;
}
/*
* Now you can get frame data as ByteBuffer(as YUV/RGB565/RGBX/NV21 pixel format) using IFrameCallback interface
* with UVCCamera#setFrameCallback instead of using following code samples.
*/
/* // for part1
private static final int BUF_NUM = 1;
private static final int BUF_STRIDE = 640 * 480;
private static final int BUF_SIZE = BUF_STRIDE * BUF_NUM;
int cnt = 0;
int offset = 0;
final int pixels[] = new int[BUF_SIZE];
final IntBuffer buffer = IntBuffer.wrap(pixels); */
/* // for part2
private ByteBuffer buf = ByteBuffer.allocateDirect(640 * 480 * 4);
*/
/**
* draw a frame (and request to draw for video capturing if it is necessary)
*/
public final void onDrawFrame() {
mEglSurface.makeCurrent();
// update texture(came from camera)
mPreviewSurface.updateTexImage();
// get texture matrix
mPreviewSurface.getTransformMatrix(mStMatrix);
// notify video encoder if it exist
if (mEncoder != null) {
// notify to capturing thread that the camera frame is available.
if (mEncoder instanceof MediaVideoEncoder)
((MediaVideoEncoder)mEncoder).frameAvailableSoon(mStMatrix);
else
mEncoder.frameAvailableSoon();
}
// draw to preview screen
mDrawer.draw(mTexId, mStMatrix, 0);
mEglSurface.swap();
/* // sample code to read pixels into Buffer and save as a Bitmap (part1)
buffer.position(offset);
GLES20.glReadPixels(0, 0, 640, 480, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
if (++cnt == 100) { // save as a Bitmap, only once on this sample code
// if you save every frame as a Bitmap, app will crash by Out of Memory exception...
Log.i(TAG, "Capture image using glReadPixels:offset=" + offset);
final Bitmap bitmap = createBitmap(pixels,offset, 640, 480);
final File outputFile = MediaMuxerWrapper.getCaptureFile(Environment.DIRECTORY_DCIM, ".png");
try {
final BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
try {
try {
bitmap.compress(CompressFormat.PNG, 100, os);
os.flush();
bitmap.recycle();
} catch (IOException e) {
}
} finally {
os.close();
}
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
offset = (offset + BUF_STRIDE) % BUF_SIZE;
*/
/* // sample code to read pixels into Buffer and save as a Bitmap (part2)
buf.order(ByteOrder.LITTLE_ENDIAN); // it is enough to call this only once.
GLES20.glReadPixels(0, 0, 640, 480, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
buf.rewind();
if (++cnt == 100) { // save as a Bitmap, only once on this sample code
// if you save every frame as a Bitmap, app will crash by Out of Memory exception...
final File outputFile = MediaMuxerWrapper.getCaptureFile(Environment.DIRECTORY_DCIM, ".png");
BufferedOutputStream os = null;
try {
try {
os = new BufferedOutputStream(new FileOutputStream(outputFile));
Bitmap bmp = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
bmp.copyPixelsFromBuffer(buf);
bmp.compress(Bitmap.CompressFormat.PNG, 90, os);
bmp.recycle();
} finally {
if (os != null) os.close();
}
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
*/
}
/* // sample code to read pixels into IntBuffer and save as a Bitmap (part1)
private static Bitmap createBitmap(final int[] pixels, final int offset, final int width, final int height) {
final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
paint.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(new float[] {
0, 0, 1, 0, 0,
0, 1, 0, 0, 0,
1, 0, 0, 0, 0,
0, 0, 0, 1, 0
})));
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
final Matrix matrix = new Matrix();
matrix.postScale(1.0f, -1.0f);
matrix.postTranslate(0, height);
canvas.concat(matrix);
canvas.drawBitmap(pixels, offset, width, 0, 0, width, height, false, paint);
return bitmap;
} */
@Override
public final void run() {
Log.d(TAG, getName() + " started");
init();
Looper.prepare();
synchronized (mSync) {
mHandler = new RenderHandler(mFpsCounter, this);
mSync.notify();
}
Looper.loop();
Log.d(TAG, getName() + " finishing");
release();
synchronized (mSync) {
mHandler = null;
mSync.notify();
}
}
private final void init() {
if (DEBUG) Log.v(TAG, "RenderThread#init:");
// create EGLContext for this thread
mEgl = EGLBase.createFrom(null, false, false);
mEglSurface = mEgl.createFromSurface(mSurface);
mEglSurface.makeCurrent();
// create drawing object
mDrawer = new GLDrawer2D(true);
}
private final void release() {
if (DEBUG) Log.v(TAG, "RenderThread#release:");
if (mDrawer != null) {
mDrawer.release();
mDrawer = null;
}
if (mPreviewSurface != null) {
mPreviewSurface.release();
mPreviewSurface = null;
}
if (mTexId >= 0) {
GLHelper.deleteTex(mTexId);
mTexId = -1;
}
if (mEglSurface != null) {
mEglSurface.release();
mEglSurface = null;
}
if (mEgl != null) {
mEgl.release();
mEgl = null;
}
}
}
}
}

View File

@@ -0,0 +1,61 @@
package org.easydarwin.sw;
/**
*/
public class JNIUtil {
static {
System.loadLibrary("Utils");
}
/**
* 都是YUV = 411但 U与 V顺序相反。变换可逆
*
* @param buffer
* @param width
* @param height
*/
public static void yV12ToYUV420P(byte[] buffer, int width, int height) {
callMethod("YV12ToYUV420P", null, buffer, width, height);
}
/**
* 都是YU+V = 42,但是这两者U、V方向相反。变换可逆
*
* @param buffer
* @param width
* @param height
*/
public static void nV21To420SP(byte[] buffer, int width, int height) {
callMethod("NV21To420SP", null, buffer, width, height);
}
/**
* 旋转1个字节为单位的矩阵
*
* @param data 要旋转的矩阵
* @param offset 偏移量
* @param width 宽度
* @param height 高度
* @param degree 旋转度数
*/
public static void rotateMatrix(byte[] data, int offset, int width, int height, int degree) {
callMethod("RotateByteMatrix", null, data, offset, width, height, degree);
}
/**
* 旋转2个字节为单位的矩阵
*
* @param data 要旋转的矩阵
* @param offset 偏移量
* @param width 宽度
* @param height 高度
* @param degree 旋转度数
*/
public static void rotateShortMatrix(byte[] data, int offset, int width, int height, int degree) {
callMethod("RotateShortMatrix", null, data, offset, width, height, degree);
}
private static native void callMethod(String methodName, Object[] returnValue, Object... params);
}

View File

@@ -0,0 +1,93 @@
package org.easydarwin.sw;
import android.content.Context;
import android.content.res.AssetManager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by jiangdg on 2020/1/11.
*/
public class TxtOverlay {
static {
System.loadLibrary("TxtOverlay");
}
private static TxtOverlay instance;
private final Context context;
private long ctx;
private TxtOverlay(Context context){
this.context = context;
}
public static TxtOverlay getInstance() {
if(instance == null) {
throw new IllegalArgumentException("please call install in your application!");
}
return instance;
}
public static void install(Context context) {
if(instance == null) {
instance = new TxtOverlay(context.getApplicationContext());
File youyuan = context.getFileStreamPath("SIMYOU.ttf");
if (!youyuan.exists()){
AssetManager am = context.getAssets();
try {
InputStream is = am.open("zk/SIMYOU.ttf");
FileOutputStream os = context.openFileOutput("SIMYOU.ttf", Context.MODE_PRIVATE);
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void init(int width, int height) {
File youyuan = context.getFileStreamPath("SIMYOU.ttf");
if (!youyuan.exists()){
throw new IllegalArgumentException("the font file must be exists,please call install before!");
}
ctx = txtOverlayInit(width, height,youyuan.getAbsolutePath());
}
public void overlay(byte[] data,
String txt) {
// txt = "drawtext=fontfile="+context.getFileStreamPath("SIMYOU.ttf")+": text='EasyPusher 2017':x=(w-text_w)/2:y=H-60 :fontcolor=white :box=1:boxcolor=0x00000000@0.3";
// txt = "movie=/sdcard/qrcode.png [logo];[in][logo] "
// + "overlay=" + 0 + ":" + 0
// + " [out]";
// if (ctx == 0) throw new RuntimeException("init should be called at first!");
if (ctx == 0) return;
txtOverlay(ctx, data, txt);
}
public void release() {
if (ctx == 0) return;
txtOverlayRelease(ctx);
ctx = 0;
}
private static native long txtOverlayInit(int width, int height, String fonts);
private static native void txtOverlay(long ctx, byte[] data, String txt);
private static native void txtOverlayRelease(long ctx);
}

Binary file not shown.

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/vertical_margin"
android:paddingLeft="@dimen/horizontal_margin"
android:paddingRight="@dimen/horizontal_margin"
android:paddingTop="@dimen/vertical_margin"
tools:context="com.serenegiant.usb.CameraDialog" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/camera" />
<Spinner
android:id="@+id/spinner1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@id/android:empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/no_device" />
</LinearLayout>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/list_height_min"
android:textSize="@dimen/list_font_size" >
</CheckedTextView>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ UVCCamera
~ library and sample to access to UVC web camera on non-rooted Android device
~
~ Copyright (c) 2014-2017 saki t_saki@serenegiant.com
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
~ All files in the folder are under this Apache License, Version 2.0.
~ Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
~ may have a different license, see the respective files.
-->
<resources>
<dimen name="horizontal_margin">16dp</dimen>
<dimen name="vertical_margin">16dp</dimen>
<dimen name="button_size">48dp</dimen>
<dimen name="list_font_size">18sp</dimen>
<dimen name="list_height_min">32dp</dimen>
</resources>

View File

@@ -0,0 +1,7 @@
<resources>
<string name="app_name">libusbcamera</string>
<string name="select">请选择USB摄像头</string>
<string name="refresh">刷新</string>
<string name="camera">Camera</string>
<string name="no_device">未搜索到可用USB Camera设备</string>
</resources>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ UVCCamera
~ library and sample to access to UVC web camera on non-rooted Android device
~
~ Copyright (c) 2014-2017 saki t_saki@serenegiant.com
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
~ All files in the folder are under this Apache License, Version 2.0.
~ Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
~ may have a different license, see the respective files.
-->
<usb>
<usb-device class="239" subclass="2" /> <!-- all device of UVC -->
<!-- a few android 9.0 -->
<usb-device class="14" subclass="9" />
<usb-device class="2" subclass="0" />
<usb-device class="6" subclass="-1" />
<usb-device class="39" subclass="0" />
<usb-device product-id="4836" vendor-id="9573" />
<usb-device product-id="2229" vendor-id="1133" />
<usb-device product-id="640" vendor-id="1409" />
<usb-device product-id="258" vendor-id="9228" />
</usb>

View File

@@ -14,6 +14,7 @@
:foudations:mogo-commons
:tts:tts-di
:tts:tts-noop
:libraries:map-usbcamera
:libraries:map-autonavi
:libraries:map-custom
:libraries:mogo-map

View File

@@ -52,6 +52,7 @@ include ':foudations:mogo-utils'
include ':foudations:mogo-commons'
// 基础库
include ':libraries:map-usbcamera'
include ':libraries:map-custom'
include ':libraries:mogo-map-api'
include ':libraries:map-autonavi'