Tuesday, March 16, 2021

Data Visualization with PyGal -- Pymntos

Recently presented 'Data Visualization with PyGal' to the Python Mn Meetup group https://www.meetup.com/PyMNtos-Twin-Cities-Python-User-Group/

A pretty remarkable group, full of very knowledgeable and supportive individuals.  I'd highly recommend attending this meetup if you're interested in Python, ranging from beginner to advanced.  

I slapped together a video on YouTube for your amusement;


Slides and code segments are available at our GitHub repo: https://github.com/fsk-software/pub

Enjoy.

Saturday, March 13, 2021

RealTime Adjustable FFmpeg Filters -- Part 1


FFmpeg allows dynamic filter adjustments, in past posts we've demonstrated how filters can be adjusted with respect to frame number and/or timestamp.  A more unconventional use allows real-time adjustments of filters by an independent process.  FFmpeg allows the ability of sending filter parameters by means of the 'zmqsend' utility.  This would allow modifying filter parameters during the processing pipeline.

Two things before we jump into the details; 1) only a select list of filters allow dynamic modifications, and 2) this is an non-standard feature which must be enabled as part of building FFmpeg from source.

Building with ZMQ Support

The zmqsend utility is used for sending filter parameters into the processing pipeline.  This utility utilizes ZMQ libraries, to enable you need to install the zmq libraries followed by enabling zmq support when building FFmpeg;

Install ZMQ Libraries

$ sudo apt-get install -y libzmq3 libzmq3-dev

Build FFmpeg

Building FFmpeg from source is done via the autotools process: configure, make, make install.  Typically I build utilizing the following flags, but the purposes of this post the '--enable-libzmq' serves the trick;

$ ./configure --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame --enable-libzmq


Simple Example

FFmpeg documentation provides a simple example; https://ffmpeg.org/ffmpeg-filters.html#zmq_002c-azmq

This simple example does little more than demonstrate sending in parameters.  Let's focus on dynamically changing a crop filter instead.

Suppose you have a video presentation with a moving speaker and you wish to crop to the presenter while in motion.  Doing so can't readily be done by a time/frame-based equation and opens a door for using real-time parameter adjustments.

The following video snippet is that of a presentation I did some months ago.  The camera captures the 'stage'.

Let's say we want to crop a 640x480 region, then pad it to 720x480 we would specify a filter similar to this: -filter_complex "crop=640:480:0:0,pad=720:480:(ow-iw)/2:(oh-ih)/2"

The filter graph of this takes the form:

Of particular interest, the 'Parsed_crop_1' filter parameter name is needed for our purposes.  The x and y paramets specify the upper-left position of the crop window; refer https://ffmpeg.org/ffmpeg-filters.html#crop

So the idea is to begin the processing of the video file, then send in Parsed_crop_1 x and y parameter values.

$ ffmpeg -y -i input.mp4 -filter_complex "zmq,crop=640:480:0:0,pad=720:480:(ow-iw)/2:(oh-ih)/2" -acodec copy crop.mp4

The following command would update the x location to 100, albeit an jump motion;
$ echo Parsed_crop_1 x 100 | zmqsend

A slightly more complex motion would be to slowly adjust (x,y) positions in a linear manner;
$ cat mover.sh 
#!/bin/bash
for n in `seq 1 375`; do
  echo $n
  echo Parsed_crop_1 x $n | zmqsend
  echo Parsed_crop_1 y $n | zmqsend
  sleep 0.1
done;

The result would take the form of this:

This example again is not very useful, consider it a building block to a more sophisticated example which will be the subject of a future post.

Cheers.

Formatting Numerics with Bash

Seem like every couple months I need to re-look up how to perform numeric formatting with bash.  Here's a quick example of how to do it.


$ cat /tmp/go 
#!/bin/bash

for i in `seq 100`; do
  N=$(printf %03d $i)
  echo $N
done

$ ./go
001
002
003
...
097
098
099
100


I find doing this that one is no longer the loneliest number, it's paired with a couple of real zeros, but never lonely.


Cheers.

Linux Find Command with 'or'



