Unified · Split
| @@ -31,6 +31,7 @@ · 30 unmodified lines | |||
| 31 | #include "libavutil/intreadwrite.h" | 31 | #include "libavutil/intreadwrite.h" |
| 32 | #include "libavutil/opt.h" | 32 | #include "libavutil/opt.h" |
| 33 | #include "libavutil/dict.h" | 33 | #include "libavutil/dict.h" |
| 34 | #include "libavutil/avassert.h" | ||
| 34 | 35 | ||
| 35 | static int id3v1_set_string(AVFormatContext *s, const char *key, | 36 | static int id3v1_set_string(AVFormatContext *s, const char *key, |
| 36 | uint8_t *buf, int buf_size) | 37 | uint8_t *buf, int buf_size) |
| @@ -75,16 +76,25 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) · 38 unmodified lines | |||
| 75 | 76 | ||
| 76 | typedef struct MP3Context { | 77 | typedef struct MP3Context { |
| 77 | const AVClass *class; | 78 | const AVClass *class; |
| 79 | ID3v2EncContext id3; | ||
| 78 | int id3v2_version; | 80 | int id3v2_version; |
| 79 | int write_id3v1; | 81 | int write_id3v1; |
| 80 | int64_t nb_frames_offset; | 82 | int64_t nb_frames_offset; |
| 83 | |||
| 84 | /* index of the audio stream */ | ||
| 85 | int audio_stream_idx; | ||
| 86 | /* number of attached pictures we still need to write */ | ||
| 87 | int pics_to_write; | ||
| 88 | |||
| 89 | /* audio packets are queued here until we get all the attached pictures */ | ||
| 90 | AVPacketList *queue, *queue_end; | ||
| 81 | } MP3Context; | 91 | } MP3Context; |
| 82 | 92 | ||
| 83 | /* insert a dummy frame containing number of frames */ | 93 | /* insert a dummy frame containing number of frames */ |
| 84 | static void mp3_write_xing(AVFormatContext *s) | 94 | static void mp3_write_xing(AVFormatContext *s) |
| 85 | { | 95 | { |
| 86 | MP3Context *mp3 = s->priv_data; | 96 | MP3Context *mp3 = s->priv_data; |
| 87 | AVCodecContext *codec = s->streams[0]->codec; | 97 | AVCodecContext *codec = s->streams[mp3->audio_stream_idx]->codec; |
| 88 | int bitrate_idx = 1; // 32 kbps | 98 | int bitrate_idx = 1; // 32 kbps |
| 89 | int64_t xing_offset = (codec->channels == 2) ? 32 : 17; | 99 | int64_t xing_offset = (codec->channels == 2) ? 32 : 17; |
| 90 | int32_t header; | 100 | int32_t header; |
| @@ -129,11 +139,37 @@ static void mp3_write_xing(AVFormatContext *s) · 38 unmodified lines | |||
| 129 | ffio_fill(s->pb, 0, mpah.frame_size); | 139 | ffio_fill(s->pb, 0, mpah.frame_size); |
| 130 | } | 140 | } |
| 131 | 141 | ||
| 142 | static int mp3_queue_flush(AVFormatContext *s) | ||
| 143 | { | ||
| 144 | MP3Context *mp3 = s->priv_data; | ||
| 145 | AVPacketList *pktl; | ||
| 146 | int ret = 0, write = 1; | ||
| 147 | |||
| 148 | ff_id3v2_finish(&mp3->id3, s->pb); | ||
| 149 | mp3_write_xing(s); | ||
| 150 | |||
| 151 | while ((pktl = mp3->queue)) { | ||
| 152 | if (write && (ret = ff_raw_write_packet(s, &pktl->pkt)) < 0) | ||
| 153 | write = 0; | ||
| 154 | av_free_packet(&pktl->pkt); | ||
| 155 | mp3->queue = pktl->next; | ||
| 156 | av_freep(&pktl); | ||
| 157 | } | ||
| 158 | mp3->queue_end = NULL; | ||
| 159 | return ret; | ||
| 160 | } | ||
| 161 | |||
| 132 | static int mp3_write_trailer(struct AVFormatContext *s) | 162 | static int mp3_write_trailer(struct AVFormatContext *s) |
| 133 | { | 163 | { |
| 134 | uint8_t buf[ID3v1_TAG_SIZE]; | 164 | uint8_t buf[ID3v1_TAG_SIZE]; |
| 135 | MP3Context *mp3 = s->priv_data; | 165 | MP3Context *mp3 = s->priv_data; |
| 136 | 166 | ||
| 167 | if (mp3 && mp3->pics_to_write) { | ||
| 168 | av_log(s, AV_LOG_WARNING, "No packets were sent for some of the " | ||
| 169 | "attached pictures.\n"); | ||
| 170 | mp3_queue_flush(s); | ||
| 171 | } | ||
| 172 | |||
| 137 | /* write the id3v1 tag */ | 173 | /* write the id3v1 tag */ |
| 138 | if (mp3 && mp3->write_id3v1 && id3v1_create_tag(s, buf) > 0) { | 174 | if (mp3 && mp3->write_id3v1 && id3v1_create_tag(s, buf) > 0) { |
| 139 | avio_write(s->pb, buf, ID3v1_TAG_SIZE); | 175 | avio_write(s->pb, buf, ID3v1_TAG_SIZE); |
| @@ -142,7 +178,7 @@ static int mp3_write_trailer(struct AVFormatContext *s) · 2 unmodified lines | |||
| 142 | /* write number of frames */ | 178 | /* write number of frames */ |
| 143 | if (mp3 && mp3->nb_frames_offset) { | 179 | if (mp3 && mp3->nb_frames_offset) { |
| 144 | avio_seek(s->pb, mp3->nb_frames_offset, SEEK_SET); | 180 | avio_seek(s->pb, mp3->nb_frames_offset, SEEK_SET); |
| 145 | avio_wb32(s->pb, s->streams[0]->nb_frames); | 181 | avio_wb32(s->pb, s->streams[mp3->audio_stream_idx]->nb_frames); |
| 146 | avio_seek(s->pb, 0, SEEK_END); | 182 | avio_seek(s->pb, 0, SEEK_END); |
| 147 | } | 183 | } |
| 148 | 184 | ||
| @@ -181,6 +217,50 @@ static const AVClass mp3_muxer_class = { · 32 unmodified lines | |||
| 181 | .version = LIBAVUTIL_VERSION_INT, | 217 | .version = LIBAVUTIL_VERSION_INT, |
| 182 | }; | 218 | }; |
| 183 | 219 | ||
| 220 | static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt) | ||
| 221 | { | ||
| 222 | MP3Context *mp3 = s->priv_data; | ||
| 223 | |||
| 224 | if (pkt->stream_index == mp3->audio_stream_idx) { | ||
| 225 | if (mp3->pics_to_write) { | ||
| 226 | /* buffer audio packets until we get all the pictures */ | ||
| 227 | AVPacketList *pktl = av_mallocz(sizeof(*pktl)); | ||
| 228 | if (!pktl) | ||
| 229 | return AVERROR(ENOMEM); | ||
| 230 | |||
| 231 | pktl->pkt = *pkt; | ||
| 232 | pkt->destruct = NULL; | ||
| 233 | |||
| 234 | if (mp3->queue_end) | ||
| 235 | mp3->queue_end->next = pktl; | ||
| 236 | else | ||
| 237 | mp3->queue = pktl; | ||
| 238 | mp3->queue_end = pktl; | ||
| 239 | } else | ||
| 240 | return ff_raw_write_packet(s, pkt); | ||
| 241 | } else { | ||
| 242 | int ret; | ||
| 243 | |||
| 244 | /* warn only once for each stream */ | ||
| 245 | if (s->streams[pkt->stream_index]->nb_frames == 1) { | ||
| 246 | av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," | ||
| 247 | " ignoring.\n", pkt->stream_index); | ||
| 248 | } | ||
| 249 | if (!mp3->pics_to_write || s->streams[pkt->stream_index]->nb_frames >= 1) | ||
| 250 | return 0; | ||
| 251 | |||
| 252 | if ((ret = ff_id3v2_write_apic(s, &mp3->id3, pkt)) < 0) | ||
| 253 | return ret; | ||
| 254 | mp3->pics_to_write--; | ||
| 255 | |||
| 256 | /* flush the buffered audio packets */ | ||
| 257 | if (!mp3->pics_to_write && | ||
| 258 | (ret = mp3_queue_flush(s)) < 0) | ||
| 259 | return ret; | ||
| 260 | } | ||
| 261 | |||
| 262 | return 0; | ||
| 263 | } | ||
| 184 | 264 | ||
| 185 | /** | 265 | /** |
| 186 | * Write an ID3v2 header at beginning of stream | 266 | * Write an ID3v2 header at beginning of stream |
| @@ -189,14 +269,40 @@ static const AVClass mp3_muxer_class = { · 2 unmodified lines | |||
| 189 | static int mp3_write_header(struct AVFormatContext *s) | 269 | static int mp3_write_header(struct AVFormatContext *s) |
| 190 | { | 270 | { |
| 191 | MP3Context *mp3 = s->priv_data; | 271 | MP3Context *mp3 = s->priv_data; |
| 192 | int ret; | 272 | int ret, i; |
| 193 | 273 | ||
| 194 | ret = ff_id3v2_write_simple(s, mp3->id3v2_version, ID3v2_DEFAULT_MAGIC); | 274 | /* check the streams -- we want exactly one audio and arbitrary number of |
| 275 | * video (attached pictures) */ | ||
| 276 | mp3->audio_stream_idx = -1; | ||
| 277 | for (i = 0; i < s->nb_streams; i++) { | ||
| 278 | AVStream *st = s->streams[i]; | ||
| 279 | if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | ||
| 280 | if (mp3->audio_stream_idx >= 0 || st->codec->codec_id != CODEC_ID_MP3) { | ||
| 281 | av_log(s, AV_LOG_ERROR, "Invalid audio stream. Exactly one MP3 " | ||
| 282 | "audio stream is required.\n"); | ||
| 283 | return AVERROR(EINVAL); | ||
| 284 | } | ||
| 285 | mp3->audio_stream_idx = i; | ||
| 286 | } else if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) { | ||
| 287 | av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in MP3.\n"); | ||
| 288 | return AVERROR(EINVAL); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | if (mp3->audio_stream_idx < 0) { | ||
| 292 | av_log(s, AV_LOG_ERROR, "No audio stream present.\n"); | ||
| 293 | return AVERROR(EINVAL); | ||
| 294 | } | ||
| 295 | mp3->pics_to_write = s->nb_streams - 1; | ||
| 296 | |||
| 297 | ff_id3v2_start(&mp3->id3, s->pb, mp3->id3v2_version, ID3v2_DEFAULT_MAGIC); | ||
| 298 | ret = ff_id3v2_write_metadata(s, &mp3->id3); | ||
| 195 | if (ret < 0) | 299 | if (ret < 0) |
| 196 | return ret; | 300 | return ret; |
| 197 | 301 | ||
| 198 | if (s->pb->seekable) | 302 | if (!mp3->pics_to_write) { |
| 303 | ff_id3v2_finish(&mp3->id3, s->pb); | ||
| 199 | mp3_write_xing(s); | 304 | mp3_write_xing(s); |
| 305 | } | ||
| 200 | 306 | ||
| 201 | return 0; | 307 | return 0; |
| 202 | } | 308 | } |
| @@ -208,9 +314,9 @@ AVOutputFormat ff_mp3_muxer = { · 5 unmodified lines | |||
| 208 | .extensions = "mp3", | 314 | .extensions = "mp3", |
| 209 | .priv_data_size = sizeof(MP3Context), | 315 | .priv_data_size = sizeof(MP3Context), |
| 210 | .audio_codec = CODEC_ID_MP3, | 316 | .audio_codec = CODEC_ID_MP3, |
| 211 | .video_codec = CODEC_ID_NONE, | 317 | .video_codec = CODEC_ID_PNG, |
| 212 | .write_header = mp3_write_header, | 318 | .write_header = mp3_write_header, |
| 213 | .write_packet = ff_raw_write_packet, | 319 | .write_packet = mp3_write_packet, |
| 214 | .write_trailer = mp3_write_trailer, | 320 | .write_trailer = mp3_write_trailer, |
| 215 | .flags = AVFMT_NOTIMESTAMPS, | 321 | .flags = AVFMT_NOTIMESTAMPS, |
| 216 | .priv_class = &mp3_muxer_class, | 322 | .priv_class = &mp3_muxer_class, |