Categories
Uncategorized

How to Test WebRTC Stream Quality? Use StreamTest Extension [Free]

StreamTest UI with the article title

As someone specializing in video chat and video streaming services, our QAs (quality assurance angels) used to deal with the problem of how to test a live stream quality with a load of tools and apps, all different for each metrics. But now there’s this all-in-one solution — StreamTest

It’s a Google Chrome Extension, a testing tool for WebRTC video chats like Google Meet, iMind, Whereby, and ProVideoMeeting. Not sure you’re on a WebRTC stream? Enable StreamTest and if you’re trying to test a non-WebRTC stream, the extension will respond with an error. 

a stream page with a non WebRTC stream error
Non-WebRTC stream error

It’ll give you a live status on 7 essential parameters with an insight on the connection and stream behavior. 

Quick note for non-tech folks:

If you have no technical background and all of these indicators are just letters, figures, and colors to you or don’t want to analyze them yourself, just screenshot (Windows: Win + Shift + S; MacOS: Command + Shift + 3) the status you got immediately, download the report as a .csv file, and show both to your tech specialist. 

We are already working on making the plugin accessible for you, so you could test your live streams easily, guys. So follow us on LinkedIn to stay turned. 

TestStream report UI
Video conference live status

Frame rate or just FPS. The higher the better. To calculate it the plugin measures the time between two frames and calculates the average for the last X frames.

Video and Audio delay. Delay is never welcome, so the lower the better. Basically it’s a sum for jitterBufferDelay and average round trip time. Totals are relevant for the last 3*X seconds, where X is the time set in setInterval.

Packet loss. The lower the better. It shows if any packets were lost on the way to the server, as a percentage. Calculated as (packetsLost stat / packetsSent stat) * 100%. 

Resolution. The higher the better. It mostly depends on the participant’s camera and the device you’re using to chat. 

Freezes and stalls. The lower this indicator is the better. It shows how much % of your call was wasted due to lags. Consider something as freeze if the comparison (!wasDocumentJustHidden && dt > Math.max(dtTimestampsQueue.getAverage() * 3, dtTimestampsQueue.getAverage() + 0.150)) is true. In simple terms freeze happens if the time of the current frame is greater than the average time of frame existence multiplied by 3. And at the same time is greater than the average time of frame existence for at least 150ms. 

Bitrate. This indicator is responsible for how smooth the picture is, the higher the indicator the smoother the picture.

Video and Audio codec — for a developer to know how the data is compressed.

In the status you’ll also notice that the indicator measures are colored in Red, Yellow, or Green. Here’s what the colors mean:

Table withe report metrics coloring
StreamTest live status colors meaning

Besides the data we described previously, there’re also SDP offer, answer, iceConnectionState, iceGatheringState, and WebRTC Stats info in the downloadable .csv report. It will give the bigger picture of the connection and the stream behavior. 

StreamTest starts collecting data once you enable it. To do it, click your right mouse button on the other participant’s stream and tap “Test stream” in the context menu. It will stop collecting the data once you close the extension / start testing another stream / go to the extension main screen. 

To test live streams, download this WebRTC tester for your Chrome from the official store here. For free. 

Categories
Uncategorized

How to Implement Delayed Messages with RabbitMQ? Code Examples

Graphic article cover with colorful command windows

Sometimes you need to implement scheduled or repeating actions into your app. For example, sending a push-notification in 10 minutes or clearing a temporary folder every day.

To do this, you can use cron-tasks, that run scripts on your server automatically, or node-schedule package (a task-planning library for Node.js).

But with both these solutions there’s a scaling problem:

  • There’re several servers so it might be unclear on which one to run the task
  • The selected server might crash
  • The node might get deleted for freed up resources

One of possible solutions here is RabbitMQ, a message broker. Check out the overall delayed messages implementation scheme in this example on GitHub. And here’s what it’s like in detail, step by step:

  1. Create 2 exchangers: regular and delayed one
export const HELLO_EXCHANGE = Object.freeze({ 
    name: 'hello',
    type: 'direct',
    options: {
        durable: true,
     },
   queues: {},
});

export const HELLO_DELAYED_EXCHANGE = Object.freeze({
    name: 'helloDelayed',
    type: 'direct',
    options: {
        durable: true,
    },
   queues: {},
});

2. In each of the exchangers create queues with the same binding type but different names.

For HELLO_EXCHANGE:

queues: { 
        WORLD: {
            name: 'hello.world', // subscribe to this queue
            binding: 'hello.world',
            options: {
                durable: true,
            },
        },
    },

