FFmpeg memory-based transcoding instance-input/output video is in memory

Source: Internet
Author: User
Tags goto int size min

I wrote an article in June, "ffmpeg memory-based transcoding example," How to transcode the video into memory, and then send it over the network. However, the article is only half done, that is, the input data is still read from the disk file. In practical applications, there is a lot of data that is put into memory, such as playing the video received from the server, is in memory. After a lapse of 2 months, the project was finally completed, although it will take a lot of time in the closing phase, but it is also a little idle. So we continue to improve.

In this article, it is assumed that there is a video that has been put into memory, which needs to be transcoded into another package format, or put into memory. Because it is a test, the video is first read from the file into memory, and the converted video is then written to another file to check for normal. Of course, limited to the ability, the code is not suitable for all situations, but it can clearly demonstrate the use of the custom IO input and output.

The technical points are summarized as follows:

1, user-defined operation

For memory operations, use structure encapsulation:

typedef struct AVIOBUFFERCONTEXT {
unsigned char* ptr;
int POS;
int totalsize;
int realsize;
}aviobuffercontext;

The structure is used for both input and output:

Aviobuffercontext g_avbuffer_in;
Aviobuffercontext g_avbuffer_out;

Implementation, read, write, seek functions. Where read is used to read, the other 2 is written to memory to use. Take read for example:

static int my_read (void *opaque, unsigned char *buf, int size)
{
aviobuffercontext* op = (aviobuffercontext*) opaque;
int len = size;
if (op->pos + size > Op->totalsize)
{
Len = op->totalsize-op->pos;
}
memcpy (buf, Op->ptr + Op->pos, Len);
if (Op->pos + len >= op->realsize)
Op->realsize + = Len;

Op->pos + = Len;

return Len;
}

In essence, it reads the size data from the existing memory and copies it to the BUF. Opaque convenient parameter transfer. Note that the POS is accumulated at the time of copying.

Other functions are similar.

2. Output configuration key code:

Avio_out =avio_alloc_context ((unsigned char *) g_ptr_out, io_buffer_size, 1,
&g_avbuffer_out, NULL, My_write, My_seek);

AVFORMAT_ALLOC_OUTPUT_CONTEXT2 (&OFMT_CTX, NULL, NULL, out_filename);
if (!OFMT_CTX)
{
printf ("Could not create output context\n");
ret = Averror_unknown;
Goto end;
}
ofmt_ctx->pb=avio_out; Assign a custom IO structure body
ofmt_ctx->flags=avfmt_flag_custom_io; Specify as Custom

This is consistent with the above mentioned article. Just a few more custom structures.

3. Enter the configuration key code:

Avio_in =avio_alloc_context ((unsigned char *) g_ptr_in, io_buffer_size, 0,
&g_avbuffer_in, my_read, NULL, NULL);
if (!avio_in)
{
printf ("Avio_alloc_context for Input failed\n");
ret = Averror_unknown;
Goto end;
}
Assigning the input Avformatcontext
Ifmt_ctx=avformat_alloc_context ();
if (!IFMT_CTX)
{
printf ("Could not create output context\n");
ret = Averror_unknown;
Goto end;
}
ifmt_ctx->pb=avio_in; Assign a custom IO structure body
ifmt_ctx->flags=avfmt_flag_custom_io; Specify as Custom
if (ret = Avformat_open_input (&ifmt_ctx, "WTF", NULL, NULL)) < 0)
{
printf ("Cannot open input file\n");
return ret;
}
if (ret = Avformat_find_stream_info (Ifmt_ctx, NULL)) < 0)
{
printf ("Cannot find stream information\n");
return ret;
}

For avio_alloc_context assignment and output, just no write and seek. The Avformatcontext variable used for the input is allocated with the Avformat_alloc_context. Because it is read-memory data, Avformat_open_input does not have to specify a file name.

I try to add a comment in the code, here's the code:

