tkanarsky's blog

Projects, musings, rants, etc.

TLDR: rtsp://<ip>:554/av0_0

I recently got a 3D printer, and I wanted to set up an IP camera to monitor it while I'm away from home.

I got the cheapest one at the newly-opened Santa Clara Microcenter, a Night Owl CAM-FWIP2-IN, for $30.

Initial impressions: somehow, an American company produced a camera that competes with no-name Chinese manufacturers for the dubious title of “I can't believe it's not AliExpress shovelware!” Terrible app whose backend is probably a breach waiting to happen. It's unfortunately required to set up the camera — there's no local configuration web UI.

I had trouble getting my phone to do the whole “connect to the no-internet-available AP it's broadcasting and give it the wifi credentials” song and dance. I had to disable always-on VPN (tailscale, in my case) for it to go through.

It took some time to discover the RTSP stream url for this one. (RTSP is a standard protocol that most IP cameras speak, and would allow me to add it to Home Assistant, Octoprint, Frigate, etc.)

I found the camera on the network, and confirmed the rtsp port (554) was open. However, none of the RTSP stream URLs in this table worked — I had Claude write a script to try them all against the camera.

The next best approach was to snoop in on what RTSP url the app would give the camera when it connected. I tried putting my laptop into promiscuous mode to sniff the traffic between the camera and the app to no avail. I later realized that my understanding of how sniffing on a WPA2 network works was incorrect — I needed to capture the initial handshake between the client and the AP to be able to decode subsequent frames.

I realized I could instead install the app in the Android Studio emulator so that the app's traffic would be sent through my laptop's network interface and could be decoded. I'm surprised it actually let me log into the Play Store and download the camera app just like on a normal phone.

After starting Wireshark, setting it to capture on en0, setting the filter to rtsp, and connecting to the camera through the app, I finally got what I was searching for. There are actually three RTSP streams requested by the app for this camera:

rtsp://ip:554/av0_0
rtsp://ip:554/av0_0/track1
rtsp://ip:554/av0_0/track2

I have not been able to determine the difference between them. All appear identical, codec-wise. At least the specs are honest. It's a full 1080p stream, 15fps, with audio.

track2

With RTSP url in hand, I was able to set up the generic camera integration and monitor my printer through the web interface and through my Samsung phone's built-in smart home grid.

I then cut off Internet access to the camera, and gave it a static IP address on my local network. The phone app can no longer communicate with the camera, but because my Home Assistant instance proxies the stream over Tailscale to my phone and other devices, I can still view the camera's feed without it ever touching the public internet.

prusa

Good night, happy little Prusa in my phone!

happyprusa

Wanted to write a few words praising Oracle Cloud's VPS free tier.

For a grand total of $0 a month, you get:

An Ampere Arm-backed instance with: – 4 cores (technically vCPUs which may be oversubscribed, but I haven't pushed them hard enough to notice a difference) – 24 GB RAM – 4 Gbit (!) networking – 20 TB/month of total ingress/egress traffic

Is that not a screaming deal?

It's out of capacity all the time so you have to play the “install a script to auto-acquire one when it's availalbe” metagame. However, once you have it and you've upgraded to a pay-as-you-go plan, Oracle seems to be pretty good (fingers crossed!) about keeping it working.

Thanks Mr Ellison :)

Slept in mega late, until around 1pm, and then languished in bed for an hour and a half reading the news.

I did my 30 push-ups and squats for the day while I microwaved a large chicken pot pie from Ralphs. The instructions say to cook it for around 10 minutes in a 1100-watt microwave oven, and warn to not attempt cooking it in any oven less powerful than that (ostensibly to avoid salmonella-related lawsuits.) I have a crappy 700-watt microwave, so I disregarded the warning and threw it in there for 15 minutes at full-yet-paltry power silently hoping that the magnetron will make it. It survived, and fifteen minutes plus cooling later I had a piping hot pot pie, which I ate with a can of cranberry Sprite left over from the holiday season. It's a filling yet nutritionally void meal.

Decided to forgo my scooter today as I made the trek to Wooden at 4 to play badminton with my makerspace coworkers. Though I'm pretty trash when it comes to scored play, I'm slowly improving at blocking smashes and general strategy.

Walked to Chick-fil-A and redeemed my free fry coupon. Grabbed a brownie as well.

