Recherche avancée

Médias (0)

Mot : - Tags -/albums

Aucun média correspondant à vos critères n’est disponible sur le site.

Autres articles (82)

  • Amélioration de la version de base

    13 septembre 2013

    Jolie sélection multiple
    Le plugin Chosen permet d’améliorer l’ergonomie des champs de sélection multiple. Voir les deux images suivantes pour comparer.
    Il suffit pour cela d’activer le plugin Chosen (Configuration générale du site > Gestion des plugins), puis de configurer le plugin (Les squelettes > Chosen) en activant l’utilisation de Chosen dans le site public et en spécifiant les éléments de formulaires à améliorer, par exemple select[multiple] pour les listes à sélection multiple (...)

  • Menus personnalisés

    14 novembre 2010, par

    MediaSPIP utilise le plugin Menus pour gérer plusieurs menus configurables pour la navigation.
    Cela permet de laisser aux administrateurs de canaux la possibilité de configurer finement ces menus.
    Menus créés à l’initialisation du site
    Par défaut trois menus sont créés automatiquement à l’initialisation du site : Le menu principal ; Identifiant : barrenav ; Ce menu s’insère en général en haut de la page après le bloc d’entête, son identifiant le rend compatible avec les squelettes basés sur Zpip ; (...)

  • Configuration spécifique pour PHP5

    4 février 2011, par

    PHP5 est obligatoire, vous pouvez l’installer en suivant ce tutoriel spécifique.
    Il est recommandé dans un premier temps de désactiver le safe_mode, cependant, s’il est correctement configuré et que les binaires nécessaires sont accessibles, MediaSPIP devrait fonctionner correctement avec le safe_mode activé.
    Modules spécifiques
    Il est nécessaire d’installer certains modules PHP spécifiques, via le gestionnaire de paquet de votre distribution ou manuellement : php5-mysql pour la connectivité avec la (...)

