Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
O
opencv
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
opencv
Commits
a32004f9
Commit
a32004f9
authored
Sep 28, 2012
by
Andrey Pavlenko
Committed by
Andrey Kamaev
Oct 23, 2012
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
adding initial version of a sample with new Camera-View handling design
parent
769f61f8
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
620 additions
and
0 deletions
+620
-0
CMakeLists.txt
samples/android/CMakeLists.txt
+2
-0
.classpath
samples/android/camera-preview/.classpath
+8
-0
.project
samples/android/camera-preview/.project
+33
-0
org.eclipse.jdt.core.prefs
...droid/camera-preview/.settings/org.eclipse.jdt.core.prefs
+5
-0
AndroidManifest.xml
samples/android/camera-preview/AndroidManifest.xml
+28
-0
CMakeLists.txt
samples/android/camera-preview/CMakeLists.txt
+6
-0
ic_action_search.png
.../android/camera-preview/res/drawable/ic_action_search.png
+0
-0
ic_launcher.png
samples/android/camera-preview/res/drawable/ic_launcher.png
+0
-0
activity_camera_writer.xml
...roid/camera-preview/res/layout/activity_camera_writer.xml
+13
-0
activity_camera_writer.xml
...ndroid/camera-preview/res/menu/activity_camera_writer.xml
+6
-0
styles.xml
samples/android/camera-preview/res/values-v11/styles.xml
+6
-0
strings.xml
samples/android/camera-preview/res/values/strings.xml
+8
-0
styles.xml
samples/android/camera-preview/res/values/styles.xml
+6
-0
CameraWriterActivity.java
...rc/org/opencv/test/camerawriter/CameraWriterActivity.java
+72
-0
OpenCvCameraBridgeViewBase.java
.../opencv/test/camerawriter/OpenCvCameraBridgeViewBase.java
+301
-0
OpenCvNativeCameraView.java
.../org/opencv/test/camerawriter/OpenCvNativeCameraView.java
+126
-0
No files found.
samples/android/CMakeLists.txt
View file @
a32004f9
...
...
@@ -17,6 +17,8 @@ add_subdirectory(tutorial-2-opencvcamera)
add_subdirectory
(
tutorial-3-native
)
add_subdirectory
(
tutorial-4-mixed
)
add_subdirectory
(
camera-preview
)
#hello-android sample
if
(
HAVE_opencv_highgui
)
ocv_include_modules_recurse
(
opencv_highgui opencv_core
)
...
...
samples/android/camera-preview/.classpath
0 → 100644
View file @
a32004f9
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry
kind=
"src"
path=
"src"
/>
<classpathentry
kind=
"src"
path=
"gen"
/>
<classpathentry
kind=
"con"
path=
"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"
/>
<classpathentry
kind=
"con"
path=
"com.android.ide.eclipse.adt.LIBRARIES"
/>
<classpathentry
kind=
"output"
path=
"bin/classes"
/>
</classpath>
samples/android/camera-preview/.project
0 → 100644
View file @
a32004f9
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>
CameraWriter
</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>
com.android.ide.eclipse.adt.ResourceManagerBuilder
</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>
com.android.ide.eclipse.adt.PreCompilerBuilder
</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>
org.eclipse.jdt.core.javabuilder
</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>
com.android.ide.eclipse.adt.ApkBuilder
</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>
com.android.ide.eclipse.adt.AndroidNature
</nature>
<nature>
org.eclipse.jdt.core.javanature
</nature>
</natures>
</projectDescription>
samples/android/camera-preview/.settings/org.eclipse.jdt.core.prefs
0 → 100644
View file @
a32004f9
#Wed Jun 29 04:36:40 MSD 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.source=1.5
samples/android/camera-preview/AndroidManifest.xml
0 → 100644
View file @
a32004f9
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"org.opencv.test.camerawriter"
android:versionCode=
"1"
android:versionName=
"1.0"
>
<uses-permission
android:name=
"android.permission.CAMERA"
/>
<uses-feature
android:name=
"android.hardware.camera"
/>
<uses-feature
android:name=
"android.hardware.camera.autofocus"
/>
<uses-sdk
android:minSdkVersion=
"8"
/>
<application
android:icon=
"@drawable/ic_launcher"
android:label=
"@string/app_name"
android:theme=
"@style/AppTheme"
>
<activity
android:name=
".CameraWriterActivity"
android:label=
"@string/title_activity_camera_writer"
>
<intent-filter>
<action
android:name=
"android.intent.action.MAIN"
/>
<category
android:name=
"android.intent.category.LAUNCHER"
/>
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
samples/android/camera-preview/CMakeLists.txt
0 → 100644
View file @
a32004f9
set
(
sample example-camera-preview
)
add_android_project
(
${
sample
}
"
${
CMAKE_CURRENT_SOURCE_DIR
}
"
LIBRARY_DEPS
${
OpenCV_BINARY_DIR
}
SDK_TARGET 11
${
ANDROID_SDK_TARGET
}
)
if
(
TARGET
${
sample
}
)
add_dependencies
(
opencv_android_examples
${
sample
}
)
endif
()
samples/android/camera-preview/res/drawable/ic_action_search.png
0 → 100644
View file @
a32004f9
3.05 KB
samples/android/camera-preview/res/drawable/ic_launcher.png
0 → 100644
View file @
a32004f9
2.94 KB
samples/android/camera-preview/res/layout/activity_camera_writer.xml
0 → 100644
View file @
a32004f9
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<view
class=
"org.opencv.test.camerawriter.OpenCvNativeCameraView"
android:id=
"@+id/camera_surface_view"
android:layout_width =
"fill_parent"
android:layout_height=
"fill_parent"
/>
</RelativeLayout>
samples/android/camera-preview/res/menu/activity_camera_writer.xml
0 → 100644
View file @
a32004f9
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:id=
"@+id/menu_settings"
android:title=
"@string/menu_settings"
android:orderInCategory=
"100"
android:showAsAction=
"never"
/>
</menu>
samples/android/camera-preview/res/values-v11/styles.xml
0 → 100644
View file @
a32004f9
<resources>
<style
name=
"AppTheme"
parent=
"android:Theme.Holo.Light"
/>
</resources>
\ No newline at end of file
samples/android/camera-preview/res/values/strings.xml
0 → 100644
View file @
a32004f9
<resources>
<string
name=
"app_name"
>
CameraWriter
</string>
<string
name=
"menu_settings"
>
Settings
</string>
<string
name=
"title_activity_camera_writer"
>
CameraWriterActivity
</string>
</resources>
\ No newline at end of file
samples/android/camera-preview/res/values/styles.xml
0 → 100644
View file @
a32004f9
<resources>
<style
name=
"AppTheme"
parent=
"android:Theme.Light"
/>
</resources>
\ No newline at end of file
samples/android/camera-preview/src/org/opencv/test/camerawriter/CameraWriterActivity.java
0 → 100644
View file @
a32004f9
package
org
.
opencv
.
test
.
camerawriter
;
import
org.opencv.android.BaseLoaderCallback
;
import
org.opencv.android.LoaderCallbackInterface
;
import
org.opencv.android.OpenCVLoader
;
import
org.opencv.core.Mat
;
import
org.opencv.test.camerawriter.OpenCvCameraBridgeViewBase.CvCameraViewListener
;
import
android.os.Bundle
;
import
android.app.Activity
;
import
android.util.Log
;
import
android.view.Menu
;
public
class
CameraWriterActivity
extends
Activity
implements
CvCameraViewListener
{
protected
static
final
String
TAG
=
"CameraWriterActivity"
;
private
OpenCvCameraBridgeViewBase
mCameraView
;
private
BaseLoaderCallback
mLoaderCallback
=
new
BaseLoaderCallback
(
this
)
{
@Override
public
void
onManagerConnected
(
int
status
)
{
switch
(
status
)
{
case
LoaderCallbackInterface
.
SUCCESS
:
Log
.
i
(
TAG
,
"OpenCV loaded successfully"
);
// Create and set View
mCameraView
.
setMaxFrameSize
(
640
,
480
);
mCameraView
.
enableView
();
break
;
default
:
super
.
onManagerConnected
(
status
);
break
;
}
}
};
@Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setContentView
(
R
.
layout
.
activity_camera_writer
);
mCameraView
=
(
OpenCvCameraBridgeViewBase
)
findViewById
(
R
.
id
.
camera_surface_view
);
mCameraView
.
setCvCameraViewListener
(
this
);
OpenCVLoader
.
initAsync
(
OpenCVLoader
.
OPENCV_VERSION_2_4_2
,
this
,
mLoaderCallback
);
}
@Override
public
boolean
onCreateOptionsMenu
(
Menu
menu
)
{
getMenuInflater
().
inflate
(
R
.
menu
.
activity_camera_writer
,
menu
);
return
true
;
}
@Override
public
void
onCameraViewStarted
(
int
width
,
int
height
)
{
// TODO Auto-generated method stub
}
@Override
public
void
onCameraViewStopped
()
{
// TODO Auto-generated method stub
}
@Override
public
Mat
onCameraFrame
(
Mat
inputFrame
)
{
return
inputFrame
;
}
}
samples/android/camera-preview/src/org/opencv/test/camerawriter/OpenCvCameraBridgeViewBase.java
0 → 100644
View file @
a32004f9
package
org
.
opencv
.
test
.
camerawriter
;
import
java.util.List
;
import
org.opencv.android.Utils
;
import
org.opencv.core.Mat
;
import
org.opencv.core.Size
;
import
org.opencv.highgui.Highgui
;
import
org.opencv.highgui.VideoCapture
;
import
android.content.Context
;
import
android.graphics.Bitmap
;
import
android.graphics.Canvas
;
import
android.util.AttributeSet
;
import
android.util.Log
;
import
android.view.SurfaceHolder
;
import
android.view.SurfaceView
;
/**
* This is a basic class, implementing the interaction with Camera and OpenCV library.
* The main responsibility of it - is to control when camera can be enabled, process the frame,
* call external listener to make any adjustments to the frame and then draw the resulting
* frame to the screen.
* The clients shall implement CvCameraViewListener
*/
public
abstract
class
OpenCvCameraBridgeViewBase
extends
SurfaceView
implements
SurfaceHolder
.
Callback
{
private
static
final
int
MAX_UNSPECIFIED
=
-
1
;
protected
int
mFrameWidth
;
protected
int
mFrameHeight
;
protected
int
mMaxHeight
;
protected
int
mMaxWidth
;
private
Bitmap
mCacheBitmap
;
public
OpenCvCameraBridgeViewBase
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
getHolder
().
addCallback
(
this
);
mMaxWidth
=
MAX_UNSPECIFIED
;
mMaxHeight
=
MAX_UNSPECIFIED
;
}
public
interface
CvCameraViewListener
{
/**
* This method is invoked when camera preview has started. After this method is invoked
* the frames will start to be delivered to client via the onCameraFrame() callback.
* @param width - the width of the frames that will be delivered
* @param height - the height of the frames that will be delivered
*/
public
void
onCameraViewStarted
(
int
width
,
int
height
);
/**
* This method is invoked when camera preview has been stopped for some reason.
* No frames will be delivered via onCameraFrame() callback after this method is called.
*/
public
void
onCameraViewStopped
();
/**
* This method is invoked when delivery of the frame needs to be done.
* The returned values - is a modified frame which needs to be displayed on the screen.
*/
public
Mat
onCameraFrame
(
Mat
inputFrame
);
}
private
static
final
int
STOPPED
=
0
;
private
static
final
int
STARTED
=
1
;
private
static
final
String
TAG
=
"SampleCvBase"
;
private
CvCameraViewListener
mListener
;
private
int
mState
=
STOPPED
;
private
boolean
mEnabled
;
private
boolean
mSurfaceExist
;
private
Object
mSyncObject
=
new
Object
();
public
void
surfaceChanged
(
SurfaceHolder
arg0
,
int
arg1
,
int
arg2
,
int
arg3
)
{
synchronized
(
mSyncObject
)
{
if
(!
mSurfaceExist
)
{
mSurfaceExist
=
true
;
checkCurrentState
();
}
else
{
/** Surface changed. We need to stop camera and restart with new parameters */
/* Pretend that old surface has been destroyed */
mSurfaceExist
=
false
;
checkCurrentState
();
/* Now use new surface. Say we have it now */
mSurfaceExist
=
true
;
checkCurrentState
();
}
}
}
public
void
surfaceCreated
(
SurfaceHolder
holder
)
{
/* Do nothing. Wait until surfaceChanged delivered */
}
public
void
surfaceDestroyed
(
SurfaceHolder
holder
)
{
synchronized
(
mSyncObject
)
{
mSurfaceExist
=
false
;
checkCurrentState
();
}
}
/**
* This method is provided for clients, so they can enable the camera connection.
* The actuall onCameraViewStarted callback will be delivered only after both this method is called and surface is available
*/
public
void
enableView
()
{
synchronized
(
mSyncObject
)
{
mEnabled
=
true
;
checkCurrentState
();
}
}
/**
* This method is provided for clients, so they can disable camera connection and stop
* the delivery of frames eventhough the surfaceview itself is not destroyed and still stays on the scren
*/
public
void
disableView
()
{
synchronized
(
mSyncObject
)
{
mEnabled
=
false
;
checkCurrentState
();
}
}
public
void
setCvCameraViewListener
(
CvCameraViewListener
listener
)
{
mListener
=
listener
;
}
/**
* This method sets the maximum size that camera frame is allowed to be. When selecting
* size - the biggest size which less or equal the size set will be selected.
* As an example - we set setMaxFrameSize(200,200) and we have 176x152 and 320x240 sizes. The
* preview frame will be selected with 176x152 size.
* This method is usefull when need to restrict the size of preview frame for some reason (for example for video recording)
* @param maxWidth - the maximum width allowed for camera frame.
* @param maxHeight - the maxumum height allowed for camera frame
*/
public
void
setMaxFrameSize
(
int
maxWidth
,
int
maxHeight
)
{
mMaxWidth
=
maxWidth
;
mMaxHeight
=
maxHeight
;
}
/**
* Called when mSyncObject lock is held
*/
private
void
checkCurrentState
()
{
int
targetState
;
if
(
mEnabled
&&
mSurfaceExist
)
{
targetState
=
STARTED
;
}
else
{
targetState
=
STOPPED
;
}
if
(
targetState
!=
mState
)
{
/* The state change detected. Need to exit the current state and enter target state */
processExitState
(
mState
);
mState
=
targetState
;
processEnterState
(
mState
);
}
}
private
void
processEnterState
(
int
state
)
{
switch
(
state
)
{
case
STARTED:
onEnterStartedState
();
if
(
mListener
!=
null
)
{
mListener
.
onCameraViewStarted
(
mFrameWidth
,
mFrameHeight
);
}
break
;
case
STOPPED:
onEnterStoppedState
();
if
(
mListener
!=
null
)
{
mListener
.
onCameraViewStopped
();
}
break
;
};
}
private
void
processExitState
(
int
state
)
{
switch
(
state
)
{
case
STARTED:
onExitStartedState
();
break
;
case
STOPPED:
onExitStoppedState
();
break
;
};
}
private
void
onEnterStoppedState
()
{
/* nothing to do */
}
private
void
onExitStoppedState
()
{
/* nothing to do */
}
private
void
onEnterStartedState
()
{
connectCamera
(
getWidth
(),
getHeight
());
/* Now create cahe Bitmap */
mCacheBitmap
=
Bitmap
.
createBitmap
(
mFrameWidth
,
mFrameHeight
,
Bitmap
.
Config
.
ARGB_8888
);
}
private
void
onExitStartedState
()
{
disconnectCamera
();
if
(
mCacheBitmap
!=
null
)
{
mCacheBitmap
.
recycle
();
}
}
/**
* This method shall be called by the subclasses when they have valid
* object and want it to be delivered to external client (via callback) and
* then displayed on the screen.
* @param frame - the current frame to be delivered
*/
protected
void
deliverAndDrawFrame
(
Mat
frame
)
{
Mat
modified
;
synchronized
(
mSyncObject
)
{
if
(
mListener
!=
null
)
{
modified
=
mListener
.
onCameraFrame
(
frame
);
}
else
{
modified
=
frame
;
}
if
(
modified
!=
null
)
{
Utils
.
matToBitmap
(
modified
,
mCacheBitmap
);
}
if
(
mCacheBitmap
!=
null
)
{
Canvas
canvas
=
getHolder
().
lockCanvas
();
if
(
canvas
!=
null
)
{
canvas
.
drawBitmap
(
mCacheBitmap
,
(
canvas
.
getWidth
()
-
mCacheBitmap
.
getWidth
())
/
2
,
(
canvas
.
getHeight
()
-
mCacheBitmap
.
getHeight
())
/
2
,
null
);
getHolder
().
unlockCanvasAndPost
(
canvas
);
}
}
}
}
/**
* This method is invoked shall perform concrete operation to initialize the camera.
* CONTRACT: as a result of this method variables mFrameWidth and mFrameHeight MUST be
* initialized with the size of the Camera frames that will be delivered to external processor.
* @param width - the width of this SurfaceView
* @param height - the height of this SurfaceView
*/
protected
abstract
void
connectCamera
(
int
width
,
int
height
);
/**
* Disconnects and release the particular camera object beeing connected to this surface view.
* Called when syncObject lock is held
*/
protected
abstract
void
disconnectCamera
();
/**
* This helper method can be called by subclasses to select camera preview size.
* It goes over the list of the supported preview sizes and selects the maximum one which
* fits both values set via setMaxFrameSize() and surface frame allocated for this view
* @param supportedSizes
* @param surfaceWidth
* @param surfaceHeight
* @return
*/
protected
Size
calculateCameraFrameSize
(
List
<
Size
>
supportedSizes
,
int
surfaceWidth
,
int
surfaceHeight
)
{
int
calcWidth
=
0
;
int
calcHeight
=
0
;
int
maxAllowedWidth
=
(
mMaxWidth
!=
MAX_UNSPECIFIED
&&
mMaxWidth
<
surfaceWidth
)?
mMaxWidth
:
surfaceWidth
;
int
maxAllowedHeight
=
(
mMaxHeight
!=
MAX_UNSPECIFIED
&&
mMaxHeight
<
surfaceHeight
)?
mMaxHeight
:
surfaceHeight
;
for
(
Size
size
:
supportedSizes
)
{
if
(
size
.
width
<=
maxAllowedWidth
&&
size
.
height
<=
maxAllowedHeight
)
{
if
(
size
.
width
>=
calcWidth
&&
size
.
height
>=
calcHeight
)
{
calcWidth
=
(
int
)
size
.
width
;
calcHeight
=
(
int
)
size
.
height
;
}
}
}
return
new
Size
(
calcWidth
,
calcHeight
);
}
}
samples/android/camera-preview/src/org/opencv/test/camerawriter/OpenCvNativeCameraView.java
0 → 100644
View file @
a32004f9
package
org
.
opencv
.
test
.
camerawriter
;
import
org.opencv.android.Utils
;
import
org.opencv.core.Mat
;
import
org.opencv.core.Size
;
import
org.opencv.highgui.Highgui
;
import
org.opencv.highgui.VideoCapture
;
import
android.content.Context
;
import
android.graphics.Bitmap
;
import
android.graphics.Canvas
;
import
android.util.AttributeSet
;
import
android.util.Log
;
/**
* This class is an implementation of a bridge between SurfaceView and native OpenCV camera.
* Due to the big amount of work done, by the base class this child is only responsible
* for creating camera, destroying camera and delivering frames while camera is enabled
*/
public
class
OpenCvNativeCameraView
extends
OpenCvCameraBridgeViewBase
{
public
static
final
String
TAG
=
"OpenCvNativeCameraView"
;
private
boolean
mStopThread
;
private
Thread
mThread
;
private
VideoCapture
mCamera
;
public
OpenCvNativeCameraView
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
}
@Override
protected
void
connectCamera
(
int
width
,
int
height
)
{
/* 1. We need to instantiate camera
* 2. We need to start thread which will be getting frames
*/
/* First step - initialize camera connection */
initializeCamera
(
getWidth
(),
getHeight
());
/* now we can start update thread */
mThread
=
new
Thread
(
new
CameraWorker
(
getWidth
(),
getHeight
()));
mThread
.
start
();
}
@Override
protected
void
disconnectCamera
()
{
/* 1. We need to stop thread which updating the frames
* 2. Stop camera and release it
*/
try
{
mStopThread
=
true
;
mThread
.
join
();
}
catch
(
InterruptedException
e
)
{
e
.
printStackTrace
();
}
finally
{
mThread
=
null
;
mStopThread
=
false
;
}
/* Now release camera */
releaseCamera
();
}
private
void
initializeCamera
(
int
width
,
int
height
)
{
mCamera
=
new
VideoCapture
(
Highgui
.
CV_CAP_ANDROID
);
//TODO: improve error handling
java
.
util
.
List
<
Size
>
sizes
=
mCamera
.
getSupportedPreviewSizes
();
/* Select the size that fits surface considering maximum size allowed */
Size
frameSize
=
calculateCameraFrameSize
(
sizes
,
width
,
height
);
double
frameWidth
=
frameSize
.
width
;
double
frameHeight
=
frameSize
.
height
;
mCamera
.
set
(
Highgui
.
CV_CAP_PROP_FRAME_WIDTH
,
frameWidth
);
mCamera
.
set
(
Highgui
.
CV_CAP_PROP_FRAME_HEIGHT
,
frameHeight
);
mFrameWidth
=
(
int
)
frameWidth
;
mFrameHeight
=
(
int
)
frameHeight
;
Log
.
i
(
TAG
,
"Selected camera frame size = ("
+
mFrameWidth
+
", "
+
mFrameHeight
+
")"
);
}
private
void
releaseCamera
()
{
if
(
mCamera
!=
null
)
{
mCamera
.
release
();
}
}
private
class
CameraWorker
implements
Runnable
{
private
Mat
mRgba
=
new
Mat
();
private
int
mWidth
;
private
int
mHeight
;
CameraWorker
(
int
w
,
int
h
)
{
mWidth
=
w
;
mHeight
=
h
;
}
@Override
public
void
run
()
{
Mat
modified
;
do
{
if
(!
mCamera
.
grab
())
{
Log
.
e
(
TAG
,
"Camera frame grab failed"
);
break
;
}
mCamera
.
retrieve
(
mRgba
,
Highgui
.
CV_CAP_ANDROID_COLOR_FRAME_RGBA
);
deliverAndDrawFrame
(
mRgba
);
}
while
(!
mStopThread
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment