23/11/2023

Build lại app tích hợp flipper
nghiên cứu chức năng mới (camera , karaoke UI).

Agenda

info
fix flipper
use_flipper!()

// @ts-ignore
const store = configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware => {
const middlewares = getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(sagaMiddleware)

const createDebugger = require('redux-flipper').default
middlewares.push(createDebugger())

return middlewares
},
})
→ install redux debugger

build android:
FLIPPER_VERSION=0.125.0
New topic
Đánh giá chức năng
3
Màn hình
Tên chức năng
mô tả chức năng theo ý hiểu
tham khảo
SDK bên thứ 3
Tính khả thi
1
Màn hình 1
image.png
Bật đèn flash trong camera
Có 1 màn hình camera
thêm 1 button để bật - tắt flash
sử dụng function của thư viện để bật flash

yarn add react-native-vision-camera
Example:
import React, { useState } from 'react';
import { Camera } from 'react-native-vision-camera';

const FlashCamera = () => {
const [isFlashOn, setFlashOn] = useState(false);

const toggleFlash = () => {
setFlashOn(!isFlashOn);
};

return (
<Camera torch={isFlashOn} style={{ flex: 1 }}>
<button onPress={toggleFlash}>Toggle Flash</button>
</Camera>
);
};

export default FlashCamera;
✅ khả thi 100%
chỉ cần xử lý trên js
có thư viện support
2
màn hình 2
image.png
Hẹn giờ bắt đầu quay video
như trong description UI thì chỉ có phần đếm ngược → không có phần đặt giới hạn quay (cần re-confirm)
sau khi đếm ngược X giây, camera sẽ tự động quay clip

Example:
import React, { useState, useEffect } from 'react';
import { Camera } from 'react-native-vision-camera';

const CountdownCamera = () => {
const [countdown, setCountdown] = useState(0);
const cameraRef = useRef(null);

useEffect(() => {
let interval = null;
if (countdown > 0) {
interval = setInterval(() => {
setCountdown(countdown - 1);
}, 1000);
} else if (countdown === 0) {
startRecording();
}
return () => clearInterval(interval);
}, [countdown]);

const startCountdown = () => {
setCountdown(3); // Start countdown from 3
};

const startRecording = async () => {
if (cameraRef.current) {
const data = await cameraRef.current.startRecording();
console.log(data);
}
};

return (
<Camera ref={cameraRef} style={{ flex: 1 }}>
<button onPress={startCountdown}>Start Countdown</button>
{countdown > 0 && <p>Recording in {countdown}</p>}
</Camera>
);
};

export default CountdownCamera;
✅ khả thi 100%
chỉ cần xử lý trên js
có thư viện support
3
Màn hình 3
image.png
thay đổi tốc độ quay video (timelapse, slowmotion, ...)
keyword: Trim control
có thể thay đổi tốc độ quay video theo các mức 0.3, 0.5, 1, 2, 3

Example:
import React, { useRef } from 'react';
import { Camera } from 'react-native-vision-camera';

const SlowMotionCamera = () => {
const cameraRef = useRef(null);

const startRecording = async () => {
if (cameraRef.current) {
const options = { frameRate: 120 }; // Set high frame rate for slow motion
const data = await cameraRef.current.startRecording(options);
console.log(data);
}
};

const stopRecording = () => {
if (cameraRef.current) {
cameraRef.current.stopRecording();
}
};

return (
<Camera ref={cameraRef} style={{ flex: 1 }}>
<button onPress={startRecording}>Start Recording</button>
<button onPress={stopRecording}>Stop Recording</button>
</Camera>
);
};

export default SlowMotionCamera;
❗️ khả thi 80%
chỉ cần xử lý trên js
có thư viện support
nhưng không biết code như này có chạy được thật không, nếu không chạy được như kỳ vọng thì phải build native module.
Example:
import AVFoundation

let captureSession = AVCaptureSession()
guard let camera = AVCaptureDevice.default(for: .video) else { return }

do {
let input = try AVCaptureDeviceInput(device: camera)
captureSession.addInput(input)
} catch {
print("Error creating input: \(error)")
return
}

