Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video player fixes #2451

Merged
merged 10 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading