1. Introduction
Dans cet article, nous allons explorer en détail comment utiliser le composant MediaPlayer pour la lecture audio et vidéo dans un environnement React Native sur OpenHarmony. Nous aborderons les concepts clés, les différences d'implémentation entre les plateformes, et fournirons des exemples de code pour illustrer les points importants.
2. Présentation de MediaPlayer
Dans l'écosystème React Native, MediaPlayer est un terme générique qui englobe les capacités de lecture audio et vidéo. Généralement, on utilise des bibliothèques comme react-native-video ou des modules natifs pour implémenter ces fonctionnalités.
2.1 Adapting React Native to OpenHarmony
React Native offre l'avantage de "Learn Once, Write Anywhere", mais la lecture multimédia présente des défis spécifiques en raison des différences de rendu sous-jacentes entre les systèmes d'exploitation.
- Différences architecturales : Sur Android, MediaPlayer repose souvent sur
ExoPlayerou leMediaPlayernatif, tandis que sur iOS, c'estAVPlayer. Sur OpenHarmony, le moduleOH_AVPlayerest utilisé, avec sa propre gestion des états et du cycle de vie. - Adaptation du rendu : Pour que React Native fonctionne sur OpenHarmony, il faut pontifier les instructions JavaScript vers le niveau natif. Cela nécessite que les bibliothèques tierces incluent une implémentation native (généralement en C++ ou ArkTS).
- Gestion des permissions : OpenHarmony a des contrôles de permissions stricts pour l'accès au réseau et aux fichiers. Il est crucial de déclarer correctement ces permissions dans
module.json5.
2.2 Principes techniques de base
Le MediaPlayer est essentiellement un automate d'états. Quelle que soit la platfeorme, il suit un flux basique : Idle -> Initialized -> Preparing -> Prepared -> Started -> Paused -> Stopped -> End.
Sur OpenHarmony, les appels de méthodes natives sont effectués via le Bridge (ou TurboModules dans la nouvelle architecture). Par exemple, lorsqu'on appelle seek(1000) en JavaScript, le processus se déroule ainsi :
Comprendre ce flux est crucial pour diagnostiquer les problèmes de latence ou de blocage sur OpenHarmony.
2.3 Scénarios d'utilisation
Les applications de MediaPlayer sont nombreuses :
- Applications de vidéos courtes : Exigent un chargement rapide et un préchargement par glissement.
- Éducation en ligne : Supporte la lecture accélérée, le changement de qualité, et la reprise après interruption.
- Flux en direct : Nécessite une faible latence et supporte HLS (m3u8) ou RTMP.
- Musique de fond : Doit continuer à jouer même lorsque l'application est en arrière-plan.
3. Mise en pratique de MediaPlayer
Avant de commencer, assurez-vous que votre environnement est configuré correctement.
3.1 Préparation de l'environnement
- Node.js: >= 16.x
- React Native: >= 0.72.x
- OpenHarmony SDK: API Level 9 et plus
- Bibliothèques :
npm install react-native-video
Après l'installation, configurez les liens des modules natifs dans votre projet OpenHarmony.
3.2 Implémentation d'un lecteur vidéo simple
Voici un exemple minimal d'un lecteur vidéo avec des fonctionnalités de base :
import React, { useRef, useState } from 'react';
import { View, Text, StyleSheet, SafeAreaView, TouchableOpacity } from 'react-native';
import Video from 'react-native-video';
const SimpleMediaPlayer = () => {
const videoPlayer = useRef(null);
const [paused, setPaused] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const videoSource = {
uri: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4',
};
const onLoad = (meta) => {
console.log('Video Loaded:', meta.duration);
setDuration(meta.duration);
};
const onProgress = (data) => {
setCurrentTime(data.currentTime);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.videoContainer}>
<Video
source={videoSource}
ref={videoPlayer}
resizeMode="contain"
repeat={false}
paused={paused}
style={styles.video}
onLoad={onLoad}
onProgress={onProgress}
onEnd={() => setPaused(true)}
volume={1.0}
rate={1.0}
/>
</View>
<View style={styles.controls}>
<TouchableOpacity onPress={() => setPaused(!paused)}>
<Text style={styles.controlText}>{paused ? '▶️ Play' : '⏸️ Pause'}</Text>
</TouchableOpacity>
<Text style={styles.timeText}>
Time: {Math.floor(currentTime)} / {Math.floor(duration)}
</Text>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
videoContainer: {
width: '100%',
height: 300,
backgroundColor: '#000',
justifyContent: 'center',
alignItems: 'center',
},
video: {
width: '100%',
height: '100%',
},
controls: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
padding: 20,
backgroundColor: '#f0f0f0',
},
controlText: {
fontSize: 18,
color: '#333',
fontWeight: 'bold',
},
timeText: {
fontSize: 16,
color: '#666',
},
});
export default SimpleMediaPlayer;
Points clés
- Attriubt
source: Spécifie l'URL ou le chemin local du fichier vidéo. - Attribut
resizeMode: Contrôle le mode de remplissage de la vidéo. - État
paused: Permet de contrôler la lecture et la pause. - Référence
ref: Utilisée pour accéder aux méthodes du composant.
4. Fonctionnalités avancées
Pour des fonctionnalités plus complexes, comme le contrôle de la progression et la lecture accélérée, voici un exemple amélioré :
import React, { useRef, useState, useEffect } from 'react';
import { View, StyleSheet, SafeAreaView, Text, TouchableOpacity } from 'react-native';
import Video from 'react-native-video';
import Slider from '@react-native-community/slider';
const AdvancedMediaPlayer = () => {
const videoRef = useRef(null);
const [paused, setPaused] = useState(true);
const [currentTime, setCurrentTime] = useState(0);
const [seekableDuration, setSeekableDuration] = useState(0);
const [rate, setRate] = useState(1.0);
const [isFullscreen, setIsFullscreen] = useState(false);
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins < 10 ? '0' : ''}${mins}:${secs < 10 ? '0' : ''}${secs}`;
};
const onSliderValueChange = (value) => {
setCurrentTime(value);
};
const onSlidingComplete = (value) => {
videoRef.current?.seek(value);
setCurrentTime(value);
};
const changeRate = (newRate) => {
setRate(newRate);
};
return (
<SafeAreaView style={[styles.container, isFullscreen && styles.fullscreenContainer]}>
<View style={styles.videoWrapper}>
<Video
source={{ uri: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4' }}
ref={videoRef}
resizeMode="contain"
paused={paused}
rate={rate}
volume={1.0}
muted={false}
style={isFullscreen ? styles.fullscreenVideo : styles.video}
onLoad={(data) => setSeekableDuration(data.duration)}
onProgress={(data) => {
if (Math.abs(data.currentTime - currentTime) > 0.5) {
setCurrentTime(data.currentTime);
}
}}
onEnd={() => {
setPaused(true);
setCurrentTime(0);
}}
onError={(e) => console.error('Video Error:', e)}
/>
</View>
<View style={styles.controlsContainer}>
<View style={styles.progressContainer}>
<Text style={styles.timeText}>{formatTime(currentTime)}</Text>
<Slider
style={styles.slider}
minimumValue={0}
maximumValue={seekableDuration > 0 ? seekableDuration : 1}
value={currentTime}
onValueChange={onSliderValueChange}
onSlidingComplete={onSlidingComplete}
minimumTrackTintColor="#1EB1FC"
maximumTrackTintColor="#d3d3d3"
thumbTintColor="#1EB1FC"
/>
<Text style={styles.timeText}>{formatTime(seekableDuration)}</Text>
</View>
<View style={styles.buttonsContainer}>
<TouchableOpacity onPress={() => setPaused(!paused)}>
<Text style={styles.buttonText}>{paused ? 'Play' : 'Pause'}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => changeRate(rate === 1.0 ? 1.5 : 1.0)}>
<Text style={styles.buttonText}>{rate}x</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => setIsFullscreen(!isFullscreen)}>
<Text style={styles.buttonText}>{isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}</Text>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
justifyContent: 'center',
},
fullscreenContainer: {
...StyleSheet.absoluteFillObject,
zIndex: 999,
},
videoWrapper: {
width: '100%',
height: 220,
backgroundColor: 'black',
},
video: {
width: '100%',
height: '100%',
},
fullscreenVideo: {
...StyleSheet.absoluteFillObject,
},
controlsContainer: {
padding: 10,
backgroundColor: '#1c1c1c',
},
progressContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
},
slider: {
flex: 1,
marginHorizontal: 10,
},
timeText: {
color: '#fff',
fontSize: 12,
width: 40,
},
buttonsContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
},
buttonText: {
color: '#fff',
fontSize: 14,
fontWeight: '600',
},
});
export default AdvancedMediaPlayer;
Points clés
- Attribut
rate: Contrôle la vitesse de lecture. - Opération
seek: Permet de naviguer dans la vidéo. - Logique plein écran : Modifie le style du composant pour simuler le plein écran.
5. Considérations spécifiques à OpenHarmony
5.1 Compatibilité de décodage matériel
- Problème : La prise en charge du décodage matériel varie selon les appareils.
- Solution : Fournir plusieurs formats de flux vidéo et détecter les capacités de l'appareil.
5.2 Limites de la lecture en arrière-plan
- Configuration : Demander la permission
ohos.permission.KEEP_BACKGROUND_RUNNING. - API de tâches longues : Utiliser les API de tâches longues pour la lecture en arrière-plan prolongée.
5.3 Protocoles de streaming supportés
| Protocole | Android | iOS | OpenHarmony | Remarques |
|---|---|---|---|---|
| HLS (m3u8) | Supporté | Nativement supporté | Supporté (API 9+) | Bon support, mais DRM peut nécessiter des versions spécifiques |
| MP4 | Supporté | Supporté | Supporté | H.264 offre la meilleure compatibilité |
| DASH | ⚠️ Nécessite ExoPlayer | ⚠️ Nécessite une bibliothèque | ⚠️ Support limité | Recommandé de transcoder en HLS |
| RTMP | Non recommandé | Non recommandé | Non recommandé | WebRTC recommandé pour la faible latence |
5.4 Problèmes de couche de rendu
- Phénomène : Les vidéos peuvent masquer d'autres composants.
- Solution : Éviter de superposer des popups complexes sur la vidéo, ou ajuster l'ordre Z des couches natives.
6. Optimisation des performances et comparaison
Pour évaluer les performances, voici un tableau de comparaison basé sur des tests sur Huawei Mate 60 Pro (OpenHarmony 4.0) et iPhone 13 (iOS 17).
| Métrique | OpenHarmony (RN) | iOS (RN) | Analyse des différences |
|---|---|---|---|
| Temps de chargement de la première image | 450ms | 300ms | Initialisation légèrement plus lente sur OpenHarmony |
Latence de seek |
300ms | 150ms | Performances de seek légèrement inférieures |
| Occupation CPU (lecture) | 12% | 8% | Coût de rendu logiciel plus élevé |
| Occupation mémoire | 85MB | 70MB | Coût de pontage Bridge |
| Chauffe | Chaude | Fraîche | Plus notable lors de la lecture de vidéos 4K |
7. Conclusion
Cet article a couvert l'utilisation de MediaPlayer dans React Native sur OpenHarmony, en mettant l'accent sur les principes de base, les fonctionnalités avancées, et les considérations spécifiques à la plateforme. Ces connaissances vous permettront de construire des lecteurs multimédias robustes et performants.