On a number of occasions I found it necessary to locate a list of files by extension (e.g. header/source files) and found that if I needed to locate files of multiple extensions I took the 'cowards way out', executing two 'find' commands and appending the results to a temporary output file, then using the output file as input to the next command in the pipeline (e.g. grep).

e.g.

$ find . -type f -name "*.c*" > /tmp/junk

$ find . -type f -name "*.h*" >> /tmp/junk

$ grep -l SetEvent `cat /tmp/junk`


Every single time I do this, one of the voices in my head will mock me in the manner of Monty Python, apparently one of the voices in my head is from medieval France with a talent for hurling insults.


So, today we begin a short journey to the correct way to utilize the 'or' functionality in a Unix find command.



$ find . -type f \( -name "*.c*" -o -name "*.h*" \) -exec grep -l SetEvent {} \;



The above command will locate all header and implementation files (e.g. *.h, *.hpp, *.c, *.cpp...) and return a list of files that contain the SetEvent expression within them. Note the parenthesis are significant to ensure the or'd list is piped to the exec command, otherwise only the second extension list with run through the exec command.


Cheers.

Linux Simple Text-to-Speech



As we enter our 6th month of self-quarantining, I find the hermit lifestyle no longer to my liking.  A virtual friend is needed and this post, my friend, is your ticket to electronic companionship.  Enjoy.



# apt-get install espeak



Then, simply run it.



$ espeak "hello, how are you doing?"



Whelp, look at you!  You made it to the end you big big overachiever.  If you found enjoyment in this particular post, please consider seeking counsel, there are medications and therapy practices that can snap you right quick outta that.


Cheers.

Tcpdump Examples



As much as I like using Wireshark, I'm a sucker for a command interface.  The trouble is properly defining a filter has been difficult to say the least, boring simple post, but below is a list of brief examples and descriptions.


#!/bin/bash


#--grab the first 10 packets and write to file

  tcpdump -c 10 -i wlan0 -w /tmp/data.pcap


#--grab the first 10 packets to/from host and write to file

  tcpdump -c 10 -i wlan0 host 192.168.1.125 -w /tmp/data.pcap


#--grab the first 10 packets from host and write to file

  tcpdump -c 10 -i wlan0 src host 192.168.1.125 -w /tmp/data.pcap


#--grab the first 10 packets to host and write to file

  tcpdump -c 10 -i wlan0 dst host 192.168.1.125 -w /tmp/data.pcap


#--grab the first 10 packets to/from port and write to file

  tcpdump -c 10 -i wlan0 port 80 -w /tmp/data.pcap


#--grab the first 10 packets to/from net and write to file

  tcpdump -c 10 -i wlan0 net 192.168.1 -w /tmp/data.pcap


#--grab the first 10 packets from network and write to file

  tcpdump -c 10 -i wlan0 src net 192.168.1 -w /tmp/data.pcap


#--grab the first 10 packets from network and write to file

  tcpdump -c 10 -i wlan0 dst net 192.168.1 -w /tmp/data.pcap


#--grab the first 10 packets from network and write to file

  tcpdump -c 10 -i wlan0 '(dst host 192.168.1.125) and (port 443)' -w /tmp/data.pcap


Now, go put your virtual ear to a NIC and hear whatcha hear.


Cheers.

C Storage Classes




In investigating the proper means to declare a global variable, I found myself reviewing some C documentation that I had long forgotten, concerning the Storage Class.

To briefly review, each variable declaration consists of 3 elements: the storage class, the type, and the variable name. For example:


auto int x;
static int y;


The storage class can take the form of auto, extern, static, or register. If not specified, the implicit storage class is that of auto.

The auto storage class is the most frequently used of the classes, primarily because it is the implicit default. Local variables take this form, where the storage is not allocated until the block in which the variable is defined is entered.

The extern storage class specified simply a reference to a variable that is defined elsewhere. Space is therefore not allocated upon encountering this reference, since the storage is allocated elsewhere. This as you may recall is the means to declare a global variable.

The static storage class specifies that the variable cannot be access by functions outside the translation unit in which it was defined. A common error is declaration of a static variable in a header file, which is imported by more than one translation unit not understand that each translation unit essentially created independent copies of the variable. A common practice of declaring a static constant in a header, used by multiple translation units results in multiple copies of the constant, in each translation unit. However, if the static variable simply defines a constant....generally, no-harm-no-foul. It is however worth understanding that it is not a shared reference.

