Recherche avancée

Médias (0)

Mot : - Tags -/organisation

Aucun média correspondant à vos critères n’est disponible sur le site.

Autres articles (44)

  • Amélioration de la version de base

    13 septembre 2013

    Jolie sélection multiple
    Le plugin Chosen permet d’améliorer l’ergonomie des champs de sélection multiple. Voir les deux images suivantes pour comparer.
    Il suffit pour cela d’activer le plugin Chosen (Configuration générale du site > Gestion des plugins), puis de configurer le plugin (Les squelettes > Chosen) en activant l’utilisation de Chosen dans le site public et en spécifiant les éléments de formulaires à améliorer, par exemple select[multiple] pour les listes à sélection multiple (...)

  • Menus personnalisés

    14 novembre 2010, par

    MediaSPIP utilise le plugin Menus pour gérer plusieurs menus configurables pour la navigation.
    Cela permet de laisser aux administrateurs de canaux la possibilité de configurer finement ces menus.
    Menus créés à l’initialisation du site
    Par défaut trois menus sont créés automatiquement à l’initialisation du site : Le menu principal ; Identifiant : barrenav ; Ce menu s’insère en général en haut de la page après le bloc d’entête, son identifiant le rend compatible avec les squelettes basés sur Zpip ; (...)

  • Encodage et transformation en formats lisibles sur Internet

    10 avril 2011

    MediaSPIP transforme et ré-encode les documents mis en ligne afin de les rendre lisibles sur Internet et automatiquement utilisables sans intervention du créateur de contenu.
    Les vidéos sont automatiquement encodées dans les formats supportés par HTML5 : MP4, Ogv et WebM. La version "MP4" est également utilisée pour le lecteur flash de secours nécessaire aux anciens navigateurs.
    Les documents audios sont également ré-encodés dans les deux formats utilisables par HTML5 :MP3 et Ogg. La version "MP3" (...)

Sur d’autres sites (3799)

  • Downscaling a video from 1080p to 480p using swscale and encoding to x265 gives a glitched output

    5 mai 2023, par lokit khemka

    I am basically first scaling a frame and then sending the frame to the encoder as below :

    


    scaled_frame->pts = input_frame->pts;
scaled_frame->pkt_dts = input_frame->pkt_dts;
scaled_frame->pict_type = input_frame->pict_type;
sws_scale_frame(encoder->sws_ctx, scaled_frame, input_frame);
if (encode_video(decoder, encoder, scaled_frame))
     return -1;


    


    The scaling context is configured as :

    


    scaled_frame->width = 854;
scaled_frame->height=480; 
encoder->sws_ctx = sws_getContext(1920, 1080,
                            decoder->video_avcc->pix_fmt, 
                           scaled_frame->width, scaled_frame->height, decoder->video_avcc->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL );
    if (!encoder->sws_ctx){logging("Cannot Create Scaling Context."); return -1;}


    


    The encoder is configured as :

    


        encoder_sc->video_avcc->height = decoder_ctx->height; //1080
    encoder_sc->video_avcc->width = decoder_ctx->width; //1920
    encoder_sc->video_avcc->bit_rate = 2 * 1000 * 1000;
    encoder_sc->video_avcc->rc_buffer_size = 4 * 1000 * 1000;
    encoder_sc->video_avcc->rc_max_rate = 2 * 1000 * 1000;
    encoder_sc->video_avcc->rc_min_rate = 2.5 * 1000 * 1000;

    encoder_sc->video_avcc->time_base = av_inv_q(input_framerate);
    encoder_sc->video_avs->time_base = encoder_sc->video_avcc->time_base;


    


    When I get the output, the output video is 1080p and I have glitches like : enter image description here

    


    I changed the encoder avcc resolution to 480p (854 x 480). However, that is causing the video to get sliced to the top quarter of the original frame.