For HELLO_DELAYED_EXCHANGE:

  queues: {
        WORLD: {
            name: 'helloDelayed.world',
            binding: 'hello.world',
            options: {
                durable: true,
                queueMode: 'lazy', // set the message to remain in the hard memory
            },
        }

For the delayed-exchanger’s queue, set the x-dead-letter-exchange argument with the regular queue’s name. The argument tells the RabbitMQ broker to transfer the message to this exchanger if it’s not processed.

  arguments: {
                    'x-dead-letter-exchange': HELLO_EXCHANGE.name, // set the queue to transfer the message to once it’s dead
                }

3. Publish the message to the delayed-exchanger’s queue with the expiration period

// services/base-service/src/broker/hello/publisher.ts
export const publishHelloDelayedWorld = createPublisher({
    exchangeName: exchangeNameDelayed,
    queue: WORLD_DELAYED,
    expirationInMs: 30000, //set when the message dies (in 30s) 
});

Once the delayed message expires, it will go to the regular exchanger’s queue.

Now you only have to set a consumer for the regular exchanger’s queue:

// services/base-service/src/broker/hello/consumer.ts
export const initHelloExchange = () => Promise.all([
    createConsumer(
        {
            queueName: HELLO_EXCHANGE.queues.WORLD.name,
            prefetch: 50,
            log: true,
        },
        controller.consumeHelloWorld,
    ),
]);
// services/base-service/src/broker/hello/controller.ts
export const consumeHelloWorld: IBrokerHandler = async ({ payload }) => {
    const result = await world({ name: payload.name });
    logger.info(result.message);
    // await publishHelloDelayedWorld({ name: payload.name }); // if you need to process the message again
};

Profit!

If you need to run the action periodically, publish the message to the delayed exchanger again at the end of the consumer section. 

    // await publishHelloDelayedWorld({ name: payload.name });

NOTE: RabbitMQ operates on FIFO (first in, first out) – it processes commands in the same order they were set. So if you publish a delayed message with 1 day expiration and a message with 1 minute expiration in the same queue, it will process the second message after the first one, and the target action for the second message will happen a minute after the first.

Eventually, this is what you get:

  1. Create the exchangers and queues
// services/base-service/src/broker/const/exchanges.ts
export const HELLO_EXCHANGE = Object.freeze({
    name: 'hello',
    type: 'direct',
    options: {
        durable: true,
     },
     queues: { 
        WORLD: {
            name: 'hello.world', // subscribe to this queue
            binding: 'hello.world',
            options: {
            durable: true,
            },
        },
    },
});
export const HELLO_DELAYED_EXCHANGE = Object.freeze({
    name: 'helloDelayed',
    type: 'direct',
    options: {
        durable: true,
        queueMode: 'lazy', // specify that the hard memory must store this message
    },
    queues: {
        WORLD: {
            name: 'helloDelayed.world',
            binding: 'hello.world',
            options: {
                durable: true,
                queueMode: 'lazy', // specify that the hard memory must store this message                arguments: {
                    'x-dead-letter-exchange': HELLO_EXCHANGE.name, // specify the queue to which the message must relocate after its death
                },
            },
        },
    },
});

2. Add the publisher that will send the message to the delayed queue


// services/base-service/src/broker/hello/publisher.ts
export const publishHelloDelayedWorld = createPublisher({
    exchangeName: exchangeNameDelayed,
    queue: WORLD_DELAYED,
    expirationInMs: 30000, // set when the message dies (in 30s)
});

3. Add the consumer for the regular exchanger’s queue


// services/base-service/src/broker/hello/consumer.ts
export const initHelloExchange = () => Promise.all([
    createConsumer(
        {
            queueName: HELLO_EXCHANGE.queues.WORLD.name,
            prefetch: 50,
            log: true,
        },
        controller.consumeHelloWorld,
    ),
]);
// services/base-service/src/broker/hello/controller.ts
export const consumeHelloWorld: IBrokerHandler = async ({ payload }) => {
    const result = await world({ name: payload.name });
    logger.info(result.message);
    // await publishHelloDelayedWorld({ name: payload.name }); // if you need to process the message again
};

4. Profit!

There’s also a plugin that does this work for you and makes the implementation easier. You only create one exchanger, one queue, one publisher, and one consumer.

When publishing, the plugin will process the delayed message and, once it’s expired, will transfer the message to the right queue. All on its own. 

With this plugin the scheduled messages are processed in the order of the expiration time. That means, if you publish a message with a 1-day delay and then a message with 1-minute delay, the second one will be processed before the first.