The last storage class is that of register, which notifies the compiler to make the variable as efficient as possible. Ideally, the variable will retain it's location in a cpu register for optimal performance.

4 Reference Books Every C++ Developer Should Have


Over the years development languages change from project-to-project. A challenge for learning a language involves selection of what references are worthwhile. Far too often I've made poor selections in textbooks and selecting C++ references some years back was no exception.

I'd like to list the top 4 books that I've selected for my arsenal that I feel are essential to doing proper C++ (in no particular order):

The C++ Programming Language - Stroustrup

To understand C++, or any other language, you've gotta understand the language syntax and semantics. The rules...what's allowed and what is not. Who better to inform you of such things than the father of the programming language; nuff said.

C++ FAQs 2nd Edition - Marshall Cline

After you know the language syntax and semantics, the next thing you should be educated in is what should you do and what shouldn't you do. Marshall Cline identifies many of the pitfalls of common practices in an educational and surprisingly entertaining manner. I've attended a series of training sessions with the man himself and I highly recommend both the book and the courses if you can get someone else to pay for it :)

Effective C++ 3rd Edition - Scott Meyer

I'd consider this book to be the Bible with respect to best C++ programming practices. I cannot say enough good things about this book, but be forwarned...it expects you to understand the language to a fair degree. Don't buy this as your introduction to C++, buy this as a moderately experienced developer.

Effective STL - Scott Meyer

What is C++ without the STL.....the equivalent of a three-legged dog; never living up to the potential of a 4-legged dog. Come on, most interesting problem domains are complex enough to benefit from utilizing the STL including a great deal of embedded software products.

Scott Meyer for the win, leading 2 books to 1 in the competition; actually, the 'Effective ...' books are closer to the style of cliff notes, paling in comparison of sheer paper girth.  

Likely no surprise to anyone, the creator of the language (Stroustrup) lays the foundation of the language, how to do nearly anything.  Marshall Cline lays our best practices, things that you can do with the language but probably shouldn't.  Scott Meyer's books take the same tact, only in a highly compressed format.

These are the books that I'd recommend to anyone developing in C++. I have a set of Sutter and Koenig books available to me and I've heard great things and have referenced them on occasion but not yet to give them sufficient time to reading them.

Enjoy your journey into an amazing language.

Playing Raw Video with Mplayer


Mplayer adopts the 'can do' attitude as a media player. Generally, this utility will play most anything, employs most standard codecs and many that you'll likely never need. If you have a video file, likely you can play it with Mplayer...including raw data.


A past program I was working on grabbed raw LVDS data streams from a Flir infrared camera. First task, confirm the video output is reasonable. Cue investigation of how to play raw data streams without requiring encoding. Mplayer met this need quite nicely. For fun, I thought I'd post some of our findings, hopefully you can find them useful.


Lets start with generating our own raw video stream:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <fcntl.h>
#include <assert.h>

int main()
{
static const int NumFrames = 10;
static const int Width = 640;
static const int Height = 480;

printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
std::string fileName="/tmp/video.raw";
printf("(%s:%d) fileName='%s'\n",__FILE__,__LINE__,fileName.c_str());
int fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);

for (int i=0; i<NumFrames; ++i)
{
unsigned char frame[Width*Height];

// generate frame; white box, framed with black lines
memset (frame,255,Width*Height);
for(int c=0; c<Width; ++c) frame[c+Width*0]=0;
for(int c=0; c<Width; ++c) frame[c+Width*(Height-1)]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h+(Width-1)]=0;

// write frame contents to file
for(int k=0; k<Width*Height; ++k)
{
void* tmp=(void*)(frame+k);
int ret=write(fp,tmp,sizeof(unsigned char));
assert(ret==sizeof(unsigned char));
}
}

close(fp);

printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}



The above code simply generates a sequence of 10 640x480 8-bit grayscale frames, the content of each frame a white background framed with a black one-pixel wide border.


You can play the video contents of this file by issuing the following Mplayer command:


$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307200:fps=1 /tmp/video.raw


