1/* 2 * SMJPEG muxer 3 * Copyright (c) 2012 Paul B Mahol 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * This is a muxer for Loki SDL Motion JPEG files 25 */ 26 27#include "avformat.h" 28#include "internal.h" 29#include "smjpeg.h" 30 31typedef struct SMJPEGMuxContext { 32 uint32_t duration; 33} SMJPEGMuxContext; 34 35static int smjpeg_write_header(AVFormatContext *s) 36{ 37 AVDictionaryEntry *t = NULL; 38 AVIOContext *pb = s->pb; 39 int n, tag; 40 41 if (s->nb_streams > 2) { 42 av_log(s, AV_LOG_ERROR, "more than >2 streams are not supported\n"); 43 return AVERROR(EINVAL); 44 } 45 avio_write(pb, SMJPEG_MAGIC, 8); 46 avio_wb32(pb, 0); 47 avio_wb32(pb, 0); 48 49 while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { 50 avio_wl32(pb, SMJPEG_TXT); 51 avio_wb32(pb, strlen(t->key) + strlen(t->value) + 3); 52 avio_write(pb, t->key, strlen(t->key)); 53 avio_write(pb, " = ", 3); 54 avio_write(pb, t->value, strlen(t->value)); 55 } 56 57 for (n = 0; n < s->nb_streams; n++) { 58 AVStream *st = s->streams[n]; 59 AVCodecContext *codec = st->codec; 60 if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { 61 tag = ff_codec_get_tag(ff_codec_smjpeg_audio_tags, codec->codec_id); 62 if (!tag) { 63 av_log(s, AV_LOG_ERROR, "unsupported audio codec\n"); 64 return AVERROR(EINVAL); 65 } 66 avio_wl32(pb, SMJPEG_SND); 67 avio_wb32(pb, 8); 68 avio_wb16(pb, codec->sample_rate); 69 avio_w8(pb, codec->bits_per_coded_sample); 70 avio_w8(pb, codec->channels); 71 avio_wl32(pb, tag); 72 avpriv_set_pts_info(st, 32, 1, 1000); 73 } else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { 74 tag = ff_codec_get_tag(ff_codec_smjpeg_video_tags, codec->codec_id); 75 if (!tag) { 76 av_log(s, AV_LOG_ERROR, "unsupported video codec\n"); 77 return AVERROR(EINVAL); 78 } 79 avio_wl32(pb, SMJPEG_VID); 80 avio_wb32(pb, 12); 81 avio_wb32(pb, 0); 82 avio_wb16(pb, codec->width); 83 avio_wb16(pb, codec->height); 84 avio_wl32(pb, tag); 85 avpriv_set_pts_info(st, 32, 1, 1000); 86 } 87 } 88 89 avio_wl32(pb, SMJPEG_HEND); 90 avio_flush(pb); 91 92 return 0; 93} 94 95static int smjpeg_write_packet(AVFormatContext *s, AVPacket *pkt) 96{ 97 SMJPEGMuxContext *smc = s->priv_data; 98 AVIOContext *pb = s->pb; 99 AVStream *st = s->streams[pkt->stream_index]; 100 AVCodecContext *codec = st->codec; 101 102 if (codec->codec_type == AVMEDIA_TYPE_AUDIO) 103 avio_wl32(pb, SMJPEG_SNDD); 104 else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) 105 avio_wl32(pb, SMJPEG_VIDD); 106 else 107 return 0; 108 109 avio_wb32(pb, pkt->pts); 110 avio_wb32(pb, pkt->size); 111 avio_write(pb, pkt->data, pkt->size); 112 113 smc->duration = FFMAX(smc->duration, pkt->pts + pkt->duration); 114 return 0; 115} 116 117static int smjpeg_write_trailer(AVFormatContext *s) 118{ 119 SMJPEGMuxContext *smc = s->priv_data; 120 AVIOContext *pb = s->pb; 121 int64_t currentpos; 122 123 if (pb->seekable) { 124 currentpos = avio_tell(pb); 125 avio_seek(pb, 12, SEEK_SET); 126 avio_wb32(pb, smc->duration); 127 avio_seek(pb, currentpos, SEEK_SET); 128 } 129 130 avio_wl32(pb, SMJPEG_DONE); 131 132 return 0; 133} 134 135AVOutputFormat ff_smjpeg_muxer = { 136 .name = "smjpeg", 137 .long_name = NULL_IF_CONFIG_SMALL("Loki SDL MJPEG"), 138 .priv_data_size = sizeof(SMJPEGMuxContext), 139 .audio_codec = AV_CODEC_ID_PCM_S16LE, 140 .video_codec = AV_CODEC_ID_MJPEG, 141 .write_header = smjpeg_write_header, 142 .write_packet = smjpeg_write_packet, 143 .write_trailer = smjpeg_write_trailer, 144 .flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT, 145 .codec_tag = (const AVCodecTag *const []){ ff_codec_smjpeg_video_tags, ff_codec_smjpeg_audio_tags, 0 }, 146}; 147