/** Mountain Stone, study-oriented, copyright No, pirated, no blame late Lee 2015.08 memory-based format encapsulation test (from memory video to another memory video) using./a.out A.avi a.mkv Supported:

AVI mkv mp4 flv ts ... Reference: http://blog.csdn.net/leixiaohua1020/article/details/25422685 Log new version appears: Using AVStream.codec.time_base as a Timebase hint to the muxer is deprecated.

Set Avstream.time_base instead.

Test passed!! Mp4->avi failed appears: H. bitstream malformed, no Startcode found, use the H264_MP4TOANNEXB bitstream Filter solution See: http ://blog.chinaunix.net/uid-11344913-id-4432752.html Official Explanation: https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#
H264_005FMP4TOANNEXB TS--avi passed other: 1, the memory p and size of the Avio_alloc_context passed to FFmpeg, can use 32768. If the converted data is stored in memory P1, this memory P1 must be different from the previous p.
Because the BUF parameter in the custom write is P, it is copied to other memory. If the definition p is 32768, but the definition of P1 is 50MB, you can convert 50MB video test: P is 32768, call write 1351 times twice times the size of the call to write 679 times p Larger, the least number of calls, memory consumption is greater (time test, no change in timing,

The former is 4.7s, the latter is 4.6s, but in theory the memory is a little better) 2, Optimization: conversion function Interface encapsulation as a class, write, Seek and other memory-related operations into the class outside the implementation, and then passed to the class, the class does not have a better memory management.
Todo reads the file repeatedly, how to do it. */

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include < sys/stat.h> #include <fcntl.h> extern "C" {#include "libavcodec/avcodec.h" #include "libavformat/avformat.h" # Include "Libswscale/swscale.h"} #include "file_utils.h" #ifndef min #define min (a) > (b)? (b): (a) #endif #define _LL_DEBUG_//Low level debug #ifdef _ll_debug_ #define DEBUG (FMT, ...) printf (FMT, # #__VA _args__) #define LL_DEBUG (FMT, ...) printf ("[DEBUG%s ().%d @%s]:" FMT, \ __func__, __line__, P_SRC, # #__VA_ARGS_
    _) #else #define DEBUG (FMT, ...) #define LL_DEBUG (FMT, ...) #endif #define DEFAULT_MEM (10*1024*1024)//Refer to the memory of the file protocol, use size 32768, or #define Io_buffer_s
    IZE (32768*1) typedef struct AVIOBUFFERCONTEXT {unsigned char* ptr;
    int POS;
    int totalsize;
int realsize;

}aviobuffercontext; Note These two are user video data,//g_avbuffer_in for the video that has been read//g_avbuffer_out is ffmpeg converted video, directly write the memory to the file can be AviobufferconText g_avbuffer_in;

Aviobuffercontext g_avbuffer_out;
Note These two are internal IO memory used by ffmpeg, unlike Aviobuffercontext's PTR//When testing, found directly defined as an array, there will be errors, so use malloc static char *g_ptr_in = NULL;

