Picture of the authorSX Development

Bodycam & Dashcam

A bodycam and dashcam script for FiveM, providing immersive video and recording features for law enforcement roleplay.

Installation

Download the Script

Download Bodycam & Dashcam (sx_bodycamdashcam) using the Cfx Portal.

Download the Dependencies

Download the latest ox_lib GitHub.

Server.cfg Configuration

Add the following lines to your server.cfg to ensure the required resources are loaded:

server.cfg
ensure ox_lib
ensure sx_bodycamdashcam

Make sure ox_lib is started before sx_bodycamdashcam.

If you are using R2 for recording storage, also add:

server.cfg
set R2_BUCKET ""
set R2_ACCOUNT_ID ""
set R2_ACCESS_KEY_ID ""
set R2_SECRET_ACCESS_KEY ""

Configuration

Ace Permission Setup

Available Ace Permissions

PermissionDescription
DashcamSetupAllows user to set up dashcam positions using the in-game setup command (/dashcamsetup).
BodycamDashcam(Standalone only) Allows user to use bodycam & dashcam features when framework is set to standalone.

Example Usage

server.cfg
add_ace group.admin "DashcamSetup" allow
add_ace group.police "BodycamDashcam" allow # Only needed for standalone mode

Inventory Integration

ox_inventory

If you are using the ox_inventory inventory, you must include the export table within the item data for the bodycam item.

Example:

ox_inventory/data/items.lua
['bodycam'] = {
  label = 'Bodycam',
  weight = 0.1,
  consume = 0,
  server = {
    export = 'sx_bodycamdashcam.bodycam'
  }
},

-- Optional and only required if you're using the battery feature
['bodycam_battery'] = {
  label = 'Bodycam Battery',
  weight = 0.1,
  consume = 0,
  server = {
    export = 'sx_bodycamdashcam.battery'
  }
},

This is required for the item(s) to function properly with ox_inventory.

Bodycam Configuration

The bodycam script is highly configurable via the ClientConfig.Bodycam section. Below are the key options and their explanations:

Prop

Type

Recording Storage

Bodycam recordings support:

Discord webhooks are not supported for bodycam video uploads as it is very limited in terms of the max file size.

Fivemanage Setup

To use Fivemanage for recording storage, you need to generate an API key from your Fivemanage dashboard.

Retrieve API Key from Fivemanage

  1. Go to the Fivemanage Dashboard.
  2. In the navigation menu, select API Keys.
  3. Click Create Token.
  4. Enter any name for your token (e.g., "Bodycam Videos").
  5. Set the Token Type to Videos Only.
  6. Click Create and copy your new API token.

Add Fivemanage API Key and Configuration to config_server.lua

Add the Fivemanage API Key to your config_server.lua:

config_server.lua
Fivemanage = {
  ApiUrl = 'https://api.fivemanage.com/api',
  ApiKey = 'replace_with_your_api_key', -- HERE
  OnlyShowDepartmentRecordings = true,
  DepartmentFolderPaths = {
    ['police'] = '/police',
  },
}

Set Provider to Fivemanage in config_server.lua

In config_server.lua, set the storage provider to fivemanage:

config_server.lua
Storage = {
  Provider = "fivemanage"
}

Restart the Resource

Restart the script to apply the changes.

Cloudflare R2 Setup

To use Cloudflare R2, configure both your Cloudflare credentials and storage provider.

Create or Select an R2 Bucket

Open the Cloudflare Dashboard and go to Storage & databases > R2 Object Storage. Create or use an existing bucket for bodycam recordings.

Generate API Credentials

Generate credentials and copy an Access Key ID + Secret Access Key.

  1. Go to the Cloudflare Dashboard.
  2. Select Storage & databases > R2 Object Storage > Overview.
  3. Select Manage next to API Tokens.
  4. Select Create Account API token or Create User API token.
  5. Choose Object Read & Write permission.
  6. Set Apply to specific buckets only and select your bucket.
  7. Select Create API Token.
  8. Copy the Access Key ID and Secret Access Key.