A few things worth mentioning; first, since this is a raw video the frame size must be specified (ie. w=640:h=480), the frame rate (fps=1) must be specified as well or it will default to 24fps, giving us little time to review the contents of the video. Lastly, the size=307200 (640*480) is optional at for this format, but will become relevant in later examples. By default, the size will be defined by the width*height, so for this example you could simply not specify the value and all would still be well.


That was fun, but let's give it a little more snap. Suppose you wanted to add some frame metadata following each frame. We'll modify our above example a bit, each frame will be followed by a fixed 28-byte character string identifying the frame number.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <fcntl.h>
#include <assert.h>

int main()
{
static const int NumFrames = 10;
static const int Width = 640;
static const int Height = 480;

printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
std::string fileName="/tmp/video.raw";
printf("(%s:%d) fileName='%s'\n",__FILE__,__LINE__,fileName.c_str());
int fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);

for (int i=0; i<NumFrames; ++i)
{
unsigned char frame[Width*Height];

// generate frame; white box, framed with black lines
memset (frame,255,Width*Height);
for(int c=0; c<Width; ++c) frame[c+Width*0]=0;
for(int c=0; c<Width; ++c) frame[c+Width*(Height-1)]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h+(Width-1)]=0;

// write frame contents to file
for(int k=0; k<Width*Height; ++k)
{
void* tmp=(void*)(frame+k);
int ret=write(fp,tmp,sizeof(unsigned char));
assert(ret==sizeof(unsigned char));
}
//write some metadata
char metaData[28];
sprintf(metaData,"frame%04d",i);
assert(write(fp,metaData,sizeof(metaData))==sizeof(metaData));
}

close(fp);

printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}


Now the frame size becomes a required argument, since the frame size must now account for the metadata, otherwise the metadata would be interpreted as video and you'd have a mess. You can play the above generated video by issuing the command as follows:


$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307228:fps=1 /tmp/video.raw


Ok, now we've learned how to play video with metadata. Our last trick will be playing video with a initial video header.


Let's start by adding some video metadata at the beginning of the video, a 200-byte string.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <fcntl.h>
#include <assert.h>

int main()
{
static const int NumFrames = 10;
static const int Width = 640;
static const int Height = 480;

printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
std::string fileName="/tmp/video.raw";
printf("(%s:%d) fileName='%s'\n",__FILE__,__LINE__,fileName.c_str());
int fp=open(fileName.c_str(),O_CREAT|O_WRONLY|O_TRUNC);

char videoMetaData[200];
sprintf(videoMetaData,"some cool video");
assert(write(fp,videoMetaData,sizeof(videoMetaData))==sizeof(videoMetaData));

for (int i=0; i<NumFrames; ++i)
{
unsigned char frame[Width*Height];

// generate frame; white box, framed with black lines
memset (frame,255,Width*Height);
for(int c=0; c<Width; ++c) frame[c+Width*0]=0;
for(int c=0; c<Width; ++c) frame[c+Width*(Height-1)]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h]=0;
for(int h=0; h<Height; ++h) frame[0+Width*h+(Width-1)]=0;

// write frame contents to file
for(int k=0; k<Width*Height; ++k)
{
void* tmp=(void*)(frame+k);
int ret=write(fp,tmp,sizeof(unsigned char));
assert(ret==sizeof(unsigned char));
}
//write some metadata
char metaData[28];
sprintf(metaData,"frame%04d",i);
assert(write(fp,metaData,sizeof(metaData))==sizeof(metaData));
}

close(fp);

printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
return EXIT_SUCCESS;
}



You play this form of video by specifying a start byte offset of 200 and you're set.


$ mplayer -demuxer rawvideo -rawvideo w=640:h=480:y8:size=307228:fps=1 -sb 200 /tmp/video.raw

Beethoven or a core file, mplayer can play it.

Cheers.

Android Rtsp Usage



Loved my old phone but recently replaced it.  Result is that I have a relatively decent smart phone with little, to no, purpose in life.  Repurposing as a RTSP server device seemed like a reasonable task.


Reviewed a series of video server apps in the Google Play Store, but only one that worked well for me with Linux RTSP clients was:

https://play.google.com/store/apps/details?id=com.pas.webcam


After installing the app and short configuration, you start the server by clicking the 'Start server' button in the video preference activity.