static char *g_ptr_out = NULL; Each time the read_frame is called, the function is invoked to read the data from g_avbuffer_in to static int my_read (void *opaque, unsigned char *buf, int size) {AVIOB
    uffercontext* op = (aviobuffercontext*) opaque;
    int len = size;
    if (op->pos + size > op->totalsize) {len = op->totalsize-op->pos;
    } memcpy (buf, Op->ptr + Op->pos, Len);
    
    if (Op->pos + len >= op->realsize) op->realsize + = Len;

    Op->pos + = Len;
return Len; } static int my_write (void *opaque, unsigned char *buf, int size) {aviobuffercontext* op = (aviobuffercontext*) opaqu
    E if (op->pos + size > Op->totalsize) {//re-apply//incrementally increase by value int Newtotallen = Op->to
        Talsize*sizeof (char) * 3/2; unsigned char* ptr = (unsigned char*) Av_realloC (Op->ptr, Newtotallen);
            if (ptr = = NULL) {//TODO frees memory here.
        return-1; } debug ("org ptr:%p new ptr:%p size:%d (%0.FMB)", Op->ptr, PTR, Newtotallen, Newtotalle
        n/1024.0/1024.0);
        Op->totalsize = Newtotallen;
        Op->ptr = ptr; Debug ("ReAlloc!!!!!!!!!!!!!!!!!!!!!!!
    \ n ");

    } memcpy (Op->ptr + op->pos, buf, size);

    if (op->pos + size >= op->realsize) op->realsize + = size;
    static int cnt = 1;
    
    Debug ("%d Write%p%p pos:%d len:%d\n", cnt++, Op->ptr, buf, op->pos, size);

    Op->pos + = size;
return 0; } static int64_t My_seek (void *opaque, int64_t offset, int whence) {aviobuffercontext* op = (aviobuffercontext*) Opaq
    Ue int64_t new_pos = 0;

    can be negative int64_t fake_pos = 0;
            Switch (whence) {case seek_set:new_pos = offset;
        Break
          Case Seek_cur:  New_pos = Op->pos + offset;
        Break
            Case Seek_end://There may be a problem New_pos = op->totalsize + offset;
        Break
    default:return-1;
    } fake_pos = min (New_pos, op->totalsize);
    if (fake_pos! = op->pos) {op->pos = Fake_pos;
    }//debug ("Seek pos:%d (%d) \ n", offset, op->pos);
return new_pos; } int Remuxer_mem_read (int argc, char* argv[]) {//input corresponds to one avformatcontext, output corresponds to one avformatcontext avformatcontext *i
    Fmt_ctx = null, *OFMT_CTX = NULL;
    Aviocontext *avio_in = null, *avio_out = NULL;
    const char *in_filename = NULL, *out_filename = NULL;
    
    Avpacket PKT;

    int ret = 0;
        if (ARGC < 3) {printf ("Usage:%s [input file] [output file]\n", argv[0]);
        printf ("Eg%s Foo.avi bar.ts\n", argv[0]);
    return-1;
    } in_filename = argv[1];

    Out_filename = argv[2]; memset (&g_avbuffer_in, ' n ', sizeof (Aviobuffercontext));
    memset (&g_avbuffer_out, ' n ', sizeof (Aviobuffercontext));
    
    Read_file (In_filename, (char**) &g_avbuffer_in.ptr, &g_avbuffer_in.totalsize);  Allocate output video data space g_avbuffer_out.ptr = (unsigned char*) av_realloc (NULL, default_mem*sizeof (char));
        New if (g_avbuffer_out.ptr = = NULL) {debug ("Alloc Output mem failed.\n");
    return-1;
    } g_avbuffer_out.totalsize = Default_mem;
    
    memset (g_avbuffer_out.ptr, ' g_avbuffer_out.totalsize ');
    g_ptr_in = (char*) malloc (io_buffer_size*sizeof (char));

    G_ptr_out = (char*) malloc (io_buffer_size*sizeof (char));

    Initialize Av_register_all (); Output correlation//Note To specify IO memory, also specify custom action functions, here are write and seek avio_out =avio_alloc_context ((unsigned char *) g_ptr_out, Io_buf 
    Fer_size, 1, &g_avbuffer_out, NULL, My_write, My_seek);
        if (!avio_out) {printf ("Avio_alloc_context failed\n");
        ret = Averror_unknown;
    Goto end; }//Assign AvformAtcontext//For convenience, use Out_filename to determine the format according to the output file extension//If you want to use such as "Avi", "MP4" and other designations, assign to the 3rd parameter can//Note that the function will be assigned Avoutputformat
    AVFORMAT_ALLOC_OUTPUT_CONTEXT2 (&AMP;OFMT_CTX, NULL, NULL, out_filename);
        if (!ofmt_ctx) {printf ("Could not create output context\n");
        ret = Averror_unknown;
    Goto end; } ofmt_ctx->pb=avio_out; Assign a custom IO structure body ofmt_ctx->flags=avfmt_flag_custom_io; Specified as Custom debug ("Guess Format:%s (%s) flag:%d\n", Ofmt_ctx->oformat->name, ofmt_ctx->oformat-&


    Gt;long_name, Ofmt_ctx->oformat->flags); Input related//assign custom Aviocontext to be distinguished from the output of buffer//Because the data is already in memory, so specify read, without write and seek avio_in =avio_alloc_context ( 
    (unsigned char *) g_ptr_in, io_buffer_size, 0, &g_avbuffer_in, my_read, NULL, NULL);
        if (!avio_in) {printf ("Avio_alloc_context for Input failed\n");
        ret = Averror_unknown;
    Goto end; }//Assign input to Avformatcontext Ifmt_cTx=avformat_alloc_context ();
        if (!ifmt_ctx) {printf ("Could not create output context\n");
        ret = Averror_unknown;
    Goto end; } ifmt_ctx->pb=avio_in; Assign a custom IO structure body ifmt_ctx->flags=avfmt_flag_custom_io; Specify as Custom//NOTE: The second parameter would have been a file name, but based on memory, no longer meaningful, use the string if (ret = Avformat_open_input (&ifmt_ctx, "WTF", NULL, NULL)) & Lt
        0) {printf ("Cannot open input file\n");
    return ret; } if (ret = Avformat_find_stream_info (Ifmt_ctx, NULL)) < 0) {printf ("Cannot find stream information\n
        ");
    return ret; }//Copy all stream for (int i = 0; i < (int) (ifmt_ctx->nb_streams); i++) {//Create output stream based on input stream A
        Vstream *in_stream = ifmt_ctx->streams[i];
        Avstream *out_stream = Avformat_new_stream (Ofmt_ctx, In_stream->codec->codec);
            if (!out_stream) {printf ("Failed Allocating output stream\n");
      ret = Averror_unknown;      Goto end;
        }//Copy the settings of avcodeccontext ret = Avcodec_copy_context (Out_stream->codec, In_stream->codec);
            if (Ret < 0) {printf ("Failed to copy context from input to output stream codec context\n");
        Goto end;
        } out_stream->codec->codec_tag = 0; if (Ofmt_ctx->oformat->flags & Avfmt_globalheader) out_stream->codec->flags |= CODEC_FLAG_GLOB
    Al_header;
    }//Output The format------------------printf ("Output format:\n");

    Av_dump_format (ofmt_ctx, 0, Out_filename, 1);
    Write file header ret = Avformat_write_header (Ofmt_ctx, NULL);
        if (Ret < 0) {printf ("Error occurred when opening output file\n");
    Goto end;
        }//Frame while (1) {Avstream *in_stream, *out_stream;
        Get a avpacket ret = Av_read_frame (Ifmt_ctx, &AMP;PKT); if (Ret < 0) {printf ("Av_read_frame failed or endof stream.\n ");
        Break
        } In_stream = ifmt_ctx->streams[pkt.stream_index];

        Out_stream = ofmt_ctx->streams[pkt.stream_index]; Convert Pts/dts pkt.pts = av_rescale_q_rnd (pkt.pts, In_stream->time_base, Out_stream->time_base, (A vrounding) (av_round_near_inf|
        Av_round_pass_minmax)); Pkt.dts = Av_rescale_q_rnd (Pkt.dts, In_stream->time_base, Out_stream->time_base, (AVRounding) (AV_ROUND_N Ear_inf|
        Av_round_pass_minmax));
        Pkt.duration = Av_rescale_q (pkt.duration, In_stream->time_base, out_stream->time_base);

        Pkt.pos =-1;
        Write a frame ret = Av_interleaved_write_frame (Ofmt_ctx, &AMP;PKT);
            if (Ret < 0) {printf ("Error muxing packet\n");
        Break
    } av_free_packet (&AMP;PKT);
    }//write the end of the file (write files trailer) printf ("--------write trailer------------\ n");

    Av_write_trailer (OFMT_CTX); Write the output video to a file PrinTF ("Write to file:%s%p%d\n", Out_filename, G_avbuffer_out.ptr, g_avbuffer_out.realsize);

Write_file (Out_filename, (char*) g_avbuffer_out.ptr, g_avbuffer_out.realsize, 1); 
    End:if (avio_in! = NULL) av_freep (avio_in);
    if (avio_out! = NULL) av_freep (avio_out);
    if (g_ptr_in! = NULL) free (g_ptr_in);

    if (g_ptr_out! = NULL) free (g_ptr_out);
    The function releases the user-defined IO buffer, which is no longer released, otherwise it will corrupted double-linked list avformat_close_input (&AMP;IFMT_CTX);

    Avformat_free_context (OFMT_CTX);
    if (g_avbuffer_in.ptr! = NULL) free (g_avbuffer_in.ptr);

    if (g_avbuffer_out.ptr! = NULL) free (g_avbuffer_out.ptr);
return ret;
 }


PS: Someone asked me why I often see ll_debug in the code, actually "LL" is my name. As for the interpretation of "low level", it is deceptive.


Li Yu 2015.8.26 Wednesday evening, eat the item rice


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.