Add R2 Credentials to server.cfg

server.cfg
set R2_BUCKET "your_bucket_name"
set R2_ACCOUNT_ID "your_account_id"
set R2_ACCESS_KEY_ID "your_access_key_id"
set R2_SECRET_ACCESS_KEY "your_secret_access_key"

Set Provider to R2 in config_server.lua

In config_server.lua, set the storage provider to r2:

config_server.lua
Storage = {
  Provider = "r2"
}

Restart the Resource

Restart your server or restart the script to apply the changes.

If you decide to only restart the script, make sure to paste the four R2_* convars into the server console otherwise they will not be set.

Recording Time Limit: The maximum recording duration can be configured in Recording.RecordingLimit (default: 300 seconds).

Permissions Configuration

If you are using a framework (not standalone), you must configure the Permissions table to control access to bodycam and dashcam features:

config_client.lua
Permissions = {
    BodycamDashcam = {
        Ace = 'BodycamDashcam',
        Jobs = {
            ['police'] = 1
        }
    },
    ViewCameras = {
        Ace = 'BodycamDashcam',
        Jobs = {
            ['police'] = 2
        }
    },
    DeleteRecordings = {
        Ace = 'BodycamDashcam',
        Jobs = {
            ['police'] = 3
        }
    }
},
  • BodycamDashcam: Allows the specified jobs to use bodycam & dashcam features.
  • ViewCameras: Allows the specified jobs to view camera recordings.
  • DeleteRecordings: Allows the specified jobs to delete recordings.

If you set ClientConfig.Framework to standalone, the script will ignore the Jobs table and instead require the Ace permission.

Storage Options

  • OnlyShowDepartmentRecordings: Ensures that players with a job can only see recordings from their own folder path set in DepartmentFolderPaths. For example, a player with the police job will only be able to see recordings in the /police folder.
  • DepartmentFolderPaths: Specifies the folder where recordings for each job are saved. For example, ['police'] = '/police' saves police recordings in the /police folder. Leaving this table empty will save all recordings in the root directory.

Dashcam Configuration

The dashcam script is configurable via the ClientConfig.Dashcam section. Below are the key options and their explanations:

Prop

Type

Usage

Bodycam

  • Toggle the bodycam overlay using the /bodycam command.
  • Start recording by pressing the X or using the /recordbodycam command.
  • The bodycam must be equipped and allowed for your job.
  • If ClientConfig.Bodycam.Battery.Enabled is true, battery drains based on DrainInterval and DrainPercentage and the remaining percentage is shown in the Bodycam UI.
  • Recording uploads footage to your configured provider (fivemanage or r2).

Dashcam

Use the /dashcamsetup command in-game to set up dashcam positions for vehicles. This command requires the DashcamSetup ace permission.

Viewing Dashcam

Drivers and passengers can press C to switch to their vehicle's dashcam view, if the vehicle is registered with a dashcam. See the next step for instructions on registering a dashcam.

Registering a Vehicle Dashcam

To integrate a dashcam into a vehicle, use the following export in your script:

Register Vehicle Dashcam
exports['sx_bodycamdashcam']:registerVehicleDashcam(netId, {
    jobName = player.PlayerData.job
})

Make sure to replace player.PlayerData.job with your framework's player job name.

Prop

Type

Developers

Exports

Client exports

openCamerasTablet

Opens the Cameras tablet UI.

Returns true if the tablet was opened, false if the player is missing permissions.

Example Code
local opened = exports.sx_bodycamdashcam:openCamerasTablet()
if not opened then
  -- player does not have ViewCameras permission
end

viewBodycamFeed

Starts viewing a specific bodycam feed.

Parameters

NameTypeDescription
bodycamIdnumberThe id field from getActiveBodycams().
Example Code
local result = exports.sx_bodycamdashcam:viewBodycamFeed(1)
if not result then
  -- e.g. show an error notification
end

