FFMPEG小记
来源:互联网 发布:php if 多条件 编辑:程序博客网 时间:2024/06/05 21:14
先吐一下槽,Android下的多媒体框架真不好搞,感觉还是微软的DirectShow框架更容易扩展。如果有人知道其它更好的完成下面任务的方法,请告知我。
任务是在现有的音视频格式上做一层包装/加密,打开这种新格式文件时文件流要先经过一层自定义的解包处理,再把解包后的文件流交由解码器处理。
任务看似简单,在DirectShow框架中只需要做个源过滤器,随后即可调用现有的解码器等设施成功解码。但Android上就挣扎了。现有的App框架完全满足不了需求(或许是我没找到),使用opencore又似乎太庞大而且工作量不小,最终选择了FFMPEG。
通过FFMPEG完成上面的任务主要途径有两种:一是修改打开文件读文件流识别解码器部分和解码器读文件流部分;二是重新写一个解码器(这似乎和在opencore中实现一个解码器相当)。
分析一下打开文件读文件流识别解码器的部分吧,入口函数是av_open_input_file,其最重要的作用就是填充好AVFormatContext结构。
AVFormatContext结构是解析文件格式、找到对应的解码器的重要的结构之一。其定义略长,在此简述。
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
ByteIOContext *pb;
iformat和oformat两者只有一个会生效,如果是播放iformat生效,录制则oformat生效。当然还有转码的过程中,输入iformat生效,输出oformat生效。
pb是对文件流操作的一个抽象,接下来讲解。
FFMPEG打开文件后会读一段文件流,根据文件流中的内容来找到一个对应AVInputFormat,即一种文件格式的抽象。比如mp4,它的AVInputFormat定义在/libffmpeg/libavformat/mov.c中:
AVInputFormat mov_demuxer = { "mov,mp4,m4a,3gp,3g2,mj2", NULL_IF_CONFIG_SMALL("QuickTime/MPEG-4/Motion JPEG 2000 format"), sizeof(MOVContext), mov_probe, mov_read_header, mov_read_packet, mov_read_close, mov_read_seek,};
AVInputFormat定义在/libffmpeg/libavformat/avformat.h中,其定义略长,在此简述。
const char *name; // 格式别名
const char *long_name; // 格式的全名,方便人阅读
int priv_data_size; // 额外数据长度
int (*read_probe)(AVProbeData *); // 读取适配数据的函数指针
... // 其它函数指针
FFMPEG努力用C来实现面向对象,其中结构体中含函数指针的写法非常常见。所有格式的AVInputFormat(还有AVOutput协议)都会在/libffmpeg/libavformat/Allformats.c的av_register_all函数中注册,形成一个以
/** head of registered input format linked list */AVInputFormat *first_iformat = NULL;
为链表头的链表。ff_probe_input_buffer会通过ByteIOContext读取一段适配数据然后调用av_probe_input_format2找到正确的AVInputFormat(即通过遍历first_iformat)。
那么实现上面的任务通过修改ff_probe_input_buffer,在其中先解包,再让其调用av_probe_input_format2即可以达到部分欺骗FFMPEG的目的。但事实并不那么容易,找到正确的AVInputFormat后FFMPEG还会在av_open_input_stream中调用它的read_header来读取文件头部信息。这下麻烦大了,如果包装的文件原来是mp4格式的,那么得改mp4的AVInputFormat的read_header函数,如果是rmvb的……这个复杂性就不用说了。
回过头来看FFMPEG读文件流的过程,依赖于ByteIOContext,在url_fopen--->url_fdopen中生成了该对象并通过
if (init_put_byte(*s, buffer, buffer_size, (h->flags & URL_WRONLY || h->flags & URL_RDWR), h, url_read, url_write, url_seek) < 0)
设置了其内容。ByteIOContext定义如下:
/** * Bytestream IO Context. * New fields can be added to the end with minor version bumps. * Removal, reordering and changes to existing fields require a major * version bump. * sizeof(ByteIOContext) must not be used outside libav*. */typedef struct { unsigned char *buffer; int buffer_size; unsigned char *buf_ptr, *buf_end; 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); int64_t pos; /**< position in the file of the current buffer */ int must_flush; /**< true if the next seek should flush */ int eof_reached; /**< true if eof reached */ int write_flag; /**< true if open for writing */ int is_streamed; int max_packet_size; unsigned long checksum; unsigned char *checksum_ptr; unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size); int error; ///< contains the error code or 0 if no error happened int (*read_pause)(void *opaque, int pause); int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);void *URLContext;} ByteIOContext;
通过分析可以看出,url_read就是其读取文件流的方式。这里注意到url_read的实现:
int url_read(URLContext *h, unsigned char *buf, int size){ int ret; if (h->flags & URL_WRONLY) return AVERROR(EIO); ret = h->prot->url_read(h, buf, size); return ret;}
可见读取文件流的过程又与具体的协议内容(URLContext)有关。其实在这里已经能够完成任务了,如下:
int fd = h->prot->url_get_file_handle(h); // 其实就是h->priv_datalong pos = lseek(fd, 0, SEEK_CUR);__android_log_print(ANDROID_LOG_INFO, TAG, "url_read ftell pos: %d", pos);
pos代表文件流当前的位置,这样在哪个位置如何解包就一清二楚了。
下面还是简单说说URLContext与URLProtolcol。URLContext的定义如下:
/** * URL Context. * New fields can be added to the end with minor version bumps. * Removal, reordering and changes to existing fields require a major * version bump. * sizeof(URLContext) must not be used outside libav*. */typedef struct URLContext {#if LIBAVFORMAT_VERSION_MAJOR >= 53 const AVClass *av_class; ///< information for av_log(). Set by url_open().#endif struct URLProtocol *prot; int flags; int is_streamed; /**< true if streamed (no seek possible), default = false */ int max_packet_size; /**< if non zero, the stream is packetized with this max packet size */ void *priv_data; char *filename; /**< specified URL */} URLContext;
其中最重要的就是URLProtocol了,打开文件的过程中会根据文件的URL生成对应的URLProtocol及URLContext,才有了后面url_read中的ret = h->prot->url_read(h, buf, size);这儿的调用。URLProtocol才是真正负责协议中文件流数据读取的底层数据结构。下面举file协议(即本地文件)的例子。
URLProtocol file_protocol = { "file", file_open, file_read, file_write, file_seek, file_close, .url_get_file_handle = file_get_handle,};
其中file_read:
static int file_read(URLContext *h, unsigned char *buf, int size){ int fd = (intptr_t) h->priv_data; return read(fd, buf, size);}
可以看到了,就是Linux中read读文件。
- FFMPEG小记
- FFmpeg 小记
- ffmpeg使用小记
- FFmpeg使用小记
- ffmpeg 使用小记
- FFmpeg使用小记
- ffmpeg+ffserver媒体服务搭建小记
- ffmpeg+ffserver媒体服务搭建小记
- ffmpeg+ffserver媒体服务搭建小记
- ffmpeg+ffserver媒体服务搭建小记
- ffmpeg+ffserver媒体服务搭建小记
- ffmpeg for mac osx 64bit 小记
- ffmpeg 使用小记 h264 to ts
- ubuntu下折腾ffmpeg小记(音视频录制、转换)
- 小记
- 小记
- 小记
- 小记
- 遍历List<String[]>并 转换为String[][],List<List<String>>,Map<String,String[]>
- android Toast大全(五种情形)建立属于你自己的Toast
- flex中文本去前后空格
- Robert边缘检测算子的程序。换做其他算子,只要该其中的一个矩阵就行了
- 对于下面这个局部变量未初始化就使用的问题int x = x;
- FFMPEG小记
- CE驱动程序快速入门
- Ubuntu10.04 上 安装 samba
- Ajax的异步和同步
- ffmpeg
- 什么是GPS热启动、冷启动和温启动
- PopupWindow
- python26高级[读写Excel]
- union用来节省空间,有些数据项不会同时出现