Giter VIP home page Giter VIP logo

gpmf-extract's Introduction

GPMF extract

Finds the metadata track in GoPro (Hero5 and later) video files (or any other camera that implements GPMF) and extracts it for later analysis and processing.

Accepts a File and returns a Promise that resolves to an object with a rawData (Buffer in NodeJS, UInt8Array in Browser) and timing data (timing), useful for interpreting the data.

Once extracted, you can process the data with gopro-telemetry.

Install:

$ npm i gpmf-extract

Use:

const gpmfExtract = require('gpmf-extract');
gpmfExtract(file).then(res => {
  console.log('Length of data received:', res.rawData.length);
  console.log('Framerate of data received:', 1 / res.timing.frameDuration);
  // Do what you want with the data
});

You can specify some options in an object as a second argument:

  • browserMode: Default: false. Change behaviour to use in browser. This is optional for debugging reasons
  • useWorker: Default: true. In browser mode, use a web worker to avoid locking the browser. This is optional as it seems to crash on some recent browsers
  • progress: Pass a function to read the processed percentage updates
  • cancellationToken: An optional object, containing a cancelled property, that allows for cancelling the extraction process. Currently only supported in browser mode. If cancelled, the extraction process will fail with the error message "Canceled by user".
const gpmfExtract = require('gpmf-extract');
const progress = percent => console.log(`${percent}% processed`);
const cancellationToken = { cancelled: false };
gpmfExtract(file, { browserMode: true, progress, cancellationToken }).then(
  res => {
    if (!res) return; //cancelled
    // Do what you want with the data
  }
);
// Some other processes
cancellationToken.cancelled = true;

About

This code was created for the GoPro Telemetry Extractor.

Here's a gallery with cool uses of the GoPro telemetry.

This project is possible thanks to the gpmf-parser documentation, open sourced by GoPro.

More creative coding

If you liked this you might like some of my app prototyping.

Contribution

Please make your changes to the dev branch, so that automated tests can be run before merging to master. Also, if possible, provide tests for new functionality.

To-DO

  • Fix #46 Memory allocation with large files on certain browsers when using the web worker option
  • Increase browser compatibility
  • Extract highlights

Acknowledgements/credits

gpmf-extract's People

Contributors

akxe avatar dependabot[bot] avatar hugopoi avatar juanirache avatar jwagner avatar motoyasu-yamada avatar sarfata avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

gpmf-extract's Issues

Update `mp4box` to fix browser errors

