使用ffmpeg开源库将h264封装为mp4格式

最近一直在做使用ffmpeg关于读取标准h264格式内存如何封装为mp4格式文件,在经过一周的持续奋战之后在网上找了一些代码,特别的雷神的博客让我获益匪浅,开始不知道如何持续读取发送来的内存块,如何边接受内存边封装为mp4格式文件,找了很多代码也没想出来,后来发现ffmpeg注册的读取拷贝内存的回调函数是自己控制的人为无法控制,经过一番折腾,找了一个比较容易的方法,如果代码有错误的地方希望大家指导,想写一篇博客把自己的代码贴出来。



#include "StdAfx.h"
#include "format.h"


ITAVFormat::ITAVFormat(void)
{
    m_ifmt_ctx = NULL;
    m_ofmt_ctx = NULL;
    m_ofmt       = NULL;
    m_bufAvalloc = NULL;
    m_fileName = NULL;
    for(int i=0; i<BUF_UNIT_NUM; i++)
    {
        m_bufDeq.data[i].ptr = new unsigned char[UNIT_SIZE];
        memset(m_bufDeq.data[i].ptr,0,UNIT_SIZE);
        m_bufDeq.data[i].size = 0;
        m_bufDeq.data[i].readsize = 0;
    }
    m_bufDeq.head = 0;
    m_bufDeq.tail = 0;
    m_countFill = 0;
    m_frame_index = 0;
    m_startCode = 0;
}


ITAVFormat::~ITAVFormat(void)
{
    for(int i=0; i<BUF_UNIT_NUM; i++)
    {
        delete []m_bufDeq.data[i].ptr;
        m_bufDeq.data[i].size = 0;
        m_bufDeq.data[i].readsize = 0;
    }
    m_bufDeq.head = 0;
    m_bufDeq.tail = 0;
}

int ITAVFormat::read_data(void *opaque, uint8_t *buf, int buf_size)
{
    //printf("readData!!!!!!!!!!!!\n");
    ITAVFormat* pThis = (ITAVFormat*)opaque;
    if(pThis->m_bufDeq.head == pThis->m_bufDeq.tail)
    {
        //printf("*********head==tail********buf_size=%ld\n",buf_size);
        return 0;
    }
    else
    {
        int head = pThis->m_bufDeq.head;
        buf_size = FFMIN(buf_size, pThis->m_bufDeq.data[head].size);
        printf("ptr:%p,size:%u,buf_size=%u\n",pThis->m_bufDeq.data[head].ptr, pThis->m_bufDeq.data[head].size,buf_size);

        /* copy internal buffer data to buf */
        memcpy(buf, pThis->m_bufDeq.data[head].ptr+pThis->m_bufDeq.data[head].readsize, buf_size);

        pThis->m_bufDeq.data[head].readsize  += buf_size;
        pThis->m_bufDeq.data[head].size -= buf_size;

        if(pThis->m_bufDeq.data[head].size <= 0)
        {
            pThis->m_bufDeq.data[head].readsize = 0;
            pThis->m_bufDeq.head = (pThis->m_bufDeq.head+1)%BUF_UNIT_NUM;

        }
        return buf_size;
    }
    
}
void ITAVFormat::init()
{
    av_register_all();
}

int ITAVFormat::fillBuffer(unsigned char* buff,int len)
{
    //buffer_data b
    if(buff == NULL)
        return TRUE;
    if(len > UNIT_SIZE)
        return FALSE;
    int tail = m_bufDeq.tail;
    memcpy(m_bufDeq.data[tail].ptr,buff,len);
    m_bufDeq.data[tail].size = len;
    m_bufDeq.data[tail].readsize = 0;
    m_bufDeq.tail = (m_bufDeq.tail+1)%BUF_UNIT_NUM;
    
    if(m_countFill == 0)
    {
    
        m_startCode = start();
        if(m_startCode < 0)
            return FALSE;
        m_countFill++;    
    }
    else
        writeFrame();
    
    return TRUE;
}

