Android рдХреЗ рд▓рд┐рдП рд╕рдордп рдЪреВрдХ рд╡реАрдбрд┐рдпреЛ рд╢реВрдЯрд┐рдВрдЧ

рдЫрд╡рд┐ рдЖрдЗрдП рдЯрд╛рдЗрдо рд▓реИрдкреНрд╕ рддрдХрдиреАрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреА рдЦреБрдж рдХреА рдлрд┐рд▓реНрдореЗрдВ рдмрдирд╛рдиреЗ рдХрд╛ рдХрд╛рд░реНрдпрдХреНрд░рдо рд▓рд┐рдЦреЗрдВред рдЖрдИрдПрд╕рдПрд╕ рд╕реЗ рдЗрд╕ рддрдХрдиреАрдХ рдореЗрдВ рд╢реВрдЯ рдХрд┐рдП рдЧрдП рд╡реАрдбрд┐рдпреЛ рдХреЛ рдпрд╣рд╛рдВ рджреЗрдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ , рдПрдХ рдЕрдзрд┐рдХ рдХрд┐рдлрд╛рдпрддреА рд╡рд┐рдХрд▓реНрдк рдЬрд┐рд╕реЗ рд╡рд░реНрдгрд┐рдд рдХрд╛рд░реНрдпрдХреНрд░рдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рджреЛрд╣рд░рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред

рдХрд╛рд░реНрдпрдХреНрд░рдо рдореЗрдВ рдПрдХ рд╕рд░рд▓ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдФрд░ рдСрдкрд░реЗрд╢рди рдХрд╛ рдПрдХ рд╕рд░рд▓ рд╕рд┐рджреНрдзрд╛рдВрдд рд╣реИ:
тАв рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рдХреИрдорд░реЗ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, 10 рдПрд╕) рдФрд░ рдЙрддреНрдкрдиреНрди рд╡реАрдбрд┐рдпреЛ рдХреА рд╡рд╛рдВрдЫрд┐рдд рдлреНрд░реЗрдо рджрд░ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, 25 рдлреНрд░реЗрдо рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб) рдХреЗ рд╕рд╛рде рдЫрд╡рд┐рдпреЛрдВ рдХреА рдЖрд╡реГрддреНрддрд┐ рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реИ;
тАв "рд╕реНрдЯрд╛рд░реНрдЯ" рдмрдЯрди рджрдмрд╛рдиреЗ рдХреЗ рдмрд╛рдж, рдкреНрд░реЛрдЧреНрд░рд╛рдо рд╣рд░ 10 рд╕реЗрдХрдВрдб рдореЗрдВ рдПрдХ рдлреЛрдЯреЛ рд▓реЗрддрд╛ рд╣реИ рдФрд░ рдПрд╕рдбреА рдХрд╛рд░реНрдб рдореЗрдВ jpg рдлрд╛рдЗрд▓ рд▓рд┐рдЦрддрд╛ рд╣реИ;
тАв "рд╕реНрдЯреЙрдк" рдФрд░ "рд╡реАрдбрд┐рдпреЛ рдмрдирд╛рдПрдВ" рдмрдЯрди рдХреЛ рджрдмрд╛рдиреЗ рддрдХ рдпрд╣ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рджреЛрд╣рд░рд╛рдИ рдЬрд╛рддреА рд╣реИ, рдЬрд┐рд╕рдХреЗ рдмрд╛рдж рддрд╕реНрд╡реАрд░реЛрдВ рдХрд╛ рдХреНрд░рдо рдореЛрд╢рди рдЬреЗрдкреАрдИрдЬреА рд╡реАрдбрд┐рдпреЛ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рдШрдЯрдирд╛рдУрдВ рдХреА рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдЧрддрд┐ рд╕реЗ 250 рдЧреБрдирд╛ (25 * 10) рдлреБрдЯреЗрдЬ рдХреЛ рддреЗрдЬреА рд╕реЗ рджрд┐рдЦрд╛рддрд╛ рд╣реИред

рдХрд╛рд░реНрдпрдХреНрд░рдо рдореЗрдВ рджреЛ рдореБрдЦреНрдп рд╡рд░реНрдЧ рд╣реИрдВ - рдореЗрдирдПрдХреНрдЯрд┐рд╡рд┐рдЯреА , рдЬреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдФрд░ рдЫрд╡рд┐рдпреЛрдВ рдХреЗ рд╕рдВрдЪрдп рдХреЗ рд╕рд╛рде рд╕рдВрд▓рдЧреНрди рд╣реИ, рдФрд░ рдПрдордЬреЗрдкреАрдЧреЗрдВрдиреЗрд░реЗрдЯрд░ , рдЬреЛ рдПрдХ рд╡реАрдбрд┐рдпреЛ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЫрд╡рд┐рдпреЛрдВ рдХреЗ рдЕрдиреБрдХреНрд░рдо рдХреЛ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИред

рдПрдВрдбреНрд░реЙрдЗрдб рдЬрд╛рд╡рд╛ рдореЗрдВ java.awt рдкреИрдХреЗрдЬ рдХреА рдХрдореА рдХреЗ рдХрд╛рд░рдг, code.google.com рд╕реЗ рд▓рд┐рдпрд╛ рдЧрдпрд╛ MJPEGGenerator рд╡рд░реНрдЧ рдереЛрдбрд╝рд╛ рдирдпрд╛ рд╣реЛ рдЧрдпрд╛ рд╣реИред

