The basic concept, used for many years now, is to just send out ray (really, line segment) queries from your character. If the character has gravity applies or is moving donwards, shoot two rays from the bottom two corners of the character's collision box and see if anything is close. You can react differently depending on which of the two registers a hit.
You may need to experiment with the number of rays, the distance cut-off (how long of a line segment you're checking), and the exact placement. Putting them at the exact corners may be problematic, but putting them some number of pixels "in" from the corners may work better. You may want a third ray in the middle. Etc.
One nice effect of this is that, since you use a ray cast and not generic SAT, you can handle collisions nicer. With SAT you often get a sliding effect on slopes. You also can't automatically deal with characters running down a slope super well (they'll end up bouncing a bit; you can see this in modern 3D games, too, like Fallout) with plain SAT/bounding volumes.
Another benefit is that with the rays you can tell if only one side of the character is off a ledge and do a different "balancing on a ledge" animation (e.g., Sonic).
Lastly, with the rays you can get continuous collision (you can see if a collision is about to happen and when rather than resolving it after a collision). This has uses in many cases (e.g., the sliding-on-slopes problem I mentioned) and helps to avoiding tunneling of high-speed objects without using small time steps - just lengthen your rays.
You can use these rays when jumping or running, too. It's way better to send out a ray and know that your character can't walk left than to move left and then be pushed out of the wall. In the latter, you'll end up with a silly "running while against a wall" animation, you aren't able to easily have box pushing or the like, and so on. The rays let you know "can't run left, don't even animate running" as well as "to the left is a box, do push animation if it can move".