if let format = camera.formats.filter({ CMTimeGetSeconds($0.videoMaxFrameDuration) >= 120 }).first {
do {
try camera.lockForConfiguration()
camera.activeFormat = format
camera.activeVideoMinFrameDuration = CMTime(value: 1, timescale: 120) // Set high frame rate for slow motion
camera.unlockForConfiguration()
} catch {
print("Error setting format: \(error)")
return
}
}

captureSession.startRunning()
4
Màn hình 4
image.png
Phân chia video
xoá đi các đoạn được phân chia đó
phân chia video thì đầu tiên phải tách video thành các image frames.
→ có thể dùng thư viện để calculate the times at which to extract frames.
xoá đc các phân đoạn video đã chọn
→ cái này có thể nhờ backend xử bằng cách : gửi video lên server, gửi thêm startTime & endTime để cắt đi đc đoạn video mong muốn
Example:
import { ProcessingManager } from 'react-native-video-processing';

const extractFrames = async (videoPath) => {
const videoInfo = await ProcessingManager.getVideoInfo(videoPath);

const frameTimes = []; // Calculate frame times based on videoInfo.duration

const frames = await Promise.all(
frameTimes.map((time) =>
ProcessingManager.getPreviewForSecond(videoPath, time)
)
);

// frames now contains the image data for each frame
};

extractFrames('path/to/video.mp4');
hoặc có thể dùng cách này
đọc ra các frame images, ghi vào file
đọc từ file ra các images đó
import { FFmpegKit, FFmpegKitConfig, ReturnCode } from 'ffmpeg-kit-react-native';
import RNFS from 'react-native-fs';

const extractFrames = async (videoPath, outputPath, frameRate) => {
const command = `-i ${videoPath} -vf fps=${frameRate} ${outputPath}/frame%03d.jpg`;

const session = await FFmpegKit.execute(command);

if (ReturnCode.isSuccess(session.getReturnCode())) {
console.log('Frames extracted successfully');
} else {
console.log('Error extracting frames:', session.getFailStackTrace());
return [];
}

const files = await RNFS.readdir(outputPath);
return files.map(file => `${outputPath}/${file}`);
};

extractFrames('path/to/video.mp4', 'path/to/output', 1)
.then(frames => console.log(frames));
đây là UI để chọn các frame

Hoặc có thể mua SDK bên thứ 3:

Hoặc có thể mua SDK bên thứ 3:
BANUBA:
token trial: "4Gvk4yzWiWh+KJ1RsvdAqzRKfl6KoEI4M7W1GH3HmBnIrkvZ5UFkfyXBArfdDPJ+ruILLhDjOrIbQji4RQLoFqZ6zIvTZOOVAcdrM/qGgzdNiv1jLHq12mexlUOOm7mxDBeuccYFsN5AggiYDzhEQAD42AxMTvFOvMP+3tmO8h9yOzUbFjK4AlOFL0jWE703NrxoOfEs79zVLdO61SjOMwcoelD8E73vWRqPzHnS+Ry5T6tg3I78bJcBtoxZp2Pp2Hs/r+Id2/7WwqUx4N3+g75l5B1UwBsQv73upPFHmlNIbz7z/KJuSpNu5T+g26UlChfzQLzs3XEC96zTNXD7I/tIkZSHnBBWdnBLjTP8kDB+f1kG0WmkhUoP1opxJ0okBsAUqEf3y2XcnZvaMTQcK7co6qLwR46GBBFWieIVMfo/jBV/iUaAyLDFbAs6B8+ed0LO+7FCEeHhM5omyQ1/xZTzqmRg4curzydcK+gg4B30vXFrWGOWJXplxP1T6g97Fz9MHJs8tAfsTPzGxTG3YfnELEEzPVNBbdBVHAS1DlD3AIid5Fi08HiH9Eb2/3tDE6M76ibEpFjKNJb8PzNL2SsdOdt7dYDiaCR6Wqtg0/moFWo=" ​
IMG LY
❗️ khả thi 50%
chỉ cần xử lý trên js
có thư viện support
nhưng không biết code như này có chạy được thật không, nếu không chạy được như kỳ vọng thì phải build native module.
cần backend support build, render video (có thể cần 80%) → để client focus xử lý các vấn đề khác .
Cần confirm lại với khách xem màn hình này có những behavor gì?
pseudocode:
Load the video file.