Hi, in a recent try to get GPS data from a GoPro video, I found out that there are multiple errors. One that can be fixed when the mp4box is updated is the ReferenceError: ref is not defined`.

Others might come later...

It looks to me like there is not enough testing done on the browser, as it requires a lot of polyfills or workarounds. Try running the library in a "strict" mode and without any polyfills to see what needs to be fixed. (I am not against using polyfill, but I would like to know I have to use one in order to have it working)

PS: I would consider buying the pro license as a "priority support" to get the GPS extraction working on the browser. (Also please add types?)

Get GPMF on large files

Hi,

Not an issue, but a resolution. This might make gpmf-extract redundant. I use the following, simply leveraging ffprobe and ffmpeg. The returned raw data can be directly passed to gopro-telemetry (thanks for this by the way!).

Requires ffprobe-installer, ffmpeg-installer, ffprobe-client, fluent-ffmpeg.

process.env.FFPROBE_PATH = require('@ffprobe-installer/ffprobe').path;
const ffprobe = require('ffprobe-client');

const ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(require('@ffmpeg-installer/ffmpeg').path);

const extractGPMF = async videoFile => {
  const ffData = await ffprobe(videoFile);

  for (let i = 0; i < ffData.streams.length; i++) {
    if (ffData.streams[i].codec_tag_string === 'gpmd') {
      return await extractGPMFAt(videoFile, i);
    }
  }
  return null;
};

const extractGPMFAt = async (videoFile, stream) => {
  let rawData = Buffer.alloc(0);
  await new Promise(resolve => {
    ffmpeg(videoFile)
      .outputOption('-y')
      .outputOptions('-codec copy')
      .outputOptions(`-map 0:${stream}`)
      .outputOption('-f rawvideo')
      .pipe()
      .on('data', chunk => {
        rawData = Buffer.concat([rawData, chunk]);
      }).on('end', resolve);
  });
  return rawData;
};

Node.js File size is to big

Hello, i´m trying to get all files from my GOPRO hero 11. and i´m trying to parse all files to node.js to extract all data to JSON files. I´m getting this error.

node:fs:424
      throw new ERR_FS_FILE_TOO_LARGE(size);
      ^

RangeError [ERR_FS_FILE_TOO_LARGE]: File size (4005420209) is greater than 2 GiB
    at new NodeError (node:internal/errors:387:5)
    at tryCreateBuffer (node:fs:424:13)
    at Object.readFileSync (node:fs:469:14)
    at Object.<anonymous> (C:\dev\gopro-telemetry-master\teste.js:6:17)
    at Module._compile (node:internal/modules/cjs/loader:1198:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10)
    at Module.load (node:internal/modules/cjs/loader:1076:32)
    at Function.Module._load (node:internal/modules/cjs/loader:911:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:22:47 {
  code: 'ERR_FS_FILE_TOO_LARGE'
}

and this is my code

const gpmfExtract = require('gpmf-extract');
const goproTelemetry = require(`gopro-telemetry`);
const fs = require('fs');


const file = fs.readFileSync('E:\\Gravações\\GoPro\\2024-06-08\\HERO5 Black 1\\GOPR9562.mp4');


gpmfExtract(file)
    .then(extracted => {
        goproTelemetry(extracted, {}, telemetry => {
            fs.writeFileSync('.\\output_path.json', JSON.stringify(telemetry));
            console.log('Telemetry saved as JSON');
        });
    })
    .catch(error => console.error(error));

In browser memory allocation

First of all, congrats for the great work! I am using your code in a project to analyze GPS data for go-kart and trackday. I extract the GPMF data in the browser, and used browserify to bundle the code together. I run the module as below:

data = await gpmfExtract([file object],true,progress);

The problem that started to show up lately, is that the memory the browser allocates to process a single big file (4GB mp4 file) is so big that eventually the window hangs. I've checked that the same issue happens when I process the file in https://goprotelemetryextractor.com/free/# (it allocates almos 7GB for the chrome process). Maybe this is a mp4box issue?

enable processing to be cancelled

If processing a large file, it can take a long time and the user may wish to cancel in that time. Additionally, I have come across a scenario where a video I've been supplied throws an error in mp4box that doesn't cancel the process, and progress goes above 100% without ever finishing (I allowed it to get to >800% before refreshing the page, meanwhile CPU usage is 100%). If running using webWorkers, throwing an Error from the progress function doesn't stop the process either.

Can you please enable functionality to pass in a cancellation token in options?

Error: Invalid GPMF data. Root object must contain DEVC key

Hi. Thanks for the work you've done on this repo.

I have footage from a GoPro 10, which I've ran through gpmf-extract, but when I pass the results on to goproTelemetry({rawData, timing}, options), I get the error in the title. The data comes to about 4mb I can send to you directly (or the video too, it's 280mb.

I put console.log(result); at line 249 of parseKLV.js and get { interpretSamples: undefined }

Parsing large .360 file data from array of file paths

I'm a little out of my element here as I'm more a Python guy and am still coming to terms with asynchronous functions and promises work, but I'm currently stuck and was hoping someone more familiar with this library could suggest a better way of trying to accomplish what I'm doing.

Basically, I'm trying to write a my own JS app on top of this library to parse a specified directory and for any files with a .360 extension, extract the gpmf data, and run it through the telemetry library. The end goal is to have a standalone tool I can use to import MAX videos to a specified directory location and parse the associated metadata, ultimately indexing it in a way that can be parsed programmatically (e.g. show all files with highlight tags recorded between a specified time/date range).

Due to the size of the .360 files being parsed, I've run into memory issues and tried implementing the bufferAppender function, but can't seem to get it to work in tandem correctly with the for loop looping through an array of files. I suspect this is an issue with the logic I've tried implementing, but nothing I've tried so far has yielded much success. It looks like the for loop is restarting before bufferAppender finishes sending all the fileData to gpmfExtract.

const goproTelemetry = require(`gopro-telemetry`);
const path = require('path');
const fs = require('fs');
const fsPromises = require('fs').promises;

const directoryPath = path.join("D:\\", "GoPro Max")

fsPromises.readdir(directoryPath)
    .then(files => {
        for (let file of files) {
            let fileExt = file.split('.').pop();
            if (fileExt == 360) {
                console.log(file + ' found');
                let fileName = file.split('.').shift();
                let filePath = path.join(directoryPath, file);
                let jsonPath = path.join(directoryPath, fileName + '.json');;

                function bufferAppender(filePath, chunkSize) {
                    return function (mp4boxFile) {
                        var stream = fs.createReadStream(filePath, { highWaterMark: chunkSize });
                        console.log('Created bufferAppender stream')
                        var bytesRead = 0;
                        stream.on('end', () => {
                            mp4boxFile.flush();
                        });
                        stream.on('data', (chunk) => {
                            console.log('Chunk read');
                            var arrayBuffer = new Uint8Array(chunk).buffer;
                            arrayBuffer.fileStart = bytesRead;
                            mp4boxFile.appendBuffer(arrayBuffer);
                            bytesRead += chunk.length;
                            //console.log('Chunk length: ' + chunk.length);
                        });
                        stream.resume();
                    };
                }

                let fileData = (async () => {
                    await bufferAppender(filePath, 500 * 1024 * 1204)})();
                console.log('running gpmfExtract')
                gpmfExtract(fileData)
                        .then(extracted => {
                            goproTelemetry(extracted, {}, telemetry => {
                                fs.writeFileSync(jsonPath, JSON.stringify((telemetry)));
                                console.log('Telemetry for ' + file + ' saved to JSON');
                            })
                        })
                        .catch(error => console.error(error))
         }
    }

Track not found

In the browser on large files I'm getting a 'Track not found' error, then progress increasing for ever to very large numbers.
image
(upto 60k + at now)

Is this working and just taking a long time?

0.1.22 produce nothing and fail silently with nodejs

Very weird but the changes introduce in v0.1.21...v0.1.22 produce nothing, the extract start but stop just before the end of the file, and nodejs exit without any error or warning.

nodejs version v15.6.0

Sample of code for reproducing

const res = await gpmfExtract(bufferAppender(options.input, 10 * 1024 * 1024));

  function bufferAppender(path, chunkSize) {
    return function (mp4boxFile) {
      console.log('SETUP STREAM');
      var stream = fs.createReadStream(path, { highWaterMark: chunkSize });
      var bytesRead = 0;
      stream.on('end', () => {
        mp4boxFile.flush();
      });
      stream.on('data', chunk => {
        var arrayBuffer = new Uint8Array(chunk).buffer;
        arrayBuffer.fileStart = bytesRead;
        mp4boxFile.appendBuffer(arrayBuffer);
        bytesRead += chunk.length;
        console.log('progress bytesRead', bytesRead);
      });
      stream.on('error', error => {
         console.error(error);
      });
      stream.resume();
    };
  }

gpmf-extract bundle error in Windows 10/11 UWP Webview on document load

Hello,

I have a strong use case for my website https://maps.video to run in a Windows 10/11 UWP Webview and the user to extract the gps data using gpmf-extract from GoPro MP4 so the video route can be added and viewed within the Windows 10/11 UWP app.

Currently the user has to extract gps and add video in the website first and then open the app to view the video routes in the Webview.

I have today created a fresh bundle (https://maps.video/public/js/bundle-3.js) out of gpmf-extract and GoPro telemetry repos but when the bundle loads in the Windows 10/11 UWP web view an error occurs which stops the gpmf-extract from working later.

I can confirm that the bundle works in all modern browsers that I use including Edge on Windows and Apple Mac WKWebView just fine, its just not working in Windows 10/11 UWP Webview which is the most important one to move my project forwards.

I attach screenshots of the error in Microsoft Edge Devtools Preview which can be downloaded from Microsoft Store. (Here bundle-2.js is just an older bundle, the error occurs in the old bundle and the latest bundle-3.js)

error-dev-tools-preview

error-dev-tools-preview-console

My app can be downloaded from https://maps.video/windows-10-app, install the app and when it opens click to choose the videos folder(any folder will do), this then opens the web view with the map in another window.

Go to Edge Devtools Preview and refresh the list, select maps.video, you should see the error. Click script on right to open console to see where the error occurs.

It seems to have something to do with sticky = { …stickiew[d[ etc…

Could it be this ‘device name’ part?

Its strange as the bundle works in all browsers including Apple Mac web view but it has this error when the bundle-3.js loads in the head of the html.

Not sure why an error would occur in the first place when the bundle loads?

Thanks in advance is someone can help.

"Uncaught ReferenceError: global is not defined at..." when using with webpack (Angular 10^)

First of all, thanks for developing this wonderful tool!

On the issue: when using the package in a browser project with webpack, in our case Angular, and importing it with import * as GpmfExtract from 'gpmf-extract';, we receive this error: Uncaught ReferenceError: global is not defined at... which seems to stem from the inline-worker: var WORKER_ENABLED = !!(global === global.window && global.URL && global.Blob && global.Worker);

For the moment, the solution is to declare (window as any).global = window; in the project's polyfills.ts file, but this feels more like a temporary fix than a proper usage.

use in React Native projects

I am trying to use this package in a React Native project, however, it complains about the 'Buffer'.

Can we fix this so that it somehow supports in a RN environment?

how resolve error [BoxParser] Unlimited box size not supported for type in module : gpmf-extract

I have an issue with gpmf-extract module to finds the metadata track in GoPro camera when I set the video with mp4 format to gpmfExtract (gpmf-extract) module.

this module page on nmp :
https://www.npmjs.com/package/gpmf-extract
and also home page on git :
https://github.com/JuanIrache/gpmf-extract#readme

const gpmfExtract = require('gpmf-extract');
const fs = require('fs');
const path = require('path');
const util = require('util');

const dirAddress = 'E:\\mp4files';
const pathStat = util.promisify(fs.stat);

pathStat(dirAddress).then(stat => {
    if (stat) {
        if (stat.isDirectory()) {
            const folderContent = fs.readdirSync(dirAddress,'utf-8');
            if(folderContent.length){
                    folderContent.forEach(contentName => {
                        const contentSliced = contentName.split('.');
                        if(contentSliced[1] === 'mp4' || contentSliced[1] === 'MP4'){
                            const fileAddress = path.join(dirAddress,contentName);
                            gpmfExtract(fileAddress).then(res => {
                                console.log('Length of data received:', res.rawData.length);
                                console.log('Framerate of data received:', 1 / res.timing.frameDuration);
                                // Do what you want with the data
                            }).catch(e=>{
                                console.log(e,111)
                            });
                        }

                    })
            }else{
                console.log('dir is empty')
            }
        } else {
            console.log('path is not valid directory')
        }
    } else {
        console.log('path is not valid')
    }

}).catch(e => {
    console.log(e)
});

this code result is :

[0:00:00.019] [BoxParser] Unlimited box size not supported for type: ' ╝ '
[0:00:00.027] [BoxParser] Unlimited box size not supported for type: ' ╔'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.