如何直接使用ffmpeg将H264原始流写入mp4

我想使用ffmpeg(SDK 2.1)将H264 Nalus(x264编码)包装到mp4中,但输出的mp4文件无法播放 . 我使用Raw H264 frames in mpegts container using libavcodec中的代码和www.ffmpeg.org上的muxing.c来编写代码 . 我的H264流没有B帧,每个nalu从00 00 00 01开始,流以sps pps开始,然后是h264数据 .

#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include "Stream2Mp4.h"

#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswresample/swresample.h>

#define STREAM_FRAME_RATE 25
#define STREAM_PIX_FMT    AV_PIX_FMT_YUV420P /* default pix_fmt */

static int ptsInc = 0;
static int vi = -1;
static int waitkey = 1;

// < 0 = error
// 0 = I-Frame
// 1 = P-Frame
// 2 = B-Frame
// 3 = S-Frame
int getVopType( const void *p, int len )
{   
if ( !p || 6 >= len )
    return -1;

unsigned char *b = (unsigned char*)p;

// Verify NAL marker
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
{   b++;
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
    return -1;
} // end if

b += 3;

// Verify VOP id
if ( 0xb6 == *b )
{   b++;
return ( *b & 0xc0 ) >> 6;
} // end if

switch( *b )
{   case 0x65 : return 0;
case 0x61 : return 1;
case 0x01 : return 2;
} // end switch

return -1;
}

int get_nal_type( void *p, int len )
{
if ( !p || 5 >= len )
    return -1;

unsigned char *b = (unsigned char*)p;

// Verify NAL marker
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
{   b++;
if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
    return -1;
} // end if

b += 3;

return *b;
}


/* Add an output stream */
AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)
{
AVCodecContext *c;
AVStream *st;

/* find the encoder */
*codec = avcodec_find_encoder(codec_id);
if (!*codec)
{
    printf("could not find encoder for '%s' \n", avcodec_get_name(codec_id));
    exit(1);
}

st = avformat_new_stream(oc, *codec);
if (!st)
{
    printf("could not allocate stream \n");
    exit(1);
}
st->id = oc->nb_streams-1;
c = st->codec;
vi = st->index;

switch ((*codec)->type)
{
case AVMEDIA_TYPE_AUDIO:
    c->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
    c->bit_rate = 64000;
    c->sample_rate = 44100;
    c->channels = 2;
    break;

case AVMEDIA_TYPE_VIDEO:
    c->codec_id = codec_id;
    c->bit_rate = 90000;
    c->width = 480;
    c->height = 354;
    c->time_base.den = 15;
    c->time_base.num = 1;
    c->gop_size = 12;
    c->pix_fmt = STREAM_PIX_FMT;
    if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
    {
        c->max_b_frames = 2;
    }
    if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
    {
        c->mb_decision = 2;
    }
    break;

default:
    break;
}

if (oc->oformat->flags & AVFMT_GLOBALHEADER)
{
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}

return st;
}



void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
int ret;
AVCodecContext *c = st->codec;

/* open the codec */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0)
{
    printf("could not open video codec");
    //exit(1);
}

}

int CreateMp4(AVFormatContext *&m_pOc, void *p, int len)
{
int ret; 
const char* pszFileName = "output002.mp4";
AVOutputFormat *fmt;
AVCodec *video_codec;
AVStream *m_pVideoSt;

if (0x67 != get_nal_type(p, len))
{
    printf("can not detect nal type");
    return -1;
}
av_register_all();

avformat_alloc_output_context2(&m_pOc, NULL, NULL, pszFileName);
if (!m_pOc)
{
    printf("Could not deduce output format from file extension: using MPEG. \n");
    avformat_alloc_output_context2(&m_pOc, NULL, "mpeg", pszFileName);
}
if (!m_pOc)
{
    return 1;
}

fmt = m_pOc->oformat;

if (fmt->video_codec != AV_CODEC_ID_NONE)
{
    m_pVideoSt = add_stream(m_pOc, &video_codec, fmt->video_codec);
}

if (m_pVideoSt)
{
    open_video(m_pOc, video_codec, m_pVideoSt); 
}

av_dump_format(m_pOc, 0, pszFileName, 1);

/* open the output file, if needed */
if (!(fmt->flags & AVFMT_NOFILE))
{
    ret = avio_open(&m_pOc->pb, pszFileName, AVIO_FLAG_WRITE);
    if (ret < 0)
    {
        printf("could not open '%s': %s\n", pszFileName);
        return 1;
    }
}

/* Write the stream header, if any */
ret = avformat_write_header(m_pOc, NULL);
if (ret < 0)
{
    printf("Error occurred when opening output file");
    return 1;
}
}


/* write h264 data to mp4 file*/


void WriteVideo(AVFormatContext *&m_pOc,void* data, int nLen)
{
int ret;

if ( 0 > vi )
{
    printf("vi less than 0");
    //return -1;
}
AVStream *pst = m_pOc->streams[ vi ];

// Init packet
AVPacket pkt;

AVCodecContext *c = pst->codec;

av_init_packet( &pkt );
pkt.flags |= ( 0 >= getVopType( data, nLen ) ) ? AV_PKT_FLAG_KEY : 0;   

pkt.stream_index = pst->index;
pkt.data = (uint8_t*)data;
pkt.size = nLen;

// Wait for key frame
if ( waitkey )
    if ( 0 == ( pkt.flags & AV_PKT_FLAG_KEY ) )
        return ;
    else
        waitkey = 0;


pkt.pts = (ptsInc++) * (90000/STREAM_FRAME_RATE);
//pkt.dts = (ptsInc++) * (90000/STREAM_FRAME_RATE);

ret = av_interleaved_write_frame( m_pOc, &pkt );
if (ret < 0)
{
    printf("cannot write frame");
}


}

void CloseMp4(AVFormatContext *&m_pOc)
{
waitkey = -1;
vi = -1;

if (m_pOc)
    av_write_trailer(m_pOc);

if (m_pOc && !(m_pOc->oformat->flags & AVFMT_NOFILE))
    avio_close(m_pOc->pb);

if (m_pOc)
{
    avformat_free_context(m_pOc);
    m_pOc = NULL;
}

}

有人能帮助我吗?非常感谢你!

回答(2)

3 years ago

你可以试试这个 .

if(pkt.pts != AV_NOPTS_VALUE)
{
    pkt.pts = av_rescale_q_rnd(ptsInc++, c->time_base, pst->time_base, 
        (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
}

3 years ago

你似乎忽略了通知解码器的sps / pps数据有点奇怪 . 您只验证CreateMp4中有sps . 我认为解码器实际上需要那些 .