video = load_video(video_path)
Define the sections of the video.

sections = [(start1, end1), (start2, end2), ..., (startN, endN)]
Split the video into sections.

clips = [video.subclip(start, end) for start, end in sections]
Define the indices of the sections to delete.

sections_to_delete = [index1, index2, ..., indexM]
Delete the specified sections.

clips = [clip for i, clip in enumerate(clips) if i not in sections_to_delete]
Concatenate the remaining sections to rebuild the video.

final_video = concatenate_clips(clips)
Save the final video to a file.

save_video(final_video, output_path)

5
Màn hình 5
image.png
chèn chữ vào video
có thể customize chữ , tuỳ chỉnh hiệu ứng theo form có sẵn
chèn chữ vào video, không biết có cần kéo thả chữ hay ko?
→ nếu cần kéo thả chữ , thì cần 1 backend service hỗ trợ, client sẽ truyền lên video và text đó + toạ độ (x,y) của text ở trên màn hình để backend chèn chữ vào đúng vị trí đó trong video.
Example :
đây là UI UX drag text to any where on screen
mport React from 'react';
import { View, StyleSheet } from 'react-native';
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, { useAnimatedGestureHandler, useAnimatedStyle, withSpring } from 'react-native-reanimated';

const DragDropUI = () => {
const position = React.useRef(new Animated.ValueXY()).current;

const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = position.value.x;
ctx.startY = position.value.y;
},
onActive: (event, ctx) => {
position.value.x = ctx.startX + event.translationX;
position.value.y = ctx.startY + event.translationY;
},
onEnd: () => {
position.value.x = withSpring(0);
position.value.y = withSpring(0);
},
});

const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{ translateX: position.value.x },
{ translateY: position.value.y },
],
};
});

return (
<View style={styles.container}>
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.box, animatedStyle]} />
</PanGestureHandler>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
box: {
width: 100,
height: 100,
backgroundColor: 'blue',
},
});

export default DragDropUI;

❗️ khả thi 60%
chỉ cần xử lý trên js
có thư viện support
cần backend support build, render video (cần 100%) → để client focus xử lý các vấn đề khác .

pseudocode
1. Load the video file.
- `video = load_video(video_path)`

2. Create a text clip with the desired text, font size, and color.
- `text_clip = create_text_clip(text, font_size, color)`

3. Set the position of the text clip.
- `text_clip = set_position(text_clip, x, y)`

4. Set the duration of the text clip to match the duration of the video.
- `text_clip = set_duration(text_clip, video_duration)`

5. Overlay the text clip on the video.
- `final_video = overlay_text_on_video(video, text_clip)`

6. Save the final video to a file.
- `save_video(final_video, output_path)`
6
màn hình 6:
image.png
Chèn âm thanh vào video
chèn âm thanh vào 1 phần của video
Cần backend render lại video, client truyền lên file audio, file video, time start , time end
reackeyword: waveform sound react native sound UI customize như trim video
❗️ khả thi 70%
chỉ cần xử lý trên js
có thư viện support
cần backend support build, render video (cần 100%) → để client focus xử lý các vấn đề khác .
pseudocode
1. Load the video file.
- `video = load_video(video_path)`

2. Load the audio file.
- `audio = load_audio(audio_path)`

3. Trim the audio to the desired start and end times.
- `audio = trim_audio(audio, start_time, end_time)`

4. Set the audio of the video to the trimmed audio.
- `video = set_audio(video, audio)`

5. Save the final video to a file.
- `save_video(video, output_path)`

