typedef enum {
VideoMedia = 0x01,
AudioMedia = 0x02,
SubtitleMedia = 0x04,
} MediaType;
class MediaBuffer {
public:
MediaType mediaType;
unsigned char* data;
int size;
int keyFrame;
int64_t pts;
int64_t dts;
public:
MediaBuffer() {
data = NULL;
size = keyFrame = 0;
pts = dts = 0;
}
virtual ~MediaBuffer() {
if (data) {
delete[] data;
data = NULL;
}
}
};
< FileWriter.h >
#ifndef __FILE_WRITER_H__
#define __FILE_WRITER_H__
#include "MediaBuffer.h"
extern "C" {
#include "libavformat/avformat.h"
};
class FileWriterOpenParam {
public:
enum AVCodecID video_codec_id;
int width;
int height;
AVRational frame_rate;
unsigned int gop_size;
enum AVCodecID audio_codec_id;
enum AVSampleFormat sample_fmt;
int channels;
int sample_rate;
unsigned char* video_extradata;
int video_extradata_size;
unsigned char* audio_extradata;
int audio_extradata_size;
int filepath_format;
int buffering_time;
FileWriterOpenParam();
FileWriterOpenParam(FileWriterOpenParam *param);
virtual ~FileWriterOpenParam();
};
enum FILE_SPLIT { SPLIT_DURATION, SPLIT_LOCALTIME };
class FileWriter
{
protected:
FileWriter();
public:
virtual ~FileWriter();
public:
virtual int open(char *filepath, FileWriterOpenParam ¶m);
virtual int write(MediaBuffer *pBuffer);
virtual void close();
protected:
int64_t m_nStartPTS;
int64_t m_nStartDTS;
int m_nFormat;
unsigned char* m_pVideoExtraData;
int m_nVideoExtraDataSize;
unsigned char* m_pAudioExtraData;
int m_nAudioExtraDataSize;
};
#endif
< FileWriter.cpp >
#include "FileWriter.h"
#include "GlobalEnv.h"
FileWriterOpenParam::FileWriterOpenParam()
{
video_codec_id = AV_CODEC_ID_NONE;
width = height = 0;
frame_rate.num = 1;
frame_rate.den = 30;
gop_size = 30;
audio_codec_id = AV_CODEC_ID_NONE;
sample_fmt = AV_SAMPLE_FMT_NONE;
channels = sample_rate = 0;
video_extradata = audio_extradata = NULL;
video_extradata_size = audio_extradata_size = 0;
filepath_format = 0;
buffering_time = 0;
}
FileWriterOpenParam::FileWriterOpenParam(FileWriterOpenParam *param)
{
video_codec_id = param->video_codec_id;
width = param->width;
height = param->height;
frame_rate.num = param->frame_rate.num;
frame_rate.den = param->frame_rate.den;
gop_size = param->gop_size;
audio_codec_id = param->audio_codec_id;
sample_fmt = param->sample_fmt;
channels = param->channels;
sample_rate = param->sample_rate;
video_extradata = audio_extradata = NULL;
video_extradata_size = audio_extradata_size = 0;
filepath_format = param->filepath_format;
if (param->video_extradata_size > 0) {
video_extradata_size = param->video_extradata_size;
video_extradata = new unsigned char[video_extradata_size];
memcpy(video_extradata, param->video_extradata, video_extradata_size);
}
if (param->audio_extradata_size > 0) {
audio_extradata_size = param->audio_extradata_size;
audio_extradata = new unsigned char[audio_extradata_size];
memcpy(audio_extradata, param->audio_extradata, audio_extradata_size);
}
buffering_time = param->buffering_time;
}
FileWriterOpenParam::~FileWriterOpenParam()
{
if (video_extradata) { delete[] video_extradata; video_extradata = NULL; }
if (audio_extradata) { delete[] audio_extradata; audio_extradata = NULL; }
}
FileWriter::FileWriter() : m_nFormat(0), m_pVideoExtraData(NULL), m_nVideoExtraDataSize(0), m_pAudioExtraData(NULL), m_nAudioExtraDataSize(0)
{
}
FileWriter::~FileWriter()
{
}
int FileWriter::open(char *filepath, FileWriterOpenParam ¶m)
{
m_nStartPTS = m_nStartDTS = 0;
m_nFormat = param.filepath_format;
DXPRINTF("FileWriter %s file opened\n", filepath);
return 0;
}
void FileWriter::close()
{
DX_DELETE_OBJECT(m_pVideoExtraData);
m_nVideoExtraDataSize = 0;
DX_DELETE_OBJECT(m_pAudioExtraData);
m_nAudioExtraDataSize = 0;
DXPRINTF("FileWriter closed\n");
}
int FileWriter::write(MediaBuffer *pBuffer)
{
return 0;
}
< FFFileWriter.h >
#ifndef __FFFILE_WRITER_H__
#define __FFFILE_WRITER_H__
#include "FileWriter.h"
class FFFileWriter : public FileWriter
{
public:
FFFileWriter();
virtual ~FFFileWriter();
virtual int open(char *filepath, FileWriterOpenParam ¶m);
virtual int write(MediaBuffer *pBuffer);
virtual void close();
protected:
AVStream* addStream(enum AVMediaType mediaType, enum AVCodecID codec_id, FileWriterOpenParam ¶m);
protected:
AVFormatContext* m_pFormatCtx;
AVStream* m_pVideoStream;
AVStream* m_pAudioStream;
AVPacket m_avPacket;
FILE* m_pFile;
bool m_bWriteHeader;
};
#endif
< FFFileWriter.cpp >
#include "FFFileWriter.h"
#include "GlobalEnv.h"
FFFileWriter::FFFileWriter() : m_pFormatCtx(NULL), m_pVideoStream(NULL), m_pAudioStream(NULL), m_pFile(NULL)
{
av_register_all();
m_bWriteHeader = false;
}
FFFileWriter::~FFFileWriter()
{
}
int FFFileWriter::open(char *filepath, struct FileWriterOpenParam ¶m)
{
int err;
char errbuf[128];
err = avformat_alloc_output_context2(&m_pFormatCtx, NULL, NULL, filepath);
if (!m_pFormatCtx) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avformat_alloc_output_context2 failed, err: %d, %s\n", err, errbuf);
if (err == -22) {
m_pFile = fopen(filepath, "wb");
if (m_pFile) {
FileWriter::open(filepath, param);
return 0;
}
}
return -1;
}
av_init_packet(&m_avPacket);
AVOutputFormat *fmt = m_pFormatCtx->oformat;
fmt->video_codec = param.video_codec_id;
fmt->audio_codec = param.audio_codec_id;
if (fmt->video_codec != AV_CODEC_ID_NONE)
m_pVideoStream = addStream(AVMEDIA_TYPE_VIDEO, param.video_codec_id, param);
if (fmt->audio_codec != AV_CODEC_ID_NONE)
m_pAudioStream = addStream(AVMEDIA_TYPE_AUDIO, param.audio_codec_id, param);
err = avio_open(&m_pFormatCtx->pb, filepath, AVIO_FLAG_WRITE);
if (err < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avio_open failed, err: %d, %s\n", err, errbuf);
return -1;
}
err = avformat_write_header(m_pFormatCtx, NULL);
if (err < 0) {
av_strerror(err, errbuf, sizeof(errbuf));
DXPRINTF("avformat_write_header failed, err: %d, %s\n", err, errbuf);
return -1;
}
m_bWriteHeader = true;
// write extra data
if (m_pVideoStream && param.video_extradata_size > 0) {
av_init_packet(&m_avPacket);
m_avPacket.stream_index = m_pVideoStream->index;
m_avPacket.data = param.video_extradata;
m_avPacket.size = param.video_extradata_size;
m_avPacket.pts = m_avPacket.dts = 0;
m_pVideoStream->cur_dts = 0;
av_write_frame(m_pFormatCtx, &m_avPacket);
}
if (m_pAudioStream && param.audio_extradata_size > 0) {
av_init_packet(&m_avPacket);
m_avPacket.stream_index = m_pAudioStream->index;
m_avPacket.data = param.audio_extradata;
m_avPacket.size = param.audio_extradata_size;
m_avPacket.pts = m_avPacket.dts = 0;
m_pVideoStream->cur_dts = 0;
av_write_frame(m_pFormatCtx, &m_avPacket);
}
FileWriter::open(filepath, param);
return 0;
}
AVStream* FFFileWriter::addStream(enum AVMediaType mediaType, enum AVCodecID codec_id, FileWriterOpenParam ¶m)
{
AVCodecContext *codecCtx;
AVStream *stream = NULL;
#if 0
AVCodec *codec = avcodec_find_encoder(codec_id);
if (!codec) {
DXPRINTF("Could not find encoder for '%s'\n", avcodec_get_name(codec_id));
}
#endif
if (mediaType == AVMEDIA_TYPE_VIDEO) {
stream = avformat_new_stream(m_pFormatCtx, m_pFormatCtx->video_codec);
} else if (mediaType == AVMEDIA_TYPE_AUDIO) {
if (param.sample_fmt == AV_SAMPLE_FMT_NONE) return NULL; // cannot write
stream = avformat_new_stream(m_pFormatCtx, m_pFormatCtx->audio_codec);
}
if (!stream) {
DXPRINTF("avformat_new_stream failed\n");
return NULL;
}
stream->id = m_pFormatCtx->nb_streams-1;
codecCtx = stream->codec;
codecCtx->codec_id = codec_id;
codecCtx->codec_type = mediaType;
if (mediaType == AVMEDIA_TYPE_AUDIO) {
int sample_rate = param.sample_rate == 0 ? 44100 : param.sample_rate; // to prevent divide by zero exception
//codecCtx->sample_fmt = param.sample_fmt;
codecCtx->channels = param.channels;
codecCtx->sample_rate = sample_rate;
codecCtx->time_base.num = 1;
codecCtx->time_base.den = sample_rate;
if (param.audio_extradata_size > 0) {
codecCtx->extradata = (uint8_t *)av_malloc(param.audio_extradata_size);
memcpy(codecCtx->extradata, param.audio_extradata, param.audio_extradata_size);
codecCtx->extradata_size = param.audio_extradata_size;
}
} else if (mediaType == AVMEDIA_TYPE_VIDEO) {
// to prevent divide by zero exception
int width = param.width == 0 ? 1280 : param.width;
int height = param.height == 0 ? 720 : param.height;
if (param.frame_rate.den == 0) {
param.frame_rate.den = 30;
param.frame_rate.num = 1;
param.gop_size = 30;
}
codecCtx->codec_id = codec_id;
codecCtx->width = width;
codecCtx->height = height;
codecCtx->time_base.den = param.frame_rate.den;
codecCtx->time_base.num = param.frame_rate.num;
codecCtx->gop_size = param.gop_size;
codecCtx->pix_fmt = PIX_FMT_YUV420P;
if (param.video_extradata_size > 0) {
codecCtx->extradata = (uint8_t *)av_malloc(param.video_extradata_size);
memcpy(codecCtx->extradata, param.video_extradata, param.video_extradata_size);
codecCtx->extradata_size = param.video_extradata_size;
}
}
if (m_pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
int err = avcodec_parameters_from_context(stream->codecpar, codecCtx);
return stream;
}
void FFFileWriter::close()
{
if (m_pFormatCtx) {
if (m_bWriteHeader) {
av_write_trailer(m_pFormatCtx);
m_bWriteHeader = false;
}
if (m_pVideoStream) {
if (m_pVideoStream->codec) {
if (m_pVideoStream->codec->extradata) {
av_freep(&m_pVideoStream->codec->extradata);
m_pVideoStream->codec->extradata_size = 0;
}
avcodec_close(m_pVideoStream->codec);
}
}
if (m_pAudioStream) {
if (m_pAudioStream->codec) {
if (m_pAudioStream->codec->extradata) {
av_freep(&m_pAudioStream->codec->extradata);
m_pAudioStream->codec->extradata_size = 0;
}
avcodec_close(m_pAudioStream->codec);
}
}
av_free_packet(&m_avPacket);
avio_close(m_pFormatCtx->pb);
avformat_free_context(m_pFormatCtx);
m_pFormatCtx = NULL;
}
if (m_pFile) {
fclose(m_pFile);
m_pFile = NULL;
}
FileWriter::close();
}
int FFFileWriter::write(MediaBuffer *pBuffer)
{
if (m_pFile) { // raw stream writing
if (pBuffer->mediaType == VideoMedia)
return fwrite(pBuffer->data, pBuffer->size, 1, m_pFile);
return -1;
}
AVStream *stream = NULL;
av_init_packet(&m_avPacket);
if (pBuffer->mediaType == VideoMedia) {
stream = m_pVideoStream;
if (pBuffer->keyFrame == 1)
m_avPacket.flags |= AV_PKT_FLAG_KEY;
} else if (pBuffer->mediaType == AudioMedia) {
stream = m_pAudioStream;
if (!m_pVideoStream)
m_avPacket.flags |= AV_PKT_FLAG_KEY;
}
if (!stream) return -1;
m_avPacket.stream_index = stream->index;
m_avPacket.data = pBuffer->data;
m_avPacket.size = pBuffer->size;
// timestamp
if (pBuffer->pts == -1) m_nStartPTS = 0;
if (pBuffer->dts == -1) m_nStartDTS = 0;
if (m_nStartPTS == 0) m_nStartPTS = pBuffer->pts;
if (m_nStartDTS == 0) m_nStartDTS = pBuffer->dts;
int64_t pts = pBuffer->pts - m_nStartPTS;
int64_t dts = pBuffer->dts - m_nStartDTS;
m_avPacket.pts = pts;
m_avPacket.dts = dts;
AVRational time_base = { 1, 1000 };
av_packet_rescale_ts(&m_avPacket, time_base, stream->time_base);
stream->cur_dts = m_avPacket.dts - 1;
return av_write_frame(m_pFormatCtx, &m_avPacket);
}
< FFFileWriter 사용법 >
FFFileWriter *pWriter = new FFFileWriter();
// 해당 스트림에 맞는 비디오/오디오 속성을 입력
FileWriterOpenParam *param = new FileWriterOpenParam();
param->video_codec_id = AV_CODEC_ID_H264;
param->width = 1280;
param->height = 720;
param->frame_rate.num = 1;
param->frame_rate.den = 30;
param->gop_size = 30;
...
// 파일 열기
pWriter->open("record_file.ts", *param);
delete param;
...
// 파일 쓰기
unsigned char *buf => encoded buffer
int buf_size => encoded buffer size
MediaBuffer *pBuffer = new MediaBuffer();
pBuffer->mediaType = VideoMedia;
memcpy(pBuffer->data, buf, buf_size);
pBuffer->size = buf_size;
pBuffer->pts = pts 값 (millisecond 단위)
pBuffer->dts = dts 값 (millisecond 단위)
pWriter->write(pBuffer);
delete pBuffer;
// 파일 닫기 - 모든 스트림 저장후 마무리
pWriter->close();
delete pWriter;
댓글 없음:
댓글 쓰기