Skip to content

Starting a Face Liveness video stream using AWS SDK, returns Is not valid JSON, sending videoChunk

0

I'm attempting to initiate a Face Liveness session by streaming an MP4 video from an S3 bucket. When sending the videoChunk I'm getting the error:

Unexpected token 'T', 'The specif' ... is not a valid JSON.

What would be the correct approach?

I use as reference https://repost.aws/questions/QUy6rWBuKWQ4qEU78oprQ45A/below-is-my-function-for-liveness-check-so-i-am-getting-the-status-expired-so-what-would-be-the-correct-approach-so-that-i-can-get-the-confidence-score-and-plese-make-it-correct-the-startiput-if-it-is.

My reproducible code is:

import { HttpRequest } from "@smithy/protocol-http";
import { BuildHandlerOptions, BuildMiddleware } from "@smithy/types";
export const eventStreamHeaderMiddleware: BuildMiddleware<any, any> = (next) => async (args) => {
  const { request } = args;
  if (!HttpRequest.isInstance(request)) return next(args);
  request.headers = {
    ...request.headers,
    "Content-Type": "application/vnd.amazon.eventstream",
    "x-amz-content-sha256": "STREAMING-AWS4-HMAC-SHA256-EVENTS",
  };
  return next({
    ...args,
    request,
  });
};

export const eventStreamHeaderMiddlewareOptions: BuildHandlerOptions = {
  step: "build",
  tags: ["EVENT_STREAM", "HEADER", "CONTENT_TYPE", "CONTENT_SHA256"],
  name: "eventStreamHeaderMiddleware",
  override: true,
};

import pkg from "@aws-sdk/client-rekognition";
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { Readable } from "stream";
import ffmpeg from "fluent-ffmpeg";

const {
  RekognitionClient,
  CreateFaceLivenessSessionCommand,
} = pkg;

import {
  RekognitionStreamingClient,
  StartFaceLivenessSessionCommand,
} from "@aws-sdk/client-rekognitionstreaming";

// Configuração dos clientes
const awsConfig = {
  region: "us-east-1",
  credentials: {
    accessKeyId: "process.env.accessKeyId", // Replace with your AWS access key ID
    secretAccessKey: "process.env.secretAccessKey", // Replace with your AWS secret access key
  },
};

const s3Client = new S3Client({ region: awsConfig.region });
const rekognitionClient = new RekognitionClient(awsConfig);

// Função para converter stream para buffer
async function bufferToStream(stream) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    stream.on("data", (chunk) => chunks.push(chunk));
    stream.on("error", reject);
    stream.on("end", () => resolve(Buffer.concat(chunks)));
  });
}

// Função para obter arquivo do S3
async function getFileFromS3(bucketName, objectKey) {
  const params = {
    Bucket: bucketName,
    Key: objectKey,
  };

  try {
    const data = await s3Client.send(new GetObjectCommand(params));
    const fileBuffer = await bufferToStream(data.Body);

    console.log(Buffer size: ${fileBuffer.length} bytes);
    return fileBuffer;
  } catch (error) {
    console.error("Error getting file from S3:", error);
    throw error;
  }
}

// Função para obter dimensões do vídeo usando ffprobe
function getVideoDimensions(fileBuffer) {
  return new Promise((resolve, reject) => {
    const fileStream = new Readable();
    fileStream.push(fileBuffer);
    fileStream.push(null);

    ffmpeg.ffprobe(fileStream, (err, metadata) => {
      if (err) {
        reject("Error probing video dimensions: " + err);
      } else {
        const width = metadata.streams[0].width;
        const height = metadata.streams[0].height;
        resolve({ width, height });
      }
    });
  });
}

// Função para gerar os chunks
function generateChunks(buffer, chunkSize = 1024 * 1024) {
  const chunks = [];
  let offset = 0;

  while (offset < buffer.length) {
    const chunk = buffer.slice(offset, offset + chunkSize);
    chunks.push({
      VideoEvent: {
        VideoChunk: Buffer.from(chunk).toString("base64"), // Certifica que o chunk está em Base64
        TimestampMillis: Date.now(),
      },
    });
    offset += chunkSize;
  }
  return chunks;
}