7
màn hình 7
Chèn nhãn dán, biểu tượng cảm xúc vào video
giống chức năm 5, nhg thay text bằng nhãn dán, biểu tượng cảm xúc , hình ảnh
Làm đc chức năng 5 thì sẽ làm đc chức năng này
❗️ khả thi 60%
chỉ cần xử lý trên js
có thư viện support
cần backend support build, render video (cần 100%) → để client focus xử lý các vấn đề khác .
There are no rows in this table
View 2 of Đánh giá chức năng
3
Màn hình
Tên chức năng
mô tả chức năng theo ý hiểu
tham khảo
SDK bên thứ 3
Tính khả thi
1
Màn hình 1
image.png
Bật đèn flash trong camera
Có 1 màn hình camera
thêm 1 button để bật - tắt flash
sử dụng function của thư viện để bật flash

yarn add react-native-vision-camera
Example:
import React, { useState } from 'react';
import { Camera } from 'react-native-vision-camera';

const FlashCamera = () => {
const [isFlashOn, setFlashOn] = useState(false);

const toggleFlash = () => {
setFlashOn(!isFlashOn);
};

return (
<Camera torch={isFlashOn} style={{ flex: 1 }}>
<button onPress={toggleFlash}>Toggle Flash</button>
</Camera>
);
};

export default FlashCamera;
✅ khả thi 100%
chỉ cần xử lý trên js
có thư viện support
2
màn hình 2
image.png
Hẹn giờ bắt đầu quay video
như trong description UI thì chỉ có phần đếm ngược → không có phần đặt giới hạn quay (cần re-confirm)
sau khi đếm ngược X giây, camera sẽ tự động quay clip

Example:
import React, { useState, useEffect } from 'react';
import { Camera } from 'react-native-vision-camera';

const CountdownCamera = () => {
const [countdown, setCountdown] = useState(0);
const cameraRef = useRef(null);

useEffect(() => {
let interval = null;
if (countdown > 0) {
interval = setInterval(() => {
setCountdown(countdown - 1);
}, 1000);
} else if (countdown === 0) {
startRecording();
}
return () => clearInterval(interval);
}, [countdown]);

const startCountdown = () => {
setCountdown(3); // Start countdown from 3
};

const startRecording = async () => {
if (cameraRef.current) {
const data = await cameraRef.current.startRecording();
console.log(data);
}
};

return (
<Camera ref={cameraRef} style={{ flex: 1 }}>
<button onPress={startCountdown}>Start Countdown</button>
{countdown > 0 && <p>Recording in {countdown}</p>}
</Camera>
);
};

export default CountdownCamera;
✅ khả thi 100%
chỉ cần xử lý trên js
có thư viện support
3
Màn hình 3
image.png
thay đổi tốc độ quay video (timelapse, slowmotion, ...)
keyword: Trim control
có thể thay đổi tốc độ quay video theo các mức 0.3, 0.5, 1, 2, 3

Example:
import React, { useRef } from 'react';
import { Camera } from 'react-native-vision-camera';

const SlowMotionCamera = () => {
const cameraRef = useRef(null);

const startRecording = async () => {
if (cameraRef.current) {
const options = { frameRate: 120 }; // Set high frame rate for slow motion
const data = await cameraRef.current.startRecording(options);
console.log(data);
}
};

const stopRecording = () => {
if (cameraRef.current) {
cameraRef.current.stopRecording();
}
};

return (
<Camera ref={cameraRef} style={{ flex: 1 }}>
<button onPress={startRecording}>Start Recording</button>
<button onPress={stopRecording}>Stop Recording</button>
</Camera>
);
};

export default SlowMotionCamera;
❗️ khả thi 80%
chỉ cần xử lý trên js
có thư viện support
nhưng không biết code như này có chạy được thật không, nếu không chạy được như kỳ vọng thì phải build native module.
Example:
import AVFoundation

let captureSession = AVCaptureSession()
guard let camera = AVCaptureDevice.default(for: .video) else { return }

do {
let input = try AVCaptureDeviceInput(device: camera)
captureSession.addInput(input)
} catch {
print("Error creating input: \(error)")
return
}

