Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions ffmpegServerApp/src/ffmpegCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ void ffmpegInitialise() {
/* check if we're already intialised */
if (ffmpegInitialised) return;

/* register all the codecs */
avcodec_register_all();

/* start the library */
av_register_all();

/* Make a big neutral array */
neutral = (unsigned char *)malloc(NEUTRAL_FRAME_SIZE * sizeof(unsigned char));
memset(neutral, 128, NEUTRAL_FRAME_SIZE);
Expand Down
1 change: 1 addition & 0 deletions ffmpegServerApp/src/ffmpegCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extern "C" {
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
}

/* areaDetector includes */
Expand Down
110 changes: 58 additions & 52 deletions ffmpegServerApp/src/ffmpegFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,23 @@ asynStatus ffmpegFile::openFile(const char *filename, NDFileOpenMode_t openMode,
}

/* If we are using image2 then we only support one frame per image */
fmt = oc->oformat;
if (strcmp("image2", fmt->name)==0) {
if (strcmp("image2", oc->oformat->name)==0) {
this->supportsMultipleArrays = 0;
} else {
this->supportsMultipleArrays = 1;
/* We want to use msmpeg4v2 instead of mpeg4 for avi files*/
if (av_match_ext(filename, "avi") && fmt->video_codec == AV_CODEC_ID_MPEG4) {
fmt->video_codec = AV_CODEC_ID_MSMPEG4V2;
}
}

codec_id = av_guess_codec(fmt, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO);
codec_id = av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO);
if (codec_id == AV_CODEC_ID_NONE) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Codec has no video stream component\n",
driverName2, functionName);
return(asynError);
}
/* We want to use msmpeg4v2 instead of mpeg4 for avi files*/
if (av_match_ext(filename, "avi") && codec_id == AV_CODEC_ID_MPEG4) {
codec_id = AV_CODEC_ID_MSMPEG4V2;
}

/* find the encoder */
video_st = NULL;
Expand All @@ -85,7 +84,6 @@ asynStatus ffmpegFile::openFile(const char *filename, NDFileOpenMode_t openMode,
return(asynError);
}
video_st->id = oc->nb_streams-1;
c = video_st->codec;

if (codec->type != AVMEDIA_TYPE_VIDEO) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
Expand All @@ -94,7 +92,7 @@ asynStatus ffmpegFile::openFile(const char *filename, NDFileOpenMode_t openMode,
return(asynError);
}

avcodec_get_context_defaults3(c, codec);
c = avcodec_alloc_context3(codec);
c->codec_id = codec_id;

/* put sample parameters */
Expand All @@ -113,6 +111,7 @@ asynStatus ffmpegFile::openFile(const char *filename, NDFileOpenMode_t openMode,
of which frame timestamps are represented. for fixed-fps content,
timebase should be 1/framerate and timestamp increments should be
identically 1. */
video_st->time_base = avr;
c->time_base = avr;

c->gop_size = 12; /* emit one intra frame every twelve frames at most */
Expand Down Expand Up @@ -147,8 +146,6 @@ asynStatus ffmpegFile::openFile(const char *filename, NDFileOpenMode_t openMode,
/* Now that all the parameters are set, we can open the audio and
* video codecs and allocate the necessary encode buffers. */

c = video_st->codec;

/* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
Expand All @@ -158,6 +155,15 @@ asynStatus ffmpegFile::openFile(const char *filename, NDFileOpenMode_t openMode,
return(asynError);
}

/* copy the stream parameters to the muxer */
ret = avcodec_parameters_from_context(video_st->codecpar, c);
if (ret < 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Could not copy the stream parameters: %s\n",
driverName2, functionName, av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, ret));
return(asynError);
}

/* dump the format so we can see it on the console... */
av_dump_format(oc, 0, filename, 1);

Expand Down Expand Up @@ -211,7 +217,7 @@ asynStatus ffmpegFile::openFile(const char *filename, NDFileOpenMode_t openMode,
/* alloc in and scaled pictures */
inPicture = av_frame_alloc();
scPicture = av_frame_alloc();
avpicture_fill((AVPicture *)scPicture,(uint8_t *)scArray->pData,c->pix_fmt,c->width,c->height);
av_image_fill_arrays(scPicture->data,scPicture->linesize,(uint8_t *)scArray->pData,c->pix_fmt,c->width,c->height,1);
scPicture->pts = 0;
needStop = 1;
return(asynSuccess);
Expand Down Expand Up @@ -245,39 +251,43 @@ asynStatus ffmpegFile::writeFile(NDArray *pArray)
}


/* encode the image */
AVPacket pkt;
int got_output;
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;

ret = avcodec_encode_video2(c, &pkt, this->scPicture, &got_output);
/* send the frame to the encoder */
ret = avcodec_send_frame(c, this->scPicture);
if (ret < 0) {
fprintf(stderr, "Error encoding video frame: %s\n", av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, ret));
exit(1);
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Error sending a frame to the encoder: %s\n",
driverName2, functionName, av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, ret));
}

/* If size is zero, it means the image was buffered. */
if (got_output) {
if (c->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;

pkt.stream_index = video_st->index;
pkt = av_packet_alloc();
while (ret >= 0) {
ret = avcodec_receive_packet(c, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Error encoding a frame: %s\n",
driverName2, functionName, av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, ret));
return(asynError);
}
/* rescale output packet timestamp values from codec to stream timebase */
av_packet_rescale_ts(pkt, c->time_base, video_st->time_base);
pkt->stream_index = video_st->index;

/* Write the compressed frame to the media file. */
ret = av_interleaved_write_frame(oc, &pkt);
} else {
printf("got_output = 0, shouldn't see this\n");
ret = 0;
ret = av_interleaved_write_frame(oc, pkt);
/* pkt is now blank (av_interleaved_write_frame() takes ownership of
* its contents and resets pkt), so that no unreferencing is necessary.
* This would be different if one used av_write_frame(). */
if (ret < 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Error while writing output packet: %s\n",
driverName2, functionName, av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, ret));
return(asynError);
}
}
scPicture->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);

if (ret != 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Error while writing video frame\n",
driverName2, functionName);
}
scPicture->pts += av_rescale_q(1, c->time_base, video_st->time_base);

return(asynSuccess);
}
Expand Down Expand Up @@ -309,22 +319,18 @@ asynStatus ffmpegFile::closeFile()

/* close each codec */
if (video_st) {
avcodec_close(video_st->codec);
avcodec_free_context(&c);
av_packet_free(&pkt);
}

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

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

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

if (scArray) {
scArray->release();
scArray = NULL;
Expand All @@ -334,8 +340,8 @@ asynStatus ffmpegFile::closeFile()
outArray = NULL;
}

av_free(inPicture);
av_free(scPicture);
av_frame_free(&inPicture);
av_frame_free(&scPicture);
return asynSuccess;
}

Expand Down Expand Up @@ -419,8 +425,8 @@ ffmpegFile::ffmpegFile(const char *portName, int queueSize, int blockingCallback
this->scPicture = NULL;
this->scArray = NULL;
this->outArray = NULL;
this->pkt = NULL;
this->ctx = NULL;
this->fmt = NULL;
this->oc = NULL;
this->video_st = NULL;

Expand Down
4 changes: 2 additions & 2 deletions ffmpegServerApp/src/ffmpegFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,19 @@ class ffmpegFile : public NDPluginFile {

private:
FILE *outFile;
AVCodec *codec;
const AVCodec *codec;
enum AVCodecID codec_id;
AVCodecContext *c;
AVFrame *inPicture;
AVFrame *scPicture;
NDArray *scArray;
NDArray *outArray;
AVPacket *pkt;
struct SwsContext *ctx;
size_t outSize;
int needStop;
int sheight, swidth;
enum AVPixelFormat spix_fmt;
AVOutputFormat *fmt;
AVFormatContext *oc;
AVStream *video_st;
double video_pts;
Expand Down
69 changes: 41 additions & 28 deletions ffmpegServerApp/src/ffmpegServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ void ffmpegStream::allocScArray(size_t size) {
if (this->scArray) {
if (this->scArray->dims[0].size >= size) {
/* the processed array is already big enough */
avpicture_fill((AVPicture *)scPicture,(uint8_t *)scArray->pData,c->pix_fmt,c->width,c->height);
av_image_fill_arrays(scPicture->data,scPicture->linesize,(uint8_t *)scArray->pData,c->pix_fmt,c->width,c->height,1);
return;
} else {
/* need a new one, so discard the old one */
Expand All @@ -366,7 +366,7 @@ void ffmpegStream::allocScArray(size_t size) {
}
this->scArray = this->pNDArrayPool->alloc(1, &size, NDInt8, 0, NULL);
/* alloc in and scaled pictures */
avpicture_fill((AVPicture *)scPicture,(uint8_t *)scArray->pData,c->pix_fmt,c->width,c->height);
av_image_fill_arrays(scPicture->data,scPicture->linesize,(uint8_t *)scArray->pData,c->pix_fmt,c->width,c->height,1);
}


Expand All @@ -384,6 +384,11 @@ void ffmpegStream::processCallbacks(NDArray *pArray)
int width, height;
/* in case we force a final size */
int setw, seth;
int num_bytes = 0;
AVPacket *pkt = NULL;
NDArray *pScratch = NULL;
int ret;
char errbuf[AV_ERROR_MAX_STRING_SIZE];

size_t size;
/* for printing errors */
Expand Down Expand Up @@ -463,8 +468,7 @@ void ffmpegStream::processCallbacks(NDArray *pArray)
avr.den = 25;
if (c != NULL) {
/* width and height changed, close old codec */
avcodec_close(c);
av_free(c);
avcodec_free_context(&c);
}
c = avcodec_alloc_context3(codec);
/* Make sure that we don't try and create an image smaller than AV_INPUT_BUFFER_MIN_SIZE */
Expand Down Expand Up @@ -527,43 +531,52 @@ void ffmpegStream::processCallbacks(NDArray *pArray)
/* lock the output plugin mutex */
pthread_mutex_lock(&this->mutex);

/* Release the last jpeg created */
if (this->jpeg) {
this->jpeg->release();
/* send the frame to the encoder */
ret = avcodec_send_frame(c, scPicture);
if (ret < 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Error sending a frame to the encoder: %s\n",
driverName, functionName, av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, ret));
goto done;
}

/* Convert it to a jpeg */
this->jpeg = this->pNDArrayPool->alloc(1, &size, NDInt8, 0, NULL);

AVPacket pkt;
int got_output;
av_init_packet(&pkt);
pkt.data = (uint8_t*)this->jpeg->pData; // packet data will be allocated by the encoder
pkt.size = c->width * c->height;

// needed to stop a stream of "AVFrame.format is not set" etc. messages
scPicture->format = c->pix_fmt;
scPicture->width = c->width;
scPicture->height = c->height;
/* allocate a new NDArray for encoded packet */
pScratch = this->pNDArrayPool->alloc(1, &size, NDInt8, 0, NULL);

pkt = av_packet_alloc();
while (ret >= 0) {
ret = avcodec_receive_packet(c, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s Error encoding a frame: %s\n",
driverName, functionName, av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, ret));
pScratch->release();
goto done;
}
memcpy((char*)pScratch->pData + num_bytes, pkt->data, pkt->size);
num_bytes += pkt->size;

if (avcodec_encode_video2(c, &pkt, scPicture, &got_output)) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: Encoding jpeg failed\n",
driverName, functionName);
got_output = 0; // got_output is undefined on error, so explicitly set it for use later
av_packet_unref(pkt);
}
pScratch->dims[0].size = num_bytes;

if (got_output) {
this->jpeg->dims[0].size = pkt.size;
av_packet_unref(&pkt);
/* Release the last jpeg created */
if (this->jpeg) {
this->jpeg->release();
}
this->jpeg = pScratch;

//printf("Frame! Size: %d\n", this->jpeg->dims[0].size);

/* signal fresh_frame to output plugin and unlock mutex */
for (int i=0; i<config.server_maxconn; i++) {
pthread_cond_signal(&(this->cond[i]));
}

done:
av_packet_free(&pkt);
pthread_mutex_unlock(&this->mutex);

/* We must enter the loop and exit with the mutex locked */
Expand Down
2 changes: 1 addition & 1 deletion ffmpegServerApp/src/ffmpegServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ffmpegStream : public NDPluginDriver {
NDArray *jpeg;
int nclients;

AVCodec *codec;
const AVCodec *codec;
AVCodecContext *c;
AVFrame *inPicture;
AVFrame *scPicture;
Expand Down
Loading