最簡單的基於FFmpeg的記憶體讀寫的例子:記憶體轉碼器,ffmpeg讀寫
上篇文章記錄了一個基於FFmpeg的記憶體播放器,可以使用FFmpeg讀取並播放記憶體中的資料。這篇文章記錄一個基於FFmpeg的記憶體轉碼器。該轉碼器可以使用FFmpeg讀取記憶體中的資料,轉碼為H.264之後再將資料輸出到記憶體。
關於如何從記憶體讀取資料,以及如何將資料輸出到記憶體,可以參考文章:
ffmpeg 從記憶體中讀取資料(或將資料輸出到記憶體)
FFmpeg讀寫記憶體的關鍵點有2個:
1. 初始化自訂的AVIOContext,指定自訂的回呼函數。
2. 自己寫回呼函數。注意函數的參數和傳回值(尤其是傳回值)。
轉碼實際上就是解碼和編碼的結合。該方面的知識可以參考文章:
解碼:100行代碼實現最簡單的基於FFMPEG+SDL的視頻播放器(SDL1.x)
編碼:最簡單的基於FFMPEG的視頻編碼器(YUV編碼為H.264)
轉碼: 最簡單的基於FFMPEG的轉碼程式
流程程式的流程圖如所示。可以看出,首先分別初始化了輸入和輸出的AVFormatContext。然後首先解碼輸入的AVPacket,得到儲存像素資料(YUV420P格式)的AVFrame,然後編碼AVFrame為H.264的AVPacket,最後將編碼後的AVPacket輸出。
代碼下面直接貼上代碼:
/** * 最簡單的基於FFmpeg的記憶體讀寫例子(記憶體轉碼器) * Simplest FFmpeg mem Transcoder * * 雷霄驊,張暉 * leixiaohua1020@126.com * 中國傳媒大學/數字電視技術 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程式實現了任意格式視頻資料(例如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. * It's the simplest example to use FFmpeg to read (or write) from * memory. * */#include <stdio.h>extern "C"{#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libavutil/avutil.h"#include "libavutil/opt.h"#include "libavutil/pixdesc.h"};FILE *fp_open;FILE *fp_write;//Read Fileint 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 Fileint 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 muxing */enc_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 frame */ret = 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);/*open input file*/AVIOContext *avio_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 etc. */if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO){/* Open decoder */ret = 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;}}}//av_dump_format(ifmt_ctx, 0, "whatever", 0);/*open output file*/AVIOContext *avio_out =avio_alloc_context(outbuffer, 32768,0,NULL,NULL,write_buffer,NULL); if(avio_out==NULL)goto end;//avio_out->write_packet=write_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;//H264的必備選項,沒有就會錯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 remuxed */ret = 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;}//av_dump_format(ofmt_ctx, 0, "whatever", 1);/* init muxer, write output file header */ret = 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 packets */while (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:%d\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:%d\n",enc_pkt.size,enc_pkt.pts);av_frame_free(&frame);if (ret < 0)goto end;if (!enc_got_frame)continue;/* prepare packet for muxing */enc_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 frame */av_write_frame(ofmt_ctx,&enc_pkt);if (ret < 0)goto end;} else {av_frame_free(&frame);}av_free_packet(&packet);}/* flush encoders */for (i = 0; i < 1; i++) {/* flush encoder */ret = flush_encoder(ofmt_ctx,i);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Flushing encoder failed\n");goto end;}}av_write_trailer(ofmt_ctx);end:av_freep(avio_in);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();if (ret < 0)av_log(NULL, AV_LOG_ERROR, "Error occurred\n");return (ret? 1:0);}
結果
程式啟動並執行結果如所示。
轉碼前的視頻資訊使用MediaInfo查看如所示。
轉碼後的視頻資訊使用MediaInfo查看如所示。
下載
SourceForge項目首頁:
https://sourceforge.net/projects/simplestffmpegmemhandler/
CSDN項目:
http://download.csdn.net/detail/leixiaohua1020/8003731
註:
本工程包含兩個FFmpeg讀寫記憶體的例子:
simplest_ffmpeg_mem_player:基於FFmpeg的記憶體播放器。
simplest_ffmpeg_mem_transcoder:基於FFmpeg的記憶體轉碼器。
怎使用ffmpeg對記憶體中的資料進行解碼
live555不是很瞭解。。。 不過在用戶端不是應該先把RTP包解出資料。。 然後放進記憶體? 要是這邊存入的已經是H264格式視頻資料,就可以直接用ffmpeg進行解碼了啊。。。。。
FFMPEG視頻轉換問題
百度網頁搜尋:視頻轉換大師專業版
國產優秀視頻格式 轉換器,專業的轉換軟體,為視頻格式檔案轉換提供終極解決的方案,它能夠讀取各種視頻和音頻檔案,並且將他們快速轉換為流行的媒體檔案格式。擁有非常漂亮 友好的介面. 它幾乎涵蓋了所有流行的影音多媒體檔案格式, 包括AVI, Mpg, RM, RMVB, 3GP, MP4, AMV, Mpeg, Mpeg1, Mpeg2, Mpeg4, VCD, SVCD, DVD, XVID, DivX, ASF, WMV, SWF, IPOD, PSP, GIF, MJPEG, QuickTime, MOV, FLV, MKV, DV以及所有的音頻格式.
支援FLV/F4V, AVI/DIVX/XVID/DIV, MPEG/MPG/DAT, WMV/ASF/ASX, RM/RMVB, MOV/QT, 3GP/3G2, MP4/M4V 和所有可播放視頻格式。支援批量檔案轉換,如何轉換mp4支援二次編碼,確保最佳的輸出視頻品質,支援直接將任意視頻檔案轉換為 3GP/3G2 格式,不損失畫面品質,輸出視頻支援多種解析度 (QCIF, SQCIF, CIF, VGA, QVGA 等)。簡潔的使用者介面,非常便於使用,參數設定十分簡單