// Função principal
async function startLivenessCheck() {
  try {
    const bucketName = "myBucketName";
    const objectKey = "myObjectKey";

    console.log("Obtendo arquivo do S3...");
    const fileBuffer = await getFileFromS3(bucketName, objectKey);
    console.log("Buffer do arquivo obtido com sucesso. Tamanho total:", fileBuffer.length, "bytes");

    console.log("Criando sessão de liveness...");
    const createInput = {
      Settings: {
        OutputConfig: {
          S3Bucket: bucketName,
          S3KeyPrefix: objectKey,
        },
        AuditImagesLimit: 4,
      },
      ClientRequestToken: Date.now().toString(),
    };

    const createCommand = new CreateFaceLivenessSessionCommand(createInput);
    const createResponse = await rekognitionClient.send(createCommand);
    console.log("Sessão de liveness criada. ID:", createResponse.SessionId);

    console.log("Gerando chunks de vídeo...");
    const chunks = generateChunks(fileBuffer, 1024 * 1024);
    chunks.forEach((chunk, index) => {
      console.log(Chunk_ ${index}: Tamanho: ${chunk.VideoEvent.VideoChunk.length} bytes);
    });

    const livenessStream = async function* () {
      for (const chunk of chunks) {
        const videoEvent = chunk.VideoEvent;
    
        // Verifica o formato do evento
        console.log("Evento enviado:", JSON.stringify({ VideoEvent: videoEvent }, null, 2));
    
        yield { VideoEvent: videoEvent };
        await new Promise((resolve) => setTimeout(resolve, 100)); // Simula streaming
      }
    };
    

    const startInput = {
      SessionId: createResponse.SessionId,
      VideoWidth: 480,
      VideoHeight: 850,
      ChallengeVersions: "challenge_1.0.0",
      LivenessRequestStream: livenessStream(),
    };

    console.log("Iniciando a sessão de liveness...");
    const rekognitionStreamingClient = new RekognitionStreamingClient({
      region: "us-east-1",
      credentials: {
        accessKeyId: "process.env.accessKeyId", // Replace with your AWS access key ID
        secretAccessKey: "process.env.secretAccessKey"
      }
    });
    
    let startResponse;
    try {
      console.log("Parâmetros enviados no StartFaceLivenessSessionCommand:", startInput);
    
      const startCommand = new StartFaceLivenessSessionCommand(startInput);
    
      rekognitionStreamingClient.middlewareStack.add((next) => async (args) => {
        console.log("Endpoint resolvido antes do envio:", args.request.url);
        console.log("Headers antes do envio:", args.request.headers);
      
        // Convertendo valores dos headers para string
        Object.keys(args.request.headers).forEach((key) => {
          args.request.headers[key] = args.request.headers[key] ? String(args.request.headers[key]) : "";
        });
      
        return next(args);
      }, { step: "build" });
      
    
      try {
        const startResponse = await rekognitionStreamingClient.send(startCommand);
        console.log("Resposta completa:", startResponse);
      } catch (error) {
        console.error("Erro ao iniciar a sessão de liveness:", error);
        if (error.$responseBodyText) {
          console.log("Corpo bruto da resposta:", error.$responseBodyText);
        }
      }
    
      if (!startResponse) {
        console.error("Resposta indefinida do servidor. Verifique o endpoint e os parâmetros.");
        return;
      }
    
      console.log("Sessão de liveness iniciada com sucesso:", startResponse);
    
      if (startResponse.LivenessResponseStream) {
        for await (const event of startResponse.LivenessResponseStream) {
          console.log("Evento recebido:", event);
        }
      } else {
        console.error("LivenessResponseStream ausente. Resposta:", startResponse);
      }
    } catch (error) {
      console.error("Erro ao iniciar a sessão de liveness:", error);
    
      if (error.$response) {
        console.log("Resposta completa do erro:", JSON.stringify(error.$response, null, 2));
      }
    
      if (error.$responseBodyText) {
        console.log("Corpo bruto da resposta:", error.$responseBodyText);
      }
    }
    
    console.log("Sessão de liveness iniciada com sucesso:", startResponse);
  
    // Tratamento de erros no fluxo de resposta
    if (startResponse.LivenessResponseStream) {
      for await (const event of startResponse.LivenessResponseStream) {
        if (event.ValidationException) {
          console.error("Erro de validação:", event.ValidationException.Message);
        } else if (event.InternalServerException) {
          console.error("Erro interno do servidor:", event.InternalServerException.Message);
        } else if (event.ThrottlingException) {
          console.error("Erro de limitação:", event.ThrottlingException.Message);
        } else if (event.ServiceUnavailableException) {
          console.error("Serviço indisponível:", event.ServiceUnavailableException.Message);
        } else {
          console.log("Evento recebido:", event);
        }
      }
    }
  
  } catch (error) {
    console.error("Erro ao iniciar a sessão de liveness:", error);
    if (error.headers) {
      console.log("Cabeçalhos inválidos:", JSON.stringify(error.headers, null, 2));
    }
    if (error.$response) {
      console.log("Resposta completa:", JSON.stringify(error.$response, null, 2));
    }
    if (error.$responseBodyText) {
      console.log("Corpo da resposta:", error.$responseBodyText);
    }
  }
}

