ffmpeg(8) AVIOContext II

来源:互联网 发布:淘宝店铺宝贝分类模板 编辑:程序博客网 时间:2024/04/27 19:59

Part I is http://blog.csdn.net/xiruanliuwei/article/details/24974873


struct AVIOContext 结构体中几个比较重要的结构体成员:

unsigned char *buffer;  /**< Start of the buffer. */
    
int buffer_size;        /**< Maximum buffer size */


unsigned char *buf_ptr; /**< Current position in the buffer */


unsigned char *buf_end; /**< End of the data, may be less than
                                 buffer+buffer_size if the read function returned
                                 less data than requested, e.g. for streams where
                                 no more data has been received yet. */

int64_t pos;            /**< position in the file of the current buffer */

int write_flag;         /**< true if open for writing */

int max_packet_size;


buffer_size = buf_end - buffer;


pos这个比较纠结,英语水平有限,看不懂这个英文注释的意思,通过试验发现,其实它是标识AVIOContext关联的那个文件中的位置:

case 1: reading

buffer_size = 32768;  // default value is 32768, 0x8000

avio_read会将数据从文件,读取到buffer缓冲区中,一次读取buffer_size个字节,所以pos总是32768的整数倍;

但是,avio_read中的参数size不必一定是32768或者其整数倍,它可以是任何值,从buffer中读取size个数返回,

剩余的数据供下次继续使用;


case 2 writing:

buffer_size = 32768;  // default value is 32768, 0x8000

avio_write会将数据从参数buf中写入到AVIOContext的buffer中,但是不一定会立即写入AVIOContext关联的文件

中,只有当buffer中的数据大于等于32768时,才会将数据写入文件;


void avio_write(AVIOContext *s, const unsigned char *buf, int size)
{

    // s->direct若为1,则不经过AVIOContext中的buffer缓冲,直接将数据从buf中写入到AVIOContext关联的文件中;
    if (s->direct && !s->update_checksum) 
    {
        avio_flush(s);
        
        writeout(s, buf, size);


        return;
    }
    
    while (size > 0)
    {

        // buf_end - buf_ptr表示buffer剩余的空间,size表示要写的数据量,如果size大于剩余空间,则要分多次写入,

        // 否则下面的memcpy会破坏内存中数据,超出的数据会覆盖buffer后面数据;
        int len = FFMIN(s->buf_end - s->buf_ptr, size);
        memcpy(s->buf_ptr, buf, len);
        s->buf_ptr += len;

        // 其实没有buf_ptr大于buf_end的情况,那样内存数据就出错了

        // 前面的FFMIN措施,保证buf_ptr总是小于等于buf_end

        // 等于时,表示AVIOContext中buffer已满,需要将数据写入AVIOContext关联的文件中
        if (s->buf_ptr >= s->buf_end)
            flush_buffer(s);


        buf += len;
        size -= len;
    }
}


static void flush_buffer(AVIOContext *s)
{
    if (s->buf_ptr > s->buffer)
    {
        writeout(s, s->buffer, s->buf_ptr - s->buffer);
        
        if (s->update_checksum) 
        {
            s->checksum     = s->update_checksum(s->checksum, s->checksum_ptr,
                                                 s->buf_ptr - s->checksum_ptr);
            s->checksum_ptr = s->buffer;
        }
    }
    s->buf_ptr = s->buffer;
}

buf_ptr > buffer表示AVIOContext的buffer中有数据,能够执行写入操作,当数据写入完毕后,

buf_ptr回到最初,再从头来过  s->buf_ptr = s->buffer;


writeout(s, s->buffer, s->buf_ptr - s->buffer);


static void writeout(AVIOContext *s, const uint8_t *data, int len)
{
    printf("Enter Function %s\n", __FUNCTION__);
    printf("data is: %p\n", data);
    printf("len is: %d\n", len);
    

    // write_packet不要为NULL, 并且error表示数据校验没有错误
    if (s->write_packet && !s->error)
    {
        int ret = s->write_packet(s->opaque, (uint8_t *)data, len);
        if (ret < 0)
        {
            s->error = ret;
        }
    }
    
    s->writeout_count ++;

    // 特别留意一下这个语句,只有当真实写操作完成后,pos所表示的文件中的位置才会变化

    // 对于非direct的,pos = buffer_size * writeout_count;

    // 对于direct的,就只能这样每次pos + len了,因为每次写入的数据的size并非一样的
    s->pos += len;
}

调用write_packet函数指针

int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);

指向的函数,将数据写入到具体文件中,当然,在write_packet指向的函数中,还有嵌套;

(在aviobuf.c文件中, AVIOContext结构体实例在创建时,write_packet函数指针被赋值指向函数ffur_write)

    *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
                            (void*)ffurl_read, (void*)ffurl_write, (void*)ffurl_seek);


int ffurl_write(URLContext *h, const unsigned char *buf, int size)
{
    printf("Enter Function %s\n", __FUNCTION__);
    // print_backtrace();
    
    if (!(h->flags & AVIO_FLAG_WRITE))
        return AVERROR(EIO);
    
    /* avoid sending too big packets */
    if (h->max_packet_size && size > h->max_packet_size)
        return AVERROR(EIO);


    return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
}

h->prot是具体的协议,URL的协议



另外一个比较纠结的变量:

int write_flag;         /**< true if open for writing */

搜索代码,给write_flag赋值的只有一个地方:

static int url_resetbuf(AVIOContext *s, int flags)
{
    av_assert1(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ);


    if (flags & AVIO_FLAG_WRITE)
    {
        s->buf_end = s->buffer + s->buffer_size;
        s->write_flag = 1;
    }
    else
    {
        s->buf_end = s->buffer;
        s->write_flag = 0;
    }
    
    return 0;
}

通过代码可以知道,需要写文件时,write_flag为1,否则write_flag都是0

写文件包含两种情况:

#define AVIO_FLAG_WRITE 2                                      /**< write-only */
#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE)  /**< read-write pseudo flag */


AVIO_FLAG_WRITE:以写的方式打开文件;

AVIO_FLAG_READ_WRITE:以读、写方式打开文件;

int ffio_init_context(AVIOContext *s,
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence))

中:

    if (!read_packet && !write_flag)
    {
        s->pos     = buffer_size;
        s->buf_end = s->buffer + buffer_size;
    }

read_packet表示没有从文件中往buffer中读取数据的函数;

write_flag为0,表示文件是以只读方式打开;

不清楚这种设置的意义?


又是读,又是写,比较乱,比较难分析,分成两种情况:

1. 只读 write_flag == 0

ffio_init_context

    s->buffer      = buffer;
    s->orig_buffer_size = s->buffer_size = buffer_size;
    s->buf_ptr     = buffer;

        s->buf_end = s->buffer;
        s->write_flag = 0;

    s->pos             = 0;


    if (!read_packet && !write_flag)
    {
        s->pos     = buffer_size;
        s->buf_end = s->buffer + buffer_size;
    }

此时write_flag必然是0,这样的设置不知道什么意义,难道是read_packet为null时,设置成一个buffer已经填充满了,可以从这里面读取数据?

read_packet不为null时,则不进行设置,因为此时可以通过read_packet从文件中读取数据到buffer中;




2. 只写 write_flag == 1

???后续分析~



需要注意,在AVIOContext中存在一个缓冲区,

    /**
     * avio_read and avio_write should if possible be satisfied directly
     * instead of going through a buffer, and avio_seek will always
     * call the underlying seek function directly.
     */
    int direct;

当direct为1时,

1. reading

AVIOContext关联的文件 ---> user buf

2. writing

user buf ---> AVIOContext关联的文件


当direct为0时,

1. reading 

AVIOContext关联的文件 ---> AVIOContext.buffer ---> user buf

2. writing

user buf ---> AVIOContext.buffer ---> AVIOContext关联的文件


only AVIO_FLAG_READ 时:

此时, write_flag == 0


// buf: user buf
// size: 表示要读取的字节数量
int avio_read(AVIOContext *s, unsigned char *buf, int size)
{
    int len, size1;


    size1 = size;
    while (size > 0)
    {
        // len表示AVIOContext.buffer中剩余的可供读取的字节数量
        len = s->buf_end - s->buf_ptr;
        if (len > size)
            len = size;
        
        // 根据前提,此时write_flag为0
        // len = 0,表示AVIOContext中buffer中没有可用数据
        if (len == 0 || s->write_flag) 
        {
            // s->direct 表示不经过AVIOContext.buffer,直接从文件读取
            // size > s->buffer_size 表示所要读取的字节数量,超过AVIOContext.buffer的容量
            if((s->direct || size > s->buffer_size) && !s->update_checksum)
            {
                // 直接从文件中读取数据到user buf中,读取size字节
                if(s->read_packet)
                    len = s->read_packet(s->opaque, buf, size);


                // len 是读取的字节数量,为0表示到文件末尾,无数据可读取, < 0是表示读取出错吧?
                if (len <= 0)
                {
                    /* do not modify buffer if EOF reached so that a seek back can
                    be done without rereading data */
                    s->eof_reached = 1;
                    if(len<0)
                        s->error= len;
                    break;
                } 
                else 
                {
                    // 读取了len字节,需要更新s->pos,其表示在文件中的偏移位置
                    s->pos += len;
                    s->bytes_read += len;
                    size -= len;
                    buf += len;
                    // 此时,原来buffer中的数据已经没有意义,复位重置一下
                    s->buf_ptr = s->buffer;
                    s->buf_end = s->buffer/* + len*/;
                }
            } 
            // 不是s->direct为1,也不是size大于buffer的容量,则走正常的渠道,
            // 先将数据从文件中读取到AVIOContext.buffer中,再从buffer中读取
            // user buf中
            else 
            {
                // 从文件中读取数据到AVIOContext.buffer中
                fill_buffer(s);
                // AVIOContext.buffer中可用数据的字节数量
                len = s->buf_end - s->buf_ptr;
                if (len == 0)
                    break;
            }
        }
        else 
        {
            // 从AVIOContext.buffer中读取数据到user buf,这是最正常的情况
            memcpy(buf, s->buf_ptr, len);
            buf += len;
            s->buf_ptr += len;
            size -= len;
        }
    }
    
    if (size1 == size)
    {
        if (s->error)      return s->error;
        if (url_feof(s))   return AVERROR_EOF;
    }
    
    return size1 - size;
}


void avio_write(AVIOContext *s, const unsigned char *buf, int size)
{
    // s->direct 为1,则直接写入到文件中
    if (s->direct && !s->update_checksum) 
    {
        avio_flush(s);
        writeout(s, buf, size);
        return;
    }
    
    while (size > 0) 
    {
        int len = FFMIN(s->buf_end - s->buf_ptr, size);
        memcpy(s->buf_ptr, buf, len);
        s->buf_ptr += len;


        // 正常情况下,当AVIOContext.buffer中数据满了,才将其中的数据写入文件
        if (s->buf_ptr >= s->buf_end)
            flush_buffer(s);


        buf += len;
        size -= len;
    }
}



AVIO_FLAG_WRITE or AVIO_FLAG_READ_WRITE 时:

此时, write_flag == 1

0 0
原创粉丝点击