root/vic/branches/mpeg4/codec/decoder-h264.cpp @ 3974

Revision 3974, 6.6 KB (checked in by piers, 7 years ago)

RTP Payload format for H.264 (RFC 3984)

Most of this work below has been done by Socrates. Tidy up and bug fixes by Piers.

General comments:

1. Implemented the H.264 RTP framing according to RFC 3984. The implementation covers the "Single NAL Unit" (Single NALU packet type) and "Non-Interleaved" modes (STAP-A, FU-A packet types). The interleaved mode (STAP-B, MTAP16, MTAP24, FU-B) has not been implemented at this stage. [I don't know of another tool, closed source or open, that has implemented this mode to date to be able to test with (the current QuickTime? Player 7.1.3 does not implement it either, as far as I know).]

2. The decoder has been tested with the MPEG4IP's mp4live server 1.5.0.1. An example SDP file is provided to set up VIC to decode mp4live streams. Please edit the file default_mpeg4ip.sdp with your details, or better generate your own SDP files using mp4live, and place the edited SDP file in your home directory. The filename should be "default.sdp"

3. The H264 depayloader implementation is currently based libavformat (part of ffpmeg). The plan to is re-write the H264 depayloader under BSD license in the near future.

4. The H.264 VIC encoder will not aggregate NAL units at the moment (STAP-A). The decoder will properly decode such packets.

5. VIC sends SPS/PPS NAL units in-band at the start of H.264 transmission. One has to check whether this can be done periodically. This way we may avoid SDP and sprep-parameter-set.

6. The SDP may be configured for the decoder which uses either a default embedded SDP or from "$HOME/default.sdp"

7. The SDP file is only used to convey SPS/PPS sprop-parameter-sets. IP addresses, ports, etc. are read from VIC command-line as normal.

Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <assert.h>
5#include <iostream>
6#include <errno.h>
7#include "inet.h"
8#include "rtp.h"
9#include "decoder.h"
10#include "renderer.h"
11#include "packetbuffer.h"
12#include "databuffer.h"
13#include "ffmpeg_codec.h"
14#include "rtp_h264_depayloader.h"
15
16
17#define SDP_LINE_LEN 10000
18
19//#define DIRECT_DISPLAY 1
20//using namespace std;
21//extern "C" UCHAR * video_frame;
22
23
24class H264Decoder:public Decoder
25{
26  public:
27    H264Decoder();
28    ~H264Decoder();
29
30    void handleSDP();
31    virtual void recv(pktbuf *);
32    int colorhist(u_int * hist) const;
33  protected:
34    void decode(const u_char * vh, const u_char * bp, int cc);
35    virtual void redraw();
36
37    /* packet statistics */
38    uint16_t last_seq;          /* sequence number */
39
40    UCHAR bitstream[MAX_CODED_SIZE];    /* bitstream data */
41
42    /* collecting data for a frame */
43    uint16_t idx;
44    int last_mbit;
45    int last_iframe;
46    bool startPkt;
47
48    /* image */
49    UCHAR xxx_frame[MAX_FRAME_SIZE];
50    FFMpegCodec h264;
51    PacketBuffer *stream; //SV: probably this is going to be substituted by the AVPacket->data when we call decode()...
52    H264Depayloader *h264depayloader;
53
54    //For DEBUG
55    //FILE *fptr;
56    FILE *sdp_fptr;
57};
58
59static class H264DecoderMatcher:public Matcher
60{
61  public:
62    H264DecoderMatcher():Matcher("decoder")
63    {
64    }
65    TclObject *match(const char *id)
66    {
67        if (strcasecmp(id, "h264") == 0)
68            return (new H264Decoder());
69        return (0);
70    }
71}
72
73dm_h264;
74
75
76H264Decoder::H264Decoder():Decoder(0 /* 0 byte extra header */)
77{                               /* , codec_(0), */
78
79
80    //Barz: =============================================
81    decimation_ = 411;
82    /*
83     * Assume CIF.  Picture header will trigger a resize if
84     * we encounter QCIF instead.
85     */
86    inw_ = 0;
87    inh_ = 0;
88
89     /*XXX*/ 
90    resize(inw_, inh_);
91
92    // libavcodec
93    h264.init(false, CODEC_ID_H264, PIX_FMT_YUV420P);
94    h264.init_decoder();
95
96    idx = 0;
97    last_mbit = 0;
98    last_iframe = 0;
99    last_seq = 0;
100    //===================================================
101
102
103    //SV: ===============================================
104    //Create payloader
105    h264depayloader = new H264Depayloader;
106
107    handleSDP();
108    //make sure all codec params are set up properly before decoding
109
110    //check whether AVPacket struct can be used in stead of Barz's PacketBuffer
111    //===================================================
112
113
114    //256 packets, each 1600 byte (default will not exceed 1600 byte)
115    //cout << "new PacketBuffer..\n";
116    stream = new PacketBuffer(1024, 1600); //SV: 1024 = ???
117    startPkt = false;
118}
119
120void
121H264Decoder::handleSDP()
122{
123    char SdpFilename[SDP_LINE_LEN];
124    char *line, *sdp_string;
125    char *SdpLine=NULL;
126    int n_char;
127    size_t nBytes = 0;
128    ssize_t SdpRead;
129    char defaultSDP[]="a=rtpmap:96 H264/90000\na=fmtp:96 profile-level-id=00000d; packetization-mode=1\n";
130
131    sprintf(SdpFilename, "%s/default.sdp", getenv("HOME"));
132
133    //fptr = fopen("out.m4v", "w");
134    if ((sdp_fptr = fopen(SdpFilename, "r")) != NULL ) {
135        //fprintf(stderr, "H264_RTP: Opened SDP file %s for read.\n", SdpFilename);
136
137      //Read SDP file into struct
138      //fprintf(stderr, "H264_RTP: Spitting SDP ================================================\n");
139      while ((SdpRead = getline(&SdpLine, &nBytes, sdp_fptr)) != -1) {
140        //fprintf(stderr, "H264_RTP: Read %d bytes from SDP file.\n", SdpRead);
141        //fprintf(stderr, "%s", SdpLine);
142        //call SDP parse h264 routine
143          h264depayloader->parse_h264_sdp_line(h264.c, h264depayloader->h264_extradata, SdpLine);
144      }
145      if (SdpLine)
146          free(SdpLine);
147       
148    } else {
149        fprintf(stderr, "H264_RTP: Couldn't open SDP file %s to read. Errno = %d\n", SdpFilename, errno);
150        fprintf(stderr, "H264_RTP: Using default SDP: %s \n", defaultSDP);
151
152      line=defaultSDP;
153      do {
154        n_char = strcspn(line, "\n");
155        SdpLine = (char *)malloc(n_char+1);
156        memset(SdpLine, '\0', n_char+1);
157        strncpy(SdpLine, line, n_char);
158        line += n_char + 1;
159          h264depayloader->parse_h264_sdp_line(h264.c, h264depayloader->h264_extradata, SdpLine);
160        free(SdpLine);
161      } while (n_char != 0);
162    //fprintf(stderr, "H264_RTP: Done spitting SDP ===========================================\n");
163    }
164}
165
166H264Decoder::~H264Decoder()
167{
168    delete stream;
169    delete h264depayloader;
170    //fclose(fptr);
171    if (sdp_fptr != NULL) fclose(sdp_fptr);
172    //fprintf(stderr, "H264_RTP: Closed SDP file.\n");
173}
174
175int H264Decoder::colorhist(u_int * hist)  const
176{
177    UNUSED(hist);
178
179    return (1);
180}
181
182void H264Decoder::recv(pktbuf * pb)
183{
184    rtphdr *rh = (rtphdr *) pb->dp;
185    int hdrsize = sizeof(rtphdr) + hdrlen();
186    u_char *bp = pb->dp + hdrsize;
187    int cc = pb->len - hdrsize;
188    //static int iframe_c = 0, pframe_c = 0;
189
190    int mbit = ntohs(rh->rh_flags) >> 7 & 1;
191    uint16_t seq = ntohs(rh->rh_seqno);
192    int ts = ntohl(rh->rh_ts);
193
194
195
196    //Barz: =============================================
197    if (!startPkt) {
198       stream->clear();
199       startPkt = true;
200       idx = seq;
201       last_seq = seq - 1;
202    }
203         
204    int pktIdx = seq - idx;
205    if (pktIdx < 0) {
206        pktIdx = (0xFFFF - idx) + seq;
207    }
208
209    if (abs(seq - last_seq) > 5) {
210            //fprintf(stderr, "H264_RTP: sequece interrupt!\n");
211            idx = seq;
212            pktIdx = 0;
213            stream->clear();
214    }else if (last_seq + 1 != seq) {
215            // oops - missing packet
216            //fprintf(stderr, "H264_RTP: missing packet\n");
217    }
218
219    //===================================================
220
221
222    //copy packet
223    //stream->write(pktIdx, cc, (char *) bp);
224    //fprintf(stderr, "H264_RTP: -------------------------------- seq = %d, m=%d, ts=%d, cc=%d\n", seq, mbit, ts, cc);
225    //fprintf(stderr, "H264_RTP: pktIdx = %d\n", pktIdx);
226    int yo = h264depayloader->h264_handle_packet(h264depayloader->h264_extradata, pktIdx, stream, /*ts,*/ bp, cc);
227    //fprintf(stderr, "H264_RTP: h264_handle_packet = %d\n", yo);
228
229
230    //Barz: =============================================
231
232    last_seq = seq;
233    int len=0;
234    if (mbit) {
235            stream->setTotalPkts(pktIdx + 1);
236
237            DataBuffer *f;
238            if (stream->isComplete()) {
239                    f = stream->getStream();
240                    len =  h264.decode((UCHAR *) f->getData(), f->getDataSize(), xxx_frame);
241            }
242           
243            if (len < 0) {
244                //fprintf(stderr, "H264_RTP: frame error\n");
245            }
246           
247            if (inw_ != h264.width || inh_ != h264.height) {
248                        inw_ = h264.width;
249                        inh_ = h264.height;
250                        resize(inw_, inh_);
251            }
252            else {
253                        Decoder::redraw(xxx_frame);
254            }
255            stream->clear();
256            idx = seq+1;
257    }
258
259    //===================================================
260
261    pb->release();
262}
263
264void H264Decoder::redraw()
265{
266    Decoder::redraw(xxx_frame);
267}
Note: See TracBrowser for help on using the browser.