Returns false on failure (permission denied, bodycam not found, target ped not reachable) or a table on success.

NameTypeDescription
targetServerIdnumberThe server ID of the target player.
targetPedCoordsvec3The coordinates of the target player.
isRecordingbooleanWhether the target player is currently recording.
Example Response
{
  targetServerId = 12,
  targetPedCoords = vec3(0, 0, 0),
  isRecording = false
}

viewDashcamFeed

Starts viewing a vehicle's dashcam feed.

Parameters

NameTypeDescription
vehicleNetIdnumberThe network ID of the vehicle.
Example Code
local vehicleNetId = NetworkGetNetworkIdFromEntity(vehicleEntity)
local result = exports.sx_bodycamdashcam:viewDashcamFeed(vehicleNetId)
if not result then
  -- e.g. show an error notification
end

Returns false on failure (no dashcam on vehicle, permission denied, vehicle not found) or a table on success.

NameTypeDescription
dashcamIdnumberUnique dashcam ID for this session.
vehicleNetIdbooleanThe network ID of the vehicle.
vehicleCoordsvec3The coordinates of the target vehicle.
Example Response
{
  dashcamId = 1,
  vehicleNetId = 123,
  vehicleCoords = vec3(0, 0, 0)
}

Server exports

getActiveBodycams

Returns a list of currently active bodycams.

Returns table[].

FieldTypeDescription
idnumberUnique bodycam ID for this session.
targetServerIdnumberThe server ID of the player wearing the bodycam.
officerNamestringThe player's in-game name.
coordsvector3Last known coordinates of the officer.
statusstring"live" or "recording".
jobNamestringThe job or department name of the officer.
sessionStartedAtnumberUnix timestamp when the bodycam was activated.
locationstringLast known area or zone name.
streetstringLast known street name.
Example Code
local bodycams = exports.sx_bodycamdashcam:getActiveBodycams()
for _, cam in ipairs(bodycams) do
  print(cam.officerName, cam.status, cam.coords)
end

isBodycamActive

Returns whether a specific player currently has an active bodycam.

Parameters

NameTypeDescription
sourcenumberThe server ID of the player.

Returns boolean

Example Code
local isActive = exports.sx_bodycamdashcam:isBodycamActive(source)

isPlayerRecording

Returns whether a specific player is currently recording their bodycam.

Parameters

NameTypeDescription
sourcenumberThe server ID of the player.

Returns boolean

Example Code
local isRecording = exports.sx_bodycamdashcam:isPlayerRecording(source)
if isRecording then
  -- player is actively recording right now
end

getActiveDashcams

Returns a list of currently registered dashcams.

Returns table[].

FieldTypeDescription
idnumberUnique dashcam ID for this session.
vehicleNetIdnumberNetwork ID of the vehicle.
jobNamestringThe job or department that owns this dashcam.
Example Code
local dashcams = exports.sx_bodycamdashcam:getActiveDashcams()
for _, cam in ipairs(dashcams) do
  print(cam.vehicleNetId, cam.jobName)
end

getRecordings

Fetches a paginated list of recordings from the configured storage backend (fivemanage or r2).

Parameters

NameTypeDefaultDescription
pagenumber1Page number.
limitnumber20Max results per page.
pathstring|nilnilOptional folder path filter (for example, "police/").
Example Code
local recordings = exports.sx_bodycamdashcam:getRecordings(1, 20, 'police/')
if not recordings then
  return
end

for _, rec in ipairs(recordings.data) do
  print(rec.name, rec.url)
end

Returns table[] or nil on error.

FieldTypeDescription
idnumberUnique recording ID .
urlstringThe recording URL.
sizenumberThe recording size.
metadatatableMeta data of the recording.
createdAtstringUnix timestamp when the recording has been uploaded.

Guides

Troubleshooting

  • Ensure ox_lib is installed and started before sx_bodycamdashcam. Click here
  • Make sure you have the correct ace permissions for the Bodycam & Dashcam. Click here

On this page