Digging In The Vineyard, Part 3
Previously:
- I figured out how to log in and post using the Vine API.
- The AWS keys were extracted from the Vine iOS app.
Now it's time to actually post something to Vine. Since the entire internet seems to have somehow regressed to 90s era technology and animated GIFs are more readily available than short videos, let's try and get one of those up there—albeit as a H.264 encoded video.
I'm going to help my friend Pazuzu here possess Vine. Some projectile vomiting of pea soup could really liven the place up.
This is, of course, a somewhat convoluted process.
Some things in this part may seem rather “deus ex machina”, and that's because
the knowledge was really discovered through a lot of boring trial and error. For
example, part way through writing these posts, Vine started to validate the
handler_name
metadata on videos—I assume as a result of discovering the
clearly not-posted-via-Vine uploads from myself or others.
Going through the process of making slight changes to the video one at a time and retrying the submission to Vine wouldn't really make for an enthralling post, so I've left it out.
To get GIFs onto Vine will require adding three new things to the toolkit:
- ImageMagick to create a thumbnail, and to extract frames from the GIF.
- ffmpeg to encode the extracted frames as a H.264 video
- mp4box to tweak the container metadata and mux video with audio.
$ brew install imagemagick ffmpeg mp4box
Creating a thumbnail from the GIF is handled by using ImageMagick's convert
command:
$ convert 'pazuzu.gif[0]' -resize 480x480! thumbnail.jpg
The [0]
after the GIF filename specifies which frame to use, otherwise, all of
them would have been resized and converted to JPEG. The !
after the new image
dimensions instruct convert
to not preserve the aspect ratio—unnecessary
in this case, but handy to know about.
Extracting the frames from the GIF is also handled by convert
:
$ mkdir frames
$ convert pazuzu.gif -coalesce frames/pazuzu%05d.png
$ ls frames
pazuzu00000.png pazuzu00002.png pazuzu00004.png pazuzu00006.png pazuzu00008.png pazuzu00010.png
pazuzu00001.png pazuzu00003.png pazuzu00005.png pazuzu00007.png pazuzu00009.png
The -coalesce
option essentially instructs ImageMagick to dump the full frame,
and not just the changes between a frame and the previous frame.
Moving on to the video, it's time to weed through ffmpeg
's dense overgrowth of command line options
to try and get a usable video encoded.
$ ffmpeg -f image2 -i frames/pazuzu%05d.png -vcodec libx264 -pix_fmt yuv420p -profile:v main -s 480x480 -r 24 -an pazuzu.m4v
We're instructing ffmpeg
to create a 480x480 H.264 video from the directory of
frames, using the “main” H.264 profile, with a pixel format of YUV420p, and a
frame rate of 24 fps. No audio track. This is pretty close to what the Vine app
itself uploads to S3.
Sometimes using FFMpeg feels like using a knock-off multi-tool that's going to explode in your hand, somehow launching a nail file directly into your cornea. It does however work really well once the right incantation has been constructed.
Finally, the video needs its metadata tweaked, and it needs an audio track. It could be empty, but I'll use an old friend here, the “Sosumi” alert that's shipped with Mac OS for a long time.
# afconvert ships with OS X
$ afconvert -f mp4f /System/Library/Sounds/Sosumi.aiff /tmp/Sosumi.m4a
$ MP4Box -add pazuzu.m4v -add /tmp/Sosumi.m4a -name '1=Core Media Video' -name '2=Core Media Audio' pazuzu.mp4
Setting the name
metadata for each track is pretty important, as Vine will
reject any videos that don't match this. The API will return a successful
response, but the videos will never actually show up in your feed.
We now have a video that looks fairly close to a legitimate Vine—close enough to fool the validation that's in place, certainly. Gotta fly underneath the radar.
Time to see if those S3 credentials actually worked. Be a shame if I subjected myself to the agony of video encoding for no good reason at all. Here's some Ruby code to upload a thumbnail file and a video file to the Vine S3 bucket using the credentials obtained earlier.
#!/usr/bin/env ruby
# Usage: ./s3upload.rb THUMBNAIL_PATH VIDEO_PATH
require "aws-sdk"
require "uuidtools"
VINE_S3 = "vines.s3.amazonaws.com"
BUCKET_NAME = "vines"
ACCESS_KEY = "NOT-REAL-AWSG3TITY0URS3LF"
SECRET_KEY = "NOT-REAL-SCR4MKID"
raise "thumbnail and video are required" unless ARGV[0] && ARGV[1]
thumbnail_file = File.open(ARGV[0])
video_file = File.open(ARGV[1])
s3_client = AWS::S3.new(server: VINE_S3,
access_key_id: ACCESS_KEY,
secret_access_key: SECRET_KEY)
# Generate a key that looks at least somewhat like what Vine generates, UUID and all
video_name = "#{UUIDTools::UUID.random_create.to_s.upcase}-1234-00000105FD8E6096_1.0.5.mp4"
video_key = "videos/#{video_name}"
thumbnail_key = "thumbs/#{video_name}.jpg"
bucket = s3_client.buckets[BUCKET_NAME]
video_obj = bucket.objects[video_key]
video_version = video_obj.write(video_file, content_type: "video/mp4")
thumbnail_obj = bucket.objects[thumbnail_key]
thumb_version = thumbnail_obj.write(thumbnail_file, content_type: "image/jpeg")
puts "Thumbnail URL: #{thumbnail_obj.public_url(secure: true)}?versionId=#{thumb_version.version_id}"
puts "Video URL: #{video_obj.public_url(secure: true)}?versionId=#{video_version.version_id}"
Here's the moment of truth. Did pottering about with gdb
and writing a little
C actually pay off?
$ ./s3upload.rb thumbnail.jpg pazuzu.mp4
Thumbnail URL: https://vines.s3.amazonaws.com/thumbs/271F12BF-DF19-4FBF-836C-D1320BED15EA-1234-00000105FD8E6096_1.0.5.mp4.jpg?versionId=hB2NCVUhT8Az388kUsqpGHj7v_DnZiEN
Video URL: https://vines.s3.amazonaws.com/videos/271F12BF-DF19-4FBF-836C-D1320BED15EA-1234-00000105FD8E6096_1.0.5.mp4?versionId=RCiryWOxJCVq6_Kt_h1jaM7WCrBVBasU
Fuck yeah it did.
Digging up the code from part 1 of this series, I can plug in the URLs to the files on S3 and see if the Vine API now gives me the green light on submitting a new Vine.
$ ./vinepost.rb testvineuser@example.com passitypassword
{"code": "", "data": {"postId": 920955833397948416, "created": "2013-03-06T05:19:55.213820"}, "success": true, "error": ""}
Fuck yeah it does.
The post to Vine appeared successfully in my feed, Pazuzu beeping the “Sosumi” beep at me in all his glory.
If one were so inclined, one could tie this all together in a neat little pile of code that automates the H.264 encoding, S3 upload, and Vine authentication and submission process in one fell swoop. This is an exercise left to the reader.
This was personally a pretty fulfilling romp through some fairly disparate
grounds—sniffing out API behaviour with mitmproxy
, learning more about
the Objective-C runtime, writing C, spending some time in gdb
, looking up ARM
calling conventions, and playing about with video encoding. It's not every day
that you get to run that particular gauntlet of technology.
Indulge your curiosity. Take an application and just wail on that thing like it owes you money until something breaks, or it gives up its secrets, or both. You'll learn a thing or two, and maybe even impress and amaze your friends with the places you'll go.
Thanks to the folks who took time to read earlier drafts of this work.
If you enjoyed these posts and need to pay someone to have some computers programmed, you should get in touch with me.