if let format = camera.formats.filter({ CMTimeGetSeconds($0.videoMaxFrameDuration) >= 120 }).first {
do {
try camera.lockForConfiguration()
camera.activeFormat = format
camera.activeVideoMinFrameDuration = CMTime(value: 1, timescale: 120) // Set high frame rate for slow motion
camera.unlockForConfiguration()
} catch {
print("Error setting format: \(error)")
return
}
}

captureSession.startRunning()
4
Màn hình 4
image.png
Phân chia video
xoá đi các đoạn được phân chia đó
phân chia video thì đầu tiên phải tách video thành các image frames.
→ có thể dùng thư viện để calculate the times at which to extract frames.
xoá đc các phân đoạn video đã chọn
→ cái này có thể nhờ backend xử bằng cách : gửi video lên server, gửi thêm startTime & endTime để cắt đi đc đoạn video mong muốn
Example:
import { ProcessingManager } from 'react-native-video-processing';

const extractFrames = async (videoPath) => {
const videoInfo = await ProcessingManager.getVideoInfo(videoPath);

const frameTimes = []; // Calculate frame times based on videoInfo.duration

const frames = await Promise.all(
frameTimes.map((time) =>
ProcessingManager.getPreviewForSecond(videoPath, time)
)
);

// frames now contains the image data for each frame
};

extractFrames('path/to/video.mp4');
hoặc có thể dùng cách này
đọc ra các frame images, ghi vào file
đọc từ file ra các images đó
import { FFmpegKit, FFmpegKitConfig, ReturnCode } from 'ffmpeg-kit-react-native';
import RNFS from 'react-native-fs';

const extractFrames = async (videoPath, outputPath, frameRate) => {
const command = `-i ${videoPath} -vf fps=${frameRate} ${outputPath}/frame%03d.jpg`;

const session = await FFmpegKit.execute(command);

if (ReturnCode.isSuccess(session.getReturnCode())) {
console.log('Frames extracted successfully');
} else {
console.log('Error extracting frames:', session.getFailStackTrace());
return [];
}

const files = await RNFS.readdir(outputPath);
return files.map(file => `${outputPath}/${file}`);
};

extractFrames('path/to/video.mp4', 'path/to/output', 1)
.then(frames => console.log(frames));
đây là UI để chọn các frame

Hoặc có thể mua SDK bên thứ 3:

Hoặc có thể mua SDK bên thứ 3:
BANUBA:
token trial: "4Gvk4yzWiWh+KJ1RsvdAqzRKfl6KoEI4M7W1GH3HmBnIrkvZ5UFkfyXBArfdDPJ+ruILLhDjOrIbQji4RQLoFqZ6zIvTZOOVAcdrM/qGgzdNiv1jLHq12mexlUOOm7mxDBeuccYFsN5AggiYDzhEQAD42AxMTvFOvMP+3tmO8h9yOzUbFjK4AlOFL0jWE703NrxoOfEs79zVLdO61SjOMwcoelD8E73vWRqPzHnS+Ry5T6tg3I78bJcBtoxZp2Pp2Hs/r+Id2/7WwqUx4N3+g75l5B1UwBsQv73upPFHmlNIbz7z/KJuSpNu5T+g26UlChfzQLzs3XEC96zTNXD7I/tIkZSHnBBWdnBLjTP8kDB+f1kG0WmkhUoP1opxJ0okBsAUqEf3y2XcnZvaMTQcK7co6qLwR46GBBFWieIVMfo/jBV/iUaAyLDFbAs6B8+ed0LO+7FCEeHhM5omyQ1/xZTzqmRg4curzydcK+gg4B30vXFrWGOWJXplxP1T6g97Fz9MHJs8tAfsTPzGxTG3YfnELEEzPVNBbdBVHAS1DlD3AIid5Fi08HiH9Eb2/3tDE6M76ibEpFjKNJb8PzNL2SsdOdt7dYDiaCR6Wqtg0/moFWo=" ​
IMG LY
❗️ khả thi 50%
chỉ cần xử lý trên js
có thư viện support
nhưng không biết code như này có chạy được thật không, nếu không chạy được như kỳ vọng thì phải build native module.
cần backend support build, render video (có thể cần 80%) → để client focus xử lý các vấn đề khác .
Cần confirm lại với khách xem màn hình này có những behavor gì?
pseudocode:
Load the video file.

