How to control multiple video with expo-av - expo

Issue Description
I'm trying to stop playing all the video when it goes to other screen.
I define videoRef to control the video.
videoRef.current.pauseAsync();
But this only works when there is only 1 video. How does it work when there are multiple video. How to make it work. Plz help!
Steps to Reproduce / Code Snippets
const videoRef = React.useRef();
const renderMessageVideo = (props) => (
<View>
<Video
ref={videoRef}
style={styles.video}
source={{
uri: props.currentMessage.video,
}}
useNativeControls={true}
isMuted={false}
shouldPlay={play}
volume={1.0}
resizeMode="cover"
isLooping={false}
onPlaybackStatusUpdate={onPlaybackStatusUpdate}
/>
</View>
);
useEffect(() => {
// Do something when mount
const unsubscribe = navigation.addListener("transitionStart", (e) => {
// Do something when unmount
videoRef.current.pauseAsync();
});
return unsubscribe;
}, [navigation]);
Update
I've changed the way to store the ref. (counter = 0, and will plus one every time)
ref={(r) => (videoRef.current[counter] = r)}
By using this, I can log the data of videoRef.current[0] if there is only 1 video.
console.log(videoRef.current[0]);
// This will print the data of the first video
But if there is a second video, the second video will store in videoRef.current[1], and videoRef.current[0] will replace by null.
console.log(videoRef.current[0])
// null
console.log(videoRef.current[1])
// This will print the data of the second video

Related

Parallel HTTP requests with limited concurrency in redux-observable epic using rxjs

