Single Camera Motion Capture: Uploading a Video via the Reboot Motion API

This tutorial uses code samples written in Python; however, if your organization uses a different language, you can still use the tutorials as a reference point — you will just need to translate the examples to the language of your choosing. If you need help with getting set up to use the Reboot Motion API, you can check out our Getting Set Up tutorial.

Welcome! This tutorial walks you through the process of uploading one or more video files to Reboot Motion's Single Camera Motion Capture tool via the Reboot Motion API. For more information about the endpoints included in this tutorial, or any other endpoint, you can check out our API documentation.

Note: You will need an API Key to interact with the API endpoints mentioned in this tutorial. API Keys are available to all active Reboot Motion customers. If you do not already have an API Key from Reboot Motion, you can generate one from your Reboot Motion Dashboard.

Step 0: Getting Set Up

First, let’s take a look at the Reboot Motion documentation for Video Uploads. You’ll notice there are three different endpoints:

EndpointDescription
POST /upload_videosCreate Video Upload Session
PATCH /upload_videosAppend to Upload Session
DEL /upload_videos/{upload_session_id}Cancel Entire Upload

For this tutorial, we will use the first two endpoints to create a video upload session, upload files to the session, and mark the session as ready for analysis. You may notice that these endpoints each have a different HTTP method, which correspond to the action being taken. POST signifies the creation of a resource (in this case, a video upload session), while PATCH signifies updates to a resource. DEL, as you might guess, is used for deleting a resource (or, in this case, telling the API that the upload session has been aborted).

Let's set up our Python script. Ours is named main.py, but you can name yours whatever you prefer. We'll import two key dependencies: the os module, which we'll use to get the size of the video file(s) we're uploading, and the requests module, which we'll use to make the API calls.

We'll also set up a few constants that we'll use throughout the tutorial: API_HEADER, which constructs the header to be sent with the request and includes your API Key, and VIDEO_FILE, which contains the path to the video file we'll be uploading. In practice, you'll likely be uploading more than one video file at a time, but for the sake of simplicity, we'll just use one video file in this tutorial.

import os
import requests

API_HEADER = {‘x-api-key’: <YOUR-API-KEY>}
VIDEO_FILE = <PATH-TO-VIDEO>

def main():
    create_video_upload_session = requests.post(
        "https://api.rebootmotion.com/upload_videos",
        headers=API_HEADER,
        json={}
    )

if __name__ == "__main__":
	main()

If you aren’t sure why we are setting our code up like this, take a look at the Getting Set Up tutorial to get yourself caught up.

Step 1: Create Video Upload Session

As mentioned above, we are going to need to first use the POST /upload_videos endpoint in order to create a video upload session. Taking a look at the documentation for POST /upload_videos, you can see that we need a few parameters to properly make this request: files, movement_type_id, player_id, session_date, and session_type_id. Let's go through what each of the parameters mean:

files:

This is a list of files in an array, regardless of whether you have one or more files to upload. This parameter requires the:

  • raw_file_name: the name of the file you are uploading without any path connected to it — we will address how we get it once we get through these definitions.
  • file_size_bytes: the file's size in bytes; this needs to be calculated using the os module and will also be addressed in the next step.
  • number_of_chunks: this allows you to break the file you are uploading into smaller chunks if the file is too large to upload in one piece. It is the number of chunks you will be splitting the file into (can be 1 if you are not splitting the file into chunks).

You can also include the following (not required):

  • file_id: the ID of the file you are uploading. This is not the same as the raw_file_name; it is an integer. If you are uploading more than one file at a time, you will want to make sure you are using a unique file_id for each file you are uploading.
  • frame_rate: the frame rate of the video you are uploading.

movement_type_id:

This is the type of movement we want to analyze; it is an integer. For our example case, we are looking at baseball pitching movements, so we are using a movement_type_id of 2.

For the names and IDs of other movement types, you can query the API via the List Movement Types endpoint.

player_id:

This is the ID of the player whose video we are uploading, input as a string. We connect the Player ID to the video so we know who the player in the video is and can easily access all their videos when needed. This ID is the player_id; to find that, you can query the API via the List Players endpoint.