int ITAVFormat::writeFrame()
{
    int ret = -1;
    AVPacket pkt;
    while(1)
    {
        AVStream *in_stream, *out_stream;
        //Get an AVPacket
        ret = av_read_frame(m_ifmt_ctx, &pkt);
        //printf("ret=%d\n",ret);
        if (ret < 0)
        {
            printf("ret = %d\n",ret);
            break;
        }
        in_stream  = m_ifmt_ctx->streams[pkt.stream_index];
        out_stream = m_ofmt_ctx->streams[pkt.stream_index];

        ///如果没有显示时间戳自己加上时间戳并且将显示时间戳赋值给解码时间戳
        if(pkt.pts==AV_NOPTS_VALUE){
            //Write PTS
            AVRational time_base1=in_stream->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);
            //Parameters
            pkt.pts=(double)(m_frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
            pkt.dts=pkt.pts;
            pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
            m_frame_index++;
        }

        //Convert PTS/DTS
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);

        pkt.pos = -1;
        //Write
        if (av_interleaved_write_frame(m_ofmt_ctx, &pkt) < 0) {
            printf( "Error muxing packet\n");
            break;
        }
        //printf("Write %8d frames to output file\n",frame_index);
        av_free_packet(&pkt);
        m_frame_index++;
    }
     return TRUE;
}
// av_probe_input_buffer2
//     AVPROBE_SCORE_MAX
int ITAVFormat::start()
{
    int ret,i;
    AVIOContext* pb = NULL;
    AVInputFormat* piFmt = NULL;
    m_bufAvalloc = (unsigned char*)av_malloc(32768);
    pb = avio_alloc_context(m_bufAvalloc, 32768, 0, (void*)this, ITAVFormat::read_data, NULL, NULL);

    if (av_probe_input_buffer(pb, &piFmt, "", NULL, 0, 0) < 0)
        return -1;
    else{
        printf("format:%s[%s]\n", piFmt->name, piFmt->long_name);
    }
    m_ifmt_ctx = avformat_alloc_context();
    m_ifmt_ctx->pb = pb;

    //Input
    if (avformat_open_input(&m_ifmt_ctx, "", piFmt, NULL) != 0){//iformat,priv_data赋值,pb, nbstreams,streams为null
        printf("Couldn't open input stream.(无法打开输入流)\n");
        return -2;
    }

    if ((ret = avformat_find_stream_info(m_ifmt_ctx, 0)) < 0) {
        printf( "Failed to retrieve input stream information");
        return -3;
    }
    //Output
    avformat_alloc_output_context2(&m_ofmt_ctx, NULL, NULL, m_fileName);
    if (!m_ofmt_ctx) {
        printf( "Could not create output context\n");
        return -4;
        //goto end;
    }
    m_ofmt = m_ofmt_ctx->oformat;
    for (i = 0; i < m_ifmt_ctx->nb_streams; i++) {
        //Create output AVStream according to input AVStream
        AVStream *in_stream = m_ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(m_ofmt_ctx, in_stream->codec->codec);
        if (!out_stream) {
            printf( "Failed allocating output stream\n");
            return -5;
        }
        //Copy the settings of AVCodecContext
        if (avcodec_copy_context(out_stream->codec, in_stream->codec) < 0) {
            printf( "Failed to copy context from input to output stream codec context\n");
            return -6;
        }
        out_stream->codec->codec_tag = 0;
        if (m_ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
        {
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }
    }
    //Output information------------------
    av_dump_format(m_ofmt_ctx, 0, m_fileName, 1);
    //Open output file
    if (!(m_ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&m_ofmt_ctx->pb, m_fileName, AVIO_FLAG_WRITE);
        if (ret < 0) {
            printf( "Could not open output file '%s'", m_fileName);
            return -7;
        }
    }
    //Write file header
    if (avformat_write_header(m_ofmt_ctx, NULL) < 0) {
        printf( "Error occurred when opening output file\n");
        return -8;
    }
    return TRUE;
}

void ITAVFormat::setFileName(char* fileName)
{
    m_fileName = fileName;
}

void ITAVFormat::finish()
{
    if(m_startCode < 0)
        return;
    //Write file trailer
    av_write_trailer(m_ofmt_ctx);
    //av_free(m_bufAvalloc);
    avformat_close_input(&m_ifmt_ctx);
    /* close output */
    if (m_ofmt_ctx && !(m_ofmt->flags & AVFMT_NOFILE))
        avio_close(m_ofmt_ctx->pb);
    avformat_free_context(m_ofmt_ctx);
    //av_free(m_bufAvalloc);
}



猜你喜欢

转载自blog.csdn.net/huangyifei_1111/article/details/46955021