Appearance
Overview
- File:
lib/views/hvc_camera_page.dart
The hvc_camera_page.dart file implements the camera functionality of the HVC XR application. It provides a comprehensive camera interface with features for video recording, image capture, torch control, camera switching, and audio output selection. The page is designed to work with RealWear devices and supports both video and image capture with proper storage management.
Class Structure
dart
class HvcCameraPage extends StatefulWidget {
static const String routeName = '/HvcCameraPage';
const HvcCameraPage({Key? key}) : super(key: key);
@override
_HvcCameraPageState createState() => _HvcCameraPageState();
}
class _HvcCameraPageState extends State<HvcCameraPage> {
// State variables and methods
}Dependencies
dart
import 'dart:async';
import 'dart:core';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:hippo_web_rtc/hippo_web_rtc.dart';
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';
// Additional imports for controllers and utilitiesKey Components
State Variables
dart
MediaStream? _localStream;
final _localRenderer = RTCVideoRenderer();
bool _inCalling = false;
MediaRecorder? _mediaRecorder;
bool get _isRec => _mediaRecorder != null;
List<MediaDeviceInfo>? _mediaDevicesList;
RxString timeString = ''.obs;Controllers
dart
final MediaController mediaController = Get.find();
final RealTimeController realTimeController = Get.find();Camera Initialization
Renderer Setup
dart
void initRenderers() async {
await _localRenderer.initialize();
}Media Stream Configuration
dart
void _makeCall() async {
final mediaConstraints = <String, dynamic>{
'audio': true,
'video': {
'mandatory': {
'minWidth': '640',
'minHeight': '480',
'minFrameRate': '30',
},
'facingMode': 'environment',
'optional': [],
}
};
try {
var stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
_mediaDevicesList = await navigator.mediaDevices.enumerateDevices();
_localStream = stream;
_localRenderer.srcObject = _localStream;
} catch (e) {
print(e.toString());
}
}Recording Functionality
Video Recording
dart
void _startRecording() async {
if (_localStream == null) throw Exception('Stream is not initialized');
if (Platform.isIOS) {
print('Recording is not available on iOS');
return;
}
final dir = await getExternalStorageDirectory() ??
await getApplicationDocumentsDirectory();
_fileName = 'xr_vid_${DateTime.now().millisecondsSinceEpoch}.mp4';
_filePath = '${dir.path}/hvc_xr/$_fileName';
_mediaRecorder = MediaRecorder();
setState(() {});
final videoTrack = _localStream!
.getVideoTracks()
.firstWhere((track) => track.kind == 'video');
await _mediaRecorder!.start(
_filePath,
videoTrack: videoTrack,
);
recTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
timeString.value = Duration(seconds: timer.tick).toFormattedString();
});
}Stop Recording
dart
void _stopRecording() async {
await _mediaRecorder?.stop().whenComplete(() async =>
await mediaController.saveFileRecord(
_sessionDate, _filePath, _fileName, null, 'video', _sessionId));
recTimer.cancel();
HlkDialog.showSuccessSnackBar('video recorded');
await Future.delayed(
const Duration(seconds: 2), () => timeString.value = '');
setState(() {
_mediaRecorder = null;
});
}Camera Controls
Torch Control
dart
void _toggleTorch() async {
if (_localStream == null) throw Exception('Stream is not initialized');
final videoTrack = _localStream!
.getVideoTracks()
.firstWhere((track) => track.kind == 'video');
final has = await videoTrack.hasTorch();
if (has) {
await videoTrack.setTorch(realTimeController.isTouchOn.value);
}
}Camera Switching
dart
void _toggleCamera() async {
if (_localStream == null) throw Exception('Stream is not initialized');
final videoTrack = _localStream!
.getVideoTracks()
.firstWhere((track) => track.kind == 'video');
await Helper.switchCamera(videoTrack);
}Image Capture
dart
void _captureFrame() async {
if (_localStream == null) throw Exception('Stream is not initialized');
final videoTrack = _localStream!
.getVideoTracks()
.firstWhere((track) => track.kind == 'video');
final frame = await videoTrack.captureFrame();
final byteArray = frame.asUint8List();
_fileName = 'xr_img_${DateTime.now().millisecondsSinceEpoch}.jpg';
var dir = await getExternalStorageDirectory();
dir ??= await getApplicationDocumentsDirectory();
File imgFile = File(path.join(dir.path, 'hvc_xr', _fileName));
imgFile.createSync(recursive: true);
await showDialog(
context: context,
builder: (context) => AlertDialog(
content: Image.memory(byteArray, height: 720, width: 1280),
actions: <Widget>[
TextButton(
onPressed: Navigator.of(context, rootNavigator: true).pop,
child: const Text('OK'),
)
],
));
byteArrayToFile(byteArray, imgFile, isAsync: true);
await mediaController.saveFileRecord(
_sessionDate, imgFile.path, _fileName, null, 'image', _sessionId);
}UI Implementation
Camera View
dart
Center(
child: Container(
margin: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: RTCVideoView(
_localRenderer,
mirror: true,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
),
),
)Control Buttons
dart
persistentFooterButtons: [
Obx(
() => IconButton(
icon: Icon(
realTimeController.isTouchOn.value
? Icons.flash_off
: Icons.flash_on,
color: Colors.white,
),
onPressed: _toggleTorch,
tooltip: realTimeController.isTouchOn.value ? 'flash off' : 'flash on',
),
),
IconButton(
icon: const Icon(Icons.switch_video, color: Colors.white),
onPressed: _toggleCamera,
tooltip: 'switch camera',
),
IconButton(
icon: const Icon(Icons.camera, color: kAppColorWhite),
onPressed: _captureFrame,
tooltip: 'take picture',
),
IconButton(
icon: Icon(
_isRec ? Icons.stop : Icons.fiber_manual_record,
color: _isRec ? kAppColorBlue : kAppColorWhite,
),
onPressed: _isRec ? _stopRecording : _startRecording,
tooltip: _isRec ? 'stop recording' : 'start recording',
),
// Audio output selection menu
]Dependencies
hippo_web_rtc: WebRTC implementationget: State managementpath_provider: File system accessuuid: Unique identifier generationflutter/material.dart: UI components
Styling
- Uses consistent color scheme
- Implements responsive design
- Provides clear visual feedback
- Maintains consistent button styling
- Uses appropriate icons for actions