Have been trying to solve the issue for a while.
Currently I have an array of objects (i call them tiles), which is pretty big.
I have an API endpoint where I should send this objects one by one, this API returns nothing, just status.
I need to send this objects to endpoint in parallel and concurrent manner and when the last of them is successful I should emit some string value which goes to redux store.
const tilesEpic =(action$, _state$) => {
action$.pipe(
ofType('TILE_ACTION'),
map(tilesArray => postTilesConcurrently(tilesArray),
map(someId => someReduxAction(someId),
)
const postTilesConcurrently = (tilesArray) => {
const tilesToObservables = tilesArray.map(tile => defer(() => postTile(tile))
return from(tileToObservables).pipe(mergeAll(concurrencyLimit))
}
The problem is that I have no idea how to emit someId from postTilesConcurrently, now it triggers action after each request is complete.
mergeAll() will subscribe to all sources in parallel but it will also emit each result immediatelly. So instead you could use for example forkJoin() (
you could use toArray() operator as well).
forkJoin(tilesToObservables)
.pipe(
map(results => results???), // Get `someId` somehow from results
);
forkJoin() will emit just once after all source Observables emit at least once and complete. This means for each source Observable you'll get only the last value it emitted.
After Martin's reply I have adjusted my code in order to use forkJoin
const tilesEpic =(action$, _state$) => {
action$.pipe(
ofType('TILE_ACTION'),
concatMap(tilesArray => postTilesConcurrently(tilesArray),
map(({someId}) => someReduxAction(someId),
)
const postTilesConcurrently = (tilesArray) => {
const tilesToObservables = tilesArray.map(tile => defer(() => postTile(tile))
return forkJoin({
images: from(tileToObservables).pipe(mergeAll(concurrencyLimit)),
someId: from([someId]),
}

How to listen the state changes in svelte like useEffect

I have read some article about state change listener, As I am a very beginner to the svelte environment I can't figure out what is the most efficient way to listen to the state change.
Let us take state variable as X and Y
Method 1:
$: if (X||Y) {
console.log("yes");
}
Method 2:
Use a combination of afterUpdate and onDestroy
REPL: https://svelte.dev/repl/300c16ee38af49e98261eef02a9b04a8?version=3.38.2
import { afterUpdate, onDestroy } from 'svelte';
export function useEffect(cb, deps) {
let cleanup;
function apply() {
if (cleanup) cleanup();
cleanup = cb();
}
if (deps) {
let values = [];
afterUpdate(() => {
const new_values = deps();
if (new_values.some((value, i) => value !== values[i])) {
apply();
values = new_values;
}
});
} else {
// no deps = always run
afterUpdate(apply);
}
onDestroy(() => {
if (cleanup) cleanup();
});
}
Method 3:
Use writable and subscribe
<script>
import { writable } from 'svelte/store';
const X = writable(0);
const Y = writable(0);
X.subscribe(value => {
console.log("X was changed", value);
});
Y.subscribe(value => {
console.log("Y was changed", value);
});
</script>
<button on:click={(e)=>{
X.update((val)=>val++)
}}>Change X</button>
<button on:click={(e)=>{
Y.update((val)=>val++)
}}>Change Y</button>
Reactive statements
Svelte does not have an equivalent of useEffect. Instead, Svelte has reactive statements.
// Svelte
// doubled will always be twice of single. If single updates, doubled will run again.
$: doubled = single * 2
// equivalent to this React
let single = 0
const [doubled, setDoubled] = useState(single * 2)
useEffect(() => {
setDoubled(single * 2)
}, [single])
This may seem like magic, since you don't define dependencies or teardown functions, but Svelte takes care of all that under the hood. Svelte is smart enough to figure out the dependencies and only run each reactive statement as needed.
So if you want to run a callback every time a value updates, you can simply do this.
<script>
let value = ''
$: console.log(value)
</script>
<input type='text' name='name' bind:value />
In short, this will console log the value of the input every time the input's value changes.
Recreating your suggestions
Method 1
That's about as concise as you can go, and is my go-to means of listening to state changes unless I need something more complex.
$: if(x || y) console.log('yes')
Though note that there may be a subtle bug here. If x or y were both truthy then turn falsy (e.g., they both became empty strings), this statement will not run.
Method 2
Here, you basically recreated React's useEffect. This works, but you can simplify the implementation in your REPL a lot using reactive statements.
<script>
let count = 1;
$: console.log(count)
</script>
<input type="number" bind:value={count}>
Method 3
Stores are great for passing data between components without using props or context. Check out this answer for more: https://stackoverflow.com/a/67681054/11506995
In this case, though, we can simplify everything using regular reactive context (again).
<script>
let x = 0
let y = 0
$: console.log('x was changed', x)
$: console.log('y was changed', y)
</script>
<button on:click={() => x++}>Change x</button>
<button on:click={() => y++}>Change x</button>
I also have a detailed answer of comparing React's context/useEffect with Svelte's context/stores/reactive statements in Understanding Context in Svelte (convert from React Context)
https://stackoverflow.com/a/67681054/11506995
How about this?
<script>
let count = 0;
$: doubled = (() => {
return count * 2
})()
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
<p>{count} doubled is {doubled}</p>

UI-grid grouping auto expand

Does anybody know how to automatically expand a UI-grid that is performing a grouping? I need the grid to open up and start up with it being completely expanded.
Their API and Tutorial reference doesn't explain explicitly enough for me to understand.
My HTML div
<code>
<div id="grid1" ui-grid="resultsGrid" class="myGrid" ui-grid-grouping></div>
</code>
My Javascript
$scope.resultsGrid = {
,columnDefs: [
{ field:'PhoneNum', name:'Phone'},
{ field:'Extension', name:'Extension'},
{ name:'FirstName'},
{ field:'DeptDesc', grouping: {groupPriority: 0}}
]
,onRegisterApi: function(gridApi)
{
$scope.gridApi = gridApi;
}
}
you just need to add
//expand all rows when loading the grid, otherwise it will only display the totals only
$scope.gridApi.grid.registerDataChangeCallback(function() {
$scope.gridApi.treeBase.expandAllRows();
});
in your onRegisterApi: function(gridApi)that should be updated like this onRegisterApi: function(gridApi) so your function will be like this
$scope.resultsGrid.onRegisterApi = function(gridApi) {
//set gridApi on scope
$scope.gridApi = gridApi;
//expand all rows when loading the grid, otherwise it will only display the totals only
$scope.gridApi.grid.registerDataChangeCallback(function() {
$scope.gridApi.treeBase.expandAllRows();
});
};
or you can add botton to expand data like shown in this plunker
My Module - I had to add ui.gridl.selection
<pre>
<code>
angular.module('ddApp',['ngRoute','ngSanitize','ngCookies','ngResource','ui.grid.selection'])
</code>
</pre>
My Controller - Amongh the other Dependency Injected items, I also had to add $timeout
<pre>
<code>
.controller('myCtrl', function(`$`timeout)){}
</code>
</pre>
<pre>
<code>
$timeout(function(){
if($scope.gridApi.grouping.expandAllRows){
$scope.gridApi.grouping.expandAllRows();
}
});
</code>
</pre>
The closest analogy would the selection tutorial, in which we select the first row after the data finishes loading: http://ui-grid.info/docs/#/tutorial/210_selection
$http.get('/data/500_complex.json')
.success(function(data) {
$scope.gridOptions.data = data;
$timeout(function() {
if($scope.gridApi.selection.selectRow){
$scope.gridApi.selection.selectRow($scope.gridOptions.data[0]);
}
});
});
The key understanding is that you can't select (or expand) data that hasn't been loaded yet. So you wait for the data to return from $http, then you give it to the grid, and you wait for 1 digest cycle for the grid to ingest the data and render it - this is what the $timeout does. Then you can call the api to select (or in your case, expand) the data.
So for you, you'd probably have:
$http.get('/data/500_complex.json')
.success(function(data) {
$scope.gridOptions.data = data;
$timeout(function() {
if($scope.gridApi.grouping.expandAllRows){
$scope.gridApi.grouping.expandAllRows();
}
});
});
If you're on the latest unstable, that call will change to $scope.gridApi.treeBase.expandAllRows.

How do you capture current frame from a MediaElement in WinRT (8.1)?

I am trying to implement a screenshot functionality in a WinRT app that shows Video via a MediaElement. I have the following code, it saves a screenshot that's the size of the MediaElement but the image is empty (completely black). Tried with various types of Media files. If I do a Win Key + Vol Down on Surface RT, the screen shot includes the Media frame content, but if I use the following code, it's blackness all around :(
private async Task SaveCurrentFrame()
{
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(Player);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
MultimediaItem currentItem = (MultimediaItem)this.DefaultViewModel["Group"];
StorageFolder currentFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
var saveFile = await currentFolder.CreateFileAsync(currentItem.UniqueId + ".png", CreationCollisionOption.ReplaceExisting);
if (saveFile == null)
return;
// Encode the image to the selected file on disk
using (var fileStream = await saveFile.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, fileStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
pixelBuffer.ToArray());
await encoder.FlushAsync();
}
}
Here MultimediaItem is my View Model class that among other things has a UniqueId property that's a string.
'Player' is the name of the Media Element.
Is there anything wrong with the code or this approach is wrong and I've to get in the trenches with C++?
P.S. I am interested in the WinRT API only.
Update 1 Looks like RenderTargetBitmap doesn't support this, the MSDN documentation clarifies it http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.rendertargetbitmap .
I'll appreciate any pointers on how to do it using DirectX C++. This is a major task for me so I'll crack this one way or the other and report back with the solution.
Yes, it is possible - little bit tricky, but working well.
You dont use mediaElement, but StorageFile itself.
You need to create writableBitmap with help of Windows.Media.Editing namespace.
Works in UWP (Windows 10)
This is complete example with file picking and getting video resolution and saving image to Picture Library
TimeSpan timeOfFrame = new TimeSpan(0, 0, 1);//one sec
//pick mp4 file
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
picker.FileTypeFilter.Add(".mp4");
StorageFile pickedFile = await picker.PickSingleFileAsync();
if (pickedFile == null)
{
return;
}
///
//Get video resolution
List<string> encodingPropertiesToRetrieve = new List<string>();
encodingPropertiesToRetrieve.Add("System.Video.FrameHeight");
encodingPropertiesToRetrieve.Add("System.Video.FrameWidth");
IDictionary<string, object> encodingProperties = await pickedFile.Properties.RetrievePropertiesAsync(encodingPropertiesToRetrieve);
uint frameHeight = (uint)encodingProperties["System.Video.FrameHeight"];
uint frameWidth = (uint)encodingProperties["System.Video.FrameWidth"];
///
//Use Windows.Media.Editing to get ImageStream
var clip = await MediaClip.CreateFromFileAsync(pickedFile);
var composition = new MediaComposition();
composition.Clips.Add(clip);
var imageStream = await composition.GetThumbnailAsync(timeOfFrame, (int)frameWidth, (int)frameHeight, VideoFramePrecision.NearestFrame);
///
//generate bitmap
var writableBitmap = new WriteableBitmap((int)frameWidth, (int)frameHeight);
writableBitmap.SetSource(imageStream);
//generate some random name for file in PicturesLibrary
var saveAsTarget = await KnownFolders.PicturesLibrary.CreateFileAsync("IMG" + Guid.NewGuid().ToString().Substring(0, 4) + ".jpg");
//get stream from bitmap
Stream stream = writableBitmap.PixelBuffer.AsStream();
byte[] pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
using (var writeStream = await saveAsTarget.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, writeStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)writableBitmap.PixelWidth,
(uint)writableBitmap.PixelHeight,
96,
96,
pixels);
await encoder.FlushAsync();
using (var outputStream = writeStream.GetOutputStreamAt(0))
{
await outputStream.FlushAsync();
}
}
Yeah...I spent lot of hours by this
Ok I have managed to get making snapshot from MediaElement on button press to work.
I am passing MediaStreamSource object to MediaElement using SetMediaStreamSource method. MediaStreamSource has event SampleRequested which is fired basicly everytime new frame is drawn. Then using boolean I control when to create bitmap
private async void MediaStream_SampleRequested(MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
if (!takeSnapshot)
{
return;
}
takeSnapshot = false;
Task.Run(() => DecodeAndSaveVideoFrame(args.Request.Sample));
}
After that what is left is to decode compressed image and convert it to WriteableBitmap. The image is (or at least was in my case) in YUV fromat. You can get the byte array using
byte[] yvuArray = sample.Buffer.ToArray();
and then get data from this array and convert it to RGB. Unfortunetly I cannot post entire code but I'm gonna give you a few more hints:
YUV to RGB wiki here you have wiki describing how does YUV to RGB conversion works.
Here I found python project which solution I have adapted (and works perfectly). To be more precise you have to analize how method NV12Converter works.
The last thing is to change takeSnapshot boolean to true after pressing button or doing other activity :).

Tinymce editor gallery plugin Regexp problem?

I am using tinymce editor for inserting contents to mysql.
I have changed wordpress gallery editor plugin according to my system.
If there is gallery code in content. I convert this code to a symbolic photo, so that user understand there is a gallery , in stead of seeing a code. Like wordpress does.
If there is only 1 gallery in content, i convert this code to image successfully, but if there is more than 1 gallery it fails.
How can i convert all {gallery} code into a symbolic image before saving to db and convert these photos back to {gallery} code again while inserting or updating into mysql.
I am so bad on regular expression.
I think do_gallery RegExp has mistake. How should i change this.
initalising editor like:
ed.onBeforeSetContent.add(function(ed, o) {
ed.dom.loadCSS(url + "/css/gallery.css");
o.content = t._do_gallery(o.content);
});
ed.onPostProcess.add(function(ed, o) {
if (o.get)
o.content = t._get_gallery(o.content);
});
My "do and get gallery" codes like that:
_do_gallery : function(co) {
return co.replace(/\{gallery([^\]]*)\}/g, function(a,b){
var image = '<img src="gallery.gif" class="wpGallery mceItem" title="gallery'+tinymce.DOM.encode(b)+'" />';
console.log(image);
return image;
});
},
_get_gallery : function(co) {
function getAttr(s, n) {
n = new RegExp(n + '="([^"]+)"', 'g').exec(s);
return n ? tinymce.DOM.decode(n[1]) : '';
};
return co.replace(/(?:<p{^>}*>)*(<img[^>]+>)(?:<\/p>)*/g, function(a,im) {
var cls = getAttr(im, 'class');
if ( cls.indexOf('wpGallery') != -1 )
return '<p>{'+tinymce.trim(getAttr(im, 'title'))+'}</p>';
return a;
});
}
If Content is:
<p>Blah</p>
<p>{gallery Name="gallery1" id="82" galeryID="15" sizeId="6" galery_type="list"}</p>
<p>test</p>
this is ok
<img src="gallery.gif" class="wpGallery mceItem" title="gallery Name="tekne1" id="82" galeryID="15" sizeId="6" galery_type="liste"" />
But, if content is:
<p>Blah</p>
<p>{gallery Name="gallery1" id="82" galeryID="15" sizeId="6" galery_type="list"}</p>
<p>test</p>
<p>{gallery Name="gallery2" id="88" galeryID="11" sizeId="1" galery_type="slide"}</p>
<p>test2</p>
it logs
<img src="gallery.gif" class="wpGallery mceItem" title="gallery Name="gallery1" id="82" galeryID="15" sizeId="6" galery_type="list"}</p> <p>test</p> <p>{gallery Name="gallery2" id="88" galeryID="11" sizeId="1" galery_type="slide"" />
I hope i could explain my problem
Thank you.
I suspect that your original regex is a typo, looks like a missing Shift when you hit the ]. Try this:
/\{gallery([^\}]*)\}/g
Then the ([^\}]*) part will (greedily) eat up any sequence of characters that aren't }; your original one would consume any sequence of character that didn't include a ] and the result is that you'd grab everything between the first { and the last } rather than just grabbing the text between pairs of braces.