рдХреИрдорд░рд╛ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛рдПрдВ рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рдПрдВрдбреНрд░реЙрдЗрдб рдореЗрдВ рдХреИрдорд░реЗ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реА рд╕рд╛рдордЧреНрд░реА рд╕реЗ рд▓реА рдЧрдИ рдереАрдВ, рдЬрд╣рд╛рдВ рд▓рд╛рдЧреВ рд╕рдорд╛рдзрд╛рдиреЛрдВ рдХрд╛ рдПрдХ рдЕрдЪреНрдЫрд╛ рд╡рд░реНрдгрди рд╣реИ, рдПрдВрдбреНрд░реЙрдЗрдб рдбрд┐рд╡рд╛рдЗрд╕ рдХреЗ рд▓реЙрдХ / рдЕрдирд▓реЙрдХ рдХреЗ рдмрд╛рдж рдХреИрдорд░рд╛ "рдЪрд┐рдкрдХреЗ" рдХреА рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╕рдорд╛рдкреНрдд рдХрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдЬрд┐рд╕рдХрд╛ рдХрд╛рд░рдг рд╕реНрдЯреИрдХрдУрд╡рд░рдлреНрд▓реЛ рдХрд╛ рдзрдиреНрдпрд╡рд╛рдж рдерд╛ред

рдПрдВрдбреНрд░реЙрдЗрдб 4.0 рдХреЗ рддрд╣рдд рдкреНрд░рд╛рдЗрд╕реНрдЯрд┐рдпреЛ рдорд▓реНрдЯреАрдкреИрдб 7.0 рдкреНрд░рд╛рдЗрдо рдЯреИрдмрд▓реЗрдЯ рдкрд░ рдХрд╛рд░реНрдпрдХреНрд░рдо рдХреЛ рдбрд┐рдмреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред


рдЫрд╡рд┐


рдЖрдЗрдП рд╣рдо рд╡реНрдпрдХреНрддрд┐рдЧрдд рдкреНрд░реЛрдЧреНрд░рд╛рдо рдШрдЯрдХреЛрдВ рдХреЗ рдХрд╛рдо рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ рдЬреЛ рд╕реНрд╡рдпрдВ рдХреИрдорд░реЗ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рдирд╣реАрдВ рд╣реИрдВред

рд╢реВрдЯрд┐рдВрдЧ рдХреЗ рджреМрд░рд╛рди рд╕реНрдХреНрд░реАрди рдХреЛ рдмрдВрдж рдХрд░рдиреЗ рд╕реЗ рд░реЛрдХрдиреЗ рдХреЗ рд▓рд┐рдП, PowerManager.WakeLock рддрдВрддреНрд░ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛:

private PowerManager.WakeLock wl; public void onCreate(Bundle savedInstanceState) { ... PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen"); } 


рдХреИрдорд░рд╛ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреА рдЖрд╡реГрддреНрддрд┐ рдПрдХ рдЯрд╛рдЗрдорд░ рджреНрд╡рд╛рд░рд╛ рд╡рд┐рдирд┐рдпрдорд┐рдд рд╣реЛрддреА рд╣реИ:

 Timer updateTimer = new Timer(); ... updateTimer = new Timer(); updateTimer.scheduleAtFixedRate(new TimerTask() { public void run() { if ((camera != null) && (workMode == 1)) { camera.takePicture(null, null, null, MainActivity.this); }} }, 0, capturePeriod * 1000); 


рдХрд╛рдо рд╢реБрд░реВ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдПрдХ рдПрд╕рдбреА рдХрд╛рд░реНрдб рдХреА рдЙрдкрд╕реНрдерд┐рддрд┐ рдХреА рдЬрд╛рдБрдЪ рдХреА рдЬрд╛рддреА рд╣реИ:

 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) Toast.makeText(MainActivity.this, "Please mount SD card", Toast.LENGTH_LONG).show(); 


рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рджрд░реНрдЬ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдбреЗрдЯрд╛ рдкреВрд░реНрд╡-рд╕рдВрд╕рд╛рдзрд┐рдд рд╣реИ: рджрд╢рдорд▓рд╡ рд╡рд┐рднрд╛рдЬрдХ рдХреЗ рд░реВрдк рдореЗрдВ рджрд░реНрдЬ рдЕрд▓реНрдкрд╡рд┐рд░рд╛рдо рдХреЛ рдПрдХ рдЕрд╡рдзрд┐ рджреНрд╡рд╛рд░рд╛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ; рдЧреИрд░-рдбрд┐рдЬрд┐рдЯрд▓ рдореВрд▓реНрдпреЛрдВ рдХреА рдЬрд╛рдБрдЪ рдХреА рдЬрд╛рддреА рд╣реИ; рдкреНрд░рд╡реЗрд╢ рдХреА рдЕрд╡рдзрд┐ рдФрд░ рдлреНрд░реЗрдо рджрд░ рдХреЗ рдореВрд▓реНрдпреЛрдВ рдХреЛ рд╕реНрд╡реАрдХрд╛рд░реНрдп рд╕реАрдорд╛ рдореЗрдВ рдкреНрд░рд╡реЗрд╢ рдХреЗ рд▓рд┐рдП рдЬрд╛рдБрдЪ рдХреА рдЬрд╛рддреА рд╣реИ:

 periodEditText.addTextChangedListener(new TextWatcher() { public void afterTextChanged(Editable s) { if (periodEditText.getText().toString().length() == 0) capturePeriod = 0; else { if (isNum(periodEditText.getText().toString().replace(',', '.'))) { float a = Float.valueOf(periodEditText.getText().toString().replace(',', '.')); capturePeriod = (int) a; } else Toast.makeText(MainActivity.this, periodEditText.getText().toString() + " - not a digit.", Toast.LENGTH_LONG).show(); }} ... if ((fps < FPSMIN) || (fps > FPSMAX)) Toast.makeText(MainActivity.this, "FPS should be " + FPSMIN + " to " + FPSMAX + " frames per second", Toast.LENGTH_LONG).show(); 


рдХрд╛рдо рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ, рдкрд┐рдЫрд▓реЗ рд╕рддреНрд░ рдХреЗ рдмрд╛рдж рдмрдЪреА рд╕рднреА * .jpg рдлрд╛рдЗрд▓реЗрдВ рдкреНрд░реЛрдЧреНрд░рд╛рдо рдлреЛрд▓реНрдбрд░ рд╕реЗ рд╣рдЯрд╛ рджреА рдЬрд╛рддреА рд╣реИрдВ:

 String sdPath = Environment.getExternalStorageDirectory().getPath() + "/TimeLapseFolder/"; File saveDir = new File(sdPath); if (saveDir.isDirectory()) { String[] children = saveDir.list(); for (int i = 0; i < children.length; i++) { if (children[i].endsWith(".jpg")) new File(saveDir, children[i]).delete(); }} saveDir.delete(); 


рдЕрдЧрд▓реЗ рдлреНрд░реЗрдо рдХреА рд╢реВрдЯрд┐рдВрдЧ рдХреЗ рдмрд╛рдж, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдорд╛рдирдЪрд┐рддреНрд░ рдкрд░ рд╢реЗрд╖ рд╕реНрдерд╛рди рджрд┐рдЦрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ:

 modeText.setText("Work mode: capturing, " + String.valueOf(roundOneDecimal(megAvailable)) + " Mbyte available on SD card"); 


рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╡реАрдбрд┐рдпреЛ рдкреАрдврд╝реА:

 generator = new MJPEGGenerator(videofile, aviWidth, aviHeight, fps, lastPicture); for (int addpic = 1; addpic <= lastPicture; addpic++) { String numWithZeroes = intToString(addpic, 7); String curjpg = sdPath + numWithZeroes + ".jpg"; publishProgress(numWithZeroes); if (DEBUG) Log.v(TAG, "Rendering jpg sdPath = " + curjpg); Bitmap bmp = BitmapFactory.decodeFile(curjpg); generator.addImage(bmp); } 


MJPEGGenerator рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдорд╛рдкрджрдВрдбреЛрдВ рд╕реЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ:
рд╡реАрдбрд┐рдпреЛ-рдлрд╛рдЗрд▓ - рд╣рд░ рдмрд╛рд░ рдПрдХ рдирдпрд╛ рд╡реАрдбрд┐рдпреЛ рдлрд╝рд╛рдЗрд▓ рдирд╛рдо рд▓рд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдЯрд╛рдЗрдорд▓реИрдкреНрд╕рдореВрд╡реАрдПрдХреНрд╕рдПрдХреНрд╕рдПрдХреНрд╕рдПрдХреНрд╕рдПрдХреНрд╕.рдПрд╡реА рдорд╛рд╕реНрдХ рджреНрд╡рд╛рд░рд╛ рдЧрд┐рдирд╛ рдЬрд╛рддрд╛ рд╣реИ рддрд╛рдХрд┐ рдкрд╣рд▓реЗ рдХрдмреНрдЬрд╛ рдХрд░ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рд╣реЛ;
рдПрд╡рд┐рд╡рд┐рде, рдПрд╡рд┐рд╣рд╛рдЗрдЯ - рдХреИрдорд░рд╛ рдЧреБрдгреЛрдВ рд╕реЗ рд▓рд┐рдпрд╛ рдЧрдпрд╛;
рдПрдлрдкреАрдПрд╕ - рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкрд░рд┐рднрд╛рд╖рд┐рдд;
lastPicture - рдЕрдВрддрд┐рдо рдлреЛрдЯреЛ рдХреА рд╕рдВрдЦреНрдпрд╛ред

рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рдирд┐рд▓рдВрдмрд┐рдд рдирд╣реАрдВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╡реАрдбрд┐рдпреЛ рдкреАрдврд╝реА рдХреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ AsyncTask рдереНрд░реЗрдб рдореЗрдВ GUI рдХреЗ рд╕рд╛рде onProgressUpdate рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЗрдВрдЯрд░реИрдХреНрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

рдХреБрдВрдЬреА рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рд╕рд╛рдордЧреНрд░реА рдиреАрдЪреЗ рджрд┐рдЦрд╛рдИ рдЧрдИ рд╣реИ, рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕рдВрдЧреНрд░рд╣ рдХреЛ sourceforge.net рд╕реЗ рдбрд╛рдЙрдирд▓реЛрдб рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

MainActivity.java
 package com.sample.timelapse; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.DecimalFormat; import java.util.Arrays; import java.util.Timer; import java.util.TimerTask; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Point; import android.hardware.Camera; import android.hardware.Camera.Size; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.PowerManager; import android.os.StatFs; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.util.TypedValue; import android.view.Display; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnFocusChangeListener; import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements SurfaceHolder.Callback, View.OnClickListener, Camera.PictureCallback, Camera.PreviewCallback { private MJPEGGenerator generator; Timer updateTimer = new Timer(); // Main shoot timer private static final int PERIODMIN = 2; // Seconds private static final int PERIODMAX = 1000; // Seconds private static final int FPSMIN = 2; private static final int FPSMAX = 30; int aviHeight = 0; // Dimensions of final video int aviWidth = 0; // Work mode // 0: Ready to start // 1: Capturing photos // 2: Ready to create video // 3: Create video private int workMode = 0; private int capturePeriod = 0; private int fps = 0; private Camera camera; private SurfaceHolder surfaceHolder; private SurfaceView preview; private static int LOGLEVEL = 2; // Set logging level private static boolean DEBUG = LOGLEVEL > 1; @SuppressWarnings("unused") private static boolean WARNING = LOGLEVEL > 0; public static final String PREFS_NAME = "MyPrefsFile"; // For save and restore preferences private static final String TAG = "MainActivity"; // Set logging tag int lastPicture = 0; // Current picture counter int lastVideo = 0; // Current video file counter int sWidth = 0; // Screen width int sHeight = 0; // Screen height int prevsWidth = 1; // Previous screen width (after previous onWindowFocusChanged) int prevsHeight = 1; // Previous screen height (after previous onWindowFocusChanged) int commentTextBottom = 0; int oldLandCommentTextBottom = 0; private TextView periodText; private TextView framerateText; private TextView totalsnapshotsText; private Button startButton; // "Start capture" private Button createButton; // "Create video" int nativeButtonColor = 0; private EditText periodEditText; // Period private TextView secondsText; private EditText fpsEditText; // Frame rate private TextView fpsText; private TextView modeText; // Show comments float roundOneDecimal(float toround) { DecimalFormat twoDForm = new DecimalFormat("#.#"); return Float.valueOf(twoDForm.format(toround)); } static String intToString(int num, int digits) { assert digits > 0 : "Invalid number of digits"; char[] zeros = new char[digits]; // Create variable length array of zeros Arrays.fill(zeros, '0'); DecimalFormat df = new DecimalFormat(String.valueOf(zeros)); // Format number as String return df.format(num); } public boolean isNum(String s) { try { Double.parseDouble(s); } catch (NumberFormatException e) { return false; } return true; } private PowerManager.WakeLock wl; // Stop screen from dimming by enforcing wake lock @Override protected void onPause() { super.onPause(); // onPause method in the parent class if (DEBUG) Log.v(TAG, "onPause"); surfaceHolder.removeCallback(this); if (camera != null) { camera.setPreviewCallback(null); camera.stopPreview(); camera.release(); camera = null; } preview.setVisibility(View.GONE); wl.release(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // onCreate method in the parent class if (DEBUG) Log.v(TAG, "onCreate"); requestWindowFeature(Window.FEATURE_NO_TITLE); // App without a title getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // App without a status bar setContentView(R.layout.activity_main); // Set user interface PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen"); periodText = (TextView) findViewById(R.id.periodText); // Text fields framerateText = (TextView) findViewById(R.id.framerateText); totalsnapshotsText = (TextView) findViewById(R.id.totalsnapshotsText); startButton = (Button) findViewById(R.id.startButton); // Start capture button startButton.setOnClickListener(this); createButton = (Button) findViewById(R.id.createButton); // Create video button createButton.setOnClickListener(this); nativeButtonColor = createButton.getCurrentTextColor(); createButton.setTextColor(Color.GRAY); periodEditText = (EditText) findViewById(R.id.periodEditText); // Period secondsText = (TextView) findViewById(R.id.secondsText); periodEditText.addTextChangedListener(new TextWatcher() { public void afterTextChanged(Editable s) { if (periodEditText.getText().toString().length() == 0) capturePeriod = 0; else { if (isNum(periodEditText.getText().toString().replace(',', '.'))) { float a = Float.valueOf(periodEditText.getText().toString().replace(',', '.')); capturePeriod = (int) a; } else Toast.makeText(MainActivity.this, periodEditText.getText().toString() + " - not a digit.", Toast.LENGTH_LONG).show(); } } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } }); fpsEditText = (EditText) findViewById(R.id.fpsEditText); // fps EditText fpsText = (TextView) findViewById(R.id.fpsText); fpsEditText.setOnFocusChangeListener(new OnFocusChangeListener() { public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { // Hide soft keyboard after input InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(fpsEditText.getWindowToken(), 0); } } }); fpsEditText.addTextChangedListener(new TextWatcher() { public void afterTextChanged(Editable s) { if (fpsEditText.getText().toString().length() == 0) fps = 0; else { if (isNum(fpsEditText.getText().toString().replace(',', '.'))) { float a = Float.valueOf(fpsEditText.getText().toString().replace(',', '.')); fps = (int) a; } else Toast.makeText(MainActivity.this, fpsEditText.getText().toString() + " - not a digit.", Toast.LENGTH_LONG).show(); } } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } }); modeText = (TextView) findViewById(R.id.modeText); // Show comments SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); // Restore preferences oldLandCommentTextBottom = settings.getInt("oldLandCommentTextBottom", 0); } @Override protected void onResume() { super.onResume(); // onResume method in the parent class if (DEBUG) Log.v(TAG, "onResume"); preview = (SurfaceView) findViewById(R.id.mSurfaceView); if (camera == null) { camera = Camera.open(); camera.startPreview(); } surfaceHolder = preview.getHolder(); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceHolder.setSizeFromLayout(); surfaceHolder.addCallback(this); preview.setVisibility(View.VISIBLE); wl.acquire(); Size previewSize = camera.getParameters().getPreviewSize(); aviHeight = previewSize.height; aviWidth = previewSize.width; modeText.setFocusableInTouchMode(true); // Set focus (and hide soft keyboard) modeText.requestFocus(); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if (DEBUG) Log.v(TAG, "surfaceChanged"); try { camera.setPreviewDisplay(surfaceHolder); } catch (IOException e) { Toast.makeText(MainActivity.this, "Error 1: " + e.toString(), Toast.LENGTH_LONG).show(); } camera.startPreview(); } public void surfaceCreated(SurfaceHolder holder) { if (DEBUG) Log.v(TAG, "surfaceCreated"); try { camera.setPreviewDisplay(holder); camera.setPreviewCallback(this); } catch (IOException e) { Toast.makeText(MainActivity.this, "Error 2: " + e.toString(), Toast.LENGTH_LONG).show(); camera.release(); camera = null; } Size previewSize = camera.getParameters().getPreviewSize(); float aspect = (float) previewSize.width / previewSize.height; int previewSurfaceWidth = preview.getWidth(); LayoutParams lp = preview.getLayoutParams(); //     preview   ,     // camera.setDisplayOrientation(0); lp.width = previewSurfaceWidth; lp.height = (int) (previewSurfaceWidth / aspect); preview.setLayoutParams(lp); camera.startPreview(); } public void surfaceDestroyed(SurfaceHolder holder) { if (DEBUG) Log.v(TAG, "surfaceDestroyed"); } @SuppressLint("NewApi") @SuppressWarnings("deprecation") void getDisplaySize() { try { if (Build.VERSION.SDK_INT >= 13) { Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); sWidth = size.x; sHeight = size.y; } else { Display display = getWindowManager().getDefaultDisplay(); sWidth = display.getWidth(); sHeight = display.getHeight(); } } catch (Exception e) { Toast.makeText(MainActivity.this, "Error 3: " + e.toString(), Toast.LENGTH_LONG).show(); } } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { getDisplaySize(); if ((prevsWidth != sWidth) || (prevsHeight != sHeight)) { // If orientation changed commentTextBottom = modeText.getTop() + modeText.getHeight(); // Calculate magnification factor float heightRatio = 0; // Landscape heightRatio = (float) sHeight / (float) commentTextBottom; oldLandCommentTextBottom = commentTextBottom; if (heightRatio > 1) heightRatio = 0.7f * heightRatio; else heightRatio = heightRatio / 0.7f; // Adjust fonts periodText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * periodText.getTextSize()); periodEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * periodEditText.getTextSize()); secondsText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * secondsText.getTextSize()); framerateText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * framerateText.getTextSize()); fpsEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * fpsEditText.getTextSize()); fpsText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * fpsText.getTextSize()); totalsnapshotsText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * totalsnapshotsText.getTextSize()); modeText.setTextSize(TypedValue.COMPLEX_UNIT_PX, heightRatio * modeText.getTextSize()); // Some components have text size a little less startButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, 0.8f * heightRatio * startButton.getTextSize()); createButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, 0.8f * heightRatio * createButton.getTextSize()); // If user comment string not formed if (modeText.getText().equals(getResources().getString(R.string.longestComment))) modeText.setText(getString(R.string.modeText)); } prevsWidth = sWidth; prevsHeight = sHeight; } } public void onClick(View v) { if (v == startButton) { if (workMode == 0) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) Toast.makeText(MainActivity.this, "Please mount SD card", Toast.LENGTH_LONG).show(); else if ((capturePeriod < PERIODMIN) || (capturePeriod > PERIODMAX)) Toast.makeText(MainActivity.this, "Snapshots period should be " + PERIODMIN + " to " + PERIODMAX + " seconds", Toast.LENGTH_LONG) .show(); else if ((fps < FPSMIN) || (fps > FPSMAX)) Toast.makeText(MainActivity.this, "FPS should be " + FPSMIN + " to " + FPSMAX + " frames per second", Toast.LENGTH_LONG).show(); else { if (updateTimer != null) updateTimer.cancel(); try { updateTimer = new Timer(); updateTimer.scheduleAtFixedRate(new TimerTask() { public void run() { if ((camera != null) && (workMode == 1)) { camera.takePicture(null, null, null, MainActivity.this); } } }, 0, capturePeriod * 1000); } catch (Exception e) { Toast.makeText(MainActivity.this, "Error 4: " + e.toString(), Toast.LENGTH_LONG).show(); } // Delete all jpg's try { String sdPath = Environment.getExternalStorageDirectory().getPath() + "/TimeLapseFolder/"; if (DEBUG) Log.v(TAG, "Delete jpg's sdPath = " + sdPath); File saveDir = new File(sdPath); if (saveDir.isDirectory()) { String[] children = saveDir.list(); for (int i = 0; i < children.length; i++) { if (children[i].endsWith(".jpg")) new File(saveDir, children[i]).delete(); } } saveDir.delete(); } catch (Exception e) { Toast.makeText(MainActivity.this, "Error 5: " + e.toString(), Toast.LENGTH_LONG).show(); } lastPicture = 0; workMode = 1; startButton.setText("Stop capture"); modeText.setText("Work mode: capturing"); totalsnapshotsText.setText("Total snapshots: " + String.valueOf(lastPicture)); } } else if (workMode == 1) { workMode = 2; createButton.setTextColor(nativeButtonColor); startButton.setText("Start capture"); startButton.setTextColor(Color.GRAY); modeText.setText("Work mode: ready to start"); } } if (v == createButton) { if (workMode == 2) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) Toast.makeText(MainActivity.this, "Please mount SD card", Toast.LENGTH_LONG).show(); else if ((capturePeriod < PERIODMIN) || (capturePeriod > PERIODMAX)) Toast.makeText(MainActivity.this, "Snapshots period should be " + PERIODMIN + " to " + PERIODMAX + " seconds", Toast.LENGTH_LONG) .show(); else if ((fps < FPSMIN) || (fps > FPSMAX)) Toast.makeText(MainActivity.this, "FPS should be " + FPSMIN + " to " + FPSMAX + " frames per second", Toast.LENGTH_LONG).show(); else { workMode = 3; createButton.setTextColor(Color.GRAY); startButton.setTextColor(Color.GRAY); modeText.setText("Work mode: create video file, please wait"); new CreateMovieInBackground().execute(); } } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // onSaveInstanceState method in the parent class if (DEBUG) Log.v(TAG, "onSaveInstanceState"); SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putInt("oldLandCommentTextBottom", oldLandCommentTextBottom); editor.commit(); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // onRestoreInstanceState method in the parent class if (DEBUG) Log.v(TAG, "onRestoreInstanceState"); } public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera) { new SaveInBackground().execute(paramArrayOfByte); if (DEBUG) Log.v(TAG, "onPictureTaken"); //  ,   ,   .    paramCamera.startPreview(); totalsnapshotsText.setText("Total snapshots: " + String.valueOf(lastPicture)); StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath()); long bytesAvailable = (long) stat.getBlockSize() * (long) stat.getAvailableBlocks(); float megAvailable = bytesAvailable / (1024.f * 1024.f); modeText.setText("Work mode: capturing, " + String.valueOf(roundOneDecimal(megAvailable)) + " Mbyte available on SD card"); } class SaveInBackground extends AsyncTask<byte[], String, String> { @Override protected String doInBackground(byte[]... arrayOfByte) { try { String sdPath = Environment.getExternalStorageDirectory().getPath() + "/TimeLapseFolder/"; File saveDir = new File(sdPath); if (!saveDir.exists()) saveDir.mkdirs(); lastPicture++; String numWithZeroes = intToString(lastPicture, 7); String curjpg = sdPath + numWithZeroes + ".jpg"; if (DEBUG) Log.v(TAG, "Save jpg sdPath = " + curjpg); FileOutputStream os = new FileOutputStream(curjpg); os.write(arrayOfByte[0]); os.close(); } catch (Exception e) { Toast.makeText(MainActivity.this, "Error 6: " + e.toString(), Toast.LENGTH_LONG).show(); } return (null); } } class CreateMovieInBackground extends AsyncTask<byte[], String, String> { protected void onProgressUpdate(String... values) { modeText.setText("Work mode: rendering " + values[0] + ".jpg"); } protected void onPostExecute(String result) { workMode = 0; totalsnapshotsText.setText("Total snapshots: 0"); lastPicture = 0; String sdPath = Environment.getExternalStorageDirectory().getPath() + "/TimeLapseFolder/"; modeText.setText("Work mode:" + sdPath + "TimeLapseMovie" + intToString(lastVideo, 3) + ".avi is rendered"); Handler handler = new Handler(); handler.postDelayed(new Runnable() { public void run() { modeText.setText("Work mode: ready to start"); startButton.setTextColor(nativeButtonColor); } }, 5000); } @Override protected String doInBackground(byte[]... arrayOfByte) { try { File videofile = null; String sdPath = Environment.getExternalStorageDirectory().getPath() + "/TimeLapseFolder/"; // Choosing a name for the file do { lastVideo++; String curavi = sdPath + "TimeLapseMovie" + intToString(lastVideo, 3) + ".avi"; if (DEBUG) Log.v(TAG, "AVI name = " + curavi); videofile = new File(curavi); } while (videofile.exists()); generator = new MJPEGGenerator(videofile, aviWidth, aviHeight, fps, lastPicture); for (int addpic = 1; addpic <= lastPicture; addpic++) { String numWithZeroes = intToString(addpic, 7); String curjpg = sdPath + numWithZeroes + ".jpg"; publishProgress(numWithZeroes); if (DEBUG) Log.v(TAG, "Rendering jpg sdPath = " + curjpg); Bitmap bmp = BitmapFactory.decodeFile(curjpg); generator.addImage(bmp); } // Delete all jpg's try { if (DEBUG) Log.v(TAG, "Delete jpg's sdPath = " + sdPath); File saveDir = new File(sdPath); if (saveDir.isDirectory()) { String[] children = saveDir.list(); for (int i = 0; i < children.length; i++) { if (children[i].endsWith(".jpg")) new File(saveDir, children[i]).delete(); } } saveDir.delete(); } catch (Exception e) { Toast.makeText(MainActivity.this, "Error 7: " + e.toString(), Toast.LENGTH_LONG).show(); } generator.finishAVI(); } catch (Exception e) { Toast.makeText(MainActivity.this, "Error 8: " + e.toString(), Toast.LENGTH_LONG).show(); } return "OK"; } } public void onPreviewFrame(byte[] paramArrayOfByte, Camera paramCamera) { } } 



MJPEGGenerator.java
 package com.sample.timelapse; // // MJPEGGenerator.java // // Created on April 17, 2006, 11:48 PM // // To change this template, choose Tools | Options and locate the template under // the Source Creation and Management node. Right-click the template and choose // Open. You can then make changes to the template in the Source Editor. // import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import android.graphics.Bitmap; // // // @author monceaux // public class MJPEGGenerator { // // Info needed for MJPEG AVI // // - size of file minus "RIFF & 4 byte file size" // int width = 0; int height = 0; double framerate = 0; int numFrames = 0; File aviFile = null; FileOutputStream aviOutput = null; FileChannel aviChannel = null; long riffOffset = 0; long aviMovieOffset = 0; AVIIndexList indexlist = null; // Creates a new instance of MJPEGGenerator public MJPEGGenerator(File aviFile, int width, int height, double framerate, int numFrames) throws Exception { this.aviFile = aviFile; this.width = width; this.height = height; this.framerate = framerate; this.numFrames = numFrames; aviOutput = new FileOutputStream(aviFile); aviChannel = aviOutput.getChannel(); RIFFHeader rh = new RIFFHeader(); aviOutput.write(rh.toBytes()); aviOutput.write(new AVIMainHeader().toBytes()); aviOutput.write(new AVIStreamList().toBytes()); aviOutput.write(new AVIStreamHeader().toBytes()); aviOutput.write(new AVIStreamFormat().toBytes()); aviOutput.write(new AVIJunk().toBytes()); aviMovieOffset = aviChannel.position(); aviOutput.write(new AVIMovieList().toBytes()); indexlist = new AVIIndexList(); } public void addImage(Bitmap image) throws Exception { byte[] fcc = new byte[] { '0', '0', 'd', 'b' }; byte[] imagedata = writeImageToBytes(image); int useLength = imagedata.length; long position = aviChannel.position(); int extra = (useLength + (int) position) % 4; if (extra > 0) useLength = useLength + extra; indexlist.addAVIIndex((int) position, useLength); aviOutput.write(fcc); aviOutput.write(intBytes(swapInt(useLength))); aviOutput.write(imagedata); if (extra > 0) { for (int i = 0; i < extra; i++) aviOutput.write(0); } imagedata = null; } public void finishAVI() throws Exception { byte[] indexlistBytes = indexlist.toBytes(); aviOutput.write(indexlistBytes); aviOutput.close(); long size = aviFile.length(); RandomAccessFile raf = new RandomAccessFile(aviFile, "rw"); raf.seek(4); raf.write(intBytes(swapInt((int) size - 8))); raf.seek(aviMovieOffset + 4); raf.write(intBytes(swapInt((int) (size - 8 - aviMovieOffset - indexlistBytes.length)))); raf.close(); } public static int swapInt(int v) { return (v >>> 24) | (v << 24) | ((v << 8) & 0x00FF0000) | ((v >> 8) & 0x0000FF00); } public static short swapShort(short v) { return (short) ((v >>> 8) | (v << 8)); } public static byte[] intBytes(int i) { byte[] b = new byte[4]; b[0] = (byte) (i >>> 24); b[1] = (byte) ((i >>> 16) & 0x000000FF); b[2] = (byte) ((i >>> 8) & 0x000000FF); b[3] = (byte) (i & 0x000000FF); return b; } public static byte[] shortBytes(short i) { byte[] b = new byte[2]; b[0] = (byte) (i >>> 8); b[1] = (byte) (i & 0x000000FF); return b; } private class RIFFHeader { public byte[] fcc = new byte[] { 'R', 'I', 'F', 'F' }; public int fileSize = 0; public byte[] fcc2 = new byte[] { 'A', 'V', 'I', ' ' }; public byte[] fcc3 = new byte[] { 'L', 'I', 'S', 'T' }; public int listSize = 200; public byte[] fcc4 = new byte[] { 'h', 'd', 'r', 'l' }; public RIFFHeader() { } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(fileSize))); baos.write(fcc2); baos.write(fcc3); baos.write(intBytes(swapInt(listSize))); baos.write(fcc4); baos.close(); return baos.toByteArray(); } } private class AVIMainHeader { // // // FOURCC fcc; DWORD cb; DWORD dwMicroSecPerFrame; DWORD dwMaxBytesPerSec; DWORD dwPaddingGranularity; DWORD // dwFlags; DWORD dwTotalFrames; DWORD dwInitialFrames; DWORD dwStreams; DWORD dwSuggestedBufferSize; DWORD // dwWidth; DWORD dwHeight; DWORD dwReserved[4]; // public byte[] fcc = new byte[] { 'a', 'v', 'i', 'h' }; public int cb = 56; public int dwMicroSecPerFrame = 0; // (1 // / // frames // per // sec) // * // 1,000,000 public int dwMaxBytesPerSec = 10000000; public int dwPaddingGranularity = 0; public int dwFlags = 65552; public int dwTotalFrames = 0; // replace // with // correct // value public int dwInitialFrames = 0; public int dwStreams = 1; public int dwSuggestedBufferSize = 0; public int dwWidth = 0; // replace // with // correct // value public int dwHeight = 0; // replace // with // correct // value public int[] dwReserved = new int[4]; public AVIMainHeader() { dwMicroSecPerFrame = (int) ((1.0 / framerate) * 1000000.0); dwWidth = width; dwHeight = height; dwTotalFrames = numFrames; } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(cb))); baos.write(intBytes(swapInt(dwMicroSecPerFrame))); baos.write(intBytes(swapInt(dwMaxBytesPerSec))); baos.write(intBytes(swapInt(dwPaddingGranularity))); baos.write(intBytes(swapInt(dwFlags))); baos.write(intBytes(swapInt(dwTotalFrames))); baos.write(intBytes(swapInt(dwInitialFrames))); baos.write(intBytes(swapInt(dwStreams))); baos.write(intBytes(swapInt(dwSuggestedBufferSize))); baos.write(intBytes(swapInt(dwWidth))); baos.write(intBytes(swapInt(dwHeight))); baos.write(intBytes(swapInt(dwReserved[0]))); baos.write(intBytes(swapInt(dwReserved[1]))); baos.write(intBytes(swapInt(dwReserved[2]))); baos.write(intBytes(swapInt(dwReserved[3]))); baos.close(); return baos.toByteArray(); } } private class AVIStreamList { public byte[] fcc = new byte[] { 'L', 'I', 'S', 'T' }; public int size = 124; public byte[] fcc2 = new byte[] { 's', 't', 'r', 'l' }; public AVIStreamList() { } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(size))); baos.write(fcc2); baos.close(); return baos.toByteArray(); } } private class AVIStreamHeader { // // FOURCC fcc; DWORD cb; FOURCC fccType; FOURCC fccHandler; DWORD dwFlags; WORD wPriority; WORD wLanguage; DWORD // dwInitialFrames; DWORD dwScale; DWORD dwRate; DWORD dwStart; DWORD dwLength; DWORD dwSuggestedBufferSize; // DWORD dwQuality; DWORD dwSampleSize; struct { short int left; short int top; short int right; short int // bottom; } rcFrame; // public byte[] fcc = new byte[] { 's', 't', 'r', 'h' }; public int cb = 64; public byte[] fccType = new byte[] { 'v', 'i', 'd', 's' }; public byte[] fccHandler = new byte[] { 'M', 'J', 'P', 'G' }; public int dwFlags = 0; public short wPriority = 0; public short wLanguage = 0; public int dwInitialFrames = 0; public int dwScale = 0; // microseconds // per // frame public int dwRate = 1000000; // dwRate // / // dwScale // = // frame // rate public int dwStart = 0; public int dwLength = 0; // num // frames public int dwSuggestedBufferSize = 0; public int dwQuality = -1; public int dwSampleSize = 0; public int left = 0; public int top = 0; public int right = 0; public int bottom = 0; public AVIStreamHeader() { dwScale = (int) ((1.0 / framerate) * 1000000.0); dwLength = numFrames; } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(cb))); baos.write(fccType); baos.write(fccHandler); baos.write(intBytes(swapInt(dwFlags))); baos.write(shortBytes(swapShort(wPriority))); baos.write(shortBytes(swapShort(wLanguage))); baos.write(intBytes(swapInt(dwInitialFrames))); baos.write(intBytes(swapInt(dwScale))); baos.write(intBytes(swapInt(dwRate))); baos.write(intBytes(swapInt(dwStart))); baos.write(intBytes(swapInt(dwLength))); baos.write(intBytes(swapInt(dwSuggestedBufferSize))); baos.write(intBytes(swapInt(dwQuality))); baos.write(intBytes(swapInt(dwSampleSize))); baos.write(intBytes(swapInt(left))); baos.write(intBytes(swapInt(top))); baos.write(intBytes(swapInt(right))); baos.write(intBytes(swapInt(bottom))); baos.close(); return baos.toByteArray(); } } private class AVIStreamFormat { // // FOURCC fcc; DWORD cb; DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD // biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD // biClrImportant; // public byte[] fcc = new byte[] { 's', 't', 'r', 'f' }; public int cb = 40; public int biSize = 40; // same // as // cb public int biWidth = 0; public int biHeight = 0; public short biPlanes = 1; public short biBitCount = 24; public byte[] biCompression = new byte[] { 'M', 'J', 'P', 'G' }; public int biSizeImage = 0; // width // x // height // in // pixels public int biXPelsPerMeter = 0; public int biYPelsPerMeter = 0; public int biClrUsed = 0; public int biClrImportant = 0; public AVIStreamFormat() { biWidth = width; biHeight = height; biSizeImage = width * height; } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(cb))); baos.write(intBytes(swapInt(biSize))); baos.write(intBytes(swapInt(biWidth))); baos.write(intBytes(swapInt(biHeight))); baos.write(shortBytes(swapShort(biPlanes))); baos.write(shortBytes(swapShort(biBitCount))); baos.write(biCompression); baos.write(intBytes(swapInt(biSizeImage))); baos.write(intBytes(swapInt(biXPelsPerMeter))); baos.write(intBytes(swapInt(biYPelsPerMeter))); baos.write(intBytes(swapInt(biClrUsed))); baos.write(intBytes(swapInt(biClrImportant))); baos.close(); return baos.toByteArray(); } } private class AVIMovieList { public byte[] fcc = new byte[] { 'L', 'I', 'S', 'T' }; public int listSize = 0; public byte[] fcc2 = new byte[] { 'm', 'o', 'v', 'i' }; // 00db size jpg image data ... public AVIMovieList() { } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(listSize))); baos.write(fcc2); baos.close(); return baos.toByteArray(); } } private class AVIIndexList { public byte[] fcc = new byte[] { 'i', 'd', 'x', '1' }; public int cb = 0; public List ind = new ArrayList(); public AVIIndexList() { } @SuppressWarnings("unused") public void addAVIIndex(AVIIndex ai) { ind.add(ai); } public void addAVIIndex(int dwOffset, int dwSize) { ind.add(new AVIIndex(dwOffset, dwSize)); } public byte[] toBytes() throws Exception { cb = 16 * ind.size(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(cb))); for (int i = 0; i < ind.size(); i++) { AVIIndex in = (AVIIndex) ind.get(i); baos.write(in.toBytes()); } baos.close(); return baos.toByteArray(); } } private class AVIIndex { public byte[] fcc = new byte[] { '0', '0', 'd', 'b' }; public int dwFlags = 16; public int dwOffset = 0; public int dwSize = 0; public AVIIndex(int dwOffset, int dwSize) { this.dwOffset = dwOffset; this.dwSize = dwSize; } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(dwFlags))); baos.write(intBytes(swapInt(dwOffset))); baos.write(intBytes(swapInt(dwSize))); baos.close(); return baos.toByteArray(); } } private class AVIJunk { public byte[] fcc = new byte[] { 'J', 'U', 'N', 'K' }; public int size = 1808; public byte[] data = new byte[size]; public AVIJunk() { Arrays.fill(data, (byte) 0); } public byte[] toBytes() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fcc); baos.write(intBytes(swapInt(size))); baos.write(data); baos.close(); return baos.toByteArray(); } } private byte[] writeImageToBytes(Bitmap image) throws Exception { ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, stream); stream.close(); return stream.toByteArray(); } } 



activity_main.xml
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/appBackgroundColor" > <TextView android:id="@+id/centerEmptyText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" /> <SurfaceView android:id="@+id/mSurfaceView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_toRightOf="@+id/centerEmptyText" > </SurfaceView> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginRight="@dimen/baseui_horizontal_margin" android:layout_toLeftOf="@+id/centerEmptyText" android:orientation="vertical" > <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TableRow android:id="@+id/periodRow" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/periodText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:focusable="true" android:focusableInTouchMode="true" android:text="@string/periodText" > <requestFocus /> </TextView> <EditText android:id="@+id/periodEditText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:ems="2" android:inputType="numberDecimal" android:singleLine="true" /> <TextView android:id="@+id/secondsText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:text="@string/seconds" /> </TableRow> <TableRow android:id="@+id/fpsRow" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/framerateText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:focusable="true" android:focusableInTouchMode="true" android:text="@string/framerateText" /> <EditText android:id="@+id/fpsEditText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:ems="2" android:inputType="numberDecimal" android:singleLine="true" /> <TextView android:id="@+id/fpsText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:text="@string/fps" /> </TableRow> </TableLayout> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/startButton" style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:layout_weight="1" android:text="@string/startButtonText" /> <Button android:id="@+id/createButton" style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:layout_weight="1" android:text="@string/createButtonText" /> </LinearLayout> <TableRow android:id="@+id/totalsnapshotsRow" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/totalsnapshotsText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:text="@string/totalsnapshotsText" /> </TableRow> </TableLayout> <TextView android:id="@+id/modeText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/baseui_horizontal_margin" android:text="@string/longestComment" /> </LinearLayout> </RelativeLayout> 



AndroidManifest.xml
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sample.timelapse" android:installLocation="preferExternal" android:versionCode="8" android:versionName="0.8" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" /> <uses-feature android:name="there.isnt.a.vibrate.feature" android:required="false" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/title_activity_main" android:screenOrientation="landscape" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 



UPD : DevAndrew рдиреЗ рджреЗрдЦрд╛ рдХрд┐ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЛ рд╕рдВрдХрд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП ij.jar рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ ( ImageJ , MJPEGGenerator рдХреЗ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ)ред рдкреБрд╕реНтАНрддрдХрд╛рд▓рдп рдХреЛ рд╕реЛрд░реНрд╕рдлрд╝реЗрдЬ рдкрд░ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдлрд╝рд╛рдЗрд▓реЛрдВ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред

Source: https://habr.com/ru/post/In172469/


All Articles