session_date:

This is the date on which the video was recorded and must be in the format "YYYY-MM-DD". This field should contain only one date, not a date range.

session_type_id:

This is the type of session you are uploading the video for; it is an integer. In our example, we are using a video from a practice, so we will be using a session_type_id of 3. If you are unsure of the session type ID for your session, you can query the API via the List Sessions endpoint and access the session_type_id from there.

Now that we’ve gone through what each of the required parameters mean, let's construct the JSON object for our request, which will be passed into the json parameter of requests.post(). Again, the below values are example values and will not work for your specific organization; you will need to replace the values with the values that are relevant to your organization:

def main():
create_video_upload_session = requests.post(
    "https://api.rebootmotion.com/upload_videos",
    headers=API_HEADER,
    json={
        "files": [
            {
                "raw_file_name": VIDEO_FILE.split("/")[-1],
                "file_size_bytes": os.path.getsize(VIDEO_FILE),
                "number_of_chunks": 1,
                "file_id": 1
            },  
        ],
        "movement_type_id": 2,
        "player_id": "a865b9db-8cae-44fa-bc8f-71969045a8c5",
        "session_date": "2022-03-30",
        "session_type_id": 3,
    }
)

json_response = create_video_upload_session.json()

The json_response should return a JSON response object that includes multiple keys and values. If you are not getting a response object back, double check to make sure you have entered all fields correctly before proceeding. The Reboot Motion API Documentation has more information on the full response object you should be getting back.

As mentioned above, we have used the os module to calculate the file_size_bytes and the raw_file_name. If you are already familiar with how we have used the os module, you can skip the next few paragraphs and move on to Step 2.

In our example, we have calculated the raw_file_name by taking the video file path and splitting it on any / characters and taking the last section of the path, which is just the raw video name.

To get the file_size_bytes, we have used the built-in os.path.getsize() function, which calculates the size of the file in bytes. This allows us to get the most accurate file size for the video we are uploading.

Step 2: Upload Video Files

Now that our POST /upload_videos request is properly set up and returning a JSON response, we can move on to the next step, which is uploading the video file(s) to Amazon S3.

Note: This part of the process interacts directly with Amazon S3, rather than the Reboot Motion API, so it is not reflected in our API documentation.

Firstly, we need to get a video file object that will provide us with the file data we need to upload the video to Amazon. We will be using the Python function open() for that – if you’re unfamiliar with open(), you can read more about it and what it’s for in the Python documentation.

