Simple movie writer for vnc; based on Libavformat API example from FFMPEGCopyright (c) 2003 Fabrice Bellard, 2004 Johannes E. Schindelin Updates copyright (c) 2017 Tyrel M. McQueen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <sys/time.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#define VNC_PIX_FMT AV_PIX_FMT_RGB565
#define OUTPUT_PIX_FMT AV_PIX_FMT_YUV420P
static int write_packet(AVFormatContext *
oc,
const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
av_packet_rescale_ts(pkt, *time_base, st->time_base);
pkt->stream_index = st->index;
return av_interleaved_write_frame(
oc, pkt);
}
typedef struct {
AVStream *st;
AVCodec *codec;
AVCodecContext *enc;
int64_t pts;
AVFrame *frame;
AVFrame *tmp_frame;
struct SwsContext *sws;
enum AVCodecID codec_id, int64_t br, int sr, int w, int h)
{
int i;
ost->
codec = avcodec_find_encoder(codec_id);
fprintf(stderr, "Could not find encoder for '%s'\n",
avcodec_get_name(codec_id));
return -1;
}
if (ost->
codec->type != AVMEDIA_TYPE_VIDEO) {
fprintf(stderr, "Encoder for '%s' does not seem to be for video.\n",
avcodec_get_name(codec_id));
return -2;
}
ost->
enc = avcodec_alloc_context3(ost->
codec);
fprintf(stderr, "Could not alloc an encoding context\n");
return -3;
}
ost->
enc->codec_id = codec_id;
ost->
enc->width = w + (w % 2);
ost->
enc->height = h + (h % 2);
ost->
enc->time_base = (AVRational){ 1, sr };
if (ost->
enc->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
ost->
enc->mb_decision = 2;
}
ost->
st = avformat_new_stream(
oc, ost->
codec);
fprintf(stderr, "Could not allocate stream\n");
avcodec_free_context(&(ost->
enc));
return -4;
}
ost->
st->id =
oc->nb_streams-1;
ost->
st->time_base = ost->
enc->time_base;
if (
oc->oformat->flags & AVFMT_GLOBALHEADER)
ost->
enc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
return 0;
}
{
AVFrame *picture;
int ret;
picture = av_frame_alloc();
if (!picture)
return NULL;
picture->format = pix_fmt;
ret = av_frame_get_buffer(picture, 64);
if (ret < 0) {
fprintf(stderr, "Could not allocate frame data.\n");
av_frame_free(&picture);
return NULL;
}
return picture;
}
{
int ret;
ret = avcodec_open2(ost->
enc, ost->
codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
return ret;
}
ret = avcodec_parameters_from_context(ost->
st->codecpar, ost->
enc);
if (ret < 0) {
fprintf(stderr, "Could not copy the stream parameters.\n");
return ret;
}
fprintf(stderr, "Could not allocate video frame\n");
return -1;
}
fprintf(stderr, "Could not allocate temporary picture\n");
av_frame_free(&(ost->
frame));
return -2;
}
ost->
sws = sws_getCachedContext(ost->
sws, ost->
enc->width, ost->
enc->height,
VNC_PIX_FMT, ost->
enc->width, ost->
enc->height, ost->
enc->pix_fmt, 0, NULL, NULL, NULL);
fprintf(stderr, "Could not get sws context\n");
av_frame_free(&(ost->
frame));
return -3;
}
}
return 0;
}
{
int ret, ret2;
AVPacket pkt = { 0 };
if (pts <= ost->pts) return 0;
sws_scale(ost->
sws, (
const uint8_t *
const *)ost->
tmp_frame->data,
}
ret = avcodec_send_frame(ost->
enc, ost->
frame);
if (ret < 0) {
fprintf(stderr, "Error sending video frame to encoder: %s\n", av_err2str(ret));
return ret;
}
ret2 = 0;
for (ret = avcodec_receive_packet(ost->
enc, &pkt); ret == 0; ret = avcodec_receive_packet(ost->
enc, &pkt)) {
ret2 = write_packet(
oc, &(ost->
enc->time_base), ost->
st, &pkt);
if (ret2 < 0) {
fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret2));
}
}
if (ret2 < 0) return ret2;
if (!(ret == AVERROR(EAGAIN))) return ret;
return 0;
}
{
int ret, ret2;
AVPacket pkt = { 0 };
ret = avcodec_send_frame(ost->
enc, NULL);
if (ret < 0) {
fprintf(stderr, "Error sending final video frame to encoder: %s\n", av_err2str(ret));
return ret;
}
ret2 = 0;
for (ret = avcodec_receive_packet(ost->
enc, &pkt); ret == 0; ret = avcodec_receive_packet(ost->
enc, &pkt)) {
ret2 = write_packet(
oc, &(ost->
enc->time_base), ost->
st, &pkt);
if (ret2 < 0) {
fprintf(stderr, "Error while writing final video frame: %s\n", av_err2str(ret2));
}
}
if (ret2 < 0) return ret2;
if (!(ret == AVERROR(EOF))) return ret;
return 0;
}
{
avcodec_free_context(&(ost->
enc));
av_frame_free(&(ost->
frame));
sws_freeContext(ost->
sws); ost->
sws = NULL;
}
int ret;
ret = avformat_alloc_output_context2(&
oc, NULL, NULL,
filename);
if (ret < 0) {
fprintf(stderr, "Warning: Could not deduce output format from file extension: using MP4.\n");
ret = avformat_alloc_output_context2(&
oc, NULL,
"mp4",
filename);
}
if (ret < 0) {
fprintf(stderr, "Error: Could not allocate media context: %s.\n", av_err2str(ret));
return NULL;
}
if (
oc->oformat->video_codec != AV_CODEC_ID_NONE) {
} else {
ret = -1;
}
if (ret < 0) {
fprintf(stderr, "Error: chosen output format does not have a video codec, or error %i\n", ret);
avformat_free_context(
oc);
oc = NULL;
return NULL;
}
if (ret < 0) {
fprintf(stderr, "Error: error opening video codec, error %i\n", ret);
avformat_free_context(
oc);
oc = NULL;
return NULL;
}
if (!(
oc->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&
oc->pb,
filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr,
"Could not open '%s': %s\n",
filename,
av_err2str(ret));
avformat_free_context(
oc);
oc = NULL;
return NULL;
}
}
ret = avformat_write_header(
oc, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when writing to output file: %s\n",
av_err2str(ret));
if (!(
oc->oformat->flags & AVFMT_NOFILE))
avformat_free_context(
oc);
oc = NULL;
}
}
AVFormatContext *
oc = *ocp;
if (!(
oc->oformat->flags & AVFMT_NOFILE))
avformat_free_context(
oc);
ocp = NULL;
}
}
AVFormatContext *
oc = NULL;
}
time_t ds =
cur_time->tv_sec - start_time->tv_sec;
long dns =
cur_time->tv_nsec - start_time->tv_nsec;
int64_t dt = (int64_t)ds*(int64_t)1000000+(int64_t)dns/(int64_t)1000;
int64_t rv = (((int64_t)
framerate)*dt + (int64_t)500000) / (int64_t)(1000000);
return rv;
}
#ifdef SIGQUIT
#endif
else
}
}
int main(
int argc,
char **argv)
{
int i,j;
#if LIBAVUTIL_VERSION_MAJOR < 56
av_register_all();
#endif
for(i=1;i<argc;i++) {
j=i;
if(argc>i+1 && !strcmp("-o",argv[i])) {
j+=2;
} else if(argc>i+1 && !strcmp("-t",argv[i])) {
if (max_time < 10 || max_time > 100000000) {
fprintf(stderr,
"Warning: Nonsensical time-per-file %li, resetting to default.\n",
max_time);
}
j+=2;
}
if(j>i) {
argc-=j-i;
memmove(argv+i,argv+j,(argc-i)*sizeof(char*));
i--;
}
}
fprintf(stderr, "Warning: No filename specified. Using output.mp4\n");
}
printf("usage: %s [-o output_file] [-t seconds-per-file] server:port\n", argv[0]);
return 1;
}
clock_gettime(CLOCK_MONOTONIC, &start_time);
if (i>0) {
} else if (i<0) {
}
clock_gettime(CLOCK_MONOTONIC, &
cur_time);
}
}
}
return 0;
}
rfbClient * rfbGetClient(int bitsPerSample, int samplesPerPixel, int bytesPerPixel)
Allocates and returns a pointer to an rfbClient structure.
int WaitForMessage(rfbClient *client, unsigned int usecs)
Waits for an RFB message to arrive from the server.
rfbBool HandleRFBServerMessage(rfbClient *client)
Handles messages from the RFB server.
rfbBool rfbInitClient(rfbClient *client, int *argc, char **argv)
Initializes the client.
GotFrameBufferUpdateProc GotFrameBufferUpdate
MallocFrameBufferProc MallocFrameBuffer
int write_video_frame(AVFormatContext *oc, VideoOutputStream *ost, int64_t pts)
VideoOutputStream video_st
void signal_handler(int signal)
int main(int argc, char **argv)
AVFrame * alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
struct timespec start_time cur_time
void movie_close(AVFormatContext **ocp, VideoOutputStream *video_st)
int open_video(AVFormatContext *oc, VideoOutputStream *ost)
AVFormatContext * movie_open(char *filename, VideoOutputStream *video_st, int br, int fr, int w, int h)
int64_t time_to_pts(int framerate, struct timespec *start_time, struct timespec *cur_time)
int write_final_video_frame(AVFormatContext *oc, VideoOutputStream *ost)
int add_video_stream(VideoOutputStream *ost, AVFormatContext *oc, enum AVCodecID codec_id, int64_t br, int sr, int w, int h)
void vnc_update(rfbClient *client, int x, int y, int w, int h)
rfbBool vnc_malloc_fb(rfbClient *client)
void close_video_stream(VideoOutputStream *ost)