公司企业邮箱网易,鄂州网站推广优化技巧,中国公司网站建设,js统计网站访问人数最简单的基于 FFmpeg 的内存读写的例子#xff1a;内存转码器 最简单的基于 FFmpeg 的内存读写的例子#xff1a;内存转码器正文源程序结果工程文件下载参考链接 最简单的基于 FFmpeg 的内存读写的例子#xff1a;内存转码器
参考雷霄骅博士的文章#xff0c;链接#xf… 最简单的基于 FFmpeg 的内存读写的例子内存转码器 最简单的基于 FFmpeg 的内存读写的例子内存转码器正文源程序结果工程文件下载参考链接 最简单的基于 FFmpeg 的内存读写的例子内存转码器
参考雷霄骅博士的文章链接最简单的基于FFmpeg的内存读写的例子内存播放器
正文
之前的所有有关 FFmpeg 的例子都是对文件进行操作的。实际上并不是所有视频的编码解码都是针对文件进行处理的。有的时候需要的解码的视频数据在一段内存中。例如通过其他系统送来的视频数据。同样有的时候编码后的视频数据也未必要保存成一个文件。例如要求将编码后的视频数据送给其他的系统进行下一步的处理。以上两种情况就要求 FFmpeg 不仅仅是对文件进行“读写”操作而是要对内存进行“读写”操作。因此打算记录的两个例子就是使用 FFmpeg 对内存进行读写的例子。
这篇文章记录一个基于 FFmpeg 的内存转码器。该转码器可以使用 FFmpeg 读取内存中的数据转码为 H.264 之后再将数据输出到内存。
关于如何从内存中读取数据在这里不再详述可以参考文章《ffmpeg 从内存中读取数据(或将数据输出到内存)》。
FFmpeg 读写内存的关键点就两个
第一初始化自定义的 AVIOContext指定自定义的回调函数。示例代码如下
//AVIOContext中的缓存
unsigned char *aviobuffer(unsigned char*)av_malloc(32768);
AVIOContext *avioavio_alloc_context(aviobuffer, 32768,0,NULL,read_buffer,NULL,NULL);
pFormatCtx-pbavio;if(avformat_open_input(pFormatCtx,NULL,NULL,NULL)!0){printf(Couldnt open inputstream.无法打开输入流\n);return -1;
}上述代码中自定义了回调函数 read_buffer()。在使用 avformat_open_input() 打开媒体数据的时候就可以不指定文件的 URL 了即其第 2 个参数为 NULL因为数据不是靠文件读取而是由 read_buffer() 提供。
第二自己写回调函数。注意函数的参数和返回值尤其是返回值。示例代码如下
//Callback
int read_buffer(void *opaque, uint8_t *buf, int buf_size){if(!feof(fp_open)){inttrue_sizefread(buf,1,buf_size,fp_open);return true_size;}else{return -1;}
}当系统需要数据的时候会自动调用该回调函数以获取数据。这个例子为了简单直接使用 fread() 读取数据至内存。回调函数需要格外注意它的参数和返回值。
转码实际上就是解码和编码的结合。该方面的知识可以参考文章
解码100行代码实现最简单的基于FFMPEGSDL的视频播放器SDL1.x编码最简单的基于FFMPEG的视频编码器YUV编码为H.264转码 最简单的基于FFMPEG的转码程序
程序的流程图如下图所示。从图中可以看出首先分别初始化了输入和输出的 AVFormatContext。然后首先解码输入的 AVPacket得到存储像素数据YUV420P 格式的 AVFrame然后编码 AVFrame 为 H.264 的 AVPacket最后将编码后的 AVPacket 输出。 源程序
/**
* 最简单的基于FFmpeg的内存读写例子内存转码器
* Simplest FFmpeg mem Transcoder
*
* 源程序
* 雷霄骅张晖
* leixiaohua1020126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 修改
* 刘文晨 Liu Wenchen
* 812288728qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.csdn.net/ProgramNovice
*
* 本程序实现了任意格式视频数据例如 MPEG2转码为 H.264 码流数据。
* 本程序并不是对文件进行处理而是对内存中的视频数据进行处理。
* 它从内存读取数据并且将转码后的数据输出到内存中。
* 是最简单的使用 FFmpeg 读写内存的例子。
*
* This software convert video bitstream (Such as MPEG2) to H.264
* bitstream. It read video bitstream from memory (not from a file),
* convert it to H.264 bitstream, and finally output to another memory.
* Its the simplest example to use FFmpeg to read (or write) from
* memory.
*
*/#include stdafx.h#include stdio.h#define __STDC_CONSTANT_MACROS#ifdef _WIN32
// Windows
extern C
{
#include libavcodec/avcodec.h
#include libavformat/avformat.h
#include libavutil/avutil.h
#include libavutil/opt.h
#include libavutil/pixdesc.h
};
#else
// Linux...
#ifdef __cplusplus
extern C
{
#endif
#include libavcodec/avcodec.h
#include libavformat/avformat.h
#include libavutil/avutil.h
#include libavutil/opt.h
#include libavutil/pixdesc.h
#ifdef __cplusplus
};
#endif
#endifFILE *fp_open;
FILE *fp_write;// Read File
int read_buffer(void *opaque, uint8_t *buf, int buf_size)
{if (!feof(fp_open)){int true_size fread(buf, 1, buf_size, fp_open);return true_size;}else{return -1;}
}// Write File
int write_buffer(void *opaque, uint8_t *buf, int buf_size)
{if (!feof(fp_write)){int true_size fwrite(buf, 1, buf_size, fp_write);return true_size;}else{return -1;}
}int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
{int ret;int got_frame;AVPacket enc_pkt;if (!(fmt_ctx-streams[stream_index]-codec-codec-capabilities CODEC_CAP_DELAY))return 0;while (1){av_log(NULL, AV_LOG_INFO, Flushing stream #%u encoder\n, stream_index);//ret encode_write_frame(NULL, stream_index, got_frame);enc_pkt.data NULL;enc_pkt.size 0;av_init_packet(enc_pkt);ret avcodec_encode_video2(fmt_ctx-streams[stream_index]-codec, enc_pkt,NULL, got_frame);av_frame_free(NULL);if (ret 0)break;if (!got_frame){ret 0;break;}// prepare packet for muxingenc_pkt.stream_index stream_index;enc_pkt.dts av_rescale_q_rnd(enc_pkt.dts,fmt_ctx-streams[stream_index]-codec-time_base,fmt_ctx-streams[stream_index]-time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.pts av_rescale_q_rnd(enc_pkt.pts,fmt_ctx-streams[stream_index]-codec-time_base,fmt_ctx-streams[stream_index]-time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.duration av_rescale_q(enc_pkt.duration,fmt_ctx-streams[stream_index]-codec-time_base,fmt_ctx-streams[stream_index]-time_base);av_log(NULL, AV_LOG_DEBUG, Muxing frame.\n);// mux encoded frameret av_write_frame(fmt_ctx, enc_pkt);if (ret 0)break;}return ret;
}int main(int argc, char* argv[])
{int ret;AVFormatContext *ifmt_ctx NULL;AVFormatContext *ofmt_ctx NULL;AVPacket packet, enc_pkt;AVFrame *frame NULL;enum AVMediaType type;unsigned int stream_index;unsigned int i 0;int got_frame, enc_got_frame;AVStream *out_stream;AVStream *in_stream;AVCodecContext *dec_ctx, *enc_ctx;AVCodec *encoder;fp_open fopen(cuc60anniversary_start.ts, rb); // 视频源文件 fp_write fopen(cuc60anniversary_start.h264, wb); // 输出文件av_register_all();ifmt_ctx avformat_alloc_context();avformat_alloc_output_context2(ofmt_ctx, NULL, h264, NULL);unsigned char* inbuffer NULL;unsigned char* outbuffer NULL;inbuffer (unsigned char*)av_malloc(32768);outbuffer (unsigned char*)av_malloc(32768);AVIOContext *avio_in NULL;AVIOContext *avio_out NULL;// open input fileavio_in avio_alloc_context(inbuffer, 32768, 0, NULL, read_buffer, NULL, NULL);if (avio_in NULL)goto end;ifmt_ctx-pb avio_in;ifmt_ctx-flags AVFMT_FLAG_CUSTOM_IO;if ((ret avformat_open_input(ifmt_ctx, whatever, NULL, NULL)) 0){av_log(NULL, AV_LOG_ERROR, Cannot open input file.\n);return ret;}if ((ret avformat_find_stream_info(ifmt_ctx, NULL)) 0){av_log(NULL, AV_LOG_ERROR, Cannot find stream information.\n);return ret;}for (i 0; i ifmt_ctx-nb_streams; i){AVStream *stream;AVCodecContext *codec_ctx;stream ifmt_ctx-streams[i];codec_ctx stream-codec;// Reencode video audio and remux subtitles etcif (codec_ctx-codec_type AVMEDIA_TYPE_VIDEO){// Open decoderret avcodec_open2(codec_ctx,avcodec_find_decoder(codec_ctx-codec_id), NULL);if (ret 0){av_log(NULL, AV_LOG_ERROR, Failed to open decoder for stream #%u.\n, i);return ret;}}}// Print some input information//av_dump_format(ifmt_ctx, 0, whatever, 0);// open output fileavio_out avio_alloc_context(outbuffer, 32768, 1, NULL, NULL, write_buffer, NULL);if (avio_out NULL)goto end;//avio_out-write_packetwrite_packet;ofmt_ctx-pb avio_out;ofmt_ctx-flags AVFMT_FLAG_CUSTOM_IO;for (i 0; i 1; i){out_stream avformat_new_stream(ofmt_ctx, NULL);if (!out_stream){av_log(NULL, AV_LOG_ERROR, Failed allocating output stream.\n);return AVERROR_UNKNOWN;}in_stream ifmt_ctx-streams[i];dec_ctx in_stream-codec;enc_ctx out_stream-codec;if (dec_ctx-codec_type AVMEDIA_TYPE_VIDEO){encoder avcodec_find_encoder(AV_CODEC_ID_H264);enc_ctx-height dec_ctx-height;enc_ctx-width dec_ctx-width;enc_ctx-sample_aspect_ratio dec_ctx-sample_aspect_ratio;enc_ctx-pix_fmt encoder-pix_fmts[0];enc_ctx-time_base dec_ctx-time_base;//enc_ctx-time_base.num 1;//enc_ctx-time_base.den 25;// H.264的必备选项没有就会错enc_ctx-me_range 16;enc_ctx-max_qdiff 4;enc_ctx-qmin 10;enc_ctx-qmax 51;enc_ctx-qcompress 0.6;enc_ctx-refs 3;enc_ctx-bit_rate 500000;ret avcodec_open2(enc_ctx, encoder, NULL);if (ret 0){av_log(NULL, AV_LOG_ERROR, Cannot open video encoder for stream #%u.\n, i);return ret;}}else if (dec_ctx-codec_type AVMEDIA_TYPE_UNKNOWN){av_log(NULL, AV_LOG_FATAL, Elementary stream #%d is of unknown type, cannot proceed.\n, i);return AVERROR_INVALIDDATA;}else{// if this stream must be remuxedret avcodec_copy_context(ofmt_ctx-streams[i]-codec,ifmt_ctx-streams[i]-codec);if (ret 0){av_log(NULL, AV_LOG_ERROR, Copying stream context failed.\n);return ret;}}if (ofmt_ctx-oformat-flags AVFMT_GLOBALHEADER)enc_ctx-flags | CODEC_FLAG_GLOBAL_HEADER;}// Print some output information//av_dump_format(ofmt_ctx, 0, whatever, 1);// init muxer, write output file headerret avformat_write_header(ofmt_ctx, NULL);if (ret 0){av_log(NULL, AV_LOG_ERROR, Error occurred when opening output file.\n);return ret;}i 0;// read all packetswhile (1){i;if ((ret av_read_frame(ifmt_ctx, packet)) 0)break;stream_index packet.stream_index;if (stream_index ! 0)continue;type ifmt_ctx-streams[packet.stream_index]-codec-codec_type;av_log(NULL, AV_LOG_DEBUG, Demuxer gave frame of stream_index %u.\n,stream_index);av_log(NULL, AV_LOG_DEBUG, Going to reencode the frame.\n);frame av_frame_alloc();if (!frame){ret AVERROR(ENOMEM);break;}// 解压缩时间戳packet.dts av_rescale_q_rnd(packet.dts,ifmt_ctx-streams[stream_index]-time_base,ifmt_ctx-streams[stream_index]-codec-time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));// 显示时间戳packet.pts av_rescale_q_rnd(packet.pts,ifmt_ctx-streams[stream_index]-time_base,ifmt_ctx-streams[stream_index]-codec-time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));// 解码输入文件ret avcodec_decode_video2(ifmt_ctx-streams[stream_index]-codec, frame,got_frame, packet);printf(Decode 1 Packet\tsize:%d\tpts:%lld.\n, packet.size, packet.pts);if (ret 0){av_frame_free(frame);av_log(NULL, AV_LOG_ERROR, Decoding failed.\n);break;}if (got_frame){frame-pts av_frame_get_best_effort_timestamp(frame);frame-pict_type AV_PICTURE_TYPE_NONE;enc_pkt.data NULL;enc_pkt.size 0;av_init_packet(enc_pkt);ret avcodec_encode_video2(ofmt_ctx-streams[stream_index]-codec, enc_pkt,frame, enc_got_frame);printf(Encode 1 Packet\tsize:%d\tpts:%lld.\n, enc_pkt.size, enc_pkt.pts);av_frame_free(frame);if (ret 0)goto end;if (!enc_got_frame)continue;// prepare packet for muxingenc_pkt.stream_index stream_index;enc_pkt.dts av_rescale_q_rnd(enc_pkt.dts,ofmt_ctx-streams[stream_index]-codec-time_base,ofmt_ctx-streams[stream_index]-time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.pts av_rescale_q_rnd(enc_pkt.pts,ofmt_ctx-streams[stream_index]-codec-time_base,ofmt_ctx-streams[stream_index]-time_base,(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));enc_pkt.duration av_rescale_q(enc_pkt.duration,ofmt_ctx-streams[stream_index]-codec-time_base,ofmt_ctx-streams[stream_index]-time_base);av_log(NULL, AV_LOG_INFO, Muxing frame %d.\n, i);// mux encoded frameav_write_frame(ofmt_ctx, enc_pkt);if (ret 0)goto end;}else{av_frame_free(frame);}av_free_packet(packet);}// flush encodersfor (i 0; i 1; i){// flush encoderret flush_encoder(ofmt_ctx, i);if (ret 0){av_log(NULL, AV_LOG_ERROR, Flushing encoder failed.\n);goto end;}}// Write file trailerav_write_trailer(ofmt_ctx);end:// avio_in 释放之前需要先释放 avio_in-buffer, 否则会出现内存泄漏av_free(avio_in-buffer);av_freep(avio_in);av_free(avio_out-buffer);av_freep(avio_out);av_free(inbuffer);av_free(outbuffer);av_free_packet(packet);av_frame_free(frame);avformat_close_input(ifmt_ctx);avformat_free_context(ofmt_ctx);//fcloseall();fclose(fp_open);fclose(fp_write);if (ret 0)av_log(NULL, AV_LOG_ERROR, Error occurred.\n);return (ret ? 1 : 0);
}结果
一开始会报错 属性页将 SDL 检查设置为否 程序的运行结果如下 输入文件 输出文件 工程文件下载
GitHubUestcXiye / Simplest-FFmpeg-Memory-Transcoder
CSDNSimplest FFmpeg Memory Transcoder.zip
参考链接
ffmpeg 从内存中读取数据(或将数据输出到内存)100行代码实现最简单的基于FFMPEGSDL的视频播放器SDL1.x最简单的基于FFMPEG的视频编码器YUV编码为H.264最简单的基于FFMPEG的转码程序