Skip to content

Commit

Permalink
fix(📹): useVideo fixes (#2451)
Browse files Browse the repository at this point in the history
  • Loading branch information
wcandillon authored May 30, 2024
1 parent fc55cb4 commit 9436209
Show file tree
Hide file tree
Showing 15 changed files with 353 additions and 101 deletions.
17 changes: 8 additions & 9 deletions docs/docs/video.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface VideoExampleProps {
export const VideoExample = ({ localVideoFile }: VideoExampleProps) => {
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const video = useVideo(
const { currentFrame } = useVideo(
require(localVideoFile),
{
paused,
Expand All @@ -52,7 +52,7 @@ export const VideoExample = ({ localVideoFile }: VideoExampleProps) => {
<Canvas style={{ flex: 1 }}>
<Fill>
<ImageShader
image={video}
image={currentFrame}
x={0}
y={0}
width={width}
Expand Down Expand Up @@ -94,12 +94,14 @@ export const useVideoFromAsset = (
};
```

## Returned Values

The `useVideo` hook returns `currentFrame` which contains the current video frame, as well as `currentTime`, and `rotationInDegrees`.

## Playback Options

You can seek a video via the `seek` playback option. By default, the seek option is null. If you set a value in milliseconds, it will seek to that point in the video and then set the option value to null again.

There is also the `currentTime` option, which is a Reanimated value that contains the current playback time of the video.

`looping` indicates whether the video should be looped or not.

`playbackSpeed` indicates the playback speed of the video (default is 1).
Expand All @@ -125,15 +127,12 @@ export const VideoExample = ({ localVideoFile }: VideoExampleProps) => {
const seek = useSharedValue<null | number>(null);
// Set this value to true to pause the video
const paused = useSharedValue(false);
// Contains the current playback time of the video
const currentTime = useSharedValue(0);
const { width, height } = useWindowDimensions();
const video = useVideo(
const {currentFrame, currentTime} = useVideo(
require(localVideoFile),
{
seek,
paused,
currentTime,
looping: true,
playbackSpeed: 1
}
Expand All @@ -145,7 +144,7 @@ export const VideoExample = ({ localVideoFile }: VideoExampleProps) => {
>
<Canvas style={{ flex: 1 }}>
<Image
image={video}
image={currentFrame}
x={0}
y={0}
width={width}
Expand Down
4 changes: 2 additions & 2 deletions example/src/Examples/Video/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useVideoFromAsset } from "../../components/Animations";
export const Video = () => {
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const video = useVideoFromAsset(
const { currentFrame } = useVideoFromAsset(
require("../../Tests/assets/BigBuckBunny.mp4"),
{
paused,
Expand All @@ -28,7 +28,7 @@ export const Video = () => {
<Canvas style={{ flex: 1 }}>
<Fill>
<ImageShader
image={video}
image={currentFrame}
x={0}
y={0}
width={width}
Expand Down
4 changes: 2 additions & 2 deletions fabricexample/src/Examples/Video/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useVideoFromAsset } from "../../components/Animations";
export const Video = () => {
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const video = useVideoFromAsset(
const { currentFrame } = useVideoFromAsset(
require("../../Tests/assets/BigBuckBunny.mp4"),
{
paused,
Expand All @@ -28,7 +28,7 @@ export const Video = () => {
<Canvas style={{ flex: 1 }}>
<Fill>
<ImageShader
image={video}
image={currentFrame}
x={0}
y={0}
width={width}
Expand Down
13 changes: 13 additions & 0 deletions package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ double RNSkAndroidVideo::duration() {
}
return env->CallDoubleMethod(_jniVideo.get(), mid);
}

double RNSkAndroidVideo::framerate() {
JNIEnv *env = facebook::jni::Environment::current();
jclass cls = env->GetObjectClass(_jniVideo.get());
Expand All @@ -89,4 +90,16 @@ void RNSkAndroidVideo::seek(double timestamp) {
env->CallVoidMethod(_jniVideo.get(), mid, static_cast<jlong>(timestamp));
}

float RNSkAndroidVideo::getRotationInDegrees() {
JNIEnv *env = facebook::jni::Environment::current();
jclass cls = env->GetObjectClass(_jniVideo.get());
jmethodID mid = env->GetMethodID(cls, "getRotationDegrees", "()I");
if (!mid) {
RNSkLogger::logToConsole("getRotationDegrees method not found");
return 0;
}
auto rotation = env->CallIntMethod(_jniVideo.get(), mid);
return static_cast<float>(rotation);
}

} // namespace RNSkia
1 change: 1 addition & 0 deletions package/android/cpp/rnskia-android/RNSkAndroidVideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class RNSkAndroidVideo : public RNSkVideo {
double duration() override;
double framerate() override;
void seek(double timestamp) override;
float getRotationInDegrees() override;
};

} // namespace RNSkia
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class RNSkVideo {
private Surface outputSurface;
private double durationMs;
private double frameRate;
private int rotationDegrees = 0;

RNSkVideo(Context context, String localUri) {
this.uri = Uri.parse(localUri);
Expand All @@ -53,6 +54,9 @@ private void initializeReader() {
if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
}
if (format.containsKey(MediaFormat.KEY_ROTATION)) {
rotationDegrees = format.getInteger(MediaFormat.KEY_ROTATION);
}
int width = format.getInteger(MediaFormat.KEY_WIDTH);
int height = format.getInteger(MediaFormat.KEY_HEIGHT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Expand Down Expand Up @@ -87,11 +91,15 @@ public double getDuration() {
}

@DoNotStrip

public double getFrameRate() {
return frameRate;
}

@DoNotStrip
public int getRotationDegrees() {
return rotationDegrees;
}

@DoNotStrip
public HardwareBuffer nextImage() {
if (!decoderOutputAvailable()) {
Expand Down
7 changes: 7 additions & 0 deletions package/cpp/api/JsiVideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,17 @@ class JsiVideo : public JsiSkWrappingSharedPtrHostObject<RNSkVideo> {
return jsi::Value::undefined();
}

JSI_HOST_FUNCTION(getRotationInDegrees) {
auto context = getContext();
auto rot = getObject()->getRotationInDegrees();
return jsi::Value(static_cast<double>(rot));
}

JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiVideo, nextImage),
JSI_EXPORT_FUNC(JsiVideo, duration),
JSI_EXPORT_FUNC(JsiVideo, framerate),
JSI_EXPORT_FUNC(JsiVideo, seek),
JSI_EXPORT_FUNC(JsiVideo, getRotationInDegrees),
JSI_EXPORT_FUNC(JsiVideo, dispose))

JsiVideo(std::shared_ptr<RNSkPlatformContext> context,
Expand Down
1 change: 1 addition & 0 deletions package/cpp/rnskia/RNSkVideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class RNSkVideo {
virtual double duration() = 0;
virtual double framerate() = 0;
virtual void seek(double timestamp) = 0;
virtual float getRotationInDegrees() = 0;
};

} // namespace RNSkia
2 changes: 2 additions & 0 deletions package/ios/RNSkia-iOS/RNSkiOSVideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class RNSkiOSVideo : public RNSkVideo {
double _framerate = 0;
void setupReader(CMTimeRange timeRange);
NSDictionary *getOutputSettings();
CGAffineTransform _preferredTransform;

public:
RNSkiOSVideo(std::string url, RNSkPlatformContext *context);
Expand All @@ -35,6 +36,7 @@ class RNSkiOSVideo : public RNSkVideo {
double duration() override;
double framerate() override;
void seek(double timestamp) override;
float getRotationInDegrees() override;
};

} // namespace RNSkia
24 changes: 22 additions & 2 deletions package/ios/RNSkia-iOS/RNSkiOSVideo.mm
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#pragma once

#include <memory>
#include <string>

Expand Down Expand Up @@ -46,6 +44,7 @@
AVAssetTrack *videoTrack =
[[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
_framerate = videoTrack.nominalFrameRate;
_preferredTransform = videoTrack.preferredTransform;

NSDictionary *outputSettings = getOutputSettings();
AVAssetReaderTrackOutput *trackOutput =
Expand Down Expand Up @@ -99,6 +98,27 @@
};
}

float RNSkiOSVideo::getRotationInDegrees() {
CGFloat rotationAngle = 0.0;
auto transform = _preferredTransform;
// Determine the rotation angle in radians
if (transform.a == 0 && transform.b == 1 && transform.c == -1 &&
transform.d == 0) {
rotationAngle = M_PI_2; // 90 degrees
} else if (transform.a == 0 && transform.b == -1 && transform.c == 1 &&
transform.d == 0) {
rotationAngle = -M_PI_2; // -90 degrees
} else if (transform.a == -1 && transform.b == 0 && transform.c == 0 &&
transform.d == -1) {
rotationAngle = M_PI; // 180 degrees
} else if (transform.a == 1 && transform.b == 0 && transform.c == 0 &&
transform.d == 1) {
rotationAngle = 0.0; // 0 degrees
}
// Convert the rotation angle from radians to degrees
return rotationAngle * 180 / M_PI;
}

void RNSkiOSVideo::seek(double timeInMilliseconds) {
if (_reader) {
[_reader cancelReading];
Expand Down
Loading

0 comments on commit 9436209

Please sign in to comment.