├── README ├── Makefile ├── mp4mux.h ├── rtph264.h ├── rtpdataheader.h ├── main.c ├── rtph264.c └── mp4mux.c /README: -------------------------------------------------------------------------------- 1 | Save RTP - H.264 into Mpeg4 file 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | C=gcc 2 | AR=ar 3 | 4 | CFLAGS=-Wall -O2 -funroll-loops -msse2 -I/usr/local/include 5 | LDFLAGS=-L/usr/local/lib -lavformat -lavcodec -lavutil -lm -lz 6 | LIBS=rtph264.o mp4mux.o 7 | 8 | %.o : %.cc 9 | $(CC) -c $(CFLAGS) $< -o $@ 10 | 11 | all : rtph264 12 | 13 | rtph264 : ${LIBS} main.o 14 | ${CC} -o $@ ${LIBS} main.o ${LDFLAGS} 15 | 16 | clean : 17 | rm -rf ./*.o 18 | rm -rf rtph264 19 | -------------------------------------------------------------------------------- /mp4mux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2010 3 | * Steve Chang 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation; either version 2 of 8 | * the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 18 | * MA 02111-1307 USA 19 | * 20 | */ 21 | 22 | #ifdef HAVE_AV_CONFIG_H 23 | #undef HAVE_AV_CONFIG_H 24 | #endif 25 | 26 | #include "libavcodec/avcodec.h" 27 | #include "libavutil/mathematics.h" 28 | #include "libavformat/avformat.h" 29 | //#include "libswscale/swscale.h" 30 | 31 | void Mp4mux_Init(); 32 | void Mp4mux_Open(const char *filename); 33 | void Mp4Mux_WriteVideo(AVPacket *packet, unsigned int timestamp); 34 | void Mp4mux_Close(); 35 | -------------------------------------------------------------------------------- /rtph264.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2010 3 | * Steve Chang 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation; either version 2 of 8 | * the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 18 | * MA 02111-1307 USA 19 | * 20 | */ 21 | 22 | #ifdef HAVE_AV_CONFIG_H 23 | #undef HAVE_AV_CONFIG_H 24 | #endif 25 | 26 | #include "libavcodec/avcodec.h" 27 | #include "libavutil/mathematics.h" 28 | #include "libavformat/avformat.h" 29 | 30 | typedef void (*RtpH264_OnPicture)(unsigned char *data, int lineSize, int width, int height); 31 | 32 | void RtpH264_Init(); 33 | void RtpH264_Deinit(); 34 | void RtpH264_Run(int sfd, RtpH264_OnPicture onPicture); 35 | void RtpH264_Stop(); 36 | -------------------------------------------------------------------------------- /rtpdataheader.h: -------------------------------------------------------------------------------- 1 | #ifndef RTPDATAHEADER_H 2 | #define RTPDATAHEADER_H 3 | 4 | // For 32bit intel machines 5 | typedef short int16; 6 | typedef int int32; 7 | typedef unsigned int u_int32; 8 | typedef unsigned short u_int16; 9 | 10 | #define RTP_BIG_ENDIAN 0 11 | #define RTP_LITTLE_ENDIAN 1 12 | 13 | // RTP data header 14 | //typedef __attribute__ ((__packed__)) struct { 15 | typedef struct { 16 | #if RTP_BIG_ENDIAN 17 | unsigned int version:2; // protocol version 18 | unsigned int p:1; // padding flag 19 | unsigned int x:1; // header extension flag 20 | unsigned int cc:4; // CSRC count 21 | unsigned int m:1; // marker bit 22 | unsigned int pt:7; // payload type 23 | #elif RTP_LITTLE_ENDIAN 24 | unsigned int cc:4; // CSRC count 25 | unsigned int x:1; // header extension flag 26 | unsigned int p:1; // padding flag 27 | unsigned int version:2; // protocol version 28 | unsigned int pt:7; // payload type 29 | unsigned int m:1; // marker bit 30 | #else 31 | #error Define one of RTP_LITTLE_ENDIAN or RTP_BIG_ENDIAN 32 | #endif 33 | unsigned int seq:16; // sequence number 34 | u_int32 ts; // timestamp 32bits 35 | u_int32 ssrc; // synchronization source 36 | } rtp_hdr_t; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2010 3 | * Steve Chang 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation; either version 2 of 8 | * the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 18 | * MA 02111-1307 USA 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "rtph264.h" 32 | 33 | void sig_handler(int s) 34 | { 35 | switch(s) { 36 | case SIGINT: RtpH264_Stop(); 37 | break; 38 | } 39 | } 40 | 41 | #include 42 | #include 43 | 44 | static in_addr_t ifconfig(char *device) 45 | { 46 | int s; 47 | struct ifreq req; 48 | struct sockaddr_in *addrp; 49 | 50 | if((s = socket(AF_INET,SOCK_DGRAM,0)) < 0){ 51 | //dtrace("fail to open socket\n"); 52 | return 0; 53 | } 54 | 55 | addrp = (struct sockaddr_in*) &(req.ifr_addr); 56 | addrp->sin_family = AF_INET; 57 | strcpy(req.ifr_name,device); 58 | if(ioctl(s,SIOCGIFADDR,&req)){ 59 | //printf("fail to get inet address\n"); 60 | goto error; 61 | } 62 | return addrp->sin_addr.s_addr; 63 | 64 | error: 65 | //printf("errno = %d\n",errno); 66 | close(s); 67 | return 0; 68 | } 69 | 70 | typedef enum 71 | { 72 | ArgID_HELP, 73 | ArgID_IP, 74 | ArgID_PORT, 75 | ArgID_DEVICE, 76 | // ArgID_FILE 77 | } ArgID; 78 | 79 | #define STR32 32 80 | typedef struct Args 81 | { 82 | in_addr_t ip; 83 | unsigned short port; 84 | char device[STR32]; 85 | } Args; 86 | 87 | #define DEFAULT_ARGS { 0, 8000, "eth0"} 88 | 89 | static void Usage(void) 90 | { 91 | fprintf(stderr, "Usage: scv [options]\n\n" 92 | "Options:\n" 93 | "-h | --help Print usage information (this message)\n" 94 | "-i | --ip Binding ip\n" 95 | "-p | --port Listen port : default 8000\n" 96 | "-d | --device Device\n" 97 | "At a minimum the IP and port *must* be given\n\n"); 98 | } 99 | 100 | #include 101 | 102 | static void ParseArgs(int argc, char *argv[], Args *argsp) 103 | { 104 | const char shortOptions[] = "hi:p:d:"; 105 | 106 | const struct option longOptions[] = { 107 | {"help", no_argument, NULL, ArgID_HELP }, 108 | {"ip", required_argument, NULL, ArgID_IP }, 109 | {"port", required_argument, NULL, ArgID_PORT }, 110 | {"device", required_argument, NULL, ArgID_DEVICE }, 111 | {0, 0, 0, 0} 112 | }; 113 | 114 | int index; 115 | int argID; 116 | 117 | for(;;) { 118 | argID = getopt_long(argc, argv, shortOptions, longOptions, &index); 119 | 120 | if(argID == -1) 121 | break; 122 | 123 | switch(argID) { 124 | case ArgID_IP: 125 | case 'i': 126 | argsp->ip = inet_addr(optarg); 127 | break; 128 | case ArgID_PORT: 129 | case 'p': 130 | if(sscanf(optarg, "%hu", &argsp->port) == -1) 131 | argsp->port = 8000; 132 | break; 133 | case ArgID_DEVICE: 134 | case 'd': 135 | snprintf(argsp->device, STR32, "%s", optarg); 136 | if((argsp->ip = ifconfig(optarg)) == 0) { 137 | printf("Error fetch %s IP...\n", optarg); 138 | exit(EXIT_FAILURE); 139 | } 140 | break; 141 | case ArgID_HELP: 142 | case 'h': 143 | default: 144 | printf("%s:%d\n", __FUNCTION__, __LINE__); 145 | Usage(); 146 | exit(EXIT_SUCCESS); 147 | } 148 | } 149 | 150 | if(optind < argc) { 151 | Usage(); 152 | exit(EXIT_FAILURE); 153 | } 154 | } 155 | 156 | int CreateUdpSocket(in_addr_t ip, unsigned short port) 157 | { 158 | struct sockaddr_in addr; 159 | int sfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 160 | if(sfd == -1) { 161 | printf("Could not create a UDP socket\n"); 162 | goto udp_socket_fail; 163 | } 164 | 165 | memset((char*) &(addr),0, sizeof((addr))); 166 | addr.sin_family = AF_INET; 167 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 168 | addr.sin_port = htons(port); 169 | 170 | addr.sin_addr.s_addr = ip; 171 | printf("Binding to interface %s\n", inet_ntoa(addr.sin_addr)); 172 | 173 | if(bind(sfd,(struct sockaddr*)&addr, sizeof(addr)) != 0) { 174 | printf("Could not bind socket\n"); 175 | goto udp_socket_fail; 176 | } 177 | 178 | return sfd; 179 | 180 | udp_socket_fail: 181 | return -1; 182 | } 183 | 184 | void OnPicture(unsigned char *data, int lineSize, int width, int height) 185 | { 186 | // printf("[ %dx%d ] : lineSize = %d\n", width, height, lineSize); 187 | } 188 | 189 | int main(int argc, char **argv) 190 | { 191 | Args args = DEFAULT_ARGS; 192 | 193 | /* Parse the arguments given to the app */ 194 | ParseArgs(argc, argv, &args); 195 | 196 | int sfd = CreateUdpSocket(args.ip, args.port); 197 | if(!sfd) { 198 | fprintf(stderr, "could not open socket\n"); 199 | exit(EXIT_FAILURE); 200 | } 201 | 202 | signal(SIGINT, sig_handler); 203 | 204 | RtpH264_Init(); 205 | 206 | RtpH264_Run(sfd, OnPicture); 207 | 208 | RtpH264_Deinit(); 209 | 210 | return 0; 211 | } -------------------------------------------------------------------------------- /rtph264.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2010 3 | * Steve Chang 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation; either version 2 of 8 | * the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 18 | * MA 02111-1307 USA 19 | * 20 | */ 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "rtpdataheader.h" 30 | #include "rtph264.h" 31 | #include "mp4mux.h" 32 | 33 | extern AVCodec aac_encoder; 34 | extern AVCodec aac_decoder; 35 | extern AVCodec h264_decoder; 36 | extern AVCodec mpeg4_encoder; 37 | extern AVCodec mpeg4_decoder; 38 | 39 | static AVCodecContext *context = NULL; 40 | extern AVCodec aac_encoder; 41 | 42 | void RtpH264_Init() 43 | { 44 | /* must be called before using avcodec lib */ 45 | avcodec_init(); 46 | 47 | /* register all the codecs */ 48 | // avcodec_register_all(); 49 | 50 | avcodec_register(&aac_encoder); 51 | avcodec_register(&aac_decoder); 52 | avcodec_register(&h264_decoder); 53 | avcodec_register(&mpeg4_encoder); 54 | avcodec_register(&mpeg4_decoder); 55 | 56 | AVCodec *codec = avcodec_find_decoder(CODEC_ID_H264); 57 | 58 | context = avcodec_alloc_context(); 59 | 60 | /* open it */ 61 | if(avcodec_open(context, codec) < 0) { 62 | fprintf(stderr, "could not open codec\n"); 63 | exit(EXIT_FAILURE); 64 | } 65 | 66 | Mp4mux_Init(); 67 | Mp4mux_Open("/tmp/scv.mp4"); 68 | } 69 | 70 | void RtpH264_Deinit() 71 | { 72 | Mp4mux_Close(); 73 | 74 | avcodec_close(context); 75 | av_free(context); 76 | } 77 | 78 | static int bStop = 0; 79 | 80 | void RtpH264_Stop() 81 | { 82 | bStop = 1; 83 | } 84 | 85 | void RtpH264_Run(int sfd, RtpH264_OnPicture onPicture) 86 | { 87 | #define INBUF_SIZE (1024 * 128) 88 | uint8_t inbuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; 89 | /* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */ 90 | memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE); 91 | 92 | AVFrame *picture = avcodec_alloc_frame(); 93 | AVPacket avpkt; 94 | av_init_packet(&avpkt); 95 | int frame_count = 0; 96 | 97 | unsigned short sequence = 0; 98 | unsigned int timestamp = 0; 99 | 100 | while(1) { 101 | //int ready = 0; 102 | fd_set rfds; 103 | FD_ZERO(&rfds); 104 | FD_SET(sfd, &rfds); 105 | 106 | struct timeval timeout; /*Timer for operation select*/ 107 | timeout.tv_sec = 0; 108 | timeout.tv_usec = 10000; /*10 ms*/ 109 | 110 | if(select(sfd+1, &rfds, 0, 0, &timeout) <= 0) { 111 | if(bStop) 112 | break; 113 | else 114 | continue; 115 | } 116 | 117 | if(FD_ISSET(sfd, &rfds) <= 0) 118 | continue; 119 | 120 | rtp_hdr_t rtp; 121 | unsigned char buf[64]; 122 | memset(buf, 0, 64); 123 | int r = recv(sfd, buf, sizeof(rtp_hdr_t) + 8, MSG_PEEK); /* Peek data from socket, so that we could determin how to read data from socket*/ 124 | if(r <= sizeof(rtp_hdr_t) || r == -1) { 125 | recv(sfd, buf, sizeof(rtp_hdr_t) + 8, 0); /*Read invalid packet*/ 126 | printf("Warning !!! Invalid packet\n"); 127 | continue; /*Invalid packet ???*/ 128 | } 129 | 130 | int ready = 0; 131 | 132 | /* Handle H.264 RTP Header */ 133 | /* +---------------+ 134 | * |0|1|2|3|4|5|6|7| 135 | * +-+-+-+-+-+-+-+-+ 136 | * |F|NRI| Type | 137 | * +---------------+ 138 | * 139 | * F must be 0. 140 | */ 141 | unsigned char nal_ref_idc, nal_unit_type; 142 | unsigned char *header = buf + sizeof(rtp_hdr_t); /* NAL Header */ 143 | nal_ref_idc = (header[0] & 0x60) >> 5; /* NRI */ 144 | //printf("nal_ref_idc = %d\n", nal_ref_idc); 145 | nal_unit_type = header[0] & 0x1f; /* Type */ 146 | //printf("nal_unit_type = %d\n", nal_unit_type); 147 | 148 | switch (nal_unit_type) { 149 | case 0: 150 | case 30: 151 | case 31: 152 | /* undefined */ 153 | break; 154 | case 25: 155 | /* STAP-B Single-time aggregation packet 5.7.1 */ 156 | /* 2 byte extra header for DON */ 157 | /* fallthrough */ 158 | case 24: 159 | /* STAP-A Single-time aggregation packet 5.7.1 */ 160 | break; 161 | case 26: 162 | /* MTAP16 Multi-time aggregation packet 5.7.2 */ 163 | /* fallthrough, not implemented */ 164 | case 27: 165 | /* MTAP24 Multi-time aggregation packet 5.7.2 */ 166 | break; 167 | case 28: 168 | /* FU-A Fragmentation unit 5.8 */ 169 | case 29: { 170 | /* FU-B Fragmentation unit 5.8 */ 171 | /* +---------------+ 172 | * |0|1|2|3|4|5|6|7| 173 | * +-+-+-+-+-+-+-+-+ 174 | * |S|E|R| Type | 175 | * +---------------+ 176 | * 177 | * R is reserved and always 0 178 | */ 179 | 180 | /*Really read packet*/ 181 | struct iovec data[3]; 182 | 183 | data[0].iov_base = &rtp; 184 | data[0].iov_len = sizeof(rtp_hdr_t); 185 | 186 | if(nal_unit_type == 28) { 187 | /* strip off FU indicator and FU header bytes */ 188 | data[1].iov_base = buf; 189 | data[1].iov_len = 2; 190 | } else if(nal_unit_type == 29) { 191 | /* strip off FU indicator and FU header and DON bytes */ 192 | data[1].iov_base = buf; 193 | data[1].iov_len = 4; 194 | } 195 | 196 | /* NAL unit starts here */ 197 | if((header[1] & 0x80) == 0x80) { 198 | data[2].iov_base = &inbuf[5]; 199 | data[2].iov_len = INBUF_SIZE - 5; 200 | 201 | r = readv(sfd, data, 3); 202 | if(r <= (sizeof(rtp_hdr_t) + 2)) { 203 | printf("Socket read fail !!!\n"); 204 | goto cleanup; 205 | } 206 | 207 | unsigned char fu_indicator = buf[0]; 208 | unsigned char fu_header = buf[1]; 209 | 210 | timestamp = ntohl(rtp.ts); 211 | sequence = ntohs(rtp.seq); 212 | 213 | inbuf[0] = 0x00; 214 | inbuf[1] = 0x00; 215 | inbuf[2] = 0x00; 216 | inbuf[3] = 0x01; 217 | inbuf[4] = (fu_indicator & 0xe0) | (fu_header & 0x1f); 218 | if(nal_unit_type == 28) 219 | avpkt.size = (r - sizeof(rtp_hdr_t) - 2 + 5); 220 | else if(nal_unit_type == 29) 221 | avpkt.size = (r - sizeof(rtp_hdr_t) - 4 + 5); 222 | avpkt.data = inbuf; 223 | //printf("nalu = %d\n", fu_header & 0x1f); 224 | 225 | if((fu_header & 0x1f) == 7) 226 | avpkt.flags |= PKT_FLAG_KEY; 227 | // avpkt.pts = frame_count++; 228 | //printf("avpkt.pts = %d\n", avpkt.pts); 229 | break; 230 | } else { 231 | data[2].iov_base = inbuf + avpkt.size; 232 | data[2].iov_len = INBUF_SIZE - avpkt.size; 233 | 234 | r = readv(sfd, data, 3); 235 | 236 | //unsigned char fu_indicator = buf[0]; 237 | unsigned char fu_header = buf[1]; 238 | 239 | if(r <= (sizeof(rtp_hdr_t) + 2) || r == -1) { 240 | printf("Socket read fail !!!\n"); 241 | goto cleanup; 242 | } 243 | 244 | if(ntohl(rtp.ts) != timestamp) { 245 | printf("Miss match timestamp %d ( expect %d )\n", ntohl(rtp.ts), timestamp); 246 | } 247 | 248 | if(ntohs(rtp.seq) != ++sequence) { 249 | printf("Wrong sequence number %u ( expect %u )\n", ntohs(rtp.seq), sequence); 250 | } 251 | 252 | if(nal_unit_type == 28) 253 | avpkt.size += (r - sizeof(rtp_hdr_t) - 2); 254 | else if(nal_unit_type == 29) 255 | avpkt.size += (r - sizeof(rtp_hdr_t) - 4); 256 | //printf("frame size : %d\n", avpkt.size); 257 | } 258 | 259 | /* NAL unit ends */ 260 | if((header[1] & 0x40) == 0x40) { 261 | ready = 1; /*We are done, go to decode*/ 262 | break; 263 | } 264 | 265 | break; 266 | } 267 | default: { 268 | /* 1-23 NAL unit Single NAL unit packet per H.264 5.6 */ 269 | /* the entire payload is the output buffer */ 270 | struct iovec data[2]; 271 | 272 | data[0].iov_base = &rtp; 273 | data[0].iov_len = sizeof(rtp_hdr_t); 274 | 275 | inbuf[0] = 0x00; 276 | inbuf[1] = 0x00; 277 | inbuf[2] = 0x00; 278 | inbuf[3] = 0x01; 279 | 280 | data[1].iov_base = &inbuf[4]; 281 | data[1].iov_len = INBUF_SIZE - 4; 282 | 283 | r = readv(sfd, data, 2); 284 | if(r <= sizeof(rtp_hdr_t) || r == -1) { 285 | printf("Socket read fail !!!\n"); 286 | goto cleanup; 287 | } 288 | 289 | avpkt.size = (r - sizeof(rtp_hdr_t) + 4); 290 | avpkt.data = inbuf; 291 | 292 | ready = 1; /*We are done, go to decode*/ 293 | 294 | break; 295 | } 296 | } 297 | 298 | int got_picture; 299 | while(ready && avpkt.size > 0) { 300 | avpkt.pts = ++frame_count; 301 | //printf("frame size : %d\n", avpkt.size); 302 | //printf("avpkt.dts = %d\n", avpkt.dts); 303 | // int len = avcodec_decode_video(context, picture, &got_picture, avpkt.data, avpkt.size); 304 | int len = avcodec_decode_video2(context, picture, &got_picture, &avpkt); 305 | //printf("context->coded_frame->pts = %d\n", context->coded_frame->pts); /* 0 */ 306 | //printf("picture->pts = %d\n", picture->pts); 307 | //printf("picture->pict_type = %d\n", picture->pict_type); 308 | //printf("picture->key_frame = %d\n", picture->key_frame); 309 | //printf("picture->interlaced_frame = %d\n", picture->interlaced_frame); 310 | //printf("avpkt.duration = %d\n", avpkt.duration); 311 | 312 | Mp4Mux_WriteVideo(&avpkt, timestamp); 313 | 314 | if(len < 0) { 315 | fprintf(stderr, "Error while decoding frame\n"); 316 | av_init_packet(&avpkt); 317 | break; 318 | } 319 | 320 | if(got_picture) { 321 | // printf("Decode length : %d\n", len); 322 | // fflush(stdout); 323 | /* the picture is allocated by the decoder. no need to 324 | free it */ 325 | if(onPicture) 326 | onPicture(picture->data[0], picture->linesize[0], context->width, context->height); 327 | 328 | av_init_packet(&avpkt); 329 | break; 330 | } 331 | avpkt.size -= len; 332 | avpkt.data += len; 333 | } 334 | } 335 | 336 | cleanup: 337 | av_free(picture); 338 | 339 | return; 340 | } 341 | -------------------------------------------------------------------------------- /mp4mux.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2010 3 | * Steve Chang 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License as 7 | * published by the Free Software Foundation; either version 2 of 8 | * the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 18 | * MA 02111-1307 USA 19 | * 20 | */ 21 | #include 22 | #include 23 | 24 | #include "mp4mux.h" 25 | 26 | /* 27 | * add an audio output stream 28 | */ 29 | 30 | float t, tincr, tincr2; 31 | int16_t *samples; 32 | uint8_t *audio_outbuf; 33 | int audio_outbuf_size; 34 | int audio_input_frame_size; 35 | 36 | static AVStream *add_audio_stream(AVFormatContext *oc, enum CodecID codec_id) 37 | { 38 | AVCodecContext *c; 39 | AVStream *st; 40 | 41 | st = av_new_stream(oc, 1); 42 | if (!st) { 43 | fprintf(stderr, "Could not alloc stream\n"); 44 | exit(1); 45 | } 46 | 47 | c = st->codec; 48 | c->codec_id = codec_id; 49 | c->codec_type = CODEC_TYPE_AUDIO; 50 | 51 | /* put sample parameters */ 52 | c->bit_rate = 64000; 53 | c->sample_rate = 44100; 54 | c->channels = 2; 55 | 56 | // some formats want stream headers to be separate 57 | if(oc->oformat->flags & AVFMT_GLOBALHEADER) 58 | c->flags |= CODEC_FLAG_GLOBAL_HEADER; 59 | 60 | return st; 61 | } 62 | 63 | static void open_audio(AVFormatContext *oc, AVStream *st) 64 | { 65 | AVCodecContext *c; 66 | AVCodec *codec; 67 | 68 | c = st->codec; 69 | 70 | /* find the audio encoder */ 71 | codec = avcodec_find_encoder(c->codec_id); 72 | if (!codec) { 73 | fprintf(stderr, "codec not found\n"); 74 | exit(1); 75 | } 76 | 77 | /* open it */ 78 | if (avcodec_open(c, codec) < 0) { 79 | fprintf(stderr, "could not open codec\n"); 80 | exit(1); 81 | } 82 | 83 | /* init signal generator */ 84 | t = 0; 85 | tincr = 2 * M_PI * 110.0 / c->sample_rate; 86 | /* increment frequency by 110 Hz per second */ 87 | tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; 88 | 89 | audio_outbuf_size = 10000; 90 | audio_outbuf = av_malloc(audio_outbuf_size); 91 | 92 | /* ugly hack for PCM codecs (will be removed ASAP with new PCM 93 | support to compute the input frame size in samples */ 94 | if (c->frame_size <= 1) { 95 | audio_input_frame_size = audio_outbuf_size / c->channels; 96 | switch(st->codec->codec_id) { 97 | case CODEC_ID_PCM_S16LE: 98 | case CODEC_ID_PCM_S16BE: 99 | case CODEC_ID_PCM_U16LE: 100 | case CODEC_ID_PCM_U16BE: 101 | audio_input_frame_size >>= 1; 102 | break; 103 | default: 104 | break; 105 | } 106 | } else { 107 | audio_input_frame_size = c->frame_size; 108 | } 109 | samples = av_malloc(audio_input_frame_size * 2 * c->channels); 110 | } 111 | 112 | static void close_audio(AVFormatContext *oc, AVStream *st) 113 | { 114 | avcodec_close(st->codec); 115 | 116 | av_free(samples); 117 | av_free(audio_outbuf); 118 | } 119 | 120 | #define STREAM_FRAME_RATE 25 /* 25 images/s */ 121 | #define STREAM_PIX_FMT PIX_FMT_YUV420P /* default pix_fmt */ 122 | 123 | //AVFrame *picture, *tmp_picture; 124 | //uint8_t *video_outbuf; 125 | //int video_outbuf_size; 126 | 127 | /* add a video output stream */ 128 | static AVStream *add_video_stream(AVFormatContext *oc, enum CodecID codec_id) 129 | { 130 | AVCodecContext *c; 131 | AVStream *st; 132 | 133 | st = av_new_stream(oc, 0); 134 | if (!st) { 135 | fprintf(stderr, "Could not alloc stream\n"); 136 | exit(1); 137 | } 138 | 139 | c = st->codec; 140 | c->codec_id = codec_id; 141 | c->codec_type = CODEC_TYPE_VIDEO; 142 | 143 | /* put sample parameters */ 144 | c->bit_rate = 4000000; 145 | /* resolution must be a multiple of two */ 146 | c->width = 640; 147 | c->height = 480; 148 | /* time base: this is the fundamental unit of time (in seconds) in terms 149 | of which frame timestamps are represented. for fixed-fps content, 150 | timebase should be 1/framerate and timestamp increments should be 151 | identically 1. */ 152 | // c->time_base.den = STREAM_FRAME_RATE; 153 | c->time_base.den = 3; /* 90000 / timestamp interval / 2 */ 154 | c->time_base.num = 1; 155 | c->gop_size = 12; /* emit one intra frame every twelve frames at most */ 156 | c->pix_fmt = STREAM_PIX_FMT; 157 | #if 0 158 | if (c->codec_id == CODEC_ID_MPEG2VIDEO) { 159 | /* just for testing, we also add B frames */ 160 | c->max_b_frames = 2; 161 | } 162 | if (c->codec_id == CODEC_ID_MPEG1VIDEO){ 163 | /* Needed to avoid using macroblocks in which some coeffs overflow. 164 | This does not happen with normal video, it just happens here as 165 | the motion of the chroma plane does not match the luma plane. */ 166 | c->mb_decision=2; 167 | } 168 | #endif 169 | // some formats want stream headers to be separate 170 | if(oc->oformat->flags & AVFMT_GLOBALHEADER) 171 | c->flags |= CODEC_FLAG_GLOBAL_HEADER; 172 | 173 | return st; 174 | } 175 | 176 | static void open_video(AVFormatContext *oc, AVStream *st) 177 | { 178 | AVCodec *codec; 179 | AVCodecContext *c; 180 | 181 | c = st->codec; 182 | 183 | /* find the video encoder */ 184 | codec = avcodec_find_decoder(c->codec_id); 185 | if (!codec) { 186 | fprintf(stderr, "codec not found\n"); 187 | exit(1); 188 | } 189 | 190 | /* open the codec */ 191 | if (avcodec_open(c, codec) < 0) { 192 | fprintf(stderr, "could not open codec\n"); 193 | exit(1); 194 | } 195 | #if 0 196 | video_outbuf = NULL; 197 | if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { 198 | /* allocate output buffer */ 199 | /* XXX: API change will be done */ 200 | /* buffers passed into lav* can be allocated any way you prefer, 201 | as long as they're aligned enough for the architecture, and 202 | they're freed appropriately (such as using av_free for buffers 203 | allocated with av_malloc) */ 204 | video_outbuf_size = 200000; 205 | video_outbuf = av_malloc(video_outbuf_size); 206 | } 207 | 208 | /* allocate the encoded raw picture */ 209 | picture = alloc_picture(c->pix_fmt, c->width, c->height); 210 | if (!picture) { 211 | fprintf(stderr, "Could not allocate picture\n"); 212 | exit(1); 213 | } 214 | 215 | /* if the output format is not YUV420P, then a temporary YUV420P 216 | picture is needed too. It is then converted to the required 217 | output format */ 218 | tmp_picture = NULL; 219 | if (c->pix_fmt != PIX_FMT_YUV420P) { 220 | tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height); 221 | if (!tmp_picture) { 222 | fprintf(stderr, "Could not allocate temporary picture\n"); 223 | exit(1); 224 | } 225 | } 226 | #endif 227 | } 228 | 229 | static void close_video(AVFormatContext *oc, AVStream *st) 230 | { 231 | avcodec_close(st->codec); 232 | /* 233 | av_free(picture->data[0]); 234 | av_free(picture); 235 | if (tmp_picture) { 236 | av_free(tmp_picture->data[0]); 237 | av_free(tmp_picture); 238 | } 239 | */ 240 | // av_free(video_outbuf); 241 | } 242 | 243 | extern AVOutputFormat mp4_muxer; 244 | extern URLProtocol file_protocol; 245 | 246 | void Mp4mux_Init() 247 | { 248 | av_register_output_format(&mp4_muxer); 249 | av_register_protocol(&file_protocol); 250 | } 251 | 252 | void print_error(const char *filename, int err) 253 | { 254 | char errbuf[128]; 255 | const char *errbuf_ptr = errbuf; 256 | 257 | if (av_strerror(err, errbuf, sizeof(errbuf)) < 0) 258 | errbuf_ptr = strerror(AVUNERROR(err)); 259 | fprintf(stderr, "%s: %s\n", filename, errbuf_ptr); 260 | } 261 | 262 | static AVFormatContext *context = 0; 263 | //static AVStream *audio_stream = NULL, *video_stream = NULL; 264 | static AVStream *video_stream = NULL; 265 | 266 | void Mp4mux_Open(const char *filename) 267 | { 268 | context = avformat_alloc_context(); 269 | AVOutputFormat *format = av_guess_format("mp4", NULL, NULL); 270 | if(!format) { 271 | fprintf(stderr, "Could not find suitable output format\n"); 272 | exit(EXIT_FAILURE); 273 | } 274 | 275 | format->video_codec = CODEC_ID_H264; 276 | context->oformat = format; 277 | snprintf(context->filename, sizeof(context->filename), "%s", filename); 278 | 279 | video_stream = add_video_stream(context, format->video_codec); 280 | //audio_stream = add_audio_stream(context, format->audio_codec); 281 | 282 | if(av_set_parameters(context, NULL) < 0) { 283 | fprintf(stderr, "Invalid output format parameters\n"); 284 | exit(EXIT_FAILURE); 285 | } 286 | 287 | dump_format(context, 0, filename, 1); 288 | 289 | open_video(context, video_stream); 290 | //open_audio(context, audio_stream); 291 | 292 | int err; 293 | if((err = url_fopen(&context->pb, filename, URL_WRONLY)) < 0) { 294 | print_error(filename, err); 295 | fprintf(stderr, "Could not open '%s'\n", filename); 296 | exit(EXIT_FAILURE); 297 | } 298 | 299 | /* write the stream header, if any */ 300 | av_write_header(context); 301 | } 302 | 303 | void Mp4Mux_WriteVideo(AVPacket *pkt, unsigned int timestamp) 304 | { 305 | static unsigned int prev_timestamp = 0; 306 | AVCodecContext *c = video_stream->codec; 307 | 308 | if(timestamp && prev_timestamp) { 309 | //printf("den = %d\n", (90000 / (timestamp - prev_timestamp)) / 2); 310 | c->time_base.den = (90000 / (timestamp - prev_timestamp)) / 2; 311 | } 312 | 313 | prev_timestamp = timestamp; 314 | 315 | // if (c->pix_fmt != PIX_FMT_YUV420P) 316 | // printf("c->pix_fmt != PIX_FMT_YUV420P\n"); 317 | 318 | // if(c->coded_frame->pts != AV_NOPTS_VALUE) 319 | // pkt->pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_stream->time_base); 320 | 321 | // if(c->coded_frame->key_frame) 322 | // pkt->flags |= PKT_FLAG_KEY; 323 | pkt->stream_index= video_stream->index; 324 | 325 | /* write the compressed frame in the media file */ 326 | int ret = av_interleaved_write_frame(context, pkt); 327 | // int ret = av_write_frame(context, pkt); 328 | 329 | if(ret != 0) 330 | fprintf(stderr, "Error while writing video frame\n"); 331 | } 332 | 333 | void Mp4mux_Close() 334 | { 335 | /* write the trailer, if any. the trailer must be written 336 | * before you close the CodecContexts open when you wrote the 337 | * header; otherwise write_trailer may try to use memory that 338 | * was freed on av_codec_close() */ 339 | av_write_trailer(context); 340 | 341 | /* close each codec */ 342 | if (video_stream) 343 | close_video(context, video_stream); 344 | // if (audio_stream) 345 | // close_audio(context, audio_stream); 346 | 347 | /* free the streams */ 348 | int i; 349 | for(i = 0; i < context->nb_streams; i++) { 350 | av_freep(&context->streams[i]->codec); 351 | av_freep(&context->streams[i]); 352 | } 353 | 354 | /* close the output file */ 355 | url_fclose(context->pb); 356 | 357 | /* free the stream */ 358 | av_free(context); 359 | } 360 | 361 | 362 | 363 | //AVCodecContext *codec_context = video_stream->codec; --------------------------------------------------------------------------------