Once the phone streaming is running, it displays a live view of the scene and the connection URL.


Open it with VLC and you're cookin' with bacon:



$ cvlc http://192.168.0.157:8080/video






Voilà.

Cpu Load Monitoring



We've recently had explore collecting and analyzing CPU resource loading metrics for our project. The host OS is Linux 2.6 based and rather than install a performance-specific set of utilities we aimed at utilizing readily available services.
Ask any Linux weenie what CPU loading they have been experiencing and most will respond by starting the top utility. Top is available on most Unix distributions, is a well known utility and has pretty much stood the test of time. For those reasons it seemed obvious to incorporate it into a performance monitoring data collection and analysis tool suite.
The following Tcl script takes two arguments, the first an output file which will be populated with the redirected top log file, the second is a duration (in seconds) of how long to monitor the performance. After the raw collection has completed the script parses the log, extracts the CPU loading metrics and outputs a median and mean CPU processing load expressed in percentage of utilization.
It's worth noting that the script incorporates searching for the regular expression *Cpu* to find the top line item holding the Cpu metrics. As a result, if you have a process running that matches *Cpu* you'll find the script errors out during processing of the log file. I'll leave it as a lesson to the reader how to correct (wink).
We've extended on the same principle to monitor network activities.
Have fun.

#!/usr/bin/tclsh
proc log { msg } {# puts stderr __$msg}
proc mean { L } { set sum 0.0 foreach e $L { set sum [expr $sum + $e] } return [expr $sum / [llength $L].]}
proc median { L } { set L1 [lsort -real $L] return [lindex $L1 [expr [llength $L1] / 2]]}
proc process { fileName } { set fp [open $fileName r] while { [gets $fp line] >= 0 } { switch -glob -- $line { *Cpu* { log "processing line '$line'" set el [split $line ,] set userLoad [string trimleft [lindex $el 0] "Cpu(s):"] set sysLoad [lindex $el 1] set niceLoad [lindex $el 2] set idleLoad [lindex $el 3] log "parsing $userLoad" log "parsing $sysLoad" log "parsing $niceLoad" log "parsing $idleLoad" set userLoad [lindex [split $userLoad \%] 0] set sysLoad [lindex [split $sysLoad \%] 0] set niceLoad [lindex [split $niceLoad \%] 0] set idleLoad [lindex [split $idleLoad \%] 0] log "extracted $userLoad" log "extracted $sysLoad" log "extracted $niceLoad" log "extracted $idleLoad"# puts [expr $userLoad+$sysLoad+$niceLoad+$idleLoad] lappend L [expr 100.0-$idleLoad] } } } close $fp puts "mean cpu load: [mean $L]" puts "median cpu load: [median $L]"}
proc monitorCpuLoad { fileName duration } { catch { exec top -b -n $duration -d 1 > $fileName } process $fileName}
proc help { } { upvar #0 argv0 argv0 puts stderr "usage: $argv0 \[logFileName\] \[duration\]" exit}
#---main--- if { [llength $argv] != 2 } { help } monitorCpuLoad [lindex $argv 0] [lindex $argv 1]

Recruiting and Staffing Agencies


Employment recruitment and staffing agencies are a pretty standard participant in the software engineering ecosystem. Someone has a job looking to fill, someone wants a job in search of one, these agencies serve as a broker of putting the wanter in the same room with the wantee.

Over the past years, I've found myself both as a hiring manager as well as a job seeker, so I feel I have some experience in both camps and I thought my observations might be of interest to some.

The Journey of a Resume

Let's take a journey, shall we?  The journey of a your engineering resume begins when one day you receive an e-mail, or perhaps a phone call, from a staffing agency.   Perhaps someone you've worked with in the past, perhaps someone you've never heard of.  I'll attempt to use the term staffing agency throughout this post, but you may prefer the use of: contracting house, consulting firm, employment services or technical recruiting house.  Regardless, they each serve as an agent that introduces those who have jobs with those seeking jobs.  These agencies have two primary purposes; tracking available job positions and tracking candidates for said jobs.  Staffing agencies act as a middle-man in introducing job seekers with job providers, their fundamental purpose.

The e-mail, or phone conversation, will be detailed in some respects and curiously vague in others.  The exchange may call out a good deal of technical details; tooling, software, programming languages and dev-op specifics, but curiously vague in the company and/or exact industry the job exists.  This is for good reason; specifically it's an effort to ensure you apply for the position through them rather than directly with the company providing the position, or an alternative agency.  If you show interest in the position, or a vague interest in alternative positions, the recruiter will tend to suggest "why don't you fire me an updated resume?".  This exchange has some significance which we'll touch on later, but let's continue following the path of your resume for a job you're interested in.

You send your resume to the recruiter for the position, the recruiter slightly tailors your resume to be later sent to the hiring manager (or HR) for consideration.  The tailoring is subtle, mostly not of consequence, and has the same desired effect of preventing direct contact between applicant and hiring manager.  Your resume, electronic or physical, is stripped of direct contact info; your e-mail address is removed as is any phone number.  Typically, your name is preserved, so with modest effort a hiring manager could contact you directly; they don't tend to however because there is a legal, or professional, agreement between the hiring company and agencies that discourages direct exchanges.  

At this stage, the recruiter has your resume and it goes to the hiring manager, right?  Whelp, not quite, but almost.  Before an agency submits your resume they establish legal protections to prevent them from being removed from the hiring process and getting paid.  It should come at no surprise that agencies are compensated for their efforts.  To prevent a hiring manager directly offering a job to a candidate the agent provided, the hiring company and agency sign legal agreements.  These agreements tend to specify that candidates provided by the agency are represented by the agency and cannot be directly approached.  Agreements also call out constraints to avoid being kicked to the curb at a later time.  For example, most agreements state that if you hire candidate X from agency Y the candidate cannot move to agency Z for the same position.  This is to avoid agency Y who may be charging $120/hr to be replaced with agency Z for $110/hr.  The candidate is locked to the agency for the existing position.  Agreements also call out contract-to-hire positions, where a candidate comes in as a contractor for an hourly rate and is allowed to be hired as a W2 (direct) employee after some period of time if the company wishes to do so.  Agreements that specify the contract-to-hire options tend to charge a bit higher rate and likely a transition bonus if the candidate goes direct.  A properly placed candidate with long-term potential is like an egg-laying goose, locking in a candidate to a long-term hourly position is extremely profitable so contract-to-hire positions tend to adjust the rate higher to offset the loss or make up for it with an agency hiring bonus.  Agency compensation details (like invoiced hourly rates or hiring bonuses) are considered competition-sensitive and typically prevented in the form of a non-disclosure agreement (NDA).  Signed agreements in place, your resume is forwarded to the hiring manager.  

If the hiring manager feels you're a good fit they will contact the agent and you will proceed on, likely with a phone interview or on-site interview.  Your resume is then likely kept on file at the staffing agency for future opportunities.

Job Position Evaluation Process 

Once the resume hits the hiring manager's hands, the staffing agency continues as a middle-man between the hiring manager and applicant.  At each stage of the hiring process, the agent participates in the process.  Often, many communications between hiring manager and candidate flows through the agent.  This can prove to be useful in uncomfortable conversations, like negotiations, so it can be beneficial to the applicant as well as the hiring manager.  If you're a good fit you'll likely proceed on with the hiring process, if not  the agent will typically discuss specific mismatches to be used to tailor their search for alternative candidates.  Exchanges between a hiring manager and candidate are restricted as a result of pre-existing agreements prevented by existing NDAs.

Hired / Continued Involvement

In the even that you're hired, your relationship with the hiring agency may be short-lived or have a longer existence depending on whether the position is hourly (e.g. contractor) or direct.
Assuming that you were hired as a direct (W2) employee, the agency involvement is nearly complete.  The hiring company is obligated to pay a placement fee, typically a percentage of the positions annual salary.  For instance, if you are hired at $100,000, the agency gets an X% placement fee of that $100,000.  Likely, that placement fee is contingent on continued employment, like staying in the position for 3-months.

Contractors, or hourly workers, have a longer-term relationship with the agency.  The contractor bills the agency $X / hr, the agency bills the hiring company $Y / hr (X > Y).  Such long-term relations between you and your representing agency can be binding, preventing you from changing to an alternative agency for the same position.  What if you love the contract job, and find the agency to be a nightmare?  You are kinda stuck, for the duration of the position, learn to work with the agency or leave the position.  A 3-month contract, 6-month, 1 year, 10 year.....you're pretty much stuck with the agency/position pairing.  It's important to choose a good agency because it's equally difficult terminating an AOL subscription.

Fire Me A Resume

Let's go back to the fork-in-the-road decision to send an agency for a job position.  Hopefully it's clear from the process discussed above that this decision shouldn't be taken lightly.  At this particular moment you are entering a relationship, possibly with a person/company you know absolutely nothing about.  I'd implore you to take this step in an informed position.  You are empowering this person to provide professional introductions and communications on your behalf.  This person/company will be representing you for a bit, or a long while, and you may very well be working with them for a while.  Work with respectable agencies and your professional life and reputation can be preserved.  Agency specifics can also effect your selection probability.   For example, an agency may bill you out significantly higher than other candidates a disorganized agency may prevent you from getting a position you're highly qualified for.

Hiring Agency Compensations

Gaining insight into agency compensation specifics is difficult and perhaps unnecessary or uninteresting for many.  Signed agreements legally constrain discussions and social conventions tend to carry the torch as well.  I'm currently contracting and my hiring manager likely is unaware of what I bill my contracting agency hourly.  My hiring manager knows what the agency is billing me out at, and only the agency has specifics of both rates.  Tomorrow, if I chose to, I could walk in and tell my hiring manager my bill rate and then he and the agency would then know both rates.  As an hourly worker it's difficult to ever learn what you are being billed to the company at.  Having positions both as a contractor and at the hiring company, having reviewed agency agreements and knowledge of industry-norms of staffing agencies I can give you some insight.  Your mileage will vary.

Let's start with the easiest; staffing direct (W2) agency compensation.  Typically, a placed candidate is compensated by a one-time placement fee paid from the hiring company to the staffing agency.  Typically, 20% of the annual salary of the new employee.  Hire in at $100,000 and the agency gets a $20,000 check.  I've seen the placement rate negotiated down to 18% and such, but typically 20% is pretty standard.

Hourly rates for contractors are compensated differently.  The agency pays the contractor $X for each hour of work, and bills the company $Y.  I've *heard* of agency adjustment rates as low as 15%, but more typically I've seen 40% adjustment rates.  In one particular case, an agency billing $120/hr, the contractor sees $85/hr.

Why should you care?  Well, it's important to know what you're getting for what you're indirectly paying for.  This awareness should be considered when you ask for annual rate adjustments and such.  It can also be of interest if you ever wish to transition from a W2 contractor to a C2C contract.

This rate adjustment is paying for agency services, administrative activities, the frequent on-site visits hustling for new job prospects, possible sick-time, vacation days, retirement accounts, professional liability insurance, legal consults, agreements,...so while at first glance it may seem extreme, understand it bankrolls a good deal of activities and expenses.

In addition, we are likely all accustomed to getting paid regularly, perhaps weekly, bi-weekly or monthly.  Businesses have been known to be sluggish in payments and it's not uncommon to invoice monthly but be paid by net-30, net-45, net-60 conventions.  That means, the company you're providing service to may only pay for those services 30/45/60 days later.  An hour you work today, invoiced later, is paid 30/45/60 days later.  Agencies buffer that payment in many cases, isolating the payment schedule from the company from the contractor.  Contractor gets paid regularly, agency gets paid less frequently.....a short-term loan orchestrated by the agency acts as a buffer to the individual contractor.

Multiple Agencies

So, what is the harm in multiple agencies distributing your resume?  Well, nothing completely, in fact it can be a good idea for a few reasons, but can have negative consequences as well.
Let's hit on some pros first.  Agencies are hustling for getting the inside track on job positions.  Some, maybe many, are constantly visiting companies and asking "Do you have any new positions coming up?".  Once an agency has a relationship with a hiring manager they will continue to reach out to them with the goal of finding out early of upcoming positions.  That gives them additional time to stack up some potential candidates.  In a fast-pace industry, many companies will fill a position soon after finding a good candidate.  A queue of potential candidates presented to a hiring manager early increases the chance of being selected.  Good agencies are constantly seeking new positions coming up rather than waiting for the position to be publicly posted.
Additionally, some hiring companies have a pre-approved list of agencies that they work for.  A job at company X which exclusively works with agency Y kind-of means you need to submit your resume through agency Y to get the job.  

There are negative consequences of shotgun blasting your resume to each and every agency that approaches you.  The first-come-first-serve representation model means that the first agency to submit your resume represents you, for now and possibly virtual-eternity.  That agent could be a flake or a superstar; just perform your do dilligence. As a seasoned contractor I have numerous stories of staffing agency shenanigans that place you in financial and professional jeopardy, be wary and vet any and all representatives that will be representing you.

Agency Expectations As A Hiring Manager

As a first-time hiring manager, one of the things I expected from a staffing agency that I later found to be unlikely is the ability of them to perform 1st stage vetting of technical expertise.  I figured that software technical recruiters would be....well....modestly technical.  You'll likely never find a recruiter with a degree in engineering, nor a graduate of a code camp, or even well-versed in informal knowledge from YouTube.  I kinda expected technical recruiters to be more skilled in vetting than a general-purpose human resources representative, but that typically isn't the case.  Staffing agencies often are representing multiple disciplines so it's unlikely they could acquire sufficient expertise in the relevant disciplines.  Staffing agencies that specialize in engineering simply means they place engineers, it doesn't mean they have proficiency in recognizing engineering fundamentals.  Keyword resume searches are about all you can expect by agency reviews. It's equally likely that the agent you are working with is a 20-year vet in technical recruiting or fresh out of college.

Our industry is perhaps overly accepting of the eccentric, genius programmer, or some on the spectrum.  Distinguishing a good-intending socially awkward person from an a$$hole isn't always difficult but in my experience you don't tend to find an agency making any effort to rule out terrible team player.  This is especially true from 'resume farm' agencies who simply collect resumes from anyone willing to provide them and shotgun them out to any/all job postings.  These agencies are the equivalent to day-traders and are easily recognized with the modest of communications, my advise would be to simply stay away from them...period, they'll do you no favors.

Most of us have a list of references available to be delivered on-request, people we have worked with closely that can speak to what we are like as a professional.  Additionally, every position we've ever had has co-workers and those co-workers know whether we are competent and if we're impossible to work with.  Agencies typically don't talk to references nor past co-workers and do little more than just talk to the candidate.  I originally thought that an agency participation would conduct such discussions, but I found this not performed by any agencies I've worked with.  The burden is typically on the hiring company and dysfunctional candidates can hide in plain sight.  

Background checks aren't provided by most agencies as well, that typically falls again to the hiring company, be wary of candidates posing false credentials as the agencies typically won't do it for you.

That said, your best use of an agency is simply providing them with a keyword search and have them open the faucet with potential candidates.  Don't anticipate the agency has the knowledge, expertise or capacity to perform any real vetting.  Prepare to review dozens, or hundreds, of resumes and prepare to completely manage the comprehensive list of candidates you've been given, who you've ruled out and who has been scheduled, disqualified......

Any agency that says they'll do it for you is misguided or only telling a partial truth.  Unless you have an exclusive agreement with ONE agency you'll have to do it yourself.  Whether it's two or twenty agencies sending candidates, the burden will be on you to manage the hiring workflow.  Make a spreadsheet right out of the gate and keep track of the evaluation process stages. 

On the topic of a single-source exclusivity agreement with a staffing agency.....for the love of all that is holy DON'T DO IT.  You may be enticed by an elaborate weave of reasons why this particular staffing agency can do it all for you, give you a sweet discount and shower you with daffodils and unicorns in a fairy-tale journey of finding the GOAT candidate.  It's all fiction, you'll have a limited stream of potential candidates and quite likely be assigned a disorganized representative from the agency, locked in likely for a year via exclusivity contract; nightmare material.

I hope this finds some of you as interesting and/or informative.  Unless you're a long-term employee who picked their full-life employer you'll likely conduct business with a staffing agency at some time in your career.  Choose them wisely, they will make this process easier and more effective.  Choose them poorly at your own peril.  Interview them with the same diligence as you would the candidate or company you seek.

Cheers.