[FFmpeg-devel] in-memory URL Protocol patch

alanzhuang(庄奇东) alanzhuang at tencent.com
Sat Dec 15 07:28:22 CET 2012


Dear all,

    In some cases, in-memory processing may be needed to avoid Random R/W on disks.
    Here I implemented in-memory URL protocol, hope you accept it :)

    inmem:<shmid>_<start_offset>_<size>.<extension>
    Example:
    inmem:753666_65536_1673216.mp4

1. Add the below in /libavformat/file.c:

/*
 implement in-mem protocol  alanzhuang at tencent.com<mailto:alanzhuang at tencent.com>
*/
 #if CONFIG_INMEM_PROTOCOL && (!defined WIN32) && (!defined WIN64)

 typedef struct InMemContext
 {
     const AVClass *class;
     int shmid;
     key_t shmkey; /* preserved for future use */
     off_t start_offset;
     size_t size; /* currently use start position with size */
     off_t cur_start_offset;
     size_t cur_size;
 } InMemContext;

 static const AVClass in_mem_class =
 {
     .class_name = "inmem",
     .item_name  = av_default_item_name,
     /* .option     = file_options, */
     .version    = LIBAVUTIL_VERSION_INT,
 };

 static int in_mem_read(URLContext *h, unsigned char *buf, int size)
 {
     /*av_log(NULL, AV_LOG_ERROR, "in_mem_read()\n");*/
     InMemContext *c = h->priv_data;
     if (c->shmid == -1)
     return -1;
     void *start = (void*)((char*)shmat(c->shmid, NULL, 0) + c->cur_start_offset);
     /*av_log(NULL, AV_LOG_ERROR, "before_read()\n");
     av_log(NULL, AV_LOG_ERROR, "start: %llu, read_size: %d\n", (unsigned long long) start, size);*/

     int real_size = FFMIN(size, c->cur_size);
     memcpy(buf, start, real_size);/* FFMIN(size, end - start)); */
     /*av_log(NULL, AV_LOG_ERROR, "after_read()\n");*/
     c->cur_start_offset += real_size;
     /*av_log(NULL, AV_LOG_ERROR, "start: %llu, read_size: %d\n", (unsigned long long)(shmat(c->shmid, NULL, 0) + c->cur_start_offset), real_size);*/
     c->cur_size -= real_size;
     /*int r = read(fd, buf, size);*/
     return real_size;
 }

 static int in_mem_write(URLContext *h, const unsigned char *buf, int size)
 {
     /*av_log(NULL, AV_LOG_ERROR, "in_mem_write()\n");*/
     InMemContext *c = h->priv_data;
     if (c->shmid == -1)
     return -1;
     void *start = (void*)((char*)shmat(c->shmid, NULL, 0) + c->cur_start_offset);
     /*av_log(NULL, AV_LOG_ERROR, "start: %llu, write_size: %d\n", (unsigned long long) start, size);
     int r = write(fd, buf, size);*/
     int real_size = FFMIN(size, c->cur_size);
     memcpy(start, buf, real_size);
     /*av_log(NULL, AV_LOG_ERROR, "after_write()\n");*/
     c->cur_start_offset += real_size;
     /*av_log(NULL, AV_LOG_ERROR, "start: %llu, read_size: %d\n", (unsigned long long) c->start, real_size);*/
     c->cur_size -= real_size;
     return real_size;
 }

 static int in_mem_get_handle(URLContext *h)
 {
     InMemContext *c = h->priv_data;
     av_log(NULL, AV_LOG_ERROR, "in_mem_get_handle()!!!\n");
     return (intptr_t) c->start_offset;
 }

 static int in_mem_check(URLContext *h, int mask)
 {
     return 0;
 }

 static int in_mem_open(URLContext *h, const char *filename, int flags)
 {
     /* av_log(NULL, AV_LOG_ERROR, "in_mem_open(): %s\n", filename); */
     InMemContext *c = h->priv_data;
     int shmid;
     off_t start_offset;
     char *final_shm, *final_start, *final_size;
     size_t size;
     av_strstart(filename, "inmem:", &filename);

     shmid = (int)strtol(filename, &final_shm, 10);
     if(filename == final_shm)
     {
         shmid = -1;
     }
     start_offset = (off_t)strtoull(final_shm + 1, &final_start, 10);
     if(final_shm + 1 == final_start)  /* No digits found, or something like 10ab */
     {
         start_offset = 0;
     }
     size = (size_t)strtoull(final_start + 1, &final_size, 10);
     if((final_start + 1 == final_size)) /* || *final_size)*/
     {
         size = 0;
     }
     c->shmid = shmid;
     c->start_offset = start_offset;
     c->cur_start_offset = start_offset;
     c->size = size;
     c->cur_size =size;
     h->is_streamed = 0;
     return 0;
 }

 /* XXX: streaming, do not support seek currently */
 static int64_t in_mem_seek(URLContext *h, int64_t pos, int whence)
 {
     int64_t ret;
     InMemContext *c = h->priv_data;

     /* av_log(NULL, AV_LOG_ERROR, "try mem_seek: pos: %lld, whence: %d\n", pos, whence); */

     if (whence == AVSEEK_SIZE)
     {
         ret = (int64_t)c->size;
         return ret;
     }

     if (whence == SEEK_SET)
     {
         c->cur_start_offset = c->start_offset + pos;
         c->cur_size = c->size - pos;
     }

     else if (whence == SEEK_END)
     {
         c->cur_start_offset = c->start_offset + c->size + pos;
         c->cur_size = 0 - pos;
     }

     return (int64_t)c->cur_start_offset;
 }

 static int in_mem_close(URLContext *h)
 {
     InMemContext *c = h->priv_data;
     void *p = shmat(c->shmid, NULL, 0);
     return shmdt(p);
     /*return shmctl(c->shmid, IPC_RMID, NULL);*/
 }

 URLProtocol ff_inmem_protocol =
 {
     .name                = "inmem",
     .url_open            = in_mem_open,
     .url_read            = in_mem_read,
     .url_write           = in_mem_write,
     .url_seek            = in_mem_seek,
     .url_close           = in_mem_close,
     .url_get_file_handle = in_mem_get_handle,
     .url_check           = in_mem_check,
     .priv_data_size      = sizeof(InMemContext),
 };

 #endif /* CONFIG_INMEM_PROTOCOL */


