Home/News | About | Download | Documentation | Forum | Bug Reports | Contact | Donations | Consulting | Projects | Legal | Security | FATE


This forum has not been maintained for a long time and will probably get deleted in the near future.
For faster responses to your questions, please use StackOverflow instead and tag your questions with "FFmpeg".
If you need a backup of the posts from this forum, please contact me directly.

How to use API ffmpeg to capture a vnc stream?

A collection of useful tutorials for some common tasks.

How to use API ffmpeg to capture a vnc stream?

Postby dcolasante80 » Thu Feb 06, 2014 11:54 am

I would like to understand how to use API ffmpeg to capture a vnc stream and create the output video file.
I tried from vnc2mpg.c (from libVNCClient/libVNCServer library client example) and muxing.c (from ffmpeg/doc/example) but I got poor results.

Has anyone ever done this? Any suggestion is welcome. Thanks to all for your attention and sorry for my English.
dcolasante80
 
Posts: 3
Joined: Thu Feb 06, 2014 11:26 am

Re: How to use API ffmpeg to capture a vnc stream?

Postby dcolasante80 » Thu Feb 06, 2014 11:59 am

This is my code:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <math.h>


#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <rfb/rfbclient.h>

#define STREAM_FRAME_RATE 25 /* 25 images/s */
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */

static int sws_flags = SWS_BICUBIC;

//VNC Password
unsigned char *GlobalPassword;

/**************************************************************/
/* video output */
static AVFrame *frame, *Appo;
static AVPicture src_picture, dst_picture;
static const char *filename;
static AVOutputFormat *fmt;
static AVFormatContext *oc;
static AVStream *video_st;
AVCodec *video_codec;
static double video_time;


/* add a video output stream */
AVStream *add_video_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id, int w, int h)
{
AVCodecContext *c;
AVStream *st;

/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (!(*codec)) {
fprintf(stderr, "Could not find encoder for '%s'\n",
avcodec_get_name(codec_id));
exit(1);
}

st = avformat_new_stream(oc, *codec);
if (!st) {
fprintf(stderr, "Could not allocate stream\n");
exit(1);
}
st->id = oc->nb_streams-1;
c = st->codec;

switch ((*codec)->type) {
case AVMEDIA_TYPE_AUDIO:
c->sample_fmt = AV_SAMPLE_FMT_FLTP;
c->bit_rate = 64000;
c->sample_rate = 44100;
c->channels = 2;
break;

case AVMEDIA_TYPE_VIDEO:
c->codec_id = codec_id;

c->bit_rate = 400000;
/* Resolution must be a multiple of two. */
c->width = w;
c->height = h;
/* timebase: This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented. For fixed-fps content,
* timebase should be 1/framerate and timestamp increments should be
* identical to 1. */
c->time_base.den = STREAM_FRAME_RATE;
c->time_base.num = 1;
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c->pix_fmt = STREAM_PIX_FMT;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
/* just for testing, we also add B frames */
c->max_b_frames = 2;
}
if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
/* Needed to avoid using macroblocks in which some coeffs overflow.
* This does not happen with normal video, it just happens here as
* the motion of the chroma plane does not match the luma plane. */
c->mb_decision = 2;
}
break;

default:
break;
}

/* Some formats want stream headers to be separate. */
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;

return st;
}

static void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
int ret;
AVCodecContext *c = st->codec;

/* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
exit(1);
}

/* allocate and init a re-usable frame */
frame = avcodec_alloc_frame();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}

/* Allocate the encoded raw picture. */
ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height);
if (ret < 0) {
fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret));
exit(1);
}

/* If the output format is not YUV420P, then a temporary YUV420P
* picture is needed too. It is then converted to the required
* output format. */
Appo = NULL;
if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
ret = avpicture_alloc(&src_picture, AV_PIX_FMT_YUV420P, c->width, c->height);
if (ret < 0) {
fprintf(stderr, "Could not allocate temporary picture: %s\n",
av_err2str(ret));
exit(1);
}
Appo = frame;
}

/* copy data and linesize picture pointers to frame */
*((AVPicture *)frame) = dst_picture;
}

