/* Encodes and writes a video frame using the av_write_frame API. This is * a helper function for ffmpeg_put_image and ffmpeg_put_other_image. */ void ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic) { int out_size, ret; #ifdef FFMPEG_AVWRITEFRAME_NEWAPI AVPacket pkt; av_init_packet(&pkt); /* init static structure */ pkt.stream_index = ffmpeg->video_st->index; #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { /* raw video case. The API will change slightly in the near future for that */ #ifdef FFMPEG_AVWRITEFRAME_NEWAPI pkt.flags |= PKT_FLAG_KEY; pkt.data = (uint8_t *)pic; pkt.size = sizeof(AVPicture); ret = av_write_frame(ffmpeg->oc, &pkt); #else ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, (uint8_t *)pic, sizeof(AVPicture)); #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ } else { /* encode the image */ out_size = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), ffmpeg->video_outbuf, ffmpeg->video_outbuf_size, pic); /* if zero size, it means the image was buffered */ if (out_size != 0) { /* write the compressed frame in the media file */ /* XXX: in case of B frames, the pts is not yet valid */ #ifdef FFMPEG_AVWRITEFRAME_NEWAPI pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) { pkt.flags |= PKT_FLAG_KEY; } pkt.data = ffmpeg->video_outbuf; pkt.size = out_size; ret = av_write_frame(ffmpeg->oc, &pkt); #else ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, ffmpeg->video_outbuf, out_size); #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ } else { ret = 0; } } if (ret != 0) { motion_log(LOG_ERR, 1, "Error while writing video frame"); return; } }
/* Clean up ffmpeg struct if something was wrong */ void ffmpeg_cleanups(struct ffmpeg *ffmpeg) { unsigned int i; /* close each codec */ if (ffmpeg->video_st) { pthread_mutex_lock(&global_lock); #if LIBAVCODEC_BUILD > 4680 if (ffmpeg->video_st->codec->priv_data != NULL) #endif avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); pthread_mutex_unlock(&global_lock); av_freep(&ffmpeg->picture); free(ffmpeg->video_outbuf); } /* free the streams */ for (i = 0; i < ffmpeg->oc->nb_streams; i++) av_freep(&ffmpeg->oc->streams[i]); /* if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { // close the output file if (ffmpeg->oc->pb) url_fclose(&ffmpeg->oc->pb); } */ /* free the stream */ av_free(ffmpeg->oc); #if LIBAVFORMAT_BUILD >= 4629 av_free(ffmpeg->c); #endif free(ffmpeg); }
/** * ffmpeg_cleanups * Clean up ffmpeg struct if something was wrong. * * Returns * Function returns nothing. */ void ffmpeg_cleanups(struct ffmpeg *ffmpeg) { /* Close each codec */ if (ffmpeg->video_st) { pthread_mutex_lock(&global_lock); avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); pthread_mutex_unlock(&global_lock); } free(ffmpeg->video_outbuf); av_freep(&ffmpeg->picture); avformat_free_context(ffmpeg->oc); free(ffmpeg); }
/** * ffmpeg_close * Closes a video file. * * Returns * Function returns nothing. */ void ffmpeg_close(struct ffmpeg *ffmpeg) { if (ffmpeg->tlapse != TIMELAPSE_APPEND) { av_write_trailer(ffmpeg->oc); } /* Close each codec */ if (ffmpeg->video_st) { pthread_mutex_lock(&global_lock); avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); pthread_mutex_unlock(&global_lock); } av_freep(&ffmpeg->picture); free(ffmpeg->video_outbuf); if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { if (ffmpeg->tlapse != TIMELAPSE_APPEND) { avio_close(ffmpeg->oc->pb); } } avformat_free_context(ffmpeg->oc); free(ffmpeg); }
/* Closes a video file. */ void ffmpeg_close(struct ffmpeg *ffmpeg) { unsigned int i; /* close each codec */ if (ffmpeg->video_st) { pthread_mutex_lock(&global_lock); avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); pthread_mutex_unlock(&global_lock); av_freep(&ffmpeg->picture); free(ffmpeg->video_outbuf); } /* write the trailer, if any */ av_write_trailer(ffmpeg->oc); /* free the streams */ for (i = 0; i < ffmpeg->oc->nb_streams; i++) av_freep(&ffmpeg->oc->streams[i]); if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { /* close the output file */ #if LIBAVFORMAT_BUILD >= (52<<16) url_fclose(ffmpeg->oc->pb); #else url_fclose(&ffmpeg->oc->pb); #endif /* LIBAVFORMAT_BUILD >= (52<<16) */ } /* free the stream */ av_free(ffmpeg->oc); #if LIBAVFORMAT_BUILD >= 4629 av_free(ffmpeg->c); #endif free(ffmpeg); }
/** * ffmpeg_open * Opens an mpeg file using the new libavformat method. Both mpeg1 * and mpeg4 are supported. However, if the current ffmpeg version doesn't allow * mpeg1 with non-standard framerate, the open will fail. Timelapse is a special * case and is tested separately. * * Returns * A new allocated ffmpeg struct or NULL if any error happens. */ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, unsigned char *y, unsigned char *u, unsigned char *v, int width, int height, int rate, int bps, int vbr) { AVCodecContext *c; AVCodec *codec; struct ffmpeg *ffmpeg; int is_mpeg1; int ret; /* * Allocate space for our ffmpeg structure. This structure contains all the * codec and image information we need to generate movies. * FIXME when motion exits we should close the movie to ensure that * ffmpeg is freed. */ ffmpeg = mymalloc(sizeof(struct ffmpeg)); memset(ffmpeg, 0, sizeof(struct ffmpeg)); ffmpeg->vbr = vbr; /* Store codec name in ffmpeg->codec, with buffer overflow check. */ snprintf(ffmpeg->codec, sizeof(ffmpeg->codec), "%s", ffmpeg_video_codec); /* Allocation the output media context. */ #if (LIBAVFORMAT_VERSION_MAJOR >= 53) ffmpeg->oc = avformat_alloc_context(); #else ffmpeg->oc = av_alloc_format_context(); #endif if (!ffmpeg->oc) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Memory error while allocating" " output media context"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Setup output format */ ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); if (!ffmpeg->oc->oformat) { ffmpeg_cleanups(ffmpeg); return NULL; } snprintf(ffmpeg->oc->filename, sizeof(ffmpeg->oc->filename), "%s", filename); /* Create a new video stream and initialize the codecs. */ ffmpeg->video_st = NULL; if (ffmpeg->oc->oformat->video_codec != AV_CODEC_ID_NONE) { ffmpeg->video_st = avformat_new_stream(ffmpeg->oc, NULL /* Codec */); if (!ffmpeg->video_st) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: av_new_stream - could" " not alloc stream"); ffmpeg_cleanups(ffmpeg); return NULL; } } else { /* We did not get a proper video codec. */ MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Failed to obtain a proper" " video codec"); ffmpeg_cleanups(ffmpeg); return NULL; } ffmpeg->c = c = AVSTREAM_CODEC_PTR(ffmpeg->video_st); c->codec_id = ffmpeg->oc->oformat->video_codec; #if (LIBAVFORMAT_VERSION_MAJOR >= 53) c->codec_type = AVMEDIA_TYPE_VIDEO; #else c->codec_type = CODEC_TYPE_VIDEO; #endif is_mpeg1 = c->codec_id == AV_CODEC_ID_MPEG1VIDEO; if (strcmp(ffmpeg_video_codec, "ffv1") == 0) c->strict_std_compliance = -2; /* Uncomment to allow non-standard framerates. */ //c->strict_std_compliance = -1; /* Set default parameters */ c->bit_rate = bps; c->width = width; c->height = height; #if LIBAVCODEC_BUILD >= 4754 /* Frame rate = 1/time_base, so we set 1/rate, not rate/1 */ c->time_base.num = 1; c->time_base.den = rate; #else c->frame_rate = rate; c->frame_rate_base = 1; #endif /* LIBAVCODEC_BUILD >= 4754 */ MOTION_LOG(INF, TYPE_ENCODER, NO_ERRNO, "%s FPS %d", rate); if (vbr) c->flags |= CODEC_FLAG_QSCALE; /* * Set codec specific parameters. * Set intra frame distance in frames depending on codec. */ c->gop_size = is_mpeg1 ? 10 : 12; /* Some formats want stream headers to be separate. */ if (!strcmp(ffmpeg->oc->oformat->name, "mp4") || !strcmp(ffmpeg->oc->oformat->name, "mov") || !strcmp(ffmpeg->oc->oformat->name, "3gp")) { c->flags |= CODEC_FLAG_GLOBAL_HEADER; } /* Dump the format settings. This shows how the various streams relate to each other. */ //dump_format(ffmpeg->oc, 0, filename, 1); /* * Now that all the parameters are set, we can open the video * codec and allocate the necessary encode buffers. */ codec = avcodec_find_encoder(c->codec_id); if (!codec) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Codec %s not found", ffmpeg_video_codec); ffmpeg_cleanups(ffmpeg); return NULL; } /* Set the picture format - need in ffmpeg starting round April-May 2005 */ c->pix_fmt = PIX_FMT_YUV420P; /* Get a mutex lock. */ pthread_mutex_lock(&global_lock); /* Open the codec */ ret = avcodec_open2(c, codec, NULL /* options */ ); if (ret < 0) { /* Release the lock. */ pthread_mutex_unlock(&global_lock); MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: avcodec_open - could not open codec %s", ffmpeg_video_codec); ffmpeg_cleanups(ffmpeg); return NULL; } /* Release the lock. */ pthread_mutex_unlock(&global_lock); ffmpeg->video_outbuf = NULL; if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { /* * Allocate output buffer * XXX: API change will be done * ffmpeg->video_outbuf_size = 200000 */ ffmpeg->video_outbuf_size = ffmpeg->c->width * 512; ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); } /* Allocate the encoded raw picture. */ ffmpeg->picture = my_frame_alloc(); if (!ffmpeg->picture) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: avcodec_alloc_frame -" " could not alloc frame"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Set variable bitrate if requested. */ if (ffmpeg->vbr) ffmpeg->picture->quality = ffmpeg->vbr; /* Set the frame data. */ ffmpeg->picture->data[0] = y; ffmpeg->picture->data[1] = u; ffmpeg->picture->data[2] = v; ffmpeg->picture->linesize[0] = ffmpeg->c->width; ffmpeg->picture->linesize[1] = ffmpeg->c->width / 2; ffmpeg->picture->linesize[2] = ffmpeg->c->width / 2; /* Open the output file, if needed. */ if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { char file_proto[256]; /* * Use append file protocol for mpeg1, to get the append behavior from * url_fopen, but no protocol (=> default) for other codecs. */ if (is_mpeg1) #if defined FF_API_NEW_AVIO snprintf(file_proto, sizeof(file_proto), "%s", filename); #else snprintf(file_proto, sizeof(file_proto), APPEND_PROTO ":%s", filename); #endif else
/* This function opens an mpeg file using the new libavformat method. Both mpeg1 * and mpeg4 are supported. However, if the current ffmpeg version doesn't allow * mpeg1 with non-standard framerate, the open will fail. Timelapse is a special * case and is tested separately. */ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, unsigned char *y, unsigned char *u, unsigned char *v, int width, int height, int rate, int bps, int vbr) { AVCodecContext *c; AVCodec *codec; struct ffmpeg *ffmpeg; int is_mpeg1; /* Allocate space for our ffmpeg structure. This structure contains all the * codec and image information we need to generate movies. * FIXME when motion exits we should close the movie to ensure that * ffmpeg is freed. */ ffmpeg = mymalloc(sizeof(struct ffmpeg)); memset(ffmpeg, 0, sizeof(struct ffmpeg)); ffmpeg->vbr = vbr; /* store codec name in ffmpeg->codec, with buffer overflow check */ snprintf(ffmpeg->codec, sizeof(ffmpeg->codec), "%s", ffmpeg_video_codec); /* allocation the output media context */ ffmpeg->oc = av_mallocz(sizeof(AVFormatContext)); if (!ffmpeg->oc) { motion_log(LOG_ERR, 1, "Memory error while allocating output media context"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Setup output format */ ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); if (!ffmpeg->oc->oformat) { ffmpeg_cleanups(ffmpeg); return NULL; } snprintf(ffmpeg->oc->filename, sizeof(ffmpeg->oc->filename), "%s", filename); /* Create a new video stream and initialize the codecs */ ffmpeg->video_st = NULL; if (ffmpeg->oc->oformat->video_codec != CODEC_ID_NONE) { ffmpeg->video_st = av_new_stream(ffmpeg->oc, 0); if (!ffmpeg->video_st) { motion_log(LOG_ERR, 1, "av_new_stream - could not alloc stream"); ffmpeg_cleanups(ffmpeg); return NULL; } } else { /* We did not get a proper video codec. */ motion_log(LOG_ERR, 0, "Failed to obtain a proper video codec"); ffmpeg_cleanups(ffmpeg); return NULL; } ffmpeg->c = c = AVSTREAM_CODEC_PTR(ffmpeg->video_st); c->codec_id = ffmpeg->oc->oformat->video_codec; c->codec_type = CODEC_TYPE_VIDEO; is_mpeg1 = c->codec_id == CODEC_ID_MPEG1VIDEO; if (strcmp(ffmpeg_video_codec, "ffv1") == 0) c->strict_std_compliance = -2; /* Uncomment to allow non-standard framerates. */ //c->strict_std_compliance = -1; /* Set default parameters */ c->bit_rate = bps; c->width = width; c->height = height; #if LIBAVCODEC_BUILD >= 4754 /* frame rate = 1/time_base, so we set 1/rate, not rate/1 */ c->time_base.num = 1; c->time_base.den = rate; #else c->frame_rate = rate; c->frame_rate_base = 1; #endif /* LIBAVCODEC_BUILD >= 4754 */ if (debug_level >= CAMERA_DEBUG) motion_log(LOG_DEBUG, 0, "%s FPS %d",__FUNCTION__,rate); if (vbr) c->flags |= CODEC_FLAG_QSCALE; /* Set codec specific parameters. */ /* set intra frame distance in frames depending on codec */ c->gop_size = is_mpeg1 ? 10 : 12; /* some formats want stream headers to be separate */ if (!strcmp(ffmpeg->oc->oformat->name, "mp4") || !strcmp(ffmpeg->oc->oformat->name, "mov") || !strcmp(ffmpeg->oc->oformat->name, "3gp")) { c->flags |= CODEC_FLAG_GLOBAL_HEADER; } /* set the output parameters (must be done even if no parameters). */ if (av_set_parameters(ffmpeg->oc, NULL) < 0) { motion_log(LOG_ERR, 0, "ffmpeg av_set_parameters error: Invalid output format parameters"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Dump the format settings. This shows how the various streams relate to each other */ //dump_format(ffmpeg->oc, 0, filename, 1); /* Now that all the parameters are set, we can open the video codec and allocate the necessary encode buffers */ codec = avcodec_find_encoder(c->codec_id); if (!codec) { motion_log(LOG_ERR, 1, "Codec not found"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Set the picture format - need in ffmpeg starting round April-May 2005 */ c->pix_fmt = PIX_FMT_YUV420P; /* Get a mutex lock. */ pthread_mutex_lock(&global_lock); /* open the codec */ if (avcodec_open(c, codec) < 0) { /* Release the lock. */ pthread_mutex_unlock(&global_lock); motion_log(LOG_ERR, 1, "avcodec_open - could not open codec"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Release the lock. */ pthread_mutex_unlock(&global_lock); ffmpeg->video_outbuf = NULL; if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { /* allocate output buffer */ /* XXX: API change will be done */ /* ffmpeg->video_outbuf_size = 20000; */ ffmpeg->video_outbuf_size = ffmpeg->c->width * 256; ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); } /* allocate the encoded raw picture */ ffmpeg->picture = avcodec_alloc_frame(); if (!ffmpeg->picture) { motion_log(LOG_ERR, 1, "avcodec_alloc_frame - could not alloc frame"); ffmpeg_cleanups(ffmpeg); return NULL; } /* set variable bitrate if requested */ if (ffmpeg->vbr) ffmpeg->picture->quality = ffmpeg->vbr; /* set the frame data */ ffmpeg->picture->data[0] = y; ffmpeg->picture->data[1] = u; ffmpeg->picture->data[2] = v; ffmpeg->picture->linesize[0] = ffmpeg->c->width; ffmpeg->picture->linesize[1] = ffmpeg->c->width / 2; ffmpeg->picture->linesize[2] = ffmpeg->c->width / 2; /* open the output file, if needed */ if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { char file_proto[256]; /* Use append file protocol for mpeg1, to get the append behavior from * url_fopen, but no protocol (=> default) for other codecs. */ if (is_mpeg1) snprintf(file_proto, sizeof(file_proto), APPEND_PROTO ":%s", filename); else snprintf(file_proto, sizeof(file_proto), "%s", filename); if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { /* path did not exist? */ if (errno == ENOENT) { /* create path for file (don't use file_proto)... */ if (create_path(filename) == -1) { ffmpeg_cleanups(ffmpeg); return NULL; } /* and retry opening the file (use file_proto) */ if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { motion_log(LOG_ERR, 1, "url_fopen - error opening file %s",filename); ffmpeg_cleanups(ffmpeg); return NULL; } /* Permission denied */ } else if (errno == EACCES) { motion_log(LOG_ERR, 1, "url_fopen - error opening file %s" " ... check access rights to target directory", filename); ffmpeg_cleanups(ffmpeg); return NULL; } else { motion_log(LOG_ERR, 1, "Error opening file %s", filename); ffmpeg_cleanups(ffmpeg); return NULL; } } } /* write the stream header, if any */ av_write_header(ffmpeg->oc); return ffmpeg; }
/** * ffmpeg_put_frame * Encodes and writes a video frame using the av_write_frame API. This is * a helper function for ffmpeg_put_image and ffmpeg_put_other_image. * * Returns * Number of bytes written or -1 if any error happens. */ int ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic) { int out_size, ret; #ifdef FFMPEG_AVWRITEFRAME_NEWAPI AVPacket pkt; av_init_packet(&pkt); /* Init static structure. */ pkt.stream_index = ffmpeg->video_st->index; #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { /* Raw video case. The API will change slightly in the near future for that. */ #ifdef FFMPEG_AVWRITEFRAME_NEWAPI #if LIBAVCODEC_VERSION_MAJOR < 53 pkt.flags |= PKT_FLAG_KEY; #else pkt.flags |= AV_PKT_FLAG_KEY; #endif pkt.data = (uint8_t *)pic; pkt.size = sizeof(AVPicture); ret = av_write_frame(ffmpeg->oc, &pkt); #else ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, (uint8_t *)pic, sizeof(AVPicture)); #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ } else { /* Encodes the image. */ out_size = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), ffmpeg->video_outbuf, ffmpeg->video_outbuf_size, pic); /* If zero size, it means the image was buffered. */ if (out_size != 0) { /* * Writes the compressed frame in the media file. * XXX: in case of B frames, the pts is not yet valid. */ #ifdef FFMPEG_AVWRITEFRAME_NEWAPI pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) #if LIBAVCODEC_VERSION_MAJOR < 53 pkt.flags |= PKT_FLAG_KEY; #else pkt.flags |= AV_PKT_FLAG_KEY; #endif pkt.data = ffmpeg->video_outbuf; pkt.size = out_size; ret = av_write_frame(ffmpeg->oc, &pkt); #else ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, ffmpeg->video_outbuf, out_size); #endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ } else { ret = 0; } } if (ret != 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing" " video frame"); ffmpeg_cleanups(ffmpeg); return -1; } return ret; }
/** * ffmpeg_put_frame * Encodes and writes a video frame using the av_write_frame API. This is * a helper function for ffmpeg_put_image and ffmpeg_put_other_image. * * Returns * Number of bytes written or -1 if any error happens. */ int ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic) { /** * Since the logic,return values and conditions changed so * dramatically between versions, the encoding of the frame * is 100% blocked based upon Libav/FFMpeg version */ #if (LIBAVFORMAT_VERSION_MAJOR >= 55) || ((LIBAVFORMAT_VERSION_MAJOR == 54) && (LIBAVFORMAT_VERSION_MINOR > 6)) int retcd; int got_packet_ptr; AVPacket pkt; char errstr[128]; av_init_packet(&pkt); /* Init static structure. */ if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { pkt.stream_index = ffmpeg->video_st->index; pkt.flags |= AV_PKT_FLAG_KEY; pkt.data = (uint8_t *)pic; pkt.size = sizeof(AVPicture); } else { pkt.data = NULL; pkt.size = 0; retcd = avcodec_encode_video2(AVSTREAM_CODEC_PTR(ffmpeg->video_st), &pkt, pic, &got_packet_ptr); if (retcd < 0 ){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error encoding video:%s",errstr); //Packet is freed upon failure of encoding return -1; } if (got_packet_ptr == 0){ //Buffered packet. Throw special return code av_free_packet(&pkt); return -2; } if (pkt.pts != AV_NOPTS_VALUE) pkt.pts = av_rescale_q(pkt.pts, ffmpeg->video_st->codec->time_base, ffmpeg->video_st->time_base); if (pkt.dts != AV_NOPTS_VALUE) pkt.dts = av_rescale_q(pkt.dts, ffmpeg->video_st->codec->time_base, ffmpeg->video_st->time_base); } if (ffmpeg->tlapse == TIMELAPSE_APPEND) { retcd = timelapse_append(ffmpeg, pkt); } else { retcd = av_write_frame(ffmpeg->oc, &pkt); } av_free_packet(&pkt); if (retcd != 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing video frame"); ffmpeg_cleanups(ffmpeg); return -1; } return retcd; #else // Old versions of Libav/FFmpeg int retcd; AVPacket pkt; av_init_packet(&pkt); /* Init static structure. */ pkt.stream_index = ffmpeg->video_st->index; if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { // Raw video case. pkt.size = sizeof(AVPicture); pkt.data = (uint8_t *)pic; pkt.flags |= AV_PKT_FLAG_KEY; } else { retcd = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), ffmpeg->video_outbuf, ffmpeg->video_outbuf_size, pic); if (retcd < 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error encoding video"); av_free_packet(&pkt); return -1; } if (retcd == 0 ){ // No bytes encoded => buffered=>special handling av_free_packet(&pkt); return -2; } pkt.size = retcd; pkt.data = ffmpeg->video_outbuf; pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) pkt.flags |= AV_PKT_FLAG_KEY; } if (ffmpeg->tlapse == TIMELAPSE_APPEND) { retcd = timelapse_append(ffmpeg, pkt); } else { retcd = av_write_frame(ffmpeg->oc, &pkt); } if (retcd != 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing video frame"); ffmpeg_cleanups(ffmpeg); return -1; } return retcd; #endif }
/** * ffmpeg_open * Opens an mpeg file using the new libavformat method. Both mpeg1 * and mpeg4 are supported. However, if the current ffmpeg version doesn't allow * mpeg1 with non-standard framerate, the open will fail. Timelapse is a special * case and is tested separately. * * Returns * A new allocated ffmpeg struct or NULL if any error happens. */ struct ffmpeg *ffmpeg_open(const char *ffmpeg_video_codec, char *filename, unsigned char *y, unsigned char *u, unsigned char *v, int width, int height, int rate, int bps, int vbr, int tlapse) { AVCodecContext *c; AVCodec *codec; struct ffmpeg *ffmpeg; int ret; char errstr[128]; AVDictionary *opts = 0; /* * Allocate space for our ffmpeg structure. This structure contains all the * codec and image information we need to generate movies. */ ffmpeg = mymalloc(sizeof(struct ffmpeg)); memset(ffmpeg, 0, sizeof(struct ffmpeg)); ffmpeg->vbr = vbr; ffmpeg->tlapse = tlapse; /* Store codec name in ffmpeg->codec, with buffer overflow check. */ snprintf(ffmpeg->codec, sizeof(ffmpeg->codec), "%s", ffmpeg_video_codec); /* Allocation the output media context. */ ffmpeg->oc = avformat_alloc_context(); if (!ffmpeg->oc) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Could not allocate output context"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Setup output format */ if (ffmpeg->tlapse == TIMELAPSE_APPEND){ ffmpeg->oc->oformat = get_oformat("tlapse", filename); } else { ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); } if (!ffmpeg->oc->oformat) { ffmpeg_cleanups(ffmpeg); return NULL; } snprintf(ffmpeg->oc->filename, sizeof(ffmpeg->oc->filename), "%s", filename); /* Create a new video stream and initialize the codecs. */ ffmpeg->video_st = NULL; if (ffmpeg->oc->oformat->video_codec != MY_CODEC_ID_NONE) { codec = avcodec_find_encoder(ffmpeg->oc->oformat->video_codec); if (!codec) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Codec %s not found", ffmpeg_video_codec); ffmpeg_cleanups(ffmpeg); return NULL; } ffmpeg->video_st = avformat_new_stream(ffmpeg->oc, codec); if (!ffmpeg->video_st) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Could not alloc stream"); ffmpeg_cleanups(ffmpeg); return NULL; } } else { /* We did not get a proper video codec. */ MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not get the codec"); ffmpeg_cleanups(ffmpeg); return NULL; } ffmpeg->c = c = AVSTREAM_CODEC_PTR(ffmpeg->video_st); c->codec_id = ffmpeg->oc->oformat->video_codec; c->codec_type = AVMEDIA_TYPE_VIDEO; c->bit_rate = bps; c->width = width; c->height = height; c->time_base.num = 1; c->time_base.den = rate; c->gop_size = 12; c->pix_fmt = MY_PIX_FMT_YUV420P; c->max_b_frames = 0; if (c->codec_id == MY_CODEC_ID_H264 || c->codec_id == MY_CODEC_ID_HEVC){ av_dict_set(&opts, "preset", "ultrafast", 0); av_dict_set(&opts, "crf", "18", 0); av_dict_set(&opts, "tune", "zerolatency", 0); } if (strcmp(ffmpeg_video_codec, "ffv1") == 0) c->strict_std_compliance = -2; if (vbr) c->flags |= CODEC_FLAG_QSCALE; if (!strcmp(ffmpeg->oc->oformat->name, "mp4") || !strcmp(ffmpeg->oc->oformat->name, "mov") || !strcmp(ffmpeg->oc->oformat->name, "3gp")) { c->flags |= CODEC_FLAG_GLOBAL_HEADER; } /* Get a mutex lock. */ pthread_mutex_lock(&global_lock); ret = avcodec_open2(c, codec, &opts); pthread_mutex_unlock(&global_lock); if (ret < 0) { if (codec->supported_framerates) { const AVRational *fps = codec->supported_framerates; while (fps->num) { MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s Reported FPS Supported %d/%d", fps->num, fps->den); fps++; } } int chkrate = 1; pthread_mutex_lock(&global_lock); while ((chkrate < 36) && (ret != 0)) { c->time_base.den = chkrate; ret = avcodec_open2(c, codec, &opts); chkrate++; } pthread_mutex_unlock(&global_lock); if (ret < 0) { av_strerror(ret, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not open codec %s",errstr); av_dict_free(&opts); ffmpeg_cleanups(ffmpeg); return NULL; } } av_dict_free(&opts); MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s Selected Output FPS %d", c->time_base.den); ffmpeg->video_outbuf = NULL; if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { ffmpeg->video_outbuf_size = ffmpeg->c->width * 512; ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); } ffmpeg->picture = my_frame_alloc(); if (!ffmpeg->picture) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: could not alloc frame"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Set variable bitrate if requested. */ if (ffmpeg->vbr) ffmpeg->picture->quality = ffmpeg->vbr; /* Set the frame data. */ ffmpeg->picture->data[0] = y; ffmpeg->picture->data[1] = u; ffmpeg->picture->data[2] = v; ffmpeg->picture->linesize[0] = ffmpeg->c->width; ffmpeg->picture->linesize[1] = ffmpeg->c->width / 2; ffmpeg->picture->linesize[2] = ffmpeg->c->width / 2; /* Open the output file, if needed. */ if ((access(filename, W_OK) == 0) || (ffmpeg->tlapse != TIMELAPSE_APPEND)) { if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { if (avio_open(&ffmpeg->oc->pb, filename, MY_FLAG_WRITE) < 0) { if (errno == ENOENT) { if (create_path(filename) == -1) { ffmpeg_cleanups(ffmpeg); return NULL; } if (avio_open(&ffmpeg->oc->pb, filename, MY_FLAG_WRITE) < 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: error opening file %s", filename); ffmpeg_cleanups(ffmpeg); return NULL; } /* Permission denied */ } else if (errno == EACCES) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Permission denied. %s",filename); ffmpeg_cleanups(ffmpeg); return NULL; } else { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error opening file %s", filename); ffmpeg_cleanups(ffmpeg); return NULL; } } } /* Write the stream header, For the TIMELAPSE_APPEND * we write the data via standard file I/O so we close the * items here */ avformat_write_header(ffmpeg->oc, NULL); if (ffmpeg->tlapse == TIMELAPSE_APPEND) { av_write_trailer(ffmpeg->oc); avio_close(ffmpeg->oc->pb); } } return ffmpeg; }