url = json_response["pre_signed_urls][0]["info"][0]["url"]

with open(VIDEO_FILE, "rb") as f:
	file_data = f.read()
	response = requests.put(url, data=file_data)

The new constant url is the unique URL for the video we have uploaded and is going to be used for uploading the file to Amazon. We get this from the json_response object we got in Step 1, filtered by the first of the "pre_signed_urls" (using the index of [0], as we just want the first URL), then info, which contains the URL we want (again, using the index of [0] because we just want the info for the first pre-signed URL), and lastly we want the url.

If you are uploading more than one file at a time, you will want to iterate through each pre-signed URL to make sure you are getting all the URLs necessary for uploading the entire file. Similarly, if you are chunking the file, you will need to iterate through all the URL indices.

After we have the URL, we use the open() function to open the video file and read the file data, which we then use in our PUT request to upload the video.

Step 3: Submit Videos for Processing

Now that we have our video uploaded to Amazon, we can move forward with part three of the video upload process, which involves using the PATCH endpoint from the API documentation, which appends the video data to the upload session we are working on.

Looking at the documentation, you will notice we have three required parameters for the PATCH endpoint — upload_session_id, affected_file_id, file_part_info, and upload_successful. These parameters are all part of the json_response we got in Step 1, but let's go through them individually:

upload_session_id:

This is the ID of the session we have just uploaded - you can access it via `json_response["upload_session_id"].

affected_file_id:

This is the file ID you are looking to activate – this is the database ID from the session_files.

You can access this again by going through the json_response, but it is a bit more involved than upload_session_id above. To get the affected file ID, you will need to access the first pre-signed URL (again, if you are uploading more than one file, you’ll want to iterate through all the pre-signed URLs, but since we just have one, we can just access the first one) and get the session_file_id through that: json_response["pre_signed_urls"][0]["session_file_id"].

file_part_info:

This is where we will be needing the ETag that we discussed in the last step - the file_part_info requires both an etag and a part_number.

  • ETag is generated by Amazon S3 and is used by Amazon S3 to determine whether two objects are the same or not, allowing you to upload multiple files or one file with multiple chunks. The ETag can be accessed in the response headers by using response.headers["ETag"].
  • part_number is where you will specify the order of the file part in the file — for this example, again, we aren’t doing a multi-part upload, so our part_number will be 1. If you are doing a multi-part upload, this is where you will put the corresponding part number to the chunk of the file you are currently working with.

upload_successful:

This is a boolean that signifies whether the upload was successful or not. You will want to set this to True.

Once you have added in the above, your code should look something like the below, and your video file should have successfully uploaded to the session:

import os
import requests

API_HEADER = {‘x-api-key’: <YOUR-API-KEY>}
VIDEO_FILE = <PATH-TO-VIDEO>

def main():
    create_video_upload_session = requests.post(
        "https://api.rebootmotion.com/upload_videos",
        headers=API_HEADER,
        json={
        "files": [
            {
                "raw_file_name": VIDEO_FILE.split("/")[-1],
                "file_size_bytes": os.path.getsize(VIDEO_FILE),
                "number_of_chunks": 1,
                "file_id": 1
            },
        ],
        "movement_type_id": 2,
        "player_id": "a865b9db-8cae-44fa-bc8f-71969045a8c5",
        "session_date": "2022-03-30",
        "session_type_id": 3,
        }
    )

json_response = create_video_upload_session.json()

url = json_response["pre_signed_urls][0]["info"][0]["url"]

with open(VIDEO_FILE, "rb") as f:
	file_data = f.read()
	response = requests.put(url, data=file_data)

upload_session_id = json_response["upload_session_id"]
affected_file_id = json_response["pre_signed_urls"][0]["session_file_id"]
etag = response.headers["ETag"]

upload_videos = requests.patch(
    "https://api.rebootmotion.com/upload_videos",
    headers=API_HEADER,
    json={
        "upload_session_id": upload_session_id,
        "affected_file_id": affected_file_id,
        "file_part_info": [
            {
                "etag": etag,
                "part_number": 1
            }
        ],
        "upload_successful": True,
    }
)

print(upload_videos.json())

if __name__ == "__main__":
	main()

We have printed out upload_videos.json() to ensure that the video has been uploaded successfully. You can also check the status code of the response to make sure it is 200 - OK.

Common Errors

Ideally, you wouldn't run into any errors while going through this tutorial, but there is a lot of information here and sometimes errors happen. That being said, here are some of the more common errors and the first steps to take to troubleshoot them.

You can always access a full list of errors in the Reboot Motion documentation, but we are going to walk you through some more specific error messages you may encounter.

ModuleNotFoundError: No module named '<module_name>'

This error comes from the module in question not being installed — you will need to install the module in order to use it. You can do this by running pip install requests in your terminal. If you are using a different package manager, you will need to use the appropriate command for that package manager.

response.json()[‘pre_signed_urls’] throws an exception

This exception is likely a key error, likely from a null value in response.json()['pre_signed_urls']. This can be fixed by making sure all required fields have been provided and that all the spelling of provided fields is correct. You may have also provided data in an incorrect format — double check with the API documentation that everything matches up.

HTTP Exception 401: Unauthorized

You need to include a proper API Key — if you don't know how to get this, take a look at the Getting Set Up tutorial.

HTTP Exception 500: Internal Server Error

This means something is wrong on the Reboot Motion server's end — please reach out to the team at [email protected].

If you need to retry the API call after a failure, check that you have everything above correct, restart your server, and make the call again. If you continue to get an error message and can't figure out why, feel free to reach out to the Reboot Motion team.

We hope you enjoyed this tutorial on Uploading a Video. As always, please feel free to reach out to the team with any additional questions you may have, or ideas for future tutorials.