startLivenessCheck();

(Attatched image of the error.)Displayed error

asked 2 years ago328 views

1 Answer
0
Accepted Answer
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { RekognitionClient, StartFaceLivenessSessionCommand } from "@aws-sdk/client-rekognitionstreaming";
import { Readable } from "stream";
import ffmpeg from "fluent-ffmpeg";

// AWS SDK configuration
const awsConfig = {
  region: "us-east-1",
  credentials: {
    accessKeyId: process.env.accessKeyId, // Use environment variables for security
    secretAccessKey: process.env.secretAccessKey, // Use environment variables for security
  },
};

// Initialize AWS clients
const s3Client = new S3Client({ region: awsConfig.region });
const rekognitionClient = new RekognitionClient(awsConfig);

// Utility to buffer the stream from S3
async function bufferToStream(stream) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    stream.on("data", (chunk) => chunks.push(chunk));
    stream.on("error", reject);
    stream.on("end", () => resolve(Buffer.concat(chunks)));
  });
}

// Get video file from S3
async function getFileFromS3(bucketName, objectKey) {
  const params = {
    Bucket: bucketName,
    Key: objectKey,
  };

  try {
    const data = await s3Client.send(new GetObjectCommand(params));
    const fileBuffer = await bufferToStream(data.Body);
    console.log(`Buffer size: ${fileBuffer.length} bytes`);
    return fileBuffer;
  } catch (error) {
    console.error("Error getting file from S3:", error);
    throw error;
  }
}

// Generate chunks from video buffer
function generateChunks(buffer, chunkSize = 1024 * 1024) {
  const chunks = [];
  let offset = 0;

  while (offset < buffer.length) {
    const chunk = buffer.slice(offset, offset + chunkSize);
    chunks.push({
      VideoEvent: {
        VideoChunk: Buffer.from(chunk).toString("base64"), // Ensure the chunk is Base64 encoded
        TimestampMillis: Date.now(),
      },
    });
    offset += chunkSize;
  }
  return chunks;
}

// Main function to start liveness check
async function startLivenessCheck() {
  try {
    const bucketName = "myBucketName";
    const objectKey = "myObjectKey";

    console.log("Fetching video file from S3...");
    const fileBuffer = await getFileFromS3(bucketName, objectKey);

    console.log("Creating liveness session...");
    const createInput = {
      Settings: {
        OutputConfig: {
          S3Bucket: bucketName,
          S3KeyPrefix: objectKey,
        },
        AuditImagesLimit: 4,
      },
      ClientRequestToken: Date.now().toString(),
    };

    const createCommand = new StartFaceLivenessSessionCommand(createInput);
    const createResponse = await rekognitionClient.send(createCommand);
    console.log("Liveness session created. SessionId:", createResponse.SessionId);

    console.log("Generating video chunks...");
    const chunks = generateChunks(fileBuffer, 1024 * 1024);
    console.log(`Generated ${chunks.length} chunks`);

    // Event streaming generator function
    const livenessStream = async function* () {
      for (const chunk of chunks) {
        const videoEvent = chunk.VideoEvent;

        // Ensure valid JSON structure for the chunk
        console.log("Sending chunk:", JSON.stringify({ VideoEvent: videoEvent }, null, 2));
        yield { VideoEvent: videoEvent };
        await new Promise((resolve) => setTimeout(resolve, 100)); // Simulate streaming delay
      }
    };

    console.log("Starting liveness session...");
    const startInput = {
      SessionId: createResponse.SessionId,
      VideoWidth: 480,
      VideoHeight: 850,
      ChallengeVersions: "challenge_1.0.0",
      LivenessRequestStream: livenessStream(),
    };

    const rekognitionStreamingClient = new RekognitionStreamingClient(awsConfig);

    const startCommand = new StartFaceLivenessSessionCommand(startInput);
    const startResponse = await rekognitionStreamingClient.send(startCommand);
    console.log("Liveness session started successfully:", startResponse);

    if (startResponse.LivenessResponseStream) {
      for await (const event of startResponse.LivenessResponseStream) {
        console.log("Event received:", event);
      }
    }

  } catch (error) {
    console.error("Error during liveness session:", error);
    if (error.$responseBodyText) {
      console.log("Raw response body:", error.$responseBodyText);
    }
  }
}

startLivenessCheck();

answered 2 years ago

EXPERT

reviewed 2 years ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.