2. Add
OBJS-$(CONFIG_INMEM_PROTOCOL) += file.o
in /libavformat/Makefile  ( # protocols I/O section)

3. Add
CONFIG_INMEM_PROTOCOL=yes
in /config.mak






Testing:


// works with the latest ffmpeg

// based on tutorial01.c
//
// This tutorial was written by Stephen Dranger (dranger at gmail.com<mailto:dranger at gmail.com>) and updated
// for ffmpeg version N-42806-gf4451d2 by Michael Penkov
// (misha.penkov at gmail.com<mailto:misha.penkov at gmail.com>).
//
// Code based on a tutorial by Martin Bohme (boehme at inb.uni-luebeckREMOVETHIS.de<mailto:boehme at inb.uni-luebeckREMOVETHIS.de>)
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1

// A small sample program that shows how to use libavformat and libavcodec to
// read video from a file.
//
// Use the Makefile to build all examples.
//
// Run using
//
// tutorial01 myvideofile.mpg
//
// to write the first five frames fr

 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
 #include <libswscale/swscale.h>

 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/shm.h>
 #include <sys/ipc.h>
 #include <fcntl.h>

 void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
 {
     FILE *pFile;
     char szFilename[32];
     int  y;
     // Open file
     sprintf(szFilename, "frame%d.ppm", iFrame);
     pFile = fopen(szFilename, "wb");
     if (pFile == NULL)
         return;
     // Write header
     fprintf(pFile, "P6\n%d %d\n255\n", width, height);
     // Write pixel data
     for (y = 0; y < height; y++)
         fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);
     // Close file
     fclose(pFile);
 }

 int main(int argc, char *argv[])
 {
     AVFormatContext *pFormatCtx = NULL;
     int             i, videoStream;
     AVCodecContext  *pCodecCtx = NULL;
     AVCodec         *pCodec = NULL;
     AVFrame         *pFrame = NULL;
     AVFrame         *pFrameRGB = NULL;
     AVPacket        packet;
     int             frameFinished;
     int             numBytes;
     uint8_t         *buffer = NULL;
     AVDictionary    *optionsDict = NULL;
     struct SwsContext      *sws_ctx = NULL;
     if (argc < 1)
     {
         printf("Please provide a movie file\n");
         return -1;
     }
     //生成key
     key_t key = ftok(".", 1002);
     //创建共享内存段
     int shmid = shmget(key, 1673218, IPC_CREAT | IPC_EXCL | 0600);
     if (shmid < 0)perror("error"), exit(-1);
     //挂接共享内存段
     void *p = shmat(shmid, NULL, 0);
     if (p == (void*) - 1)perror("error"), exit(-1);
     printf("key: %ld; shmid: %d; Addr: %llu\n", key, shmid, (unsigned long long) p);
     int fd;
     char filename[64];
     fd = open("/home/cheedoong/111.mpg", O_RDONLY, 0755);
     int nread = read(fd, p, 1673218);
     sprintf(filename, "inmem:%llu_%llu_%llu.mpg", shmid, 0 , nread);
     printf("filename: %s  read: %d bytes.\n", filename, nread);
     close(fd);
     char cmd[256];
     sprintf(cmd, "ffmpeg -i %s", filename); /* -vcodec libx264 -acodec libfaac inmem_trans.avi */
     printf("before get information of %s \n", filename);
     system(cmd);
     printf("after get information of %s \n", filename);
     //-vcodec copy -acodec copy -f mpegts 111.ts
     // Register all formats and codecs
     av_register_all();
     // Open video file
     if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0)
         return -1; // Couldn't open file
     // Retrieve stream information
     if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
         return -1; // Couldn't find stream information
     // Dump information about file onto standard error
     av_dump_format(pFormatCtx, 0, filename, 0);
     // Find the first video stream
     videoStream = -1;
     for (i = 0; i < pFormatCtx->nb_streams; i++)
         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
         {
             videoStream = i;
             break;
         }
     if (videoStream == -1)
         return -1; // Didn't find a video stream
     // Get a pointer to the codec context for the video stream
     pCodecCtx = pFormatCtx->streams[videoStream]->codec;
     // Find the decoder for the video stream
     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
     if (pCodec == NULL)
     {
         fprintf(stderr, "Unsupported codec!\n");
         return -1; // Codec not found
     }
     // Open codec
     if (avcodec_open2(pCodecCtx, pCodec, &optionsDict) < 0)
         return -1; // Could not open codec
     // Allocate video frame
     pFrame = avcodec_alloc_frame();
     // Allocate an AVFrame structure
     pFrameRGB = avcodec_alloc_frame();
     if (pFrameRGB == NULL)
         return -1;
     // Determine required buffer size and allocate buffer
     numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
                                   pCodecCtx->height);
     buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
     sws_ctx =
         sws_getContext
         (
             pCodecCtx->width,
             pCodecCtx->height,
             pCodecCtx->pix_fmt,
             pCodecCtx->width,
             pCodecCtx->height,
             PIX_FMT_RGB24,
             SWS_BILINEAR,
             NULL,
             NULL,
             NULL
         );
     // Assign appropriate parts of buffer to image planes in pFrameRGB
     // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
     // of AVPicture
     avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
                    pCodecCtx->width, pCodecCtx->height);
     // Read frames and save first five frames to disk
     i = 0;
     while (av_read_frame(pFormatCtx, &packet) >= 0)
     {
         // Is this a packet from the video stream?
         if (packet.stream_index == videoStream)
         {
             // Decode video frame
             avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,
                                   &packet);
             // Did we get a video frame?
             if (frameFinished)
             {
                 // Convert the image from its native format to RGB
                 sws_scale
                 (
                     sws_ctx,
                     (uint8_t const * const *)pFrame->data,
                     pFrame->linesize,
                     0,
                     pCodecCtx->height,
                     pFrameRGB->data,
                     pFrameRGB->linesize
                 );
                 // Save the frame to disk
                 if (++i <= 5)
                     SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,
                               i);
             }
         }
         // Free the packet that was allocated by av_read_frame
         av_free_packet(&packet);
     }
     // Free the RGB image
     av_free(buffer);
     av_free(pFrameRGB);
     // Free the YUV frame
     av_free(pFrame);
     // Close the codec
     avcodec_close(pCodecCtx);
     // Close the video file
     avformat_close_input(&pFormatCtx);
     return 0;
 }


BR~


More information about the ffmpeg-devel mailing list