i'm have a problem when tried runs a stream 1080p 60fps with Gstreamer, the image don't is showing but if i put sensorFps=58 or < runs normally the API have some block limit if stream upper 60fps, anyone had the same problem?
have anything wrong with pipeline?
def run(self, appsrc):
print("DEBUG: Thread da Câmera OAK V3 iniciada.")
try:
device = dai.Device(dai.DeviceInfo("192.168.15.156"))
print(f"INFO: Dispositivo {device.getMxId()} conectado com sucesso.")
controls = self.camera_state.get_all_controls() # 1. Pega as chaves dos presets selecionados na UI res_key = controls.get('resolucao_fps', "1080p @ 30 FPS") crop_key = controls.get('tamanho_corte', "1024x1024")
# 2. Busca os valores correspondentes nos dicionários SENSOR_WIDTH, SENSOR_HEIGHT, FPS = PRESETS_RESOLUCAO[res_key] CROP_WIDTH, CROP_HEIGHT = PRESETS_CORTE[crop_key]['size']
print(f"INFO: Usando preset de sensor: {res_key} ({SENSOR_WIDTH}x{SENSOR_HEIGHT} @ {FPS} FPS)") print(f"INFO: Usando preset de corte: {crop_key} ({CROP_WIDTH}x{CROP_HEIGHT})")
# Informa ao PipelineManager (indiretamente) o tamanho do corte para o streammux # Isso é feito através do próprio estado, que o PipelineManager vai ler self.camera_state.update_from_ui('update_crop_size_for_ds', {'w': CROP_WIDTH, 'h': CROP_HEIGHT}) # O 'with' agora gerencia o ciclo de vida do pipeline.
with dai.Pipeline(defaultDevice=device) as pipeline: device = pipeline.getDefaultDevice() print('Connected cameras:',device.getConnectedCameras()) # A definição dos nós permanece a mesma cam_rgb = pipeline.create(dai.node.Camera).build( boardSocket=dai.CameraBoardSocket.CAM_A, sensorResolution= [SENSOR_WIDTH,SENSOR_HEIGHT], sensorFps=FPS, )
isp_out = cam_rgb.requestOutput((SENSOR_WIDTH, SENSOR_HEIGHT), dai.ImgFrame.Type.YUV420p)
# 1. Criamos o nó ImageManip. manip = pipeline.create(dai.node.ImageManip)
# 2. Lemos as coordenadas de corte iniciais do nosso estado centralizado. controls = self.camera_state.get_all_controls() is_depth_enabled = controls.get('depth_enabled', False) q_depth = None crop_x = controls.get('crop_x', 0) crop_y = controls.get('crop_y', 0) # 3. Configuramos a operação de corte. Usamos setCropRect para definir a região exata. # A configuração é feita através do atributo 'initialConfig'. [cite: 693] # Os parâmetros são (xmin, ymin, xmax, ymax) em coordenadas absolutas de pixel. print(f"INFO: Adicionando operação de corte em ({crop_x}, {crop_y}) com tamanho {CROP_WIDTH}x{CROP_HEIGHT}") manip.initialConfig.addCrop(crop_x, crop_y, CROP_WIDTH, CROP_HEIGHT) # 4. Garantimos que a saída do ImageManip tenha o formato esperado pelo VideoEncoder. manip.initialConfig.setFrameType(dai.ImgFrame.Type.NV12) video_enc = pipeline.create(dai.node.VideoEncoder) video_enc.setDefaultProfilePreset(fps=FPS, profile=dai.VideoEncoderProperties.Profile.H265_MAIN)
if is_depth_enabled: print("INFO: Profundidade habilitada. Configurando pipeline estéreo...")
mono_left = pipeline.create(dai.node.Camera).build( boardSocket=dai.CameraBoardSocket.CAM_B, sensorFps=FPS )
mono_right = pipeline.create(dai.node.Camera).build( boardSocket=dai.CameraBoardSocket.CAM_C, sensorFps=FPS, )
stereo = pipeline.create(dai.node.StereoDepth) # Usamos o preset de alta precisão que é um bom equilíbrio stereo.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.DEFAULT) stereo.setDepthAlign(dai.CameraBoardSocket.CAM_A) # LeftRightCheck é MANTIDO ATIVO devido à exigência do alinhamento stereo.setLeftRightCheck(True) stereo.setSubpixel(False)
mono_left_out = mono_left.requestOutput(dai.ImgFrame.Type.GRAY8, fps=FPS) mono_right_out = mono_right.requestOutput(dai.ImgFrame.Type.GRAY8, fps=FPS)
mono_left_out.link(stereo.left) mono_right_out.link(stereo.right)
# A fila só é criada se a profundidade estiver habilitada q_depth = stereo.depth.createOutputQueue(maxSize=4, blocking=False) else: print("INFO: Profundidade desabilitada. Pulando a criação dos nós estéreo.") isp_out.link(manip.inputImage) # Saída ISP vai para o ImageManip
manip.setMaxOutputFrameSize(CROP_WIDTH * CROP_HEIGHT * 3 // 2) # NV12 tem 1.5 bytes por pixel
manip.out.link(video_enc.input) # Saída de vídeo 1080p vai para o Manip
print("INFO: Aplicando configurações iniciais da câmera...") self._apply_controls(cam_rgb.initialControl)
q_h265_out = video_enc.out.createOutputQueue(maxSize=30, blocking=True) control_in = cam_rgb.inputControl.createInputQueue() sys_logger = pipeline.create(dai.node.SystemLogger) sys_logger.setRate(1) # Envia dados a cada 1 segundo q_sys_info = sys_logger.out.createOutputQueue(maxSize=4, blocking=False) pipeline.start() print("INFO: Pipeline V3 iniciado no dispositivo.")
while self.is_running and pipeline.isRunning(): self._process_commands(control_in) h265_packet = q_h265_out.get() sys_info = q_sys_info.tryGet()
if q_depth: depth_frame = q_depth.tryGet() if depth_frame is not None: depth_cv_frame = depth_frame.getFrame() # Obter a região de interesse (ROI) do estado central controls = self.camera_state.get_all_controls() roi_x = int(controls['selection_region_x']) roi_y = int(controls['selection_region_y']) roi_w = int(controls['selection_region_w']) roi_h = int(controls['selection_region_h'])
# Calcular o centro da ROI center_x = roi_x + roi_w // 2 center_y = roi_y + roi_h // 2 # Garantir que as coordenadas estão dentro dos limites do frame if 0 <= center_y < CROP_HEIGHT and 0 <= center_x < CROP_WIDTH: # Obter o valor da distância (em mm) no pixel central dist_mm = depth_cv_frame[center_y, center_x] # Atualizar o estado central com o novo valor self.camera_state.update_depth_info({'center_depth_mm': dist_mm}) if sys_info: m = 1024 * 1024 # MiB temp = sys_info.chipTemperature.average # [cite: 3612, 4162] css_cpu = sys_info.leonCssCpuUsage.average # [cite: 4170, 4202] mss_cpu = sys_info.leonMssCpuUsage.average # [cite: 4170, 4202] ddr_mem_info = {'used': sys_info.ddrMemoryUsage.used / m, 'total': sys_info.ddrMemoryUsage.total / m} cmx_mem_info = {'used': sys_info.cmxMemoryUsage.used / m, 'total': sys_info.cmxMemoryUsage.total / m} css_mem_info = {'used': sys_info.leonCssMemoryUsage.used / m, 'total': sys_info.leonCssMemoryUsage.total / m} mss_mem_info = {'used': sys_info.leonMssMemoryUsage.used / m, 'total': sys_info.leonMssMemoryUsage.total / m}
self.camera_state.update_system_info({ 'temp': sys_info.chipTemperature.average, 'css_cpu': sys_info.leonCssCpuUsage.average, 'mss_cpu': sys_info.leonMssCpuUsage.average, 'ddr_memory': ddr_mem_info, 'cmx_memory': cmx_mem_info, 'css_memory': css_mem_info, 'mss_memory': mss_mem_info }) if h265_packet: metadata = { "lens_pos": h265_packet.getLensPosition(), # [cite: 4276] "lens_pos_raw": h265_packet.getLensPositionRaw(), # [cite: 4277] "exposure_us": h265_packet.getExposureTime().microseconds, # [cite: 4275] "iso": h265_packet.getSensitivity(), # [cite: 4278] "color_temp": h265_packet.getColorTemperature() # [cite: 4275] } with self.metadata_lock: self.shared_metadata.update(metadata)
current_time = time.time() if current_time - self.last_ui_update_time > 0.5: # Throttle a 2Hz self.camera_state.update_from_camera_metadata(metadata) self.last_ui_update_time = current_time
buf = Gst.Buffer.new_wrapped(h265_packet.getData().tobytes()) if appsrc.emit('push-buffer', buf) != Gst.FlowReturn.OK: print("AVISO: Appsrc (GStreamer) rejeitou o buffer. Parando.") break # Quando o 'with pipeline' termina, o pipeline é parado. print("INFO: O pipeline foi parado.")
except Exception as e: print(f"ERRO CRÍTICO na thread da câmera: {e}") traceback.print_exc() finally: self.is_running = False if appsrc: appsrc.emit('end-of-stream') print("INFO: Thread da câmera finalizada e EOS enviado.")
def stop(self): print("INFO: Sinal de parada recebido pela OakCameraManager.") self.is_running = False