// services/base-service/src/broker/const/exchanges.ts
export const HELLO_PLUGIN_DELAYED_EXCHANGE = Object.freeze({
    name: 'helloPluginDelayed',
    type: 'x-delayed-message', // specify the delayed queue
    options: {
        durable: true,
        arguments: {
            'x-delayed-type': 'direct', // set the recipient         },
    },
    queues: {
        WORLD_PLUGIN_DELAYED: {
            name: 'helloPluginDelayed.world', // subscribe to the queue
            binding: 'helloPluginDelayed.world',
            options: {
                durable: true,
            },
        },
    },
});

Add publisher that sends the messages to the delayed queue: 

export const publishHelloPluginDelayedWorld = createPublisher({
    exchangeName: exchangeNamePluginDelayed,
    queue: WORLD_PLUGIN_DELAYED,
    delayInMs: 60000,  // specify when the message should die (60s)
});

Add consumer to the queue:

// services/base-service/src/broker/hello/consumer.ts
export const initHelloExchange = () => Promise.all([
    createConsumer(
        {
            queueName: HELLO_PLUGIN_DELAYED_EXCHANGE.queues.WORLD_PLUGIN_DELAYED.name,
            prefetch: 50,
            log: true,
        },
        controller.consumeHelloWorld,
    ),
]);
// services/base-service/src/broker/hello/controller.ts
export const consumeHelloWorld: IBrokerHandler = async ({ payload }) => {
    const result = await world({ name: payload.name });
    logger.info(result.message);
};

Aaand — you’re done!

We regularly use RabbitMQ in our projects. For instance, check out its use case in Janson Media internet TV portfolio. It’s a movie renting service, but make it digital. 

Here we used RabbitMQ delayed messages for the app’s 3 essential features: sending emails and SMS-messages to notify users that, for example, their lease period is almost over; sending messages about completed payments to the socket and sending a notification to the user; sending uploaded videos for further processing. 

Hopefully, implementing delayed messages won’t be like falling down the rabbit hole for you anymore (if it ever was) 🙂

Categories
Uncategorized

Richard from Community Hill, ‘I’m glad that Fora Soft has designers, analytics, testers.’

Here’s an interview with Richard, the owner of Community Hill. Richard’s entrepreneurship has been going on for 10 years – since the times Amazon was selling books. Now he’s working with Fora Soft, which is his last hope.

What do you have to expect going into work with Fora Soft? What can we teach an experienced entrepreneur who had worked with many developers before coming to Fora Soft? The answers are in the interview.

I also tried my best to find out the problems Richard might’ve had with Fora Soft, so that the feedback is truly genuine. It turned out to be a difficult task, as everything has been spot on. Enjoy!


Nikita: What is Community Hill and who is it for? 

Richard: Thank you for this opportunity. Community Hill is an online platform with people of various communities, mainly the towns and the villages where people live. People sell various items, usually household items, including furniture, vehicles and others. Service providers post their services. For instance, electricians. So on that platform, people post everything, and if anyone is interested, contact them. They plan a time and location to meet and then they do the transaction. That’s what Community Hill is about. We’re building the community together, you know? 

Nikita: Cool. So you’ve come to Fora Soft. When did this happen? 

Richard: That was last year. Probably August or September? I went to an online auction platform first. I was looking for a team that could really make a good product. And then one of the team members of Fora Soft contacted me. He told me that we’d get in touch. I’m glad that we got it started. So I’ve been with you for 9-10 months. And I’m still with you because we have to do the 2nd phase. 

Nikita: That’s very good to hear. But before Fora Soft, was there any company that you were going to work with? Perhaps, Fora Soft wasn’t your first choice? 

Richard: Yes. At first I contacted one of the programmers. That platform where I went has so many programmers. You can never know whether someone really knows what he’s doing or whether he’s also outsourcing somewhere else. So the first person didn’t really do a good job at all. So I kind of lost hope in these individual freelancers. I decided that I needed a team of people that can really do something. And when I checked on that, all of a sudden Fora Soft showed up. Nothing else showed up, actually. I read the reviews on Fora Soft and it started from there. I didn’t look for anything else. So, that’s how we met.

Nikita:  You mentioned having the team in Fora Soft, a project team consists of developers, project managers, designers, QA… We’ve got marketing, promotion and so on and so forth. During the production of your app, did you ever feel that you really need all those people? Or was it like, “I don’t really understand why I need so many people, and I’m good with like 2 or 3 of them, not the whole team?” 

Richard: I needed all of them. Let me give you an example. For instance, designing a logo. I’m not an artist. I don’t even have the knowledge of how a good logo should look. So I just needed everyone to come on board because I didn’t know much about these things. I didn’t know how the best user stories should be. So I just needed a team of everyone to be on board.

And then when I was told that we even have testers, I was so glad that they had that team.

Nikita: Over the time of us working together, was there anything that you wish that Fora Soft would improve, maybe some downsides of working with us? 

Richard: I haven’t come across anything because Fora Soft is good at communicating. You keep in touch. I always communicate with the programmer, the project manager, the designer. Because of that constant communication, there hasn’t been a gap for doubt. Like every time I have an issue or an idea, I usually ask them and then we find a solution right away. We could also find a date to have a meeting and talk about it.

Nikita: Good to hear. Let’s talk a little bit about your app. So it’s an iOS app. Are you planning on going to Android or Web or something else? 

Richard: Oh, yeah, definitely. If we get more funding, I want us to finish this 2nd phase of iOS and then I also want us to do the Android version or the web version as well. 

Nikita: Okay. And Community Hill is currently an MVP, right? 

Richard: Correct. 

Nikita: During this MVP, did you have any people to test the app? Maybe yourself, your family, or friends. 

Richard: Yeah, sure. It’s been myself at the moment. I haven’t been able to ask any family members to test, so it’s just me and the programmer for now.

Nikita: I guess you did come across some bugs because, unfortunately, they’re imminent. Do you feel that there were lots of bugs or were they kept to a minimum? 

Richard: Well, at some point, as I was testing the app – the MVP –  it crashed. And then I mentioned it to the programmer, and he said that he was going to check it out to find a solution for it. Other than that, I didn’t see any other bugs on my side. Maybe on their side, they might have seen some because so many people were testing, so they could identify more issues. But on my side, I didn’t see anything else. 

Nikita: Right. And after the developer said that he was going to look into the problem, did he keep his promise? 

Richard: Yes, he told me that they have also identified other issues and they are going to work on them. And we are yet to have the meeting this week (the meeting went peachy, Fora Soft resolved everything). We’re gonna discuss the issues that they found. So I would say he kept his promise. 

Nikita: Is there something you want to add or say about Fora Soft that relate to our other clients?

Richard:

I would say that any anyone who comes Fora Soft should be really available for constant communication.

That’s what really helps to keep the project going. If one is really busy and not able to keep in touch, I think it will be so hard to keep the project moving. It’s all about communication. You’re on the same page with the programmers. Your expectations and their expectations are on the same level. I think that’s the most important thing – be ready to keep in touch with all the team members. Starting with that, with the analytic, then programmers, and all those people, it’s about communication. 

Nikita: Right. And actually, I have one more question that I kind of forgot to ask in the beginning. Richard, do you yourself have some kind of technical background? 

Richard: A little bit. I’ve always had the passion of online businesses. I started out venturing into online businesses like 10 years ago, those years when Amazon was just getting started, when they were just selling books. I had this idea of the market place, but I didn’t have the money. So I reached out to the local programmers and they really did do a good job. They didn’t do what I really wanted. So I’ve been trying out this field of online businesses for a long time. During that time, of course, I’ve come to learn a little bit of what programing is all about, what a programmer is and all that kind of stuff. I’ve developed a little bit of technical knowledge in that field. But I don’t really have that much technical background, just a little bit, only because I’ve been trying out this field for a very long time. I must say that Fora Soft is my last try. If it fails, I’m out of it because it’s been like 10 years. I’m trying this out since the time Amazon was selling books. I was building ideas on how to make a good marketplace. It’s been a long time. So I would say Fora Soft is my last try. If it fails, I let it go. 

Nikita: Well, I guess I can speak on behalf of Fora Soft, that we’ll do our best to satisfy your needs. And actually what I wanted to ask is that you barely have some technical background. You are not a developer by any means, as far as I understand. Did you ever feel that you are lacking the skills to produce Community Hill with Fora Soft? Or did it feel like you don’t actually need to understand the codes or anything like that? You can just give Fora Soft an assignment and expect a result, and you can give this assignment using some human language, not developer language. 

Richard: Yeah, I knew that I didn’t really need to be a developer, but I knew what final product should look like. Because, like I said, I’ve been trying out these things. I’ve seen how Amazon has been changing the way you interact with the app, the website, all those things that they do on their marketplaces. So I’ve kind of got the knowledge on how the best product should look like. So I said, I don’t really have to be a developer, but I know how a final product should look like. 

Nikita: Can you please rate Fora Soft on a scale of ten in terms of communication, professionalism, and determination in the project’s involvement, perhaps. 

Richard: Oh, it’s ten out of ten. The local developers and freelancers I interacted with before I came to Fora Soft, they didn’t even tell me about user stories.

I first heard about user stories from Fora Soft.

I’d never heard that word. The freelancers I reached out to before, they didn’t even know these things. Like they didn’t have the professional knowledge of how a project should run. They didn’t even give me a development plan. I first saw a development plan from Fora Soft. These freelancers didn’t have those things. They didn’t have that kind of communication. So I’m glad that I chose Fora Soft. The rating is 10 out of 10. 


So, that’s it! Wanna, just like Richard, enjoy all the best that Fora Soft has to offer? Make sure to contact us using the contact form!

Not convinced yet? Read more customer interviews:

Jesse, Vodeo – a movie renting platform
Anthony, Speakk – a mobile messenger that doesn’t consume traffic
Jan, AppyBee – a booking platform
Naseem, Mobytap – a business video review platform
Ali, TapeReal – a video social network