I realized I really need to do my 147 homework, but it's the weekend so I decided to put it off until tomorrow.

Met up with two of my friends and made the short drive to Sawtelle Japantown. While we waited for a table at Tatsu Ramen, we popped over to Daiso to kill some time.

Daiso is a Japanese chain of five-and-dime stores, selling all sorts of inexpensive items: bricks of dried noodles, stationery, cookware, cosmetics, pet toys, and tons of other household goods you never knew you needed until you saw them staring at you from the shelf. Growing up in the Bay Area, my family would sometimes go to the Mountain View location for cheap office supplies and back-to-school shopping (until it unceremoniously shut down around a decade ago.) Today, I picked up a bag of Milkita caramels and a pack of Calpico yogurt-flavored drinks. The former were good as always, while the latter turned out to be a hyper-sweet disappointing imitation of Yakult without the actual yogurt cultures.

Ramen was pretty good. Tatsu makes some of the best marinated ramen eggs I've ever tasted – creamy yolk, salty and somewhat sour white tinted a satisfying beige.

bowl of ramen on table

The chashu pork left something to be desired; the fat wasn't as melty as it was at some other ramen joints.

Drove back to my friend's place, and bantered for a while before I walked home and started writing this entry.

Roses: badminton, ramen, and friends Buds: 147 homework needs to be done Thorns: woke up late, ate too much sodium, procrastinated

End of week 2 of winter quarter.

I meant to wake up early enough to cook breakfast and do some housekeeping before heading to campus for my first discussion section at 11 am, but I kept snoozing my alarm.

$$t_{next} = \frac{t_{deadline} – t_{now}}{2}$$ The first alarm went off at 10; the next went off at 10:30, then 10:45. By 10:52 the situation was getting critical. I finally got out of bed, brushed teeth, threw on some clothes, and scootered to class.

First discussion of the day was ENGR 111, business finance. It's not my favorite subject; even though I understand the concept of the balance sheet and income statement, the example problems are so vague on details and heavy on assumptions that it feels like we're analyzing frictionless spherical (cash) cows in a hard vacuum.

Aside: this is my first Friday, and hence first discussion sections, on campus this year; I was sick with the 'rona the week prior and stayed with family until I got better.

Next discussion was in Bunche Hall, all the way in north campus. Fortunately the previous discussion let out early, so I had about 15 minutes to make the walk from Boelter to Bunche. I would have scootered, but I'd moored my trusty scooter at the bike rack on ground level and didn't feel like taking a detour or hauling it up to the fifth floor court level.

Second discussion was ECE 147, neural networks. The first homework is due Monday and it's a doozy of a problem set. My TA is helpful as always and went over the general approach to solve the hardest problem on the hw. He always comes up with a relevant bad joke at the end of the section. Today's humor, after linear algebra review, was a pun on matrix trace and “leave no trace”.

Had an hour free after discussion; got a panini from Northern Lights (pretty good for the price) and discussed other homework problems with my TA over lunch.

Last discussion was CS 143, databases. Practiced relational algebra on a toy dataset; I'm getting the hang of it.

Worked on 147 homework and slacked off in SEL until it closed at 6, then migrated to the study lounge at Olympic Hall. Worked on homework until around 10:30, then rode to town to get Chick-fil-A in the last minutes before it closed for the night. I wanted to redeem a coupon for a free fry, but the coupon reader wasn't working so the kind manager comped the fries and let me keep the coupon for later. Score!

Caught up on my fitness routine. 30 push-ups, 30 squats.

Our apartment, like many others in sunny L.A., is thinly insulated and drafty. The low is around 40 tonight, so I finally lit the janky wall furnace and turned on a box fan to circulate the warm air. This is the first night our apartment isn't miserably cold at night; working on the computer is much more pleasant when your hands are warm. Decided to start doing diary entries again, and here I am.

Roses: matrix joke, Chick-fil-A, warm house Buds: badminton tomorrow Thorns: overslept breakfast, 147 homework is painful

Testing MathJax $$e^{i\pi} = -1$$ Testing code formatting:

@cache
def fib(n: int) -> int:
    if n <= 1:
        return n
    return fib(n-1) + fib(n-2)

Testing bullet points:

  • point one
  • point two