video = load_video(video_path)
Define the sections of the video.

sections = [(start1, end1), (start2, end2), ..., (startN, endN)]
Split the video into sections.

clips = [video.subclip(start, end) for start, end in sections]
Define the indices of the sections to delete.

sections_to_delete = [index1, index2, ..., indexM]
Delete the specified sections.

clips = [clip for i, clip in enumerate(clips) if i not in sections_to_delete]
Concatenate the remaining sections to rebuild the video.

final_video = concatenate_clips(clips)
Save the final video to a file.

save_video(final_video, output_path)

5
Màn hình 5
image.png
chèn chữ vào video
có thể customize chữ , tuỳ chỉnh hiệu ứng theo form có sẵn
chèn chữ vào video, không biết có cần kéo thả chữ hay ko?
→ nếu cần kéo thả chữ , thì cần 1 backend service hỗ trợ, client sẽ truyền lên video và text đó + toạ độ (x,y) của text ở trên màn hình để backend chèn chữ vào đúng vị trí đó trong video.
Example :
đây là UI UX drag text to any where on screen
mport React from 'react';
import { View, StyleSheet } from 'react-native';
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, { useAnimatedGestureHandler, useAnimatedStyle, withSpring } from 'react-native-reanimated';

const DragDropUI = () => {
const position = React.useRef(new Animated.ValueXY()).current;

const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = position.value.x;
ctx.startY = position.value.y;
},
onActive: (event, ctx) => {
position.value.x = ctx.startX + event.translationX;
position.value.y = ctx.startY + event.translationY;
},
onEnd: () => {
position.value.x = withSpring(0);
position.value.y = withSpring(0);
},
});

const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{ translateX: position.value.x },
{ translateY: position.value.y },
],
};
});

return (
<View style={styles.container}>
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.box, animatedStyle]} />
</PanGestureHandler>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
box: {
width: 100,
height: 100,
backgroundColor: 'blue',
},
});

export default DragDropUI;

❗️ khả thi 60%
chỉ cần xử lý trên js
có thư viện support
cần backend support build, render video (cần 100%) → để client focus xử lý các vấn đề khác .

pseudocode
1. Load the video file.
- `video = load_video(video_path)`

2. Create a text clip with the desired text, font size, and color.
- `text_clip = create_text_clip(text, font_size, color)`

3. Set the position of the text clip.
- `text_clip = set_position(text_clip, x, y)`

4. Set the duration of the text clip to match the duration of the video.
- `text_clip = set_duration(text_clip, video_duration)`

5. Overlay the text clip on the video.
- `final_video = overlay_text_on_video(video, text_clip)`

6. Save the final video to a file.
- `save_video(final_video, output_path)`
6
màn hình 6:
image.png
Chèn âm thanh vào video
chèn âm thanh vào 1 phần của video
Cần backend render lại video, client truyền lên file audio, file video, time start , time end
reackeyword: waveform sound react native sound UI customize như trim video
❗️ khả thi 70%
chỉ cần xử lý trên js
có thư viện support
cần backend support build, render video (cần 100%) → để client focus xử lý các vấn đề khác .
pseudocode
1. Load the video file.
- `video = load_video(video_path)`

2. Load the audio file.
- `audio = load_audio(audio_path)`

3. Trim the audio to the desired start and end times.
- `audio = trim_audio(audio, start_time, end_time)`

4. Set the audio of the video to the trimmed audio.
- `video = set_audio(video, audio)`

5. Save the final video to a file.
- `save_video(video, output_path)`

7
màn hình 7
Chèn nhãn dán, biểu tượng cảm xúc vào video
giống chức năm 5, nhg thay text bằng nhãn dán, biểu tượng cảm xúc , hình ảnh
Làm đc chức năng 5 thì sẽ làm đc chức năng này
❗️ khả thi 60%
chỉ cần xử lý trên js
có thư viện support
cần backend support build, render video (cần 100%) → để client focus xử lý các vấn đề khác .
There are no rows in this table

Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.