I am new to FFMPEG and video processing in general.

    


    EDIT : I am adding the minimal reproducible code sample. However, it is really long because I need to include code for decoding, scaling and then encoding because the possible error is either in scaling or encoding :

    


    #include <libavcodec></libavcodec>avcodec.h>&#xA;#include <libavformat></libavformat>avformat.h>&#xA;#include <libavutil></libavutil>timestamp.h>&#xA;#include <libavutil></libavutil>opt.h>&#xA;#include <libswscale></libswscale>swscale.h>&#xA;&#xA;#include &#xA;#include &#xA;&#xA;typedef struct StreamingContext{&#xA;    AVFormatContext* avfc;&#xA;    AVCodec *video_avc;&#xA;    AVCodec *audio_avc;&#xA;    AVStream *video_avs;&#xA;    AVStream *audio_avs;&#xA;    AVCodecContext *video_avcc;&#xA;    AVCodecContext *audio_avcc;&#xA;    int video_index;&#xA;    int audio_index;&#xA;    char* filename;&#xA;    struct SwsContext *sws_ctx;&#xA;}StreamingContext;&#xA;&#xA;&#xA;typedef struct StreamingParams{&#xA;    char copy_video;&#xA;    char copy_audio;&#xA;    char *output_extension;&#xA;    char *muxer_opt_key;&#xA;    char *muxer_opt_value;&#xA;    char *video_codec;&#xA;    char *audio_codec;&#xA;    char *codec_priv_key;&#xA;    char *codec_priv_value;&#xA;}StreamingParams;&#xA;&#xA;void logging(const char *fmt, ...)&#xA;{&#xA;    va_list args;&#xA;    fprintf(stderr, "LOG: ");&#xA;    va_start(args, fmt);&#xA;    vfprintf(stderr, fmt, args);&#xA;    va_end(args);&#xA;    fprintf(stderr, "\n");&#xA;}&#xA;&#xA;int fill_stream_info(AVStream *avs, AVCodec **avc, AVCodecContext **avcc)&#xA;{&#xA;    *avc = avcodec_find_decoder(avs->codecpar->codec_id);&#xA;    if (!*avc)&#xA;    {&#xA;        logging("Failed to find the codec.\n");&#xA;        return -1;&#xA;    }&#xA;&#xA;    *avcc = avcodec_alloc_context3(*avc);&#xA;    if (!*avcc)&#xA;    {&#xA;        logging("Failed to alloc memory for codec context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avcodec_parameters_to_context(*avcc, avs->codecpar) &lt; 0)&#xA;    {&#xA;        logging("Failed to fill Codec Context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avcodec_open2(*avcc, *avc, NULL) &lt; 0)&#xA;    {&#xA;        logging("Failed to open Codec.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    return 0;&#xA;}&#xA;&#xA;int open_media(const char *in_filename, AVFormatContext **avfc)&#xA;{&#xA;    *avfc = avformat_alloc_context();&#xA;&#xA;    if (!*avfc)&#xA;    {&#xA;        logging("Failed to Allocate Memory for Format Context");&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avformat_open_input(avfc, in_filename, NULL, NULL) != 0)&#xA;    {&#xA;        logging("Failed to open input file %s", in_filename);&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avformat_find_stream_info(*avfc, NULL) &lt; 0)&#xA;    {&#xA;        logging("Failed to get Stream Info.");&#xA;        return -1;&#xA;    }&#xA;}&#xA;&#xA;int prepare_decoder(StreamingContext *sc)&#xA;{&#xA;    for (int i = 0; i &lt; sc->avfc->nb_streams; i&#x2B;&#x2B;)&#xA;    {&#xA;        if (sc->avfc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)&#xA;        {&#xA;            sc->video_avs = sc->avfc->streams[i];&#xA;            sc->video_index = i;&#xA;&#xA;            if (fill_stream_info(sc->video_avs, &amp;sc->video_avc, &amp;sc->video_avcc))&#xA;            {&#xA;                return -1;&#xA;            }&#xA;        }&#xA;        else&#xA;        {&#xA;            logging("Skipping Streams other than Video.");&#xA;        }&#xA;    }&#xA;    return 0;&#xA;}&#xA;&#xA;int prepare_video_encoder(StreamingContext *encoder_sc, AVCodecContext *decoder_ctx, AVRational input_framerate,&#xA;                          StreamingParams sp)&#xA;{&#xA;    encoder_sc->video_avs = avformat_new_stream(encoder_sc->avfc, NULL);&#xA;    encoder_sc->video_avc = avcodec_find_encoder_by_name(sp.video_codec);&#xA;    if (!encoder_sc->video_avc)&#xA;    {&#xA;        logging("Cannot find the Codec.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    encoder_sc->video_avcc = avcodec_alloc_context3(encoder_sc->video_avc);&#xA;    if (!encoder_sc->video_avcc)&#xA;    {&#xA;        logging("Could not allocate memory for Codec Context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    av_opt_set(encoder_sc->video_avcc->priv_data, "preset", "fast", 0);&#xA;    if (sp.codec_priv_key &amp;&amp; sp.codec_priv_value)&#xA;        av_opt_set(encoder_sc->video_avcc->priv_data, sp.codec_priv_key, sp.codec_priv_value, 0);&#xA;&#xA;    encoder_sc->video_avcc->height = decoder_ctx->height;&#xA;    encoder_sc->video_avcc->width = decoder_ctx->width;&#xA;    encoder_sc->video_avcc->sample_aspect_ratio = decoder_ctx->sample_aspect_ratio;&#xA;&#xA;    if (encoder_sc->video_avc->pix_fmts)&#xA;        encoder_sc->video_avcc->pix_fmt = encoder_sc->video_avc->pix_fmts[0];&#xA;    else&#xA;        encoder_sc->video_avcc->pix_fmt = decoder_ctx->pix_fmt;&#xA;&#xA;    encoder_sc->video_avcc->bit_rate = 2 * 1000 * 1000;&#xA;    encoder_sc->video_avcc->rc_buffer_size = 4 * 1000 * 1000;&#xA;    encoder_sc->video_avcc->rc_max_rate = 2 * 1000 * 1000;&#xA;    encoder_sc->video_avcc->rc_min_rate = 2.5 * 1000 * 1000;&#xA;&#xA;    encoder_sc->video_avcc->time_base = av_inv_q(input_framerate);&#xA;    encoder_sc->video_avs->time_base = encoder_sc->video_avcc->time_base;&#xA;&#xA;    &#xA;&#xA;    if (avcodec_open2(encoder_sc->video_avcc, encoder_sc->video_avc, NULL) &lt; 0)&#xA;    {&#xA;        logging("Could not open the Codec.");&#xA;        return -1;&#xA;    }&#xA;    avcodec_parameters_from_context(encoder_sc->video_avs->codecpar, encoder_sc->video_avcc);&#xA;    return 0;&#xA;}&#xA;&#xA;int encode_video(StreamingContext *decoder, StreamingContext *encoder, AVFrame *input_frame)&#xA;{&#xA;    if (input_frame)&#xA;        input_frame->pict_type = AV_PICTURE_TYPE_NONE;&#xA;&#xA;    AVPacket *output_packet = av_packet_alloc();&#xA;    if (!output_packet)&#xA;    {&#xA;        logging("Could not allocate memory for Output Packet.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    int response = avcodec_send_frame(encoder->video_avcc, input_frame);&#xA;&#xA;    while (response >= 0)&#xA;    {&#xA;        response = avcodec_receive_packet(encoder->video_avcc, output_packet);&#xA;        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF)&#xA;        {&#xA;            break;&#xA;        }&#xA;        else if (response &lt; 0)&#xA;        {&#xA;            logging("Error while receiving packet from encoder: %s", av_err2str(response));&#xA;            return -1;&#xA;        }&#xA;&#xA;        output_packet->stream_index = decoder->video_index;&#xA;        output_packet->duration = encoder->video_avs->time_base.den / encoder->video_avs->time_base.num / decoder->video_avs->avg_frame_rate.num * decoder->video_avs->avg_frame_rate.den;&#xA;&#xA;        av_packet_rescale_ts(output_packet, decoder->video_avs->time_base, encoder->video_avs->time_base);&#xA;        response = av_interleaved_write_frame(encoder->avfc, output_packet);&#xA;        if (response != 0)&#xA;        {&#xA;            logging("Error %d while receiving packet from decoder: %s", response, av_err2str(response));&#xA;            return -1;&#xA;        }&#xA;    }&#xA;&#xA;    av_packet_unref(output_packet);&#xA;    av_packet_free(&amp;output_packet);&#xA;&#xA;    return 0;&#xA;}&#xA;&#xA;int transcode_video(StreamingContext *decoder, StreamingContext *encoder, AVPacket *input_packet, AVFrame *input_frame, AVFrame *scaled_frame)&#xA;{&#xA;    int response = avcodec_send_packet(decoder->video_avcc, input_packet);&#xA;    if (response &lt; 0)&#xA;    {&#xA;        logging("Error while sending the Packet to Decoder: %s", av_err2str(response));&#xA;        return response;&#xA;    }&#xA;&#xA;    while (response >= 0)&#xA;    {&#xA;        response = avcodec_receive_frame(decoder->video_avcc, input_frame);&#xA;        &#xA;        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF)&#xA;        {&#xA;            break;&#xA;        }&#xA;        else if (response &lt; 0)&#xA;        {&#xA;            logging("Error while receiving frame from Decoder: %s", av_err2str(response));&#xA;            return response;&#xA;        }&#xA;        if (response >= 0)&#xA;        {&#xA;            scaled_frame->pts = input_frame->pts;&#xA;            scaled_frame->pkt_dts = input_frame->pkt_dts;&#xA;            scaled_frame->pict_type = input_frame->pict_type;&#xA;            sws_scale_frame(encoder->sws_ctx, scaled_frame, input_frame);&#xA;            if (encode_video(decoder, encoder, scaled_frame))&#xA;                return -1;&#xA;        }&#xA;&#xA;        av_frame_unref(input_frame);&#xA;    }&#xA;    return 0;&#xA;}&#xA;&#xA;int main(int argc, char *argv[])&#xA;{&#xA;    StreamingParams sp = {0};&#xA;    sp.copy_audio = 1;&#xA;    sp.copy_video = 0;&#xA;    sp.video_codec = "libx265";&#xA;&#xA;&#xA;    StreamingContext *decoder = (StreamingContext *)calloc(1, sizeof(StreamingContext));&#xA;    decoder->filename = argv[1];&#xA;&#xA;    StreamingContext *encoder = (StreamingContext *)calloc(1, sizeof(StreamingContext));&#xA;    encoder->filename = argv[2];&#xA;&#xA;    if (sp.output_extension)&#xA;    {&#xA;        strcat(encoder->filename, sp.output_extension);&#xA;    }&#xA;&#xA;    if (open_media(decoder->filename, &amp;decoder->avfc))&#xA;        return -1;&#xA;    if (prepare_decoder(decoder))&#xA;        return -1;&#xA;&#xA;    avformat_alloc_output_context2(&amp;encoder->avfc, NULL, NULL, encoder->filename);&#xA;    if (!encoder->avfc)&#xA;    {&#xA;        logging("Could not allocate memory for output Format Context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;        AVRational input_framerate = av_guess_frame_rate(decoder->avfc, decoder->video_avs, NULL);&#xA;        prepare_video_encoder(encoder, decoder->video_avcc, input_framerate, sp);&#xA;&#xA;&#xA;    if (encoder->avfc->oformat->flags &amp; AVFMT_GLOBALHEADER)&#xA;        encoder->avfc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;&#xA;&#xA;    if (!(encoder->avfc->oformat->flags &amp; AVFMT_NOFILE))&#xA;    {&#xA;        if (avio_open(&amp;encoder->avfc->pb, encoder->filename, AVIO_FLAG_WRITE) &lt; 0)&#xA;        {&#xA;            logging("could not open the output file");&#xA;            return -1;&#xA;        }&#xA;    }&#xA;&#xA;    AVDictionary *muxer_opts = NULL;&#xA;&#xA;    if (sp.muxer_opt_key &amp;&amp; sp.muxer_opt_value)&#xA;    {&#xA;        av_dict_set(&amp;muxer_opts, sp.muxer_opt_key, sp.muxer_opt_value, 0);&#xA;    }&#xA;&#xA;    if (avformat_write_header(encoder->avfc, &amp;muxer_opts) &lt; 0)&#xA;    {&#xA;        logging("an error occurred when opening output file");&#xA;        return -1;&#xA;    }&#xA;&#xA;    AVFrame *input_frame = av_frame_alloc();&#xA;    AVFrame *scaled_frame = av_frame_alloc();&#xA;    if (!input_frame || !scaled_frame)&#xA;    {&#xA;        logging("Failed to allocate memory for AVFrame");&#xA;        return -1;&#xA;    }&#xA;&#xA;    // scaled_frame->format = AV_PIX_FMT_YUV420P;&#xA;    scaled_frame->width = 854;&#xA;    scaled_frame->height=480;    &#xA;&#xA;    //Creating Scaling Context&#xA;    encoder->sws_ctx = sws_getContext(1920, 1080,&#xA;                            decoder->video_avcc->pix_fmt, &#xA;                           scaled_frame->width, scaled_frame->height, decoder->video_avcc->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL );&#xA;    if (!encoder->sws_ctx){logging("Cannot Create Scaling Context."); return -1;}&#xA;&#xA;&#xA;    AVPacket *input_packet = av_packet_alloc();&#xA;    if (!input_packet)&#xA;    {&#xA;        logging("Failed to allocate memory for AVPacket.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    while (av_read_frame(decoder->avfc, input_packet) >= 0)&#xA;    {&#xA;        if (decoder->avfc->streams[input_packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)&#xA;        {&#xA;                if (transcode_video(decoder, encoder, input_packet, input_frame, scaled_frame))&#xA;                    return -1;&#xA;                av_packet_unref(input_packet);&#xA;        }&#xA;        else&#xA;        {&#xA;            logging("Ignoring all nonvideo  packets.");&#xA;        }&#xA;    }&#xA;&#xA;    if (encode_video(decoder, encoder, NULL))&#xA;        return -1;&#xA;&#xA;    av_write_trailer(encoder->avfc);&#xA;&#xA;    if (muxer_opts != NULL)&#xA;    {&#xA;        av_dict_free(&amp;muxer_opts);&#xA;        muxer_opts = NULL;&#xA;    }&#xA;&#xA;    if (input_frame != NULL)&#xA;    {&#xA;        av_frame_free(&amp;input_frame);&#xA;        input_frame = NULL;&#xA;    }&#xA;&#xA;    if (input_packet != NULL)&#xA;    {&#xA;        av_packet_free(&amp;input_packet);&#xA;        input_packet = NULL;&#xA;    }&#xA;&#xA;    avformat_close_input(&amp;decoder->avfc);&#xA;&#xA;    avformat_free_context(decoder->avfc);&#xA;    decoder->avfc = NULL;&#xA;    avformat_free_context(encoder->avfc);&#xA;    encoder->avfc = NULL;&#xA;&#xA;    avcodec_free_context(&amp;decoder->video_avcc);&#xA;    decoder->video_avcc = NULL;&#xA;    avcodec_free_context(&amp;decoder->audio_avcc);&#xA;    decoder->audio_avcc = NULL;&#xA;&#xA;    free(decoder);&#xA;    decoder = NULL;&#xA;    free(encoder);&#xA;    encoder = NULL;&#xA;&#xA;    return 0;&#xA;}&#xA;

    &#xA;

    The video I am using for testing is available at the repo : https://github.com/leandromoreira/ffmpeg-libav-tutorial

    &#xA;

    The file name is small_bunny_1080p_60fps.mp4

    &#xA;

  • Adding A New System To The Game Music Website

    1er août 2012, par Multimedia Mike — General

    At first, I was planning to just make a little website where users could install a Chrome browser extension and play music from old 8-bit NES games. But, like many software projects, the goal sort of ballooned. I created a website where users can easily play old video game music. It doesn’t cover too many systems yet, but I have had individual requests to add just about every system you can think of.

    The craziest part is that I know it’s possible to represent most of the systems. Eventually, it would be great to reach Chipamp parity (a combination plugin for Winamp that packages together plugins for many of these chiptunes). But there is a process to all of this. I have taken to defining a number of phases that are required to get a new system covered.

    Phase 0 informally involves marveling at the obscurity of some of the console systems for which chiptune collections have evolved. WonderSwan ? Sharp X68000 ? PC-88 ? I may be viewing this through a terribly Ameri-centric lens. I’ve at least heard of the ZX Spectrum and the Amstrad CPC even if I’ve never seen either.

    No matter. The goal is to get all their chiptunes cataloged and playable.

    Phase 1 : Finding A Player
    The first step is to find a bit of open source code that can play a particular format. If it’s a library that can handle many formats, like Game Music Emu or Audio Overload SDK, even better (probably). The specific open source license isn’t a big concern for me. I’m almost certain that some of the libraries that SaltyGME currently mixes are somehow incompatible, license-wise. I’ll worry about it when I encounter someone who A) cares, and B) is in a position to do something about it. Historical preservation comes first, and these software libraries aren’t getting any younger (I’m finding some that haven’t been touched in a decade).

    Phase 2 : Test Program
    The next phase is to create a basic test bench program that sends a music file into the library, generates a buffer of audio, and shoves it out to the speakers via PulseAudio’s simple API (people like to rip on PulseAudio, but its simple API really lives up to its name and requires pages less boilerplate code to play a few samples than ALSA).

    Phase 3 : Plug Into Web Player
    After successfully creating the test bench and understanding exactly which source files need to be built, the next phase is to hook it up to the main SaltyGME program via the ad-hoc plugin API I developed. This API requires that a player backend can, at the very least, initialize itself based on a buffer of bytes and generate audio samples into an array of 16-bit numbers. The API also provides functions for managing files with multiple tracks and toggling individual voices/channels if the library supports such a feature. Having the test bench application written beforehand usually smooths out this step.

    But really, I’m just getting started.

    Phase 4 : Collecting A Song Corpus
    Then there is the matter of staging a collection of songs for a given system. It seems like it would just be a matter of finding a large collection of songs for a given format, downloading them in bulk, and mirroring them. Honestly, that’s the easy part. People who are interested in this stuff have been lovingly curating massive collections of these songs for years (see SNESmusic.org for one of the best examples, and they also host a torrent of all their music for really quick and easy hoarding).

    In my drive to make this game music website more useful for normal people, the goal is to extract as much metadata as possible to make searching better, and to package the data so that it’s as convenient as possible for users. Whenever I seek to add a new format to the collection, this is the phase where I invariably find that I have to fundamentally modify some of the assumptions I originally made in the player.

    First, there were the NES Sound Format (NSF) files, the original format I wanted to play. These are files that have any number of songs packed into a single file. Playback libraries expose APIs to jump to individual tracks. So the player was designed around that. Game Boy GBS files also fall into this category but present a different challenge vis-à-vis metadata, addressed in the next phase.

    Then, there were the SPC files. Each SPC file is its own song and multiple SPC files are commonly bundled as RAR files. Not wanting to deal with RAR, or any format where I interacted with a general compression API to pull a few files out, I created a custom resource format (inspired by so many I have studied and documented) and compressed it with a simpler compression API. I also had to modify some of the player’s assumptions to deal with this archive format. Genesis VGMs, bundled either in .zip or .7z, followed the same model as SPC in RAR.

    Then it was suggested that I attempt to bring SaltyGME closer to feature parity with Chipamp, rather than just being a Chrome browser frontend for Game Music Emu. When I studied the Portable Sound Format (PSF), I realized it didn’t fit into the player model I already had. PSF uses a sort of shared library model for code execution and I developed another resource archive format to cope with it. So that covers quite a few formats.

    One more architecture challenge arose when I started to study one of the prevailing metadata formats, explained in the next phase.

    Phase 5 : Metadata
    Finally, for the collections to really be useful, I need to harvest that juicy metadata for search and presentation.

    I have created a series of programs and scripts to scrape metadata out of these music files and store it all in a database that drives the website and search engine. I recognize that it’s no good to have a large corpus of songs with minimal metadata and while importing bulk quantities of music, the scripts harshly reject songs that have too little metadata.

    Again, challenges abound. One of the biggest challenges I’m facing is the peculiar quasi-freeform metadata format that emerged as .m3u that takes a form similar to :

    #################################################################
    #
    # GRADIUS2
    # (c) KONAMI  by Furukawa Motoaki, IKACHAN
    #
    #################################################################
    

    nemesis2.kss::KSS,62,[Nemesis2] (Opening),2:23,,0
    nemesis2.kss::KSS,61,[Nemesis2] (Start),7,,0
    nemesis2.kss::KSS,43,[Nemesis2] (Air Battle),34,0-
    nemesis2.kss::KSS,44,[Nemesis2] (1st. BGM),51,0-
    [...]

    A lot of file formats (including Game Boy GBS mentioned earlier) store their metadata separately using this format. I have some ideas about tools I can use to help me process this data but I’m pretty sure each one will require some manual intervention.

    As alluded to in phase 4, .m3u presents another architectural challenge : Notice the second field in the CSV .m3u data. That’s a track number. A player can’t expect every track in a bundled chiptune file to be valid, nor to be in any particular order. Thus, I needed to alter the architecture once more to take this into account. However, instead of modifying the SaltyGME player, I simply extended the metadata database to include a playback order which, by default, is the same as the track order but can also accommodate this new issue. This also has the bonus of providing a facility to exclude playback of certain tracks. This comes in handy for many PSF archives which tend to include files that only provide support for other files and aren’t meant to be played on their own.

    Bright Side
    The reward for all of this effort is that the data lands in a proper database in the end. None of it goes back into the chiptune files themselves. This makes further modification easier as all of the data that is indexed and presented on the site comes from the database. Somewhere down the road, I should probably create an API for accessing this metadata.

  • FFmpeg filtergraph memory leak

    5 juillet 2017, par Leif Andersen

    I have an FFmpeg program that :

    1. Demuxes and decodes a video file.
    2. Passes it through a filtergraph
    3. encodes and muxes the new video.

    The filtergraph itself is rather complex, and can be run directly from the command line as such :

    ffmpeg -i demo.mp4 -filter_complex \
    "[audio3]atrim=end=30:start=10[audio2];\
     [video5]trim=end=30:start=10[video4];[audio2]anull[audio6];\
     [video4]scale=width=1920:height=1080[video7];[audio6]anull[audio8];\
     [video7]fps=fps=30[video9];[audio8]anull[audio10];\
     [video9]format=pix_fmts=yuv420p[video11];\
     [audio10]asetpts=expr=PTS-STARTPTS[audio12];\
     [video11]setpts=expr=PTS-STARTPTS[video13];\
     [audio15]concat=v=0:a=1:n=1[audio14];\
     [video17]concat=v=1:a=0:n=1[video16];\
     [audio12]afifo[audio15];[video13]fifo[video17];\
     [audio14]afifo[audio18];[video16]fifo[video19];\
     [audio18]anull[audio20];\
     [video19]pad=width=1920:height=1080[video21];\
     [audio20]anull[audio22];[video21]fps=fps=25[video23];\
     [audio22]aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo[fa];\
     [video23]format=pix_fmts=yuv420p[fv];[0:a]afifo[audio3];\
     [0:v]fifo[video5]" \
    -map "[fv]" -map "[fa]" out.mp4

    I realize this is a massive filtergraph with a lot of no-op filters, it was autogenerated rather than being hand written. Here is a more cleaner version of the graph. (Its a graphviz file, you can run it in the command line or here.)

    Anyway, when I run the program that uses this filtergraph my memory usage spikes. I end up using about 7 GB of RAM for a 30 second clip. However, when I run the program using the ffmpeg command above, it peaks out at about 600 MB of RAM. This causes me to believe that the problem is not the ungodly size of the filtergraph, but a problem with how my program is using it.

    The program sets up the filtergraph (using av_filter_parse_ptr, giving the filtergraph string shown above), encoder, muxer, decoder, and demuxer, then spawns two threads, one that sends frames into the filtergraph, and one that receives them. The frame that sends them looks something like :

    void decode () {
       while(... more_frames ...) {
           AVFrame *frame = av_frame_alloc();
           ... fill next frame of stream ...
           av_buffersrc_write_frame(ctx, frame);
           av_frame_free(&amp;frame);
       }
    }

    (I have elided the av_send_packet/av_receive_frame functions as they don’t seem to be leaking memory. I have also elided the process of flushing the buffersrc as that won’t happen until the end, and the memory spikes long before that.)

    And the encoder thread looks similar :

    void encode() {
       while(... nodes_in_graph ...) {
           AVFrame *frame = av_frame_alloc();
           av_buffersink_get_frame(ctx, frame);
           ... ensure frame actually was filled ...
           ... send frame to encoder ...
           av_frame_free(&amp;frame);
       }
    }

    As with the decoder, I have elided the send_frame/receive_packet combo as they don’t seem to be leaking memory. Additionally I have elided the details of ensuring that the frame actually was filled. The code loops until the frame eventually does get filled.

    Every frame I allocate I fairly quickly deallocate. I additionally handled all of the error cases that the ffmpeg can give (Elided in the example).

    I have also tried having only one frame for the encoder and one for the decoder (and calling av_frame_unref in each iteration of the loop).

    Am I forgetting to free something, or am I just using the calls to libavfilter incorrectly such that it has to buffer all of the data ? I don’t think the leak is caused by the memory graph because running it from the command line doesn’t seem to cause the same memory explosion.

    FWIW, the actual code is here, although its written in Racket. I do have a minimal example that also seems to duplicate this behavior (modified from the doc/example/filtering_video.c file from the ffmpeg code :

    #include

    #include <libavcodec></libavcodec>avcodec.h>
    #include <libavformat></libavformat>avformat.h>
    #include <libavfilter></libavfilter>avfiltergraph.h>
    #include <libavfilter></libavfilter>buffersink.h>
    #include <libavfilter></libavfilter>buffersrc.h>
    #include <libavutil></libavutil>opt.h>

    const char *filter_descr = "trim=start=10:end=30,scale=78:24,transpose=cclock";

    static AVFormatContext *fmt_ctx;
    static AVCodecContext *dec_ctx;
    AVFilterContext *buffersink_ctx;
    AVFilterContext *buffersrc_ctx;
    AVFilterGraph *filter_graph;
    static int video_stream_index = -1;
    static int64_t last_pts = AV_NOPTS_VALUE;

    static int open_input_file(const char *filename)
    {
       int ret;
       AVCodec *dec;

       if ((ret = avformat_open_input(&amp;fmt_ctx, filename, NULL, NULL)) &lt; 0) {
           av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
           return ret;
       }

       if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) &lt; 0) {
           av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
           return ret;
       }

       /* select the video stream */
       ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &amp;dec, 0);
       if (ret &lt; 0) {
           av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
           return ret;
       }
       video_stream_index = ret;

       /* create decoding context */
       dec_ctx = avcodec_alloc_context3(dec);
       if (!dec_ctx)
           return AVERROR(ENOMEM);
       avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar);
       av_opt_set_int(dec_ctx, "refcounted_frames", 1, 0);

       /* init the video decoder */
       if ((ret = avcodec_open2(dec_ctx, dec, NULL)) &lt; 0) {
           av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n");
           return ret;
       }

       return 0;
    }

    static int init_filters(const char *filters_descr)
    {
       char args[512];
       int ret = 0;
       AVFilter *buffersrc  = avfilter_get_by_name("buffer");
       AVFilter *buffersink = avfilter_get_by_name("buffersink");
       AVFilterInOut *outputs = avfilter_inout_alloc();
       AVFilterInOut *inputs  = avfilter_inout_alloc();
       AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base;
       enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };

       filter_graph = avfilter_graph_alloc();
       if (!outputs || !inputs || !filter_graph) {
           ret = AVERROR(ENOMEM);
           goto end;
       }

       /* buffer video source: the decoded frames from the decoder will be inserted here. */
       snprintf(args, sizeof(args),
               "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
               dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
               time_base.num, time_base.den,
               dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);

       ret = avfilter_graph_create_filter(&amp;buffersrc_ctx, buffersrc, "in",
                                      args, NULL, filter_graph);
       if (ret &lt; 0) {
           av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
           goto end;
       }

       /* buffer video sink: to terminate the filter chain. */
       ret = avfilter_graph_create_filter(&amp;buffersink_ctx, buffersink, "out",
                                          NULL, NULL, filter_graph);
       if (ret &lt; 0) {
           av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
           goto end;
       }

       ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
                                 AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
       if (ret &lt; 0) {
           av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
           goto end;
       }

       outputs->name       = av_strdup("in");
       outputs->filter_ctx = buffersrc_ctx;
       outputs->pad_idx    = 0;
       outputs->next       = NULL;
       inputs->name       = av_strdup("out");
       inputs->filter_ctx = buffersink_ctx;
       inputs->pad_idx    = 0;
       inputs->next       = NULL;

       if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
                                       &amp;inputs, &amp;outputs, NULL)) &lt; 0)
           goto end;

       if ((ret = avfilter_graph_config(filter_graph, NULL)) &lt; 0)
           goto end;

    end:
       avfilter_inout_free(&amp;inputs);
       avfilter_inout_free(&amp;outputs);

       return ret;
    }

    int main(int argc, char **argv)
    {
       int ret;
       AVPacket packet;
       AVFrame *frame = av_frame_alloc();
       AVFrame *filt_frame = av_frame_alloc();

       if (!frame || !filt_frame) {
           perror("Could not allocate frame");
           exit(1);
       }
       if (argc != 2) {
           fprintf(stderr, "Usage: %s file\n", argv[0]);
           exit(1);
       }

       av_register_all();
       avfilter_register_all();

       if ((ret = open_input_file(argv[1])) &lt; 0)
           goto end;
       if ((ret = init_filters(filter_descr)) &lt; 0)
           goto end;

       /* read all packets */
       while (1) {
           if ((ret = av_read_frame(fmt_ctx, &amp;packet)) &lt; 0)
               break;

           if (packet.stream_index == video_stream_index) {
               ret = avcodec_send_packet(dec_ctx, &amp;packet);
               if (ret &lt; 0) {
                   av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");
                   break;
               }

               while (ret >= 0) {
                   ret = avcodec_receive_frame(dec_ctx, frame);
                   if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                       break;
                   } else if (ret &lt; 0) {
                       av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n");
                       goto end;
                   }

                   if (ret >= 0) {
                        frame->pts = av_frame_get_best_effort_timestamp(frame);

                       /* push the decoded frame into the filtergraph */
                       if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) &lt; 0) {
                           av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
                           break;
                       }

                       /* pull filtered frames from the filtergraph */
                       while (1) {
                           ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);
                           if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                               break;
                           if (ret &lt; 0)
                               goto end;
                           av_frame_unref(filt_frame);
                       }
                       av_frame_unref(frame);
                   }
               }
           }
           av_packet_unref(&amp;packet);
       }
    end:
       avfilter_graph_free(&amp;filter_graph);
       avcodec_free_context(&amp;dec_ctx);
       avformat_close_input(&amp;fmt_ctx);
       av_frame_free(&amp;frame);
       av_frame_free(&amp;filt_frame);

       return ret;
    }