Sur d’autres sites (4841)

  • Recording a video using MediaRecorder

    21 juillet 2016, par Cédric Portmann

    I am currently using the TextureFromCameraActivity from Grafika to record a video in square ( 1:1 ) resolution. Therefor I the GLES20.glViewport so that the video gets moved to the top and it appears to be squared. Now I would like to record this square view using the MediaRecorder or at least record the camera with normal resolutiona and then crop it using FFmpeg. However I get the same error over and over again and I cant figure out why.

    The error I get :

    start called in an invalid state : 4

    And yes I added all the necessary permissions.

    android.permission.WRITE_EXTERNAL_STORAGE android.permission.CAMERA
    android.permission.RECORD_VIDEO android.permission.RECORD_AUDIO
    android.permission.STORAGE android.permission.READ_EXTERNAL_STORAGE

    Here the modified code :

    https://github.com/google/grafika

    Thanks for your help :D

    package com.android.grafika;

    import android.graphics.SurfaceTexture;
    import android.hardware.Camera;
    import android.media.CamcorderProfile;
    import android.media.MediaRecorder;
    import android.opengl.GLES20;
    import android.opengl.Matrix;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Message;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.Surface;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.widget.Button;
    import android.widget.SeekBar;
    import android.widget.TextView;
    import android.app.Activity;
    import android.widget.Toast;

    import com.android.grafika.gles.Drawable2d;
    import com.android.grafika.gles.EglCore;
    import com.android.grafika.gles.GlUtil;
    import com.android.grafika.gles.Sprite2d;
    import com.android.grafika.gles.Texture2dProgram;
    import com.android.grafika.gles.WindowSurface;

    import java.io.File;
    import java.io.IOException;
    import java.lang.ref.WeakReference;


    public class TextureFromCameraActivity extends Activity implements View.OnClickListener, SurfaceHolder.Callback,
           SeekBar.OnSeekBarChangeListener {


       private static final int DEFAULT_ZOOM_PERCENT = 0;      // 0-100
       private static final int DEFAULT_SIZE_PERCENT = 80;     // 0-100
       private static final int DEFAULT_ROTATE_PERCENT = 75;    // 0-100

       // Requested values; actual may differ.
       private static final int REQ_CAMERA_WIDTH = 720;
       private static final int REQ_CAMERA_HEIGHT = 720;
       private static final int REQ_CAMERA_FPS = 30;

       // The holder for our SurfaceView.  The Surface can outlive the Activity (e.g. when
       // the screen is turned off and back on with the power button).
       //
       // This becomes non-null after the surfaceCreated() callback is called, and gets set
       // to null when surfaceDestroyed() is called.
       private static SurfaceHolder sSurfaceHolder;

       // Thread that handles rendering and controls the camera.  Started in onResume(),
       // stopped in onPause().
       private RenderThread mRenderThread;

       // Receives messages from renderer thread.
       private MainHandler mHandler;

       // User controls.
       private SeekBar mZoomBar;
       private SeekBar mSizeBar;
       private SeekBar mRotateBar;

       // These values are passed to us by the camera/render thread, and displayed in the UI.
       // We could also just peek at the values in the RenderThread object, but we'd need to
       // synchronize access carefully.
       private int mCameraPreviewWidth, mCameraPreviewHeight;
       private float mCameraPreviewFps;
       private int mRectWidth, mRectHeight;
       private int mZoomWidth, mZoomHeight;
       private int mRotateDeg;
       SurfaceHolder sh;
       MediaRecorder recorder;
       SurfaceHolder holder;
       boolean recording = false;

       public static final String TAG = "VIDEOCAPTURE";

       private static final File OUTPUT_DIR = Environment.getExternalStorageDirectory();


       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);

           recorder = new MediaRecorder();



           setContentView(R.layout.activity_texture_from_camera);

           mHandler = new MainHandler(this);

           SurfaceView cameraView = (SurfaceView) findViewById(R.id.cameraOnTexture_surfaceView);
           sh = cameraView.getHolder();
           cameraView.setClickable(true);// make the surface view clickable
           sh.addCallback(this);


           //prepareRecorder();


           mZoomBar = (SeekBar) findViewById(R.id.tfcZoom_seekbar);
           mSizeBar = (SeekBar) findViewById(R.id.tfcSize_seekbar);
           mRotateBar = (SeekBar) findViewById(R.id.tfcRotate_seekbar);
           mZoomBar.setProgress(DEFAULT_ZOOM_PERCENT);
           mSizeBar.setProgress(DEFAULT_SIZE_PERCENT);
           mRotateBar.setProgress(DEFAULT_ROTATE_PERCENT);
           mZoomBar.setOnSeekBarChangeListener(this);
           mSizeBar.setOnSeekBarChangeListener(this);
           mRotateBar.setOnSeekBarChangeListener(this);

           Button record_btn = (Button)findViewById(R.id.button);
           record_btn.setOnClickListener(this);
           initRecorder();


           updateControls();




       }





       @Override
       protected void onResume() {
           Log.d(TAG, "onResume BEGIN");
           super.onResume();

           mRenderThread = new RenderThread(mHandler);
           mRenderThread.setName("TexFromCam Render");
           mRenderThread.start();
           mRenderThread.waitUntilReady();

           RenderHandler rh = mRenderThread.getHandler();
           rh.sendZoomValue(mZoomBar.getProgress());
           rh.sendSizeValue(mSizeBar.getProgress());
           rh.sendRotateValue(mRotateBar.getProgress());

           if (sSurfaceHolder != null) {
               Log.d(TAG, "Sending previous surface");
               rh.sendSurfaceAvailable(sSurfaceHolder, false);
           } else {
               Log.d(TAG, "No previous surface");
           }
           Log.d(TAG, "onResume END");
       }

       @Override
       protected void onPause() {
           Log.d(TAG, "onPause BEGIN");
           super.onPause();

           RenderHandler rh = mRenderThread.getHandler();
           rh.sendShutdown();
           try {
               mRenderThread.join();
           } catch (InterruptedException ie) {
               // not expected
               throw new RuntimeException("join was interrupted", ie);
           }
           mRenderThread = null;
           Log.d(TAG, "onPause END");
       }

       @Override   // SurfaceHolder.Callback
       public void surfaceCreated(SurfaceHolder holder) {
           Log.d(TAG, "surfaceCreated holder=" + holder + " (static=" + sSurfaceHolder + ")");
           if (sSurfaceHolder != null) {
               throw new RuntimeException("sSurfaceHolder is already set");
           }

           sSurfaceHolder = holder;

           if (mRenderThread != null) {
               // Normal case -- render thread is running, tell it about the new surface.
               RenderHandler rh = mRenderThread.getHandler();
               rh.sendSurfaceAvailable(holder, true);
           } else {
               // Sometimes see this on 4.4.x N5: power off, power on, unlock, with device in
               // landscape and a lock screen that requires portrait.  The surface-created
               // message is showing up after onPause().
               //
               // Chances are good that the surface will be destroyed before the activity is
               // unpaused, but we track it anyway.  If the activity is un-paused and we start
               // the RenderThread, the SurfaceHolder will be passed in right after the thread
               // is created.
               Log.d(TAG, "render thread not running");
           }

           recorder.setPreviewDisplay(holder.getSurface());

       }

       @Override   // SurfaceHolder.Callback
       public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
           Log.d(TAG, "surfaceChanged fmt=" + format + " size=" + width + "x" + height +
                   " holder=" + holder);

           if (mRenderThread != null) {
               RenderHandler rh = mRenderThread.getHandler();
               rh.sendSurfaceChanged(format, width, height);
           } else {
               Log.d(TAG, "Ignoring surfaceChanged");
               return;
           }
       }

       @Override   // SurfaceHolder.Callback
       public void surfaceDestroyed(SurfaceHolder holder) {
           // In theory we should tell the RenderThread that the surface has been destroyed.
           if (mRenderThread != null) {
               RenderHandler rh = mRenderThread.getHandler();
               rh.sendSurfaceDestroyed();
           }
           Log.d(TAG, "surfaceDestroyed holder=" + holder);
           sSurfaceHolder = null;
       }

       @Override   // SeekBar.OnSeekBarChangeListener
       public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
           if (mRenderThread == null) {
               // Could happen if we programmatically update the values after setting a listener
               // but before starting the thread.  Also, easy to cause this by scrubbing the seek
               // bar with one finger then tapping "recents" with another.
               Log.w(TAG, "Ignoring onProgressChanged received w/o RT running");
               return;
           }
           RenderHandler rh = mRenderThread.getHandler();

           // "progress" ranges from 0 to 100
           if (seekBar == mZoomBar) {
               //Log.v(TAG, "zoom: " + progress);
               rh.sendZoomValue(progress);
           } else if (seekBar == mSizeBar) {
               //Log.v(TAG, "size: " + progress);
               rh.sendSizeValue(progress);
           } else if (seekBar == mRotateBar) {
               //Log.v(TAG, "rotate: " + progress);
               rh.sendRotateValue(progress);
           } else {
               throw new RuntimeException("unknown seek bar");
           }

           // If we're getting preview frames quickly enough we don't really need this, but
           // we don't want to have chunky-looking resize movement if the camera is slow.
           // OTOH, if we get the updates too quickly (60fps camera?), this could jam us
           // up and cause us to run behind.  So use with caution.
           rh.sendRedraw();
       }

       @Override   // SeekBar.OnSeekBarChangeListener
       public void onStartTrackingTouch(SeekBar seekBar) {}
       @Override   // SeekBar.OnSeekBarChangeListener
       public void onStopTrackingTouch(SeekBar seekBar) {}
       @Override

       /**
        * Handles any touch events that aren't grabbed by one of the controls.
        */
       public boolean onTouchEvent(MotionEvent e) {
           float x = e.getX();
           float y = e.getY();

           switch (e.getAction()) {
               case MotionEvent.ACTION_MOVE:
               case MotionEvent.ACTION_DOWN:
                   //Log.v(TAG, "onTouchEvent act=" + e.getAction() + " x=" + x + " y=" + y);
                   if (mRenderThread != null) {
                       RenderHandler rh = mRenderThread.getHandler();
                       rh.sendPosition((int) x, (int) y);

                       // Forcing a redraw can cause sluggish-looking behavior if the touch
                       // events arrive quickly.
                       //rh.sendRedraw();
                   }
                   break;
               default:
                   break;
           }

           return true;
       }

       /**
        * Updates the current state of the controls.
        */
       private void updateControls() {
           String str = getString(R.string.tfcCameraParams, mCameraPreviewWidth,
                   mCameraPreviewHeight, mCameraPreviewFps);
           TextView tv = (TextView) findViewById(R.id.tfcCameraParams_text);
           tv.setText(str);

           str = getString(R.string.tfcRectSize, mRectWidth, mRectHeight);
           tv = (TextView) findViewById(R.id.tfcRectSize_text);
           tv.setText(str);

           str = getString(R.string.tfcZoomArea, mZoomWidth, mZoomHeight);
           tv = (TextView) findViewById(R.id.tfcZoomArea_text);
           tv.setText(str);
       }

       @Override
       public void onClick(View view) {

           if (recording) {
               recorder.stop();
               recording = false;

               // Let's initRecorder so we can record again
               initRecorder();
               prepareRecorder();
           } else {
               recording = true;
               recorder.start();
           }
       }


       private void initRecorder() {
           recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
           recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);

           CamcorderProfile cpHigh = CamcorderProfile
                   .get(CamcorderProfile.QUALITY_HIGH);
           recorder.setProfile(cpHigh);
           String path = Environment.getExternalStorageDirectory() + File.separator
                   + Environment.DIRECTORY_DCIM + File.separator + "AlphaRun";

           recorder.setOutputFile(path);
           recorder.setMaxDuration(50000); // 50 seconds
           recorder.setMaxFileSize(5000000); // Approximately 5 megabytes

       }

       private void prepareRecorder() {


           try {
               recorder.prepare();
           } catch (IllegalStateException e) {
               e.printStackTrace();
               finish();
           } catch (IOException e) {
               e.printStackTrace();
               finish();
           }
       }




       /**
        * Thread that handles all rendering and camera operations.
        */
       private static class RenderThread extends Thread implements
               SurfaceTexture.OnFrameAvailableListener {
           // Object must be created on render thread to get correct Looper, but is used from
           // UI thread, so we need to declare it volatile to ensure the UI thread sees a fully
           // constructed object.
           private volatile RenderHandler mHandler;

           // Used to wait for the thread to start.
           private Object mStartLock = new Object();
           private boolean mReady = false;

           private MainHandler mMainHandler;

           private Camera mCamera;
           private int mCameraPreviewWidth, mCameraPreviewHeight;

           private EglCore mEglCore;
           private WindowSurface mWindowSurface;
           private int mWindowSurfaceWidth;
           private int mWindowSurfaceHeight;

           // Receives the output from the camera preview.
           private SurfaceTexture mCameraTexture;

           // Orthographic projection matrix.
           private float[] mDisplayProjectionMatrix = new float[16];

           private Texture2dProgram mTexProgram;
           private final ScaledDrawable2d mRectDrawable =
                   new ScaledDrawable2d(Drawable2d.Prefab.RECTANGLE);
           private final Sprite2d mRect = new Sprite2d(mRectDrawable);

           private int mZoomPercent = DEFAULT_ZOOM_PERCENT;
           private int mSizePercent = DEFAULT_SIZE_PERCENT;
           private int mRotatePercent = DEFAULT_ROTATE_PERCENT;
           private float mPosX, mPosY;


           /**
            * Constructor.  Pass in the MainHandler, which allows us to send stuff back to the
            * Activity.
            */
           public RenderThread(MainHandler handler) {
               mMainHandler = handler;

           }

           /**
            * Thread entry point.
            */
           @Override
           public void run() {
               Looper.prepare();

               // We need to create the Handler before reporting ready.
               mHandler = new RenderHandler(this);
               synchronized (mStartLock) {
                   mReady = true;
                   mStartLock.notify();    // signal waitUntilReady()
               }

               // Prepare EGL and open the camera before we start handling messages.
               mEglCore = new EglCore(null, 0);
               openCamera(REQ_CAMERA_WIDTH, REQ_CAMERA_HEIGHT, REQ_CAMERA_FPS);

               Looper.loop();

               Log.d(TAG, "looper quit");
               releaseCamera();
               releaseGl();
               mEglCore.release();

               synchronized (mStartLock) {
                   mReady = false;
               }
           }

           /**
            * Waits until the render thread is ready to receive messages.
            * <p>
            * Call from the UI thread.
            */
           public void waitUntilReady() {
               synchronized (mStartLock) {
                   while (!mReady) {
                       try {
                           mStartLock.wait();
                       } catch (InterruptedException ie) { /* not expected */ }
                   }
               }
           }

           /**
            * Shuts everything down.
            */
           private void shutdown() {
               Log.d(TAG, "shutdown");
               Looper.myLooper().quit();
           }

           /**
            * Returns the render thread's Handler.  This may be called from any thread.
            */
           public RenderHandler getHandler() {
               return mHandler;
           }

           /**
            * Handles the surface-created callback from SurfaceView.  Prepares GLES and the Surface.
            */
           private void surfaceAvailable(SurfaceHolder holder, boolean newSurface) {

               Surface surface = holder.getSurface();
               mWindowSurface = new WindowSurface(mEglCore, surface, false);
               mWindowSurface.makeCurrent();

               // Create and configure the SurfaceTexture, which will receive frames from the
               // camera.  We set the textured rect's program to render from it.
               mTexProgram = new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT);
               int textureId = mTexProgram.createTextureObject();
               mCameraTexture = new SurfaceTexture(textureId);
               mRect.setTexture(textureId);

               if (!newSurface) {
                   // This Surface was established on a previous run, so no surfaceChanged()
                   // message is forthcoming.  Finish the surface setup now.
                   //
                   // We could also just call this unconditionally, and perhaps do an unnecessary
                   // bit of reallocating if a surface-changed message arrives.
                   mWindowSurfaceWidth = mWindowSurface.getWidth();
                   mWindowSurfaceHeight = mWindowSurface.getWidth();
                   finishSurfaceSetup();
               }

               mCameraTexture.setOnFrameAvailableListener(this);



           }

           /**
            * Releases most of the GL resources we currently hold (anything allocated by
            * surfaceAvailable()).
            * </p><p>
            * Does not release EglCore.
            */
           private void releaseGl() {
               GlUtil.checkGlError("releaseGl start");

               if (mWindowSurface != null) {
                   mWindowSurface.release();
                   mWindowSurface = null;
               }
               if (mTexProgram != null) {
                   mTexProgram.release();
                   mTexProgram = null;
               }
               GlUtil.checkGlError("releaseGl done");

               mEglCore.makeNothingCurrent();
           }

           /**
            * Handles the surfaceChanged message.
            * </p><p>
            * We always receive surfaceChanged() after surfaceCreated(), but surfaceAvailable()
            * could also be called with a Surface created on a previous run.  So this may not
            * be called.
            */
           private void surfaceChanged(int width, int height) {
               Log.d(TAG, "RenderThread surfaceChanged " + width + "x" + height);

               mWindowSurfaceWidth = width;
               mWindowSurfaceHeight = width;
               finishSurfaceSetup();
           }

           /**
            * Handles the surfaceDestroyed message.
            */
           private void surfaceDestroyed() {
               // In practice this never appears to be called -- the activity is always paused
               // before the surface is destroyed.  In theory it could be called though.
               Log.d(TAG, "RenderThread surfaceDestroyed");
               releaseGl();
           }

           /**
            * Sets up anything that depends on the window size.
            * </p><p>
            * Open the camera (to set mCameraAspectRatio) before calling here.
            */
           private void finishSurfaceSetup() {
               int width = mWindowSurfaceWidth;
               int height = mWindowSurfaceHeight;
               Log.d(TAG, "finishSurfaceSetup size=" + width + "x" + height +
                       " camera=" + mCameraPreviewWidth + "x" + mCameraPreviewHeight);

               // Use full window.
               GLES20.glViewport(0, 700, width, height);

               // Simple orthographic projection, with (0,0) in lower-left corner.
               Matrix.orthoM(mDisplayProjectionMatrix, 0, 0, width, 0, height, -1, 1);

               // Default position is center of screen.
               mPosX = width / 2.0f;
               mPosY = height / 2.0f;

               updateGeometry();

               // Ready to go, start the camera.
               Log.d(TAG, "starting camera preview");
               try {
                   mCamera.setPreviewTexture(mCameraTexture);

               } catch (IOException ioe) {
                   throw new RuntimeException(ioe);
               }
               mCamera.startPreview();
           }

           /**
            * Updates the geometry of mRect, based on the size of the window and the current
            * values set by the UI.
            */
           private void updateGeometry() {
               int width = mWindowSurfaceWidth;
               int height = mWindowSurfaceHeight;


               int smallDim = Math.min(width, height);
               // Max scale is a bit larger than the screen, so we can show over-size.
               float scaled = smallDim * (mSizePercent / 100.0f) * 1.25f;
               float cameraAspect = (float) mCameraPreviewWidth / mCameraPreviewHeight;
               int newWidth = Math.round(scaled * cameraAspect);
               int newHeight = Math.round(scaled);

               float zoomFactor = 1.0f - (mZoomPercent / 100.0f);
               int rotAngle = Math.round(360 * (mRotatePercent / 100.0f));

               mRect.setScale(newWidth, newHeight);
               mRect.setPosition(mPosX, mPosY);
               mRect.setRotation(rotAngle);
               mRectDrawable.setScale(zoomFactor);

               mMainHandler.sendRectSize(newWidth, newHeight);
               mMainHandler.sendZoomArea(Math.round(mCameraPreviewWidth * zoomFactor),
                       Math.round(mCameraPreviewHeight * zoomFactor));
               mMainHandler.sendRotateDeg(rotAngle);
           }

           @Override   // SurfaceTexture.OnFrameAvailableListener; runs on arbitrary thread
           public void onFrameAvailable(SurfaceTexture surfaceTexture) {
               mHandler.sendFrameAvailable();
           }

           /**
            * Handles incoming frame of data from the camera.
            */
           private void frameAvailable() {
               mCameraTexture.updateTexImage();

               draw();
           }

           /**
            * Draws the scene and submits the buffer.
            */
           private void draw() {
               GlUtil.checkGlError("draw start");

               GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
               GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
               mRect.draw(mTexProgram, mDisplayProjectionMatrix);
               mWindowSurface.swapBuffers();

               GlUtil.checkGlError("draw done");
           }

           /**
            * Opens a camera, and attempts to establish preview mode at the specified width
            * and height with a fixed frame rate.
            * </p><p>
            * Sets mCameraPreviewWidth / mCameraPreviewHeight.
            */
           private void openCamera(int desiredWidth, int desiredHeight, int desiredFps) {
               if (mCamera != null) {
                   throw new RuntimeException("camera already initialized");
               }

               Camera.CameraInfo info = new Camera.CameraInfo();

               // Try to find a front-facing camera (e.g. for videoconferencing).
               int numCameras = Camera.getNumberOfCameras();
               for (int i = 0; i &lt; numCameras; i++) {
                   Camera.getCameraInfo(i, info);
                   if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                       mCamera = Camera.open(i);
                       break;
                   }
               }
               if (mCamera == null) {
                   Log.d(TAG, "No front-facing camera found; opening default");
                   mCamera = Camera.open();    // opens first back-facing camera
               }
               if (mCamera == null) {
                   throw new RuntimeException("Unable to open camera");
               }

               Camera.Parameters parms = mCamera.getParameters();

               CameraUtils.choosePreviewSize(parms, desiredWidth, desiredHeight);
               parms.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
               // Try to set the frame rate to a constant value.
               int thousandFps = CameraUtils.chooseFixedPreviewFps(parms, desiredFps * 1000);

               // Give the camera a hint that we're recording video.  This can have a big
               // impact on frame rate.
               parms.setRecordingHint(true);

               mCamera.setParameters(parms);

               int[] fpsRange = new int[2];
               Camera.Size mCameraPreviewSize = parms.getPreviewSize();
               parms.getPreviewFpsRange(fpsRange);
               String previewFacts = mCameraPreviewSize.width + "x" + mCameraPreviewSize.height;
               if (fpsRange[0] == fpsRange[1]) {
                   previewFacts += " @" + (fpsRange[0] / 1000.0) + "fps";
               } else {
                   previewFacts += " @[" + (fpsRange[0] / 1000.0) +
                           " - " + (fpsRange[1] / 1000.0) + "] fps";
               }
               Log.i(TAG, "Camera config: " + previewFacts);

               mCameraPreviewWidth = mCameraPreviewSize.width;
               mCameraPreviewHeight = mCameraPreviewSize.height;
               mMainHandler.sendCameraParams(mCameraPreviewWidth, mCameraPreviewHeight,
                       thousandFps / 1000.0f);
           }

           /**
            * Stops camera preview, and releases the camera to the system.
            */
           private void releaseCamera() {
               if (mCamera != null) {
                   mCamera.stopPreview();
                   mCamera.release();
                   mCamera = null;
                   Log.d(TAG, "releaseCamera -- done");
               }
           }
       }

    }
    </p>
  • A Comprehensive Guide to Robust Digital Marketing Analytics

    15 novembre 2023, par Erin — Analytics Tips

    First impressions are everything. This is not only true for dating and job interviews but also for your digital marketing strategy. Like a poorly planned job application getting tossed in the “no thank you” pile, 38% of visitors to your website will stop engaging with your content if they find the layout unpleasant. Thankfully, digital marketers can access data that can be harnessed to optimise websites and turn those “no thank you’s” into “absolutely’s.”

    So, how can we transform raw data into valuable insights that pay off ? The key is web analytics tools that can help you make sense of it all while collecting data ethically. In this article, we’ll equip you with ways to take your digital marketing strategy to the next level with the power of web analytics.

    What are the different types of digital marketing analytics ?

    Digital marketing analytics are like a cipher into the complex behaviour of your buyers. Digital marketing analytics help collect, analyse and interpret data from any touchpoint you interact with your buyers online. Whether you’re trying to gauge the effectiveness of a new email marketing campaign or improve your mobile app layout, there’s a way for you to make use of the insights you gain.

    Icons representing the 8 types of digital marketing analytics

    As we go through the eight commonly known types of digital marketing analytics, please note we’ll primarily focus on what falls under the umbrella of web analytics. 

    1. Web analytics help you better understand how users interact with your website. Good web analytics tools will help you understand user behaviour while securely handling user data. 
    2. Learn more about the effectiveness of your organisation’s social media platforms with social media analytics. Social media analytics include user engagement, post reach and audience demographics. 
    3. Email marketing analytics help you see how email campaigns are being engaged with.
    4. Search engine optimisation (SEO) analytics help you understand your website’s visibility in search engine results pages (SERPs). 
    5. Pay-per-click (PPC) or campaign analytics measure the performance of paid advertising campaigns.
    6. Content marketing analytics focus on how your content is performing with your audience. 
    7. Customer analytics helps organisations identify and examine buyer behaviour to retain the biggest spenders. 
    8. Mobile app analytics track user interactions within mobile applications. 

    Choosing which digital marketing analytics tools are the best fit for your organisation is not an easy task. When making these decisions, it’s critical to remember the ethical implications of data collection. Although data insights can be invaluable to your organisation, they won’t be of much use if you lose the trust of your users. 

    Tips and best practices for developing robust digital marketing analytics 

    So, what separates top-notch, robust digital marketing analytics from the rest ? We’ve already touched on it, but a big part involves respecting user privacy and ethically handling data. Data security should be on your list of priorities, alongside conversion rate optimisation when developing a digital marketing strategy. In this section, we will examine best practices for using digital marketing analytics while retaining user trust.

    Lightbulb with a target in the center being struck by arrows

    Clear objectives

    Before comparing digital marketing analytics tools, you should define clear and measurable goals. Try asking yourself what you need your digital marketing analytics strategy to accomplish. Do you want to improve conversion rates while remaining data compliant ? Maybe you’ve noticed users are not engaging with your platform and want to fix that. Save yourself time and energy by focusing on the most relevant pain points and areas of improvement.

    Choose the right tools for the job

    Don’t just base your decision on what other people tell you. Take the tool for a test drive — free trials allow you to test features and user interfaces and learn more about the platform before committing. When choosing digital marketing analytics tools, look for ones that ensure data accuracy as well as compliance with privacy laws like GDPR.

    Don’t overlook data compliance

    GDPR ensures organisations prioritise data protection and privacy. You could be fined up to €20 million, or 4% of the previous year’s revenue for violations. Without data compliance practices, you can say goodbye to the time and money spent on digital marketing strategies. 

    Don’t sacrifice data quality and accuracy

    Inaccurate and low-quality data can taint your analysis, making it hard to glean valuable insights from your digital marketing analytics efforts. Many analytics tools only show sampled data or use AI and ML to fill data gaps, potentially compromising the accuracy and completeness of your analytics. 

    When your analytics are based on incomplete or inaccurate data, it’s like trying to assemble a puzzle with missing pieces—you might get a glimpse of the whole picture, but it’s never quite clear. Accurate data isn’t just helpful—it’s the backbone of smart marketing strategies. It lets you make confident decisions and enables precise targeting for greater impact.

    Communicate your findings

    Having insights is one thing ; effectively communicating complex data findings is just as important. Customise dashboards to display key metrics aligned with your objectives. Make sure to automate reports, allowing stakeholders to stay updated without manual intervention. 

    Understand the user journey

    To optimise your conversion rates, you need to understand the user journey. Start by analysing visitors interactions with your website — this will help you identify conversion bottlenecks in your sales or lead generation processes. Implement A/B testing for landing page optimisation, refining elements like call-to-action buttons or copy, and leverage Form Analytics to make informed, data-driven improvements to your forms.

    Continuous improvement

    Learn from the data insights you gain, and iterate your marketing strategies based on the findings. Stay updated with evolving web analytics trends and technologies to leverage new growth opportunities. 

    Why you need web analytics to support your digital marketing analytics toolbox

    You wouldn’t set out on a roadtrip without a map, right ? Digital marketing analytics without insights into how users interact with your website are just as useless. Used ethically, web analytics tools can be an invaluable addition to your digital marketing analytics toolbox. 

    The data collected via web analytics reveals user interactions with your website. These could include anything from how long visitors stay on your page to their actions while browsing your website. Web analytics tools help you gather and understand this data so you can better understand buyer preferences. It’s like a domino effect : the more you understand your buyers and user behaviour, the better you can assess the effectiveness of your digital content and campaigns. 

    Web analytics reveal user behaviour, highlighting navigation patterns and drop-off points. Understanding these patterns helps you refine website layout and content, improving engagement and conversions for a seamless user experience.

    Magnifying glass examining various screens that contain data

    Concrete CMS harnessed the power of web analytics, specifically Matomo’s Form Analytics, to uncover crucial insights within their user onboarding process. Their data revealed a significant issue : the “address” input field was causing visitors to drop off and not complete the form, severely impacting the overall onboarding experience and conversion rate.

    Armed with these insights, Concrete CMS made targeted optimisations to the form, resulting in a substantial transformation. By addressing the specific issue identified through Form Analytics, they achieved an impressive outcome – a threefold increase in lead generation.

    This case is a great example of how web analytics can uncover customer needs and preferences and positively impact conversion rates. 

    Ethical implications of digital marketing analytics

    As we’ve touched on, digital marketing analytics are a powerful tool to help better understand online user behaviour. With great power comes great responsibility, however, and it’s a legal and ethical obligation for organisations to protect individual privacy rights. Let’s get into the benefits of practising ethical digital marketing analytics and the potential risks of not respecting user privacy : 

    • If someone uses your digital platform and then opens their email one day to find it filled with random targeted ad campaigns, they won’t be happy. Avoid losing user trust — and facing a potential lawsuit — by informing users what their data will be used for. Give them the option to consent to opt-in or opt-out of letting you use their personal information. If users are also assured you’ll safeguard personal information against unauthorised access, they’ll be more likely to trust you to handle their data securely.
    • Protecting data against breaches means investing in technology that will let you end-to-end encrypt and securely store data. Other important data-security best practices include access control, backing up data regularly and network and physical security of assets.

    A fine line separates digital marketing analytics and misusing user data — many companies have gotten into big trouble for crossing it. (By big trouble, we mean millions of dollars in fines.) When it comes to digital marketing analytics, you should never cut corners when it comes to user privacy and data security. This balance involves understanding what data can be collected and what should be collected and respecting user boundaries and preferences.

    A balanced scale with a salesperson on one side and money/profit on the other

    Learn more 

    We discussed a lot of facets of digital marketing analytics, namely how to develop a robust digital marketing strategy while prioritising data compliance. With Matomo, you can protect user data and respect user privacy while gaining invaluable insights into user behaviour with 100% accurate data. Save your organisation time and money by investing in a web analytics solution that gives you the best of both worlds. 

    If you’re ready to begin using ethical and robust digital marketing analytics on your website, try Matomo. Start your 21-day free trial now — no credit card required.

  • Encode Frames to Video with C Library

    31 juillet 2018, par NetherGranite

    For the sake of continuity, let us assume "RGB values" are the following :

    typedef struct RGB {
       uint8_t r, g, b;
    } rgb;

    However, if you feel that a different color space is more appropriate for this question, please use that instead.

    How might I go about writing 2D arrays of RGB values to a video in C given an output format and framerate ?

    Before I continue, I should specify that I wish to be able to do this all within one program. I am trying to add functionality to an application that would allow it to compile videos frame by frame without having to leave it.

    Additionally, my needs for this functionality are extremely basic ; I simply need to be able to set individual pixels to certain colors.

    The closest I have come to a solution so far is the C library FFmpeg. Allow me to describe what I was able to learn on my own :

    After looking through its documentation, I came across the function avcodec_send_frame(avctx, frame), whose parameters are of the types AVCodexContext* and const AVFrame* respectively. If these are not the right tools for what I am trying to do, please ignore the rest of the question and instead point me towards what I should be using.

    However, I do not know which fields of avctx and frame must be set manually and which do not. The reason I assume some do not is because both are extremely large structures, but correct me if I am wrong.

    Question 1 : What values of an AVCodecContext and AVFrame must be set ? Of these, what is/are the recommended value(s) for each of them ?

    Additionally, I was only able to find instructions on how to initialize an AVFrame (using av_frame_alloc() and av_frame_get_buffer()) but not for an AVCodexConstant.

    Question 2 : Is there a proper way to initialize an AVCodexConstant ? And just in case, is the method of initializing an AVFrame described above correct ? Do any of the fields of either have a proper method of initialization ?

    Also, I was not able to find official documentation on how to take this AVCodexConstant (which I assume contains the video information) and turn it into a video. I apologize if the documentation for this is easy to find and I just missed it.

    Question 3 : How do I turn an AVCodexConstant into a file of a given format ?

    And, given my limited knowledge :

    Question 4 : Are there any other parts to this process that I am missing, and do I have any of the above parts wrong ?


    Please keep in mind that I found out about FFmpeg for the first time very recently, and as a result, I am a complete beginner to this. Additionally, my experience with C is very limited, so I would greatly appreciate it if you could note which files need to be included with #include.

    Feel free to even go as far as recommending something other than FFmpeg, just as long as it is written in C. I do not need power-user options, but I would greatly prefer flexibility in what audio and video file types the library can handle.


    Addressing Potential Duplicates

    I appologize for how long this section is ; I just want to have my bases covered. I heavily apologize, however, if this is in fact a duplicate of a question that I was just unable to find.

    • ffmpeg C API documentation/tutorial [closed] — This question was too open-ended and received answers pointing the asker towards a tutorial at dranger.com, a tutorial that confusingly muddied the waters by focusing heavily on a graphics library of choice. Please do not take this as me saying it is bad ; I am just enough of a beginner that I could not wade through it all.
    • Encoding frames to video with ffmpeg — Although this question seems to have been asking the same thing, it is geared towards Unreal Engine 4, and the asker provided sample code, making it difficult for me to understand which of parts of the accepted answer were necessary for me and which were not.
    • How to write frames to a video file ? — While this also asked the same thing, the accepted answer simply provides a command instead of an explanation of code.
    • YUV Raw frames to video stream — While the accepted answer for this question is a command, the question states that it is looking for a way to encode frames generated by C++ code. Is there some way to run commands in code that I haven’t been able to find ?
    • Converting sequenced frames to video — Not only is the asker’s code written in Python, but it also seems to use already-existing image files as frames.
    • How to write bitmaps as frames to H.264 with x264 in C\C++ ? — The accepted answer seems to describe a process that would take multiple applications, but I could be wrong as I am enough of a beginner that I am not sure exactly what it means other than Step 3.
    • How to write bitmaps as frames to Ogg Theora in C\C++ ? — Although it isn’t a problem that the question specifies the ogg format, it is a problem that the accepted answer suggests libtheora, which appears to only work with ogg files.