static void write_video_frame(AVFormatContext *oc, AVStream *st)
{
int ret;
static struct SwsContext *sws_ctx;
AVCodecContext *c = st->codec;

if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
/* as we only generate a YUV420P picture, we must convert it
* to the codec pixel format if needed */
if (!sws_ctx) {
sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P,
c->width, c->height, c->pix_fmt,
sws_flags, NULL, NULL, NULL);
if (!sws_ctx) {
fprintf(stderr,"Could not initialize the conversion context\n");
exit(1);
}
}
sws_scale(sws_ctx,
(const uint8_t * const *)src_picture.data, src_picture.linesize,
0, c->height, dst_picture.data, dst_picture.linesize);
}
if (oc->oformat->flags & AVFMT_RAWPICTURE) {
/* Raw video case - directly store the picture in the packet */
AVPacket pkt;
av_init_packet(&pkt);

pkt.flags |= AV_PKT_FLAG_KEY;
pkt.stream_index = st->index;
pkt.data = dst_picture.data[0];
pkt.size = sizeof(AVPicture);

ret = av_interleaved_write_frame(oc, &pkt);
} else {
AVPacket pkt = { 0 };
int got_packet;
av_init_packet(&pkt);

/* encode the image */
ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
if (ret < 0) {
fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
exit(1);
}
/* If size is zero, it means the image was buffered. */

if (!ret && got_packet && pkt.size) {
pkt.stream_index = st->index;

/* Write the compressed frame to the media file. */
ret = av_interleaved_write_frame(oc, &pkt);
} else {
ret = 0;
}
}
if (ret != 0) {
fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
exit(1);
}
}

void close_video(AVFormatContext *oc, AVStream *st)
{
avcodec_close(st->codec);
av_free(src_picture.data[0]);
av_free(dst_picture.data[0]);
av_free(Appo);
av_free(frame);
}



static int movie_open(int w, int h) {

int ret = 0;

if (fmt->video_codec != AV_CODEC_ID_NONE) {
video_st = add_video_stream(oc, &video_codec, fmt->video_codec, w, h);
} else
return 1;

/* Now that all the parameters are set, we can open the audio and
* video codecs and allocate the necessary encode buffers. */
if (video_st) {
open_video(oc, video_codec, video_st);
}

av_dump_format(oc, 0, filename, 1);

/* open the output file, if needed */
if (!(fmt->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));
return 1;
}
}

/* Write the stream header, if any. */
ret = avformat_write_header(oc, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file: %s\n",
av_err2str(ret));
return 1;
}

return 0;
}

static int movie_close() {

int i;
/* Write the trailer, if any. The trailer must be written before you
* close the CodecContexts open when you wrote the header; otherwise
* av_write_trailer() may try to use memory that was freed on
* av_codec_close(). */
av_write_trailer(oc);

if (video_st) {
/* close each codec */
close_video(oc, video_st);
}


/* free the streams */
for(i = 0; i < oc->nb_streams; i++) {
av_freep(&oc->streams[i]);
}

if (!(fmt->flags & AVFMT_NOFILE)) {
/* Close the output file. */
avio_close(oc->pb);
}

/* free the stream */
avformat_free_context(oc);

return 0;
}

static rfbBool quit=FALSE;
static void signal_handler(int signal) {
fprintf(stderr,"Cleaning up.\n");
quit=TRUE;
}

/**************************************************************/
/* VNC callback functions */
static rfbBool resize(rfbClient* client) {
static rfbBool first=TRUE;
if(!first) {
movie_close();
perror("I don't know yet how to change resolutions!\n");
}
movie_open(client->width, client->height);
signal(SIGINT,signal_handler);
if(Appo) {
client->frameBuffer=src_picture.data[0];
}
else {
client->frameBuffer=dst_picture.data[0];
}
return TRUE;
}

static void update(rfbClient* client,int x,int y,int w,int h) {
}


void password_setup (char * pwd) {

int i;

GlobalPassword = (unsigned char *)malloc(9);

for (i = 0; i < 8; i++) {

GlobalPassword[i] = pwd[i];
}

GlobalPassword[8] = 0;
}

char * get_password (struct _rfbClient* client) {

return (char *) GlobalPassword;
}

/**************************************************************/
/* media file output */

int main(int argc, char **argv)
{
time_t stop=0;
rfbClient* client;
int i,j;

/* get a vnc client structure (don't connect yet). */
client = rfbGetClient(5,3,2);
client->format.redShift=11; client->format.redMax=31;
client->format.greenShift=5; client->format.greenMax=63;
client->format.blueShift=0; client->format.blueMax=31;

password_setup(argv[--argc]);

/* initialize libavcodec, and register all codecs and formats */
av_register_all();

if(!strncmp(argv[argc-1],":",1) ||
!strncmp(argv[argc-1],"127.0.0.1",9) ||
!strncmp(argv[argc-1],"localhost",9))
client->appData.encodingsString="raw";

filename=0;
for(i=1;i<argc;i++) {
j=i;
if(argc>i+1 && !strcmp("-o",argv[i])) {
filename=argv[2];
j+=2;
} else if(argc>i+1 && !strcmp("-t",argv[i])) {
stop=time(0)+atoi(argv[i+1]);
j+=2;
}
if(j>i) {
argc-=j-i;
memmove(argv+i,argv+j,(argc-i)*sizeof(char*));
i--;
}
}


/* allocate the output media context */
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
if (!oc) {
printf("Could not deduce output format from file extension: using MPEG.\n");
avformat_alloc_output_context2(&oc, NULL, "mpeg", filename);
}
if (!oc) {
return 1;
}
fmt = oc->oformat;

/* Add the audio and video streams using the default format codecs
* and initialize the codecs. */
video_st = NULL;

/* open VNC connection */
client->MallocFrameBuffer=resize;
client->GotFrameBufferUpdate=update;
client->GetPassword = get_password;

if(!rfbInitClient(client,&argc,argv)) {
printf("usage: %s [-o output_file] [-t seconds] server:port\n"
"Shoot a movie from a VNC server.\n", argv[0]);
exit(1);
}
if(client->serverPort==-1)
client->vncRec->doNotSleep = TRUE; /* vncrec playback */

free(GlobalPassword);

/* main loop */

//if (frame) {
// frame->pts = 0;
//}

while(!quit) {
int i=WaitForMessage(client,1000000/STREAM_FRAME_RATE);
if(i<0) {
movie_close();
return 0;
}
if(i)
if(!HandleRFBServerMessage(client))
quit=TRUE;
else {
/* compute current audio and video time */
video_time = video_st ? video_st->pts.val * av_q2d(video_st->time_base) : 0.0;

/* write interleaved audio and video frames */
write_video_frame(oc, video_st);
frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
}
if(stop!=0 && stop<time(0))
quit=TRUE;
}

movie_close();
return 0;
}


I run: ./video -o video.avi -t 30 ip:port vncpassword but the result is

Output #0, avi, to 'video.avi':
Stream #0:0: Video: mpeg4, yuv420p, 1680x1050, q=2-31, 400 kb/s, 90k tbn, 25 tbc
[avi @ 0x1ec4060] Application provided invalid, non monotonically increasing dts to muxer in stream 33754816: 269628272584 >= 0
Error while writing video frame: Invalid argument

RUN FINISHED; exit value 1; real time: 1s; user: 570ms; system: 90ms
dcolasante80
 
Posts: 3
Joined: Thu Feb 06, 2014 11:26 am

Re: How to use API ffmpeg to capture a vnc stream?

Postby llogan » Thu Feb 06, 2014 6:43 pm

Why did you not use the code tag to format your code?
Questions involving programmatic usage of the FFmpeg libraries should be directed to the libav-user mailing list.
llogan
 
Posts: 2339
Joined: Fri Jan 25, 2013 9:47 pm
Location: Alaska

Re: How to use API ffmpeg to capture a vnc stream?

Postby dcolasante80 » Fri Feb 07, 2014 9:07 am

Sorry but this is my first message. I'll be more careful next time.
dcolasante80
 
Posts: 3
Joined: Thu Feb 06, 2014 11:26 am


Return to Tutorials

Who is online

Users browsing this forum: No registered users and 2 guests