Shopify / react-native-skia

High-performance React Native Graphics using Skia
https://shopify.github.io/react-native-skia
MIT License
6.98k stars 452 forks source link

Video frames black screen flickering #2647

Closed smontlouis closed 1 month ago

smontlouis commented 1 month ago

Description

I’m using a component to load a video and an image. When I change the “ambience,” the shader handles the transition using a progress value, showing the image of the next video. Meanwhile, I load the next video by updating selectedAmbience. Once the new video is loaded (I check this by observing changes in duration), I reset progressValue to 0 without a transition.

Issues

Video

https://x.com/_smontlouis/status/1839301672633311314

Version

1.3.11

Steps to reproduce

Copy the code pasted

Snack, code example, screenshot, or link to a repository

Code

const VideoAmbienceCanvas = ({
  ambience,
}: {
  ambience: (typeof ambiences)[number]
}) => {
  const { playing: isPlaying } = useIsPlaying()
  const [selectedAmbience, setSelectedAmbience] = useState(ambience)
  const paused = useSharedValue(false)
  const { width, height } = useWindowDimensions()
  const progress = useSharedValue(0)
  const seek = useSharedValue<null | number>(null)
  const assets = useAssets()

  const { currentFrame: image1, duration } = useVideo(
    `${process.env.EXPO_PUBLIC_CDN_URL}sounds/${selectedAmbience.value}.mp4`,
    {
      looping: true,
      volume: 0,
      paused,
      seek,
    }
  )

  const image2Name = useSharedValue<(typeof ambiences)[number]['value']>(
    ambiences[0].value
  )

  const image2 = useDerivedValue(() => {
    return assets[image2Name.value]
  })

  useEffect(() => {
    paused.value = !isPlaying
  }, [isPlaying])

  useDidUpdate(() => {
    image2Name.value =
      ambiences.find((a) => a.value === ambience.value)?.value ||
      ambiences[0].value

    progress.value = withTiming(
      1,
      {
        duration: 1500,
      },
      () => {
        runOnJS(setSelectedAmbience)(ambience)
      }
    )
  }, [ambience])

  // Detect video change based on duration
  useDidUpdate(() => {
    setTimeout(() => {
      seek.value = 0
      progress.value = 0
    }, 200)
  }, [duration])

  const uniform = useDerivedValue(() => {
    return {
      progress: progress.value,
      resolution: [width, height],
    }
  })

  return (
    <Canvas style={{ flex: 1 }}>
      <Fill>
        <Shader source={source} uniforms={uniform}>
          <ImageShader
            fit="cover"
            x={0}
            y={0}
            image={image1}
            width={width}
            height={height}
          />

          <ImageShader
            fit="cover"
            x={0}
            y={0}
            image={image2}
            width={width}
            height={height}
          />
        </Shader>
      </Fill>
    </Canvas>
  )
}
wcandillon commented 1 month ago

I think your best bet at the moment would be to use the following module: https://github.com/AzzappApp/react-native-skia-video

smontlouis commented 1 month ago

Oh much more options ! Thank you I'll try that