123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875 |
- #ifdef _WIN32
- #include "stdafx.h"
- #endif
- #include "FFmpegWriter.h"
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <math.h>
- #define STREAM_DURATION 10.0
- #define STREAM_FRAME_RATE 25 /* 25 images/s */
- #define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */
- #define SCALE_FLAGS SWS_BICUBIC
- // a wrapper around a single output AVStream
- struct OutputStream_t {
- AVStream *st;
- AVCodecContext *enc;
- /* pts of the next frame that will be generated */
- int64_t next_pts;
- int samples_count;
- AVFrame *frame;
- AVFrame *tmp_frame;
- struct SwsContext *sws_ctx;
- struct SwrContext *swr_ctx;
- };
- char av_ts_string[AV_TS_MAX_STRING_SIZE] = { 0 };
- #define av_ts2str(ts) av_ts_make_string(av_ts_string, ts)
- char av_ts_buff[AV_TS_MAX_STRING_SIZE] = { 0 };
- #define av_ts2timestr(ts, tb) av_ts_make_time_string(av_ts_buff, ts, tb)
- static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, LogApi* pLogApi)
- {
- AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
- //pLogApi->Debug("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d",
- // av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
- // av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
- // av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
- // pkt->stream_index);
- }
- static void log_callback(void* ptr, int level, const char* fmt, va_list vl)
- {
- // va_list vl2;
- // char line[1024] = {0};
- // static int print_prefix = 1;
- // va_copy(vl2, vl);
- // av_log_default_callback(ptr, level, fmt, vl);
- // av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
- // va_end(vl2);
- AVClassCategory type = av_default_get_category(ptr);
- if (AV_CLASS_CATEGORY_ENCODER == type) {
- AVCodecContext* c = (AVCodecContext*)ptr;
- if (c != NULL) {
- LogApi* pLogApi = (LogApi*)c->opaque;
- if (pLogApi != NULL) {
- pLogApi->vDebug(fmt, vl);
- }
- }
- }
- //if (report_file_level >= level) {
- // fputs(line, report_file);
- // fflush(report_file);
- //}
- }
- static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt, LogApi* pLogApi)
- {
- /* rescale output packet timestamp values from codec to stream timebase */
- av_packet_rescale_ts(pkt, *time_base, st->time_base);
- pkt->stream_index = st->index;
- /* Write the compressed frame to the media file. */
- log_packet(fmt_ctx, pkt, pLogApi);
- return av_interleaved_write_frame(fmt_ctx, pkt);
- }
- /* Add an output stream. */
- static bool add_stream(LogApi* pLogApi, OutputStream *ost, AVFormatContext *oc,
- AVCodec **codec,
- enum AVCodecID codec_id,
- int width, int height, int colorbit, int nfps,
- int nSamplePsec, int nchannels)
- {
- /* find the encoder */
- *codec = avcodec_find_encoder(codec_id);
- if (!(*codec)) {
- pLogApi->Debug("add_stream failed for Could not find encoder for %s.", avcodec_get_name(codec_id));
- return false;
- }
- ost->st = avformat_new_stream(oc, NULL);
- if (!ost->st) {
- return false;
- }
-
- ost->st->id = oc->nb_streams-1;
- AVCodecContext* enc_ctx = avcodec_alloc_context3(*codec);
- if (!enc_ctx) {
- pLogApi->Debug("add_stream failed for Could not alloc an encoding context!");
- return false ;
- }
- ost->enc = enc_ctx;
- switch ((*codec)->type) {
- case AVMEDIA_TYPE_AUDIO:
- {
- enc_ctx->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
- enc_ctx->sample_rate = nSamplePsec;
- if ((*codec)->supported_samplerates) {
- for (int i = 0; (*codec)->supported_samplerates[i]; i++) {
- //pLogApi->Debug("(*codec)->supported_samplerates[%d] = %d.", i, (*codec)->supported_samplerates[i]);
- }
- }
- if (nchannels == 2){
- enc_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
- enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
- }
- else if (nchannels == 1){
- enc_ctx->channel_layout = AV_CH_LAYOUT_MONO;
- enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
- }
-
- if ((*codec)->channel_layouts) {
- enc_ctx->channel_layout = (*codec)->channel_layouts[0];
- for (int i = 0; (*codec)->channel_layouts[i]; i++) {
- if ((*codec)->channel_layouts[i] == AV_CH_LAYOUT_STEREO) {
- enc_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
- }
- }
- }
- enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
- AVRational tb = { 1, enc_ctx->sample_rate };
- ost->st->time_base = tb;
- }
- break;
- case AVMEDIA_TYPE_VIDEO:
- {
- enc_ctx->codec_id = codec_id;
- //c->bit_rate = 400000;
- /* Resolution must be a multiple of two. */
- enc_ctx->width = width;
- enc_ctx->height = height;
- /* timebase: This is the fundamental unit of time (in seconds) in terms
- * of which frame timestamps are represented. For fixed-fps content,
- * timebase should be 1/framerate and timestamp increments should be
- * identical to 1. */
- AVRational time_rb = { 1, nfps };
- //ost->st->time_base = time_rb;
- //AVRational nfps_rb = { nfps, 1 };
- //ost->st->avg_frame_rate = nfps_rb;
- //enc_ctx->framerate = nfps_rb;
- enc_ctx->time_base = time_rb;
- //c->gop_size = 12; /* emit one intra frame every twelve frames at most */
- enc_ctx->pix_fmt = STREAM_PIX_FMT;
- if (enc_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
- /* just for testing, we also add B-frames */
- enc_ctx->max_b_frames = 2;
- }
- if (enc_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
- /* Needed to avoid using macroblocks in which some coeffs overflow.
- * This does not happen with normal video, it just happens here as
- * the motion of the chroma plane does not match the luma plane. */
- enc_ctx->mb_decision = 2;
- }
- }
- av_opt_set(enc_ctx->priv_data, "preset", "superfast", 0);
- av_opt_set(enc_ctx->priv_data, "tune", "zerolatency", 0);
- break;
- default:
- break;
- }
- /* Some formats want stream headers to be separate. */
- if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
- enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
- }
- //pLogApi->Debug("add_stream success get encoder for '%s'", avcodec_get_name(codec_id));
- return true;
- }
- /**************************************************************/
- /* audio output */
- static AVFrame *alloc_audio_frame(enum AVSampleFormat sample_fmt,
- uint64_t channel_layout,
- int sample_rate, int nb_samples)
- {
- AVFrame *frame = av_frame_alloc();
- int ret = -1;
- if (!frame) {
- return NULL;
- }
- frame->format = sample_fmt;
- frame->channel_layout = channel_layout;
- frame->sample_rate = sample_rate;
- frame->nb_samples = nb_samples;
- if (nb_samples) {
- ret = av_frame_get_buffer(frame, 0);
- if (ret < 0) {
- av_frame_free(&frame);
- return NULL;
- }
- }
- return frame;
- }
- static bool open_audio(LogApi* pLogApi, AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg, enum AVSampleFormat in_sample_fmt)
- {
- AVCodecContext *c = NULL;
- int nb_samples = 0;
- int ret = -1;
- AVDictionary *opt = NULL;
- c = ost->enc;
- c->opaque = pLogApi;
- /* open it */
- av_dict_copy(&opt, opt_arg, 0);
- ret = avcodec_open2(c, codec, &opt);
- av_dict_free(&opt);
-
- if (ret < 0) {
- pLogApi->Debug("open_audio failed for could not open audio codec: %s, ret: %d.", avcodec_get_name(codec->id), ret);
- return false;
- }
- if (c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) {
- nb_samples = 10000;
- }
- else {
- nb_samples = c->frame_size;
- }
- ost->frame = alloc_audio_frame(c->sample_fmt, c->channel_layout, c->sample_rate, nb_samples);
- ost->tmp_frame = alloc_audio_frame(in_sample_fmt, c->channel_layout, c->sample_rate, nb_samples);
- /* copy the stream parameters to the muxer */
- ret = avcodec_parameters_from_context(ost->st->codecpar, c);
- if (ret < 0) {
- pLogApi->Debug("open_audio failed for could not copy the stream parameters.");
- return false;
- }
- /* create resampler context */
- ost->swr_ctx = swr_alloc();
- if (!ost->swr_ctx) {
- pLogApi->Debug("open_audio failed for could not allocate resampler context.");
- return false;
- }
- /* set options */
- av_opt_set_int(ost->swr_ctx, "in_channel_count", c->channels, 0);
- av_opt_set_int(ost->swr_ctx, "in_sample_rate", c->sample_rate, 0);
- av_opt_set_sample_fmt(ost->swr_ctx, "in_sample_fmt", in_sample_fmt, 0);
- av_opt_set_int(ost->swr_ctx, "out_channel_count", c->channels, 0);
- av_opt_set_int(ost->swr_ctx, "out_sample_rate", c->sample_rate, 0);
- av_opt_set_sample_fmt(ost->swr_ctx, "out_sample_fmt", c->sample_fmt, 0);
- /* initialize the resampling context */
- if ((ret = swr_init(ost->swr_ctx)) < 0) {
- pLogApi->Debug("open_audio failed for failed to initialize the resampling context.");
- return false;
- }
- //pLogApi->Debug("open_audio success encoder for %s, nb_samples:%d.", avcodec_get_name(codec->id), nb_samples);
- return true;
- }
- /* Prepare a 16 bit dummy audio frame of 'frame_size' samples and
- * 'nb_channels' channels. */
- static AVFrame *get_audio_frame(OutputStream *ost, ByteBuffer* audio_input_buffer)
- {
- AVFrame *frame = ost->tmp_frame;
- audio_input_buffer->getBytes(frame->data[0], av_samples_get_buffer_size(NULL, frame->channels,
- frame->nb_samples,
- (AVSampleFormat)frame->format, 0));
- frame->pts = ost->next_pts;
- ost->next_pts += frame->nb_samples;
- return frame;
- }
- /*
- * encode one audio frame and send it to the muxer
- * return 1 when encoding is finished, 0 otherwise
- */
- static int write_audio_frame(LogApi* pLogApi, AVFormatContext *oc, OutputStream *audio_ost, ByteBuffer* audio_input_buffer)
- {
- AVCodecContext *enc_ctx = NULL;
- AVPacket pkt = { 0 }; // data and size must be 0;
- AVFrame *frame = NULL;
- int ret = -1;
- int got_packet = 0;
- int dst_nb_samples = 0;
- av_init_packet(&pkt);
- enc_ctx = audio_ost->enc;
- frame = get_audio_frame(audio_ost, audio_input_buffer);
- if (frame) {
- /* convert samples from native format to destination codec format, using the resampler */
- /* compute destination number of samples */
- dst_nb_samples = av_rescale_rnd(swr_get_delay(audio_ost->swr_ctx, enc_ctx->sample_rate) + frame->nb_samples,
- enc_ctx->sample_rate, enc_ctx->sample_rate, AV_ROUND_UP);
- av_assert0(dst_nb_samples == frame->nb_samples);
- /* when we pass a frame to the encoder, it may keep a reference to it
- * internally;
- * make sure we do not overwrite it here
- */
- ret = av_frame_make_writable(audio_ost->frame);
- if (ret < 0) {
- pLogApi->Debug("write_audio_frame failed for av_frame_make_writable: %d.", ret);
- return -1;
- }
- #ifdef RVC_OS_WIN
- //pLogApi->Debug("write_audio_frame nb_samples: %d, dst_nb_samples: %d, pts: %I64d", frame->nb_samples, dst_nb_samples, frame->pts);
- #else
- //pLogApi->Debug("write_audio_frame nb_samples: %d, dst_nb_samples: %d, pts: %lld", frame->nb_samples, dst_nb_samples, frame->pts);
- #endif
- /* convert to destination format */
- ret = swr_convert(audio_ost->swr_ctx,
- audio_ost->frame->data, dst_nb_samples,
- (const uint8_t **)frame->data, frame->nb_samples);
- if (ret < 0) {
- pLogApi->Debug("write_audio_frame failed for Error while converting.");
- return -1;
- }
- frame = audio_ost->frame;
- AVRational rb = { 1, enc_ctx->sample_rate };
- frame->pts = av_rescale_q(audio_ost->samples_count, rb, enc_ctx->time_base);
- audio_ost->samples_count += dst_nb_samples;
- #ifdef RVC_OS_WIN
- //pLogApi->Debug("write_audio_frame new nb_samples: %d, pts: %I64d", frame->nb_samples, frame->pts);
- #else
- //pLogApi->Debug("write_audio_frame new nb_samples: %d, pts: %lld", frame->nb_samples, frame->pts);
- #endif
- }
- ret = avcodec_encode_audio2(enc_ctx, &pkt, frame, &got_packet);
- //ret = avcodec_send_frame(c, frame);
- //if (ret < 0) {
- // pLogApi->Debug("ffmpeg_encoder_process Error sending the frame to the encoder");
- //}
- //else {
- // got_packet = avcodec_receive_packet(c, &pkt);
- // if (got_packet < 0) {
- // pLogApi->Debug("avcodec_receive_packet failed.");
- // return got_packet;
- // }
- //}
- if (got_packet) {
- //pLogApi->Debug("write_audio_frame got_packet");
- ret = write_frame(oc, &enc_ctx->time_base, audio_ost->st, &pkt, pLogApi);
- if (ret < 0) {
- pLogApi->Debug("write_audio_frame failed for Error while writing audio frame: %d.", ret);
- return -1;
- }
- }
- return (frame || got_packet) ? 0 : 1;
- }
- /**************************************************************/
- /* video output */
- static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
- {
- AVFrame *picture = NULL;
- int ret = -1;
- picture = av_frame_alloc();
- if (!picture) {
- return NULL;
- }
- picture->format = pix_fmt;
- picture->width = width;
- picture->height = height;
- /* allocate the buffers for the frame data */
- ret = av_frame_get_buffer(picture, 32);
- if (ret < 0) {
- av_frame_free(&picture);
- return NULL;
- }
- return picture;
- }
- static bool open_video(LogApi* pLogApi, AVFormatContext *oc, AVCodec *codec, OutputStream *video_ost, AVDictionary *opt_arg, enum AVPixelFormat input_pix_fmt)
- {
- int ret = -1;
- AVCodecContext *enc_ctx = video_ost->enc;
- AVDictionary *opt = NULL;
- av_dict_copy(&opt, opt_arg, 0);
- //huchen add for msmpeg4v3
- //av_dict_set(&opt, "lmin", "1180", 0);
- //av_dict_set(&opt, "lmax", "2360", 0);
- enc_ctx->bit_rate = 128*1000;
- //c->qmin = 16;
- //c->qmax = 30;
- //c->max_b_frames = 0;
- enc_ctx->opaque = pLogApi;
- /* open the codec */
- ret = avcodec_open2(enc_ctx, codec, &opt);
- av_dict_free(&opt);
- if (ret < 0) {
- pLogApi->Debug("open_video failed for Could not open video codec: %s, ret :%d.", avcodec_get_name(codec->id), ret);
- return false;
- }
- /* allocate and init a re-usable frame */
- video_ost->frame = alloc_picture(enc_ctx->pix_fmt, enc_ctx->width, enc_ctx->height);
- if (!video_ost->frame) {
- pLogApi->Debug("open_video failed for Could not allocate video frame.");
- return false;
- }
- /* If the output format is not input_pix_fmt, then a temporary input_pix_fmt
- * picture is needed too. It is then converted to the required
- * output format. */
- video_ost->tmp_frame = NULL;
- if (enc_ctx->pix_fmt != input_pix_fmt) {
- video_ost->tmp_frame = alloc_picture(input_pix_fmt, enc_ctx->width, enc_ctx->height);
- if (!video_ost->tmp_frame) {
- pLogApi->Debug("Could not allocate temporary picture.");
- return false;
- }
- }
- /* copy the stream parameters to the muxer */
- ret = avcodec_parameters_from_context(video_ost->st->codecpar, enc_ctx);
- if (ret < 0) {
- pLogApi->Debug("open_video failed for Could not copy the stream parameters.");
- return false;
- }
- //pLogApi->Debug("open_video success encoder for %s.", avcodec_get_name(codec->id));
- //pLogApi->Debug("open_video success encoder output_fmt: %s.", av_get_pix_fmt_name(enc_ctx->pix_fmt));
- //pLogApi->Debug("open_video success encoder input_fmt: %s.", av_get_pix_fmt_name(input_pix_fmt));
- //pLogApi->Debug("open_video success encoder, video enc_ctx time_base num: %d.", enc_ctx->time_base.num);
- //pLogApi->Debug("open_video success encoder, video enc_ctx time_base den: %d.", enc_ctx->time_base.den);
- return true;
- }
- static AVFrame *get_video_frame(LogApi* pLogApi, OutputStream *video_ost, char *data, int len, AVPixelFormat input_pix_fmt)
- {
- AVCodecContext *enc_ctx = video_ost->enc;
- /* when we pass a frame to the encoder, it may keep a reference to it
- * internally; make sure we do not overwrite it here */
- if (av_frame_make_writable(video_ost->frame) < 0) {
- return NULL;
- }
-
- if (enc_ctx->pix_fmt != input_pix_fmt) {
- /* we must convert it
- * to the codec pixel format if needed */
- if (!video_ost->sws_ctx) {
- video_ost->sws_ctx = sws_getContext(enc_ctx->width, enc_ctx->height,
- input_pix_fmt,
- enc_ctx->width,
- enc_ctx->height,
- enc_ctx->pix_fmt,
- SCALE_FLAGS, NULL, NULL, NULL);
- if (!video_ost->sws_ctx) {
- return NULL;
- }
- }
- //copy
- //因videoqueue中视频均为rgb24位格式,默认为24bpp
- #ifdef _WIN32
- for (int i = video_ost->tmp_frame->height -1, j = 0; i >= 0 && j < video_ost->tmp_frame->height; i--, j++){
- memcpy(video_ost->tmp_frame->data[0] + j * video_ost->tmp_frame->linesize[0],
- data + i * video_ost->tmp_frame->width * 3, video_ost->tmp_frame->width * 3);
- }
- #else
- if (video_ost->tmp_frame->linesize[0] > video_ost->tmp_frame->width * 3) {
- for (int i = 0; i < video_ost->tmp_frame->height; i++)
- {
- memcpy(video_ost->tmp_frame->data[0] + i * video_ost->tmp_frame->linesize[0],
- data + i * video_ost->tmp_frame->width * 3, video_ost->tmp_frame->width * 3);
- }
- }
- else {
- memcpy(video_ost->tmp_frame->data[0], data, len);
- }
- #endif
-
- //convert
- sws_scale(video_ost->sws_ctx, (const uint8_t* const*)video_ost->tmp_frame->data,
- video_ost->tmp_frame->linesize, 0, enc_ctx->height, video_ost->frame->data,
- video_ost->frame->linesize);
- }
- else {
- //copy
- //因videoqueue中视频均为rgb24位格式,默认为24bpp
- #ifdef _WIN32
- for (int i = video_ost->tmp_frame->height - 1, j = 0; i >= 0 && j < video_ost->tmp_frame->height; i--, j++){
- memcpy(video_ost->tmp_frame->data[0] + j * video_ost->tmp_frame->linesize[0],
- data + i * video_ost->tmp_frame->width * 3, video_ost->tmp_frame->width * 3);
- }
- #else
- if (video_ost->tmp_frame->linesize[0] > video_ost->tmp_frame->width * 3) {
- for (int i = 0; i < video_ost->tmp_frame->height; i++)
- {
- memcpy(video_ost->tmp_frame->data[0] + i * video_ost->tmp_frame->linesize[0],
- data + i * video_ost->tmp_frame->width * 3, video_ost->tmp_frame->width * 3);
- }
- }
- else {
- memcpy(video_ost->tmp_frame->data[0], data, len);
- }
- #endif
- }
- video_ost->frame->pts = video_ost->next_pts++;
- //video_ost->frame->key_frame = 1;
- return video_ost->frame;
- }
- /*
- * encode one video frame and send it to the muxer
- * return 1 when encoding is finished, 0 otherwise
- */
- static int write_video_frame(LogApi* pLogApi, AVFormatContext *oc, OutputStream *ost, char *data, int len, AVPixelFormat input_pix_fmt)
- {
- int ret = -1;
- AVCodecContext *enc_ctx = NULL;
- AVFrame *vframe = NULL;
- int got_packet = 0;
- AVPacket pkt = { 0 };
- enc_ctx = ost->enc;
- vframe = get_video_frame(pLogApi, ost, data, len, input_pix_fmt);
- //pLogApi->Debug("get_video_frame pts = %d", vframe->pts);
- av_init_packet(&pkt);
- /* encode the image */
- int iencoderet = avcodec_encode_video2(enc_ctx, &pkt, vframe, &got_packet);
- if (iencoderet < 0) {
- pLogApi->Debug("open_video failed for Error(%d) encoding video frame.", iencoderet);
- return -1;
- }
- else {
- //pLogApi->Debug("avcodec_encode_video2 success(%d) and got_packet ret = %d.", iencoderet, got_packet);
- }
- if (got_packet) {
- //pLogApi->Debug("write_video_frame got_packet");
- ret = write_frame(oc, &enc_ctx->time_base, ost->st, &pkt, pLogApi);
- }
- else {
- ret = 0;
- }
- if (ret < 0) {
- pLogApi->Debug("open_video failed for Error while writing video frame: %d.", ret);
- return -1;
- }
- return (vframe || got_packet) ? 0 : 1;
- }
- static void close_stream(AVFormatContext *oc, OutputStream *ost)
- {
- avcodec_free_context(&ost->enc);
- av_frame_free(&ost->frame);
- av_frame_free(&ost->tmp_frame);
- sws_freeContext(ost->sws_ctx);
- swr_free(&ost->swr_ctx);
- }
- /**************************************************************/
- /* media file output */
- bool FFmpegWriter::InitWriter(char* filename, int width, int height, int colorbit, int nfps,
- int nSamplePsec, int nchannels, int nBitPerSample, int nmaxspacing, int nquality, int nOutBitRate, int iAudioType)
- {
- int iret = -1;
- AVDictionary *opt = NULL;
- bool result = true;
- if (nBitPerSample != 8 && nBitPerSample != 16 && nBitPerSample != 32){
- m_pLogApi->Debug("Init FFmpegWriter Failed for BitPerSample = %d.", nBitPerSample);
- return false;
- }
- if (nchannels != 1 && nchannels != 2){
- m_pLogApi->Debug("Init FFmpegWriter Failed for channels = %d.", nchannels);
- return false;
- }
- if (colorbit != 24){
- m_pLogApi->Debug("Init FFmpegWriter Failed for colorbit = %d.", colorbit);
- return false;
- }
- if (colorbit == 24){
- m_input_pix_fmt = AV_PIX_FMT_BGR24;
- } else {
- m_input_pix_fmt = STREAM_PIX_FMT;
- }
- /* allocate the output media context */
- avformat_alloc_output_context2(&m_formatctx, NULL, NULL, filename);
- if (!m_formatctx) {
- m_pLogApi->Debug("Init FFmpegWriter Failed for avformat_alloc_output_context2, filename = %s.", filename);
- return false;
- }
- //av_log_set_level(AV_LOG_ERROR);
- //av_log_set_callback(log_callback);
- m_outfmt = m_formatctx->oformat;
- //m_pLogApi->Debug("real output format name of '%s' is %s, long_name is %s, mime_type is %s, extensions is %s.", filename, m_outfmt->name, m_outfmt->long_name, m_outfmt->mime_type, m_outfmt->extensions);
- m_video_st = new OutputStream();
- m_audio_st = new OutputStream();
- /* Add the audio and video streams using the default format codecs
- * and initialize the codecs. */
- if (m_outfmt->video_codec != AV_CODEC_ID_NONE) {
- result = add_stream(m_pLogApi, m_video_st, m_formatctx, &m_video_codec, m_outfmt->video_codec, width, height, colorbit, nfps, nSamplePsec, nchannels);
- if (result == false){
- Close();
- m_pLogApi->Debug("Init FFmpegWriter Failed for add_stream, video_codec = %d.", m_outfmt->video_codec);
- return result;
- }
- m_bhave_video = true;
- }
- if (m_outfmt->audio_codec != AV_CODEC_ID_NONE) {
- result = add_stream(m_pLogApi, m_audio_st, m_formatctx, &m_audio_codec, m_outfmt->audio_codec, width, height, colorbit, nfps, nSamplePsec, nchannels);
- if (result == false){
- Close();
- m_pLogApi->Debug("Init FFmpegWriter Failed for add_stream, audio_codec = %d.", m_outfmt->audio_codec);
- return result;
- }
- m_bhave_audio = true;
- }
- /* Now that all the parameters are set, we can open the audio and
- * video codecs and allocate the necessary encode buffers. */
- if (m_bhave_video){
- result = open_video(m_pLogApi, m_formatctx, m_video_codec, m_video_st, opt, m_input_pix_fmt);
- if (result == false){
- Close();
- m_pLogApi->Debug("Init FFmpegWriter Failed for open_video, video_codec = %d.", m_outfmt->video_codec);
- return result;
- }
- }
- if (m_bhave_audio){
- if (nBitPerSample == 8){
- result = open_audio(m_pLogApi, m_formatctx, m_audio_codec, m_audio_st, opt, AV_SAMPLE_FMT_U8);
- if (result == false){
- Close();
- m_pLogApi->Debug("Init FFmpegWriter Failed for open_audio, AV_SAMPLE_FMT_U8.");
- return result;
- }
- }else if (nBitPerSample == 16){
- result = open_audio(m_pLogApi, m_formatctx, m_audio_codec, m_audio_st, opt, AV_SAMPLE_FMT_S16);
- if (result == false){
- Close();
- m_pLogApi->Debug("Init FFmpegWriter Failed for open_audio, AV_SAMPLE_FMT_S16.");
- return result;
- }
- }else if (nBitPerSample == 32) {
- result = open_audio(m_pLogApi, m_formatctx, m_audio_codec, m_audio_st, opt, AV_SAMPLE_FMT_FLT);
- if (result == false) {
- Close();
- m_pLogApi->Debug("Init FFmpegWriter Failed for open_audio, AV_SAMPLE_FMT_FLT.");
- return result;
- }
- }
- }
- av_dump_format(m_formatctx, 0, filename, 1);
- /* open the output file, if needed */
- if (!(m_outfmt->flags & AVFMT_NOFILE)) {
- iret = avio_open(&m_formatctx->pb, filename, AVIO_FLAG_WRITE);
- if (iret < 0) {
- Close();
- m_pLogApi->Debug("Init FFmpegWriter Failed for avio_open, %s.", filename);
- return false;
- }
- }
- m_audio_input_buffer = new ByteBuffer(3*nBitPerSample/8 * nchannels* nSamplePsec);
- m_pLogApi->Debug("Init FFmpegWriter success, audio_input_buffer:%d.", 3 * nBitPerSample / 8 * nchannels * nSamplePsec);
- m_bstart = false;
- return result;
- }
- bool FFmpegWriter::StartWrite()
- {
- AVDictionary *opt = NULL;
- /* Write the stream header, if any. */
- int ret = avformat_write_header(m_formatctx, &opt);
- if (ret < 0) {
- m_pLogApi->Debug("StartWrite Failed when opening output file: %d.", ret);
- return false;
- }
- m_pLogApi->Debug("FFmpegWriter StartWrite success.");
- m_bstart = true;
- return true;
- }
- bool FFmpegWriter::StopWrite() {
- /* Write the trailer, if any. The trailer must be written before you
- * close the CodecContexts open when you wrote the header; otherwise
- * av_write_trailer() may try to use memory that was freed on
- * av_codec_close(). */
- if (m_formatctx == NULL){
- m_pLogApi->Debug("End Failed when oc is null.");
- return false;
- }
-
- if (m_bstart) {
- av_write_trailer(m_formatctx);
- }
-
- Close();
- av_log_set_callback(NULL);
- m_bstart = false;
- m_pLogApi->Debug("FFmpegWriter End success.");
- return true;
- }
- void FFmpegWriter::Close()
- {
- /* Close each codec. */
- if (m_bhave_video) {
- close_stream(m_formatctx, m_video_st);
- }
- if (m_bhave_audio) {
- close_stream(m_formatctx, m_audio_st);
- }
- if (!(m_outfmt->flags & AVFMT_NOFILE)) {
- /* Close the output file. */
- avio_closep(&m_formatctx->pb);
- }
- /* free the stream */
- if (m_formatctx) {
- avformat_free_context(m_formatctx);
- }
- if (m_video_st != NULL) {
- delete m_video_st;
- m_video_st = NULL;
- }
- if (m_audio_st != NULL) {
- delete m_audio_st;
- m_audio_st = NULL;
- }
- if (m_audio_input_buffer != NULL) {
- delete m_audio_input_buffer;
- m_audio_input_buffer = NULL;
- }
- m_pLogApi->Debug("FFmpegWriter Close success.");
- }
- bool FFmpegWriter::ReceiveAudioData(unsigned char* pData, unsigned long len)
- {
- if (m_formatctx == NULL || m_audio_st == NULL || !m_bhave_audio){
- m_pLogApi->Debug("ReceiveAudioData Failed when oc is null.");
- return false;
- }
- //插入audio_input_buffer
- m_audio_input_buffer->putBytes(pData, len);
-
- AVFrame* frame = m_audio_st->tmp_frame;
- int frame_size = av_samples_get_buffer_size(NULL, frame->channels,
- frame->nb_samples,
- (AVSampleFormat)frame->format, 0);
- //m_pLogApi->Debug("ReceiveAudioData len:%d, available_data_len:%d, frame_size:%d.", len, audio_input_buffer->bytesRemaining(), frame_size);
- //循环写帧
- while (frame_size <= m_audio_input_buffer->bytesRemaining()) {
- //m_pLogApi->Debug("ReceiveAudioData available_data_len:%d.", audio_input_buffer->bytesRemaining());
- int result = write_audio_frame(m_pLogApi, m_formatctx, m_audio_st, m_audio_input_buffer);
- if (result < 0) {
- m_pLogApi->Debug("write_audio_frame Failed, %d.", result);
- return false;
- }
- }
- //重设置audio_input_buffer,避免buffer过大
- uint8_t* tmp_buffer = new uint8_t[frame_size];
- int bytesRemaining = m_audio_input_buffer->bytesRemaining();
- m_audio_input_buffer->getBytes(tmp_buffer, bytesRemaining);
- m_audio_input_buffer->resize(bytesRemaining);
- m_audio_input_buffer->putBytes(tmp_buffer, bytesRemaining);
- delete tmp_buffer;
- return true;
- }
- bool FFmpegWriter::ReceiveVideoData(unsigned char* pData, unsigned long len)
- {
- if (m_formatctx == NULL || m_video_st == NULL || !m_bhave_video){
- m_pLogApi->Debug("ReceiveVideoData Failed when oc is null.");
- return false;
- }
- //m_pLogApi->Debug("ReceiveVideoData len:%d", len);
- int result = write_video_frame(m_pLogApi, m_formatctx, m_video_st, (char*)pData, len, m_input_pix_fmt);
- if (result < 0){
- m_pLogApi->Debug("write_video_frame Failed, %d.", result);
- return false;
- }
- return true;
- }
|