uint8_t externalEncoder::configure(AVDMGenericVideoStream *instream, int useExistingLogFile) { ADV_Info *info; info = instream->getInfo(); _w = info->width; _h = info->height; _vbuffer = new ADMImage (_w, _h); ADM_assert(_vbuffer); _in = instream; _useExistingLogFile = useExistingLogFile; vidEncVideoProperties properties; memset(&properties, 0, sizeof(vidEncVideoProperties)); properties.structSize = sizeof(vidEncVideoProperties); properties.width = _w; properties.height = _h; properties.parWidth = instream->getPARWidth(); properties.parHeight = instream->getPARHeight(); properties.frameCount = info->nb_frames; properties.fpsNum = info->fps1000; properties.fpsDen = 1000; if (_globalHeader) properties.flags |= ADM_VIDENC_FLAG_GLOBAL_HEADER; if (_plugin->open(_plugin->encoderId, &properties)) { int64_t pixFmtMask = 0; for (int i = 0; i < properties.supportedCspsCount; i++) pixFmtMask |= (1 << getAvCodecColourspace(properties.supportedCsps[i])); _pixFmt = avcodec_find_best_pix_fmt(pixFmtMask, PIX_FMT_YUV420P, 0, NULL); if (_pixFmt != PIX_FMT_YUV420P) { AVPicture resamplePicture; _swsContext = sws_getContext( properties.width, properties.height, PIX_FMT_YUV420P, properties.width, properties.height, _pixFmt, SWS_SPLINE, NULL, NULL, NULL); _resampleSize = avpicture_fill(&resamplePicture, NULL, _pixFmt, properties.width, properties.height); _resampleBuffer = new uint8_t[_resampleSize]; } printf("[externalEncoder] Target colourspace: %s\n", _pixFmt == PIX_FMT_YUV420P ? "yv12" : avcodec_get_pix_fmt_name(_pixFmt)); return (startPass() == ADM_VIDENC_ERR_SUCCESS); } else return 0; }
MediaRet MediaRecorder::setup_video_stream(const char *fname, int w, int h, int d) { AVCodecContext *ctx; vid_st = av_new_stream(oc, 0); if(!vid_st) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } ctx = vid_st->codec; ctx->codec_id = oc->oformat->video_codec; ctx->codec_type = AVMEDIA_TYPE_VIDEO; ctx->width = w; ctx->height = h; ctx->time_base.den = 60; ctx->time_base.num = 1; // dunno if any of these help; some output just looks plain crappy // will have to investigate further ctx->bit_rate = 400000; ctx->gop_size = 12; ctx->max_b_frames = 2; switch(d) { case 16: // FIXME: test & make endian-neutral pixfmt = PIX_FMT_RGB565LE; break; case 24: pixfmt = PIX_FMT_RGB24; break; case 32: default: // should never be anything else pixfmt = PIX_FMT_RGBA; break; } ctx->pix_fmt = pixfmt; pixsize = d >> 3; linesize = pixsize * w; ctx->max_b_frames = 2; if(oc->oformat->flags & AVFMT_GLOBALHEADER) ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; AVCodec *codec = avcodec_find_encoder(oc->oformat->video_codec); // make sure RGB is supported (mostly not) if(codec->pix_fmts) { const enum PixelFormat *p; int64_t mask = 0; for(p = codec->pix_fmts; *p != -1; p++) { // may get complaints about 1LL; thus the cast mask |= ((int64_t)1) << *p; if(*p == pixfmt) break; } if(*p == -1) { // if not supported, use a converter to the next best format // this is swscale, the converter used by the output demo enum PixelFormat dp = (PixelFormat)avcodec_find_best_pix_fmt(mask, pixfmt, 0, NULL); if(dp == -1) dp = codec->pix_fmts[0]; if(!(convpic = avcodec_alloc_frame()) || avpicture_alloc((AVPicture *)convpic, dp, w, h) < 0) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } #if LIBSWSCALE_VERSION_INT < AV_VERSION_INT(0, 12, 0) converter = sws_getContext(w, h, pixfmt, w, h, dp, SWS_BICUBIC, NULL, NULL, NULL); #else converter = sws_alloc_context(); // what a convoluted, inefficient way to set options av_set_int(converter, "sws_flags", SWS_BICUBIC); av_set_int(converter, "srcw", w); av_set_int(converter, "srch", h); av_set_int(converter, "dstw", w); av_set_int(converter, "dsth", h); av_set_int(converter, "src_format", pixfmt); av_set_int(converter, "dst_format", dp); sws_init_context(converter, NULL, NULL); #endif ctx->pix_fmt = dp; } } if(!codec || avcodec_open(ctx, codec)) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOCODEC; } return MRET_OK; }
MediaRet MediaRecorder::setup_sound_stream(const char *fname, AVOutputFormat *fmt) { oc = avformat_alloc_context(); if(!oc) return MRET_ERR_NOMEM; oc->oformat = fmt; strncpy(oc->filename, fname, sizeof(oc->filename) - 1); oc->filename[sizeof(oc->filename) - 1] = 0; if(fmt->audio_codec == CODEC_ID_NONE) return MRET_OK; AVCodecContext *ctx; #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,10,0) aud_st = av_new_stream(oc, 1); #else aud_st = avformat_new_stream(oc, NULL); #endif if(!aud_st) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } AVCodec *codec = avcodec_find_encoder(fmt->audio_codec); ctx = aud_st->codec; ctx->codec_id = fmt->audio_codec; ctx->codec_type = AVMEDIA_TYPE_AUDIO; // Some encoders don't like int16_t (SAMPLE_FMT_S16) ctx->sample_fmt = codec->sample_fmts[0]; // This was changed in the initial ffmpeg 3.0 update, // but shouldn't (as far as I'm aware) cause problems with older versions ctx->bit_rate = 128000; // arbitrary; in case we're generating mp3 ctx->sample_rate = soundGetSampleRate(); ctx->channels = 2; ctx->time_base.den = 60; ctx->time_base.num = 1; if(fmt->flags & AVFMT_GLOBALHEADER) ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,6,0) if(!codec || avcodec_open(ctx, codec)) { #else if(!codec || avcodec_open2(ctx, codec, NULL)) { #endif avformat_free_context(oc); oc = NULL; return MRET_ERR_NOCODEC; } return MRET_OK; } MediaRet MediaRecorder::setup_video_stream(const char *fname, int w, int h, int d) { AVCodecContext *ctx; #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,10,0) vid_st = av_new_stream(oc, 0); #else vid_st = avformat_new_stream(oc, NULL); #endif if(!vid_st) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } ctx = vid_st->codec; ctx->codec_id = oc->oformat->video_codec; ctx->codec_type = AVMEDIA_TYPE_VIDEO; ctx->width = w; ctx->height = h; ctx->time_base.den = 60; ctx->time_base.num = 1; // dunno if any of these help; some output just looks plain crappy // will have to investigate further ctx->bit_rate = 400000; ctx->gop_size = 12; ctx->max_b_frames = 2; switch(d) { case 16: // FIXME: test & make endian-neutral pixfmt = PIX_FMT_RGB565LE; break; case 24: pixfmt = PIX_FMT_RGB24; break; case 32: default: // should never be anything else pixfmt = PIX_FMT_RGBA; break; } ctx->pix_fmt = pixfmt; pixsize = d >> 3; linesize = pixsize * w; ctx->max_b_frames = 2; if(oc->oformat->flags & AVFMT_GLOBALHEADER) ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; AVCodec *codec = avcodec_find_encoder(oc->oformat->video_codec); // make sure RGB is supported (mostly not) if(codec->pix_fmts) { const enum PixelFormat *p; #if LIBAVCODEC_VERSION_MAJOR < 55 int64_t mask = 0; #endif for(p = codec->pix_fmts; *p != -1; p++) { // may get complaints about 1LL; thus the cast #if LIBAVCODEC_VERSION_MAJOR < 55 mask |= ((int64_t)1) << *p; #endif if(*p == pixfmt) break; } if(*p == -1) { // if not supported, use a converter to the next best format // this is swscale, the converter used by the output demo #if LIBAVCODEC_VERSION_MAJOR < 55 enum PixelFormat dp = (PixelFormat)avcodec_find_best_pix_fmt(mask, pixfmt, 0, NULL); #else #if LIBAVCODEC_VERSION_MICRO >= 100 // FFmpeg enum AVPixelFormat dp = avcodec_find_best_pix_fmt_of_list(codec->pix_fmts, pixfmt, 0, NULL); #else // Libav enum AVPixelFormat dp = avcodec_find_best_pix_fmt2(codec->pix_fmts, pixfmt, 0, NULL); #endif #endif if(dp == -1) dp = codec->pix_fmts[0]; if(!(convpic = avcodec_alloc_frame()) || avpicture_alloc((AVPicture *)convpic, dp, w, h) < 0) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } #if LIBSWSCALE_VERSION_INT < AV_VERSION_INT(0, 12, 0) converter = sws_getContext(w, h, pixfmt, w, h, dp, SWS_BICUBIC, NULL, NULL, NULL); #else converter = sws_alloc_context(); // what a convoluted, inefficient way to set options av_opt_set_int(converter, "sws_flags", SWS_BICUBIC, 0); av_opt_set_int(converter, "srcw", w, 0); av_opt_set_int(converter, "srch", h, 0); av_opt_set_int(converter, "dstw", w, 0); av_opt_set_int(converter, "dsth", h, 0); av_opt_set_int(converter, "src_format", pixfmt, 0); av_opt_set_int(converter, "dst_format", dp, 0); sws_init_context(converter, NULL, NULL); #endif ctx->pix_fmt = dp; } } #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,6,0) if(!codec || avcodec_open(ctx, codec)) { #else if(!codec || avcodec_open2(ctx, codec, NULL)) { #endif avformat_free_context(oc); oc = NULL; return MRET_ERR_NOCODEC; } return MRET_OK; } MediaRet MediaRecorder::finish_setup(const char *fname) { if(audio_buf) free(audio_buf); if(audio_buf2) free(audio_buf2); audio_buf2 = NULL; in_audio_buf2 = 0; if(aud_st) { frame_len = aud_st->codec->frame_size * 4; sample_len = soundGetSampleRate() * 4 / 60; switch(aud_st->codec->codec_id) { case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: case CODEC_ID_PCM_U16BE: frame_len = sample_len; } audio_buf = (uint8_t *)malloc(AUDIO_BUF_LEN); if(!audio_buf) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } if(frame_len != sample_len && (frame_len > sample_len || sample_len % frame_len)) { audio_buf2 = (uint16_t *)malloc(frame_len); if(!audio_buf2) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } } } else audio_buf = NULL; if(video_buf) free(video_buf); if(vid_st) { video_buf = (uint8_t *)malloc(VIDEO_BUF_LEN); if(!video_buf) { avformat_free_context(oc); oc = NULL; return MRET_ERR_NOMEM; } } else { video_buf = NULL; } if(!(oc->oformat->flags & AVFMT_NOFILE)) { if(avio_open(&oc->pb, fname, AVIO_FLAG_WRITE) < 0) { avformat_free_context(oc); oc = NULL; return MRET_ERR_FERR; } } avformat_write_header(oc, NULL); return MRET_OK; } MediaRet MediaRecorder::Record(const char *fname, int width, int height, int depth) { if(oc) return MRET_ERR_RECORDING; aud_st = vid_st = NULL; AVOutputFormat *fmt = av_guess_format(NULL, fname, NULL); if(!fmt) fmt = av_guess_format("avi", NULL, NULL); if(!fmt || fmt->video_codec == CODEC_ID_NONE) return MRET_ERR_FMTGUESS; MediaRet ret; if((ret = setup_sound_stream(fname, fmt)) == MRET_OK && (ret = setup_video_stream(fname, width, height, depth)) == MRET_OK) ret = finish_setup(fname); return ret; } MediaRet MediaRecorder::Record(const char *fname) { if(oc) return MRET_ERR_RECORDING; aud_st = vid_st = NULL; AVOutputFormat *fmt = av_guess_format(NULL, fname, NULL); if(!fmt) fmt = av_guess_format("wav", NULL, NULL); if(!fmt || fmt->audio_codec == CODEC_ID_NONE) return MRET_ERR_FMTGUESS; MediaRet ret; if((ret = setup_sound_stream(fname, fmt)) == MRET_OK) ret = finish_setup(fname); return ret; } void MediaRecorder::Stop() { if(oc) { if(in_audio_buf2) AddFrame((uint16_t *)0); av_write_trailer(oc); avformat_free_context(oc); oc = NULL; } if(audio_buf) { free(audio_buf); audio_buf = NULL; } if(video_buf) { free(video_buf); video_buf = NULL; } if(audio_buf2) { free(audio_buf2); audio_buf2 = NULL; } if(convpic) { avpicture_free((AVPicture *)convpic); av_free(convpic); convpic = NULL; } if(converter) { sws_freeContext(converter); converter = NULL; } } MediaRecorder::~MediaRecorder() { Stop(); } // Still needs updating for avcodec_encode_video2 MediaRet MediaRecorder::AddFrame(const uint8_t *vid) { if(!oc || !vid_st) return MRET_OK; AVCodecContext *ctx = vid_st->codec; AVPacket pkt; #if LIBAVCODEC_VERSION_MAJOR > 56 int ret, got_packet = 0; #endif // strip borders. inconsistent between depths for some reason // but fortunately consistent between gb/gba. int tbord, rbord; switch(pixsize) { case 2: // 16-bit: 2 @ right, 1 @ top tbord = 1; rbord = 2; break; case 3: // 24-bit: no border tbord = rbord = 0; break; case 4: // 32-bit: 1 @ right, 1 @ top tbord = 1; rbord = 1; break; } avpicture_fill((AVPicture *)pic, (uint8_t *)vid + tbord * (linesize + pixsize * rbord), (PixelFormat)pixfmt, ctx->width + rbord, ctx->height); // satisfy stupid sws_scale()'s integrity check pic->data[1] = pic->data[2] = pic->data[3] = pic->data[0]; pic->linesize[1] = pic->linesize[2] = pic->linesize[3] = pic->linesize[0]; AVFrame *f = pic; if(converter) { sws_scale(converter, pic->data, pic->linesize, 0, ctx->height, convpic->data, convpic->linesize); f = convpic; } av_init_packet(&pkt); pkt.stream_index = vid_st->index; if(oc->oformat->flags & AVFMT_RAWPICTURE) { // this won't work due to border // not sure what formats set this, anyway pkt.flags |= AV_PKT_FLAG_KEY; pkt.data = f->data[0]; pkt.size = linesize * ctx->height; } else { #if LIBAVCODEC_VERSION_MAJOR > 56 pkt.data = video_buf; pkt.size = VIDEO_BUF_LEN; f->format = ctx->pix_fmt; f->width = ctx->width; f->height = ctx->height; ret = avcodec_encode_video2(ctx, &pkt, f, &got_packet); if(!ret && got_packet && ctx->coded_frame) { ctx->coded_frame->pts = pkt.pts; ctx->coded_frame->key_frame = !!(pkt.flags & AV_PKT_FLAG_KEY); } #else pkt.size = avcodec_encode_video(ctx, video_buf, VIDEO_BUF_LEN, f); #endif if(!pkt.size) return MRET_OK; if(ctx->coded_frame && ctx->coded_frame->pts != AV_NOPTS_VALUE) pkt.pts = av_rescale_q(ctx->coded_frame->pts, ctx->time_base, vid_st->time_base); if(pkt.size > VIDEO_BUF_LEN) { avformat_free_context(oc); oc = NULL; return MRET_ERR_BUFSIZE; } if(ctx->coded_frame->key_frame) pkt.flags |= AV_PKT_FLAG_KEY; pkt.data = video_buf; } if(av_interleaved_write_frame(oc, &pkt) < 0) { avformat_free_context(oc); oc = NULL; // yeah, err might not be a file error, but if it isn't, it's a // coding error rather than a user-controllable error // and better resolved using debugging return MRET_ERR_FERR; } return MRET_OK; } #if LIBAVCODEC_VERSION_MAJOR > 56 /* FFmpeg depricated avcodec_encode_audio. * It was removed completely in 3.0. * This will at least get audio recording *working* */ static inline int MediaRecorderEncodeAudio(AVCodecContext *ctx, AVPacket *pkt, uint8_t *buf, int buf_size, const short *samples) { AVFrame *frame; av_init_packet(pkt); int ret, samples_size, got_packet = 0; pkt->data = buf; pkt->size = buf_size; if (samples) { frame = frame = av_frame_alloc(); if (ctx->frame_size) { frame->nb_samples = ctx->frame_size; } else { frame->nb_samples = (int64_t)buf_size * 8 / (av_get_bits_per_sample(ctx->codec_id) * ctx->channels); } frame->format = ctx->sample_fmt; frame->channel_layout = ctx->channel_layout; samples_size = av_samples_get_buffer_size(NULL, ctx->channels, frame->nb_samples, ctx->sample_fmt, 1); avcodec_fill_audio_frame(frame, ctx->channels, ctx->sample_fmt, (const uint8_t *)samples, samples_size, 1); //frame->pts = AV_NOPTS_VALUE; } else { frame = NULL; } ret = avcodec_encode_audio2(ctx, pkt, frame, &got_packet); if (!ret && got_packet && ctx->coded_frame) { ctx->coded_frame->pts = pkt->pts; ctx->coded_frame->key_frame = !!(pkt->flags & AV_PKT_FLAG_KEY); } if (frame && frame->extended_data != frame->data) av_freep(&frame->extended_data); return ret; }
static int artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int format, struct evbuffer *evbuf) { uint8_t *buf; uint8_t *outbuf; AVCodecContext *src; AVFormatContext *dst_ctx; AVCodecContext *dst; AVOutputFormat *dst_fmt; AVStream *dst_st; AVCodec *img_decoder; AVCodec *img_encoder; int64_t pix_fmt_mask; const enum PixelFormat *pix_fmts; AVFrame *i_frame; AVFrame *o_frame; struct SwsContext *swsctx; AVPacket pkt; int have_frame; int outbuf_len; int ret; src = src_ctx->streams[s]->codec; img_decoder = avcodec_find_decoder(src->codec_id); if (!img_decoder) { DPRINTF(E_LOG, L_ART, "No suitable decoder found for artwork %s\n", src_ctx->filename); return -1; } #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 6) ret = avcodec_open2(src, img_decoder, NULL); #else ret = avcodec_open(src, img_decoder); #endif if (ret < 0) { DPRINTF(E_LOG, L_ART, "Could not open codec for decoding: %s\n", strerror(AVUNERROR(ret))); return -1; } /* Set up output */ #if LIBAVFORMAT_VERSION_MAJOR >= 53 || (LIBAVFORMAT_VERSION_MAJOR == 52 && LIBAVFORMAT_VERSION_MINOR >= 45) /* FFmpeg 0.6 */ dst_fmt = av_guess_format("image2", NULL, NULL); #else dst_fmt = guess_format("image2", NULL, NULL); #endif if (!dst_fmt) { DPRINTF(E_LOG, L_ART, "ffmpeg image2 muxer not available\n"); ret = -1; goto out_close_src; } dst_fmt->video_codec = CODEC_ID_NONE; /* Try to keep same codec if possible */ if ((src->codec_id == CODEC_ID_PNG) && (format & ART_CAN_PNG)) dst_fmt->video_codec = CODEC_ID_PNG; else if ((src->codec_id == CODEC_ID_MJPEG) && (format & ART_CAN_JPEG)) dst_fmt->video_codec = CODEC_ID_MJPEG; /* If not possible, select new codec */ if (dst_fmt->video_codec == CODEC_ID_NONE) { if (format & ART_CAN_PNG) dst_fmt->video_codec = CODEC_ID_PNG; else if (format & ART_CAN_JPEG) dst_fmt->video_codec = CODEC_ID_MJPEG; } img_encoder = avcodec_find_encoder(dst_fmt->video_codec); if (!img_encoder) { DPRINTF(E_LOG, L_ART, "No suitable encoder found for codec ID %d\n", dst_fmt->video_codec); ret = -1; goto out_close_src; } dst_ctx = avformat_alloc_context(); if (!dst_ctx) { DPRINTF(E_LOG, L_ART, "Out of memory for format context\n"); ret = -1; goto out_close_src; } dst_ctx->oformat = dst_fmt; #if LIBAVFORMAT_VERSION_MAJOR >= 53 dst_fmt->flags &= ~AVFMT_NOFILE; #else ret = snprintf(dst_ctx->filename, sizeof(dst_ctx->filename), "evbuffer:%p", evbuf); if ((ret < 0) || (ret >= sizeof(dst_ctx->filename))) { DPRINTF(E_LOG, L_ART, "Output artwork URL too long\n"); ret = -1; goto out_free_dst_ctx; } #endif #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21) dst_st = avformat_new_stream(dst_ctx, NULL); #else dst_st = av_new_stream(dst_ctx, 0); #endif if (!dst_st) { DPRINTF(E_LOG, L_ART, "Out of memory for new output stream\n"); ret = -1; goto out_free_dst_ctx; } dst = dst_st->codec; #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) avcodec_get_context_defaults3(dst, NULL); #else avcodec_get_context_defaults2(dst, AVMEDIA_TYPE_VIDEO); #endif if (dst_fmt->flags & AVFMT_GLOBALHEADER) dst->flags |= CODEC_FLAG_GLOBAL_HEADER; dst->codec_id = dst_fmt->video_codec; #if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) dst->codec_type = AVMEDIA_TYPE_VIDEO; #else dst->codec_type = CODEC_TYPE_VIDEO; #endif pix_fmt_mask = 0; pix_fmts = img_encoder->pix_fmts; while (pix_fmts && (*pix_fmts != -1)) { pix_fmt_mask |= (1 << *pix_fmts); pix_fmts++; } dst->pix_fmt = avcodec_find_best_pix_fmt(pix_fmt_mask, src->pix_fmt, 1, NULL); if (dst->pix_fmt < 0) { DPRINTF(E_LOG, L_ART, "Could not determine best pixel format\n"); ret = -1; goto out_free_dst_ctx; } DPRINTF(E_DBG, L_ART, "Selected pixel format: %d\n", dst->pix_fmt); dst->time_base.num = 1; dst->time_base.den = 25; dst->width = out_w; dst->height = out_h; #if LIBAVFORMAT_VERSION_MAJOR <= 52 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR <= 1) ret = av_set_parameters(dst_ctx, NULL); if (ret < 0) { DPRINTF(E_LOG, L_ART, "Invalid parameters for artwork output: %s\n", strerror(AVUNERROR(ret))); ret = -1; goto out_free_dst_ctx; } #endif /* Open encoder */ #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 6) ret = avcodec_open2(dst, img_encoder, NULL); #else ret = avcodec_open(dst, img_encoder); #endif if (ret < 0) { DPRINTF(E_LOG, L_ART, "Could not open codec for encoding: %s\n", strerror(AVUNERROR(ret))); ret = -1; goto out_free_dst_ctx; } i_frame = avcodec_alloc_frame(); o_frame = avcodec_alloc_frame(); if (!i_frame || !o_frame) { DPRINTF(E_LOG, L_ART, "Could not allocate input/output frame\n"); ret = -1; goto out_free_frames; } ret = avpicture_get_size(dst->pix_fmt, src->width, src->height); DPRINTF(E_DBG, L_ART, "Artwork buffer size: %d\n", ret); buf = (uint8_t *)av_malloc(ret); if (!buf) { DPRINTF(E_LOG, L_ART, "Out of memory for artwork buffer\n"); ret = -1; goto out_free_frames; } avpicture_fill((AVPicture *)o_frame, buf, dst->pix_fmt, src->width, src->height); swsctx = sws_getContext(src->width, src->height, src->pix_fmt, dst->width, dst->height, dst->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); if (!swsctx) { DPRINTF(E_LOG, L_ART, "Could not get SWS context\n"); ret = -1; goto out_free_buf; } /* Get frame */ have_frame = 0; while (av_read_frame(src_ctx, &pkt) == 0) { if (pkt.stream_index != s) { av_free_packet(&pkt); continue; } #if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 32) /* FFmpeg 0.6 */ avcodec_decode_video2(src, i_frame, &have_frame, &pkt); #else avcodec_decode_video(src, i_frame, &have_frame, pkt.data, pkt.size); #endif break; } if (!have_frame) { DPRINTF(E_LOG, L_ART, "Could not decode artwork\n"); av_free_packet(&pkt); sws_freeContext(swsctx); ret = -1; goto out_free_buf; } /* Scale */ #if LIBSWSCALE_VERSION_MAJOR >= 1 || (LIBSWSCALE_VERSION_MAJOR == 0 && LIBSWSCALE_VERSION_MINOR >= 9) /* FFmpeg 0.6, libav 0.6+ */ sws_scale(swsctx, (const uint8_t * const *)i_frame->data, i_frame->linesize, 0, src->height, o_frame->data, o_frame->linesize); #else sws_scale(swsctx, i_frame->data, i_frame->linesize, 0, src->height, o_frame->data, o_frame->linesize); #endif sws_freeContext(swsctx); av_free_packet(&pkt); /* Open output file */ #if LIBAVFORMAT_VERSION_MAJOR >= 53 dst_ctx->pb = avio_evbuffer_open(evbuf); #else ret = url_fopen(&dst_ctx->pb, dst_ctx->filename, URL_WRONLY); #endif if (ret < 0) { DPRINTF(E_LOG, L_ART, "Could not open artwork destination buffer\n"); ret = -1; goto out_free_buf; } /* Encode frame */ outbuf_len = dst->width * dst->height * 3; if (outbuf_len < FF_MIN_BUFFER_SIZE) outbuf_len = FF_MIN_BUFFER_SIZE; outbuf = (uint8_t *)av_malloc(outbuf_len); if (!outbuf) { DPRINTF(E_LOG, L_ART, "Out of memory for encoded artwork buffer\n"); #if LIBAVFORMAT_VERSION_MAJOR >= 53 avio_evbuffer_close(dst_ctx->pb); #else url_fclose(dst_ctx->pb); #endif ret = -1; goto out_free_buf; } #if LIBAVCODEC_VERSION_MAJOR >= 54 av_init_packet(&pkt); pkt.data = outbuf; pkt.size = outbuf_len; ret = avcodec_encode_video2(dst, &pkt, o_frame, &have_frame); if (!ret && have_frame && dst->coded_frame) { dst->coded_frame->pts = pkt.pts; dst->coded_frame->key_frame = !!(pkt.flags & AV_PKT_FLAG_KEY); } else if (ret < 0) { DPRINTF(E_LOG, L_ART, "Could not encode artwork\n"); ret = -1; goto out_fclose_dst; } #else ret = avcodec_encode_video(dst, outbuf, outbuf_len, o_frame); if (ret <= 0) { DPRINTF(E_LOG, L_ART, "Could not encode artwork\n"); ret = -1; goto out_fclose_dst; } av_init_packet(&pkt); pkt.stream_index = 0; pkt.data = outbuf; pkt.size = ret; #endif #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) ret = avformat_write_header(dst_ctx, NULL); #else ret = av_write_header(dst_ctx); #endif if (ret != 0) { DPRINTF(E_LOG, L_ART, "Could not write artwork header: %s\n", strerror(AVUNERROR(ret))); ret = -1; goto out_fclose_dst; } ret = av_interleaved_write_frame(dst_ctx, &pkt); if (ret != 0) { DPRINTF(E_LOG, L_ART, "Error writing artwork\n"); ret = -1; goto out_fclose_dst; } ret = av_write_trailer(dst_ctx); if (ret != 0) { DPRINTF(E_LOG, L_ART, "Could not write artwork trailer: %s\n", strerror(AVUNERROR(ret))); ret = -1; goto out_fclose_dst; } switch (dst_fmt->video_codec) { case CODEC_ID_PNG: ret = ART_FMT_PNG; break; case CODEC_ID_MJPEG: ret = ART_FMT_JPEG; break; default: DPRINTF(E_LOG, L_ART, "Unhandled rescale output format\n"); ret = -1; break; } out_fclose_dst: #if LIBAVFORMAT_VERSION_MAJOR >= 53 avio_evbuffer_close(dst_ctx->pb); #else url_fclose(dst_ctx->pb); #endif av_free(outbuf); out_free_buf: av_free(buf); out_free_frames: if (i_frame) av_free(i_frame); if (o_frame) av_free(o_frame); avcodec_close(dst); out_free_dst_ctx: avformat_free_context(dst_ctx